Use Makefile to create and push dotnet nuget packages

Viewed 32

Make is the current tool used by our devops to control the flow of our builds but we are only leveraging the simplest of use cases, example all: clean build test with relevant targets to make that work. We are not making any use of file targets.

I am in the process of trying to extend one of our builds to do the above with an addition package step and then for each generated nuget package push it to our nuget repository.

I am trying to avoid using too much bash and I would like to keep it as much as possible to native Make.

I am much more familiar with Fake and Rake and I don't seem to be able to switch my thinking but I feel like I am close. The following is a cut down version of the Makefile to try and keep things as barebones and as simple as possible to show what I am attempting to do.

What you will see is that the phoney targets work as expected, we get our build and tests to run (excluded in the example) followed by the creation of the nuget packages. It is here I have the issue. I want to run a target for each of the generated nuget packages but cannot seem to make that work.

OUT_DIR:=./artifacts
SOLUTION_FILE_PATH:=./src/foo.sln

.PHONY: all
all: clean pack push

.PHONY: clean
clean:
    if [ -d $(OUT_DIR) ]; then rm -Rf $(OUT_DIR); fi

.PHONY: pack
pack:
    # This will generate one or more nupkg files in the output directory
    dotnet pack $(SOLUTION_FILE_PATH) -c Release --output $(OUT_DIR)

# I know this part is not correct but it expresses what I am trying to achieve
.PHONY: push
push: $(OUT_DIR)/%.nupkg
    dotnet push $@

I have tried a number of different permutations to get the pattern matching to work but I am find that most of the examples are out there really don't point me in the right direction. Looking for some help to get me past the final hurdle.

** EDIT **

Good question and thanks @beta. If I was to do this manually on the command line you would expect to see this:

# Creates n number of *.nupkg files in the ./artifacts dir
dotnet pack ./src/foo.sln -c Release --output ./artifacts

# Lets assume it creates 3 nupkg files
dotnet push ./artifacts/foo.package1.nupkg
dotnet push ./artifacts/foo.package2.nupkg
dotnet push ./artifacts/foo.package3.nupkg

** Something that works **

I was able to get something to work but I now want to know if this follows the correct convention


define dotnetPush
    dotnet nuget push $(1)
endef

.PHONY: push
push:
    $(eval PACKAGES:=$(wildcard $(OUT_DIR)/*.*nupkg))

    $(foreach p,$(PACKAGES),$(call dotnetPush,$(p)))

** Resources **

Whilst it didn't help me resolve the issue I have found this page to be immensely helpful.

1 Answers

You should not use eval until you are already a makefile guru. It's needed only for the most advanced makefiles, and is otherwise just confusing. You can just set this variable globally and it's MUCH less confusing.

You also don't need call here: again, this is only needed for more advanced operations. You can just write the command directly, especially in this case.

PUSH = dotnet nuget push

PACKAGES := $(wildcard $(OUT_DIR)/*.*nupkg)

PUSH_PACKAGES := $(addprefix push_,$(PACKAGES))

.PHONY: push
push: $(PUSH_PACKAGES)

push_%:
        $(PUSH) $*

Here we create a pattern rule that will build targets starting with push_ by calling dotnet nuget push, and we create a target push that lists targets starting with push_ for each package to be pushed, as a prerequisite.

ETA

If you don't know the names of the makefiles until after you run the pack command then I recommend this rather than using eval etc.:

PACK = dotnet pack
PUSH = dotnet nuget push

OUT_DIR := ./artifacts
SOLUTION_FILE_PATH := ./src/foo.sln

.PHONY: all
all: push

.PHONY: clean
clean:
        rm -rf $(OUT_DIR)

.PHONY: pack
pack: clean
        $(PACK) $(SOLUTION_FILE_PATH) -c Release --output $(OUT_DIR)

.PHONY: push
push: pack
        for pkg in $(OUT_DIR)/*.*nupkg; do $(PUSH) $$pkg || exit 1; done
Related