Adding a Makefile for Multi-git

See how a Makefile for multi-git can tie all these capabilities together and provide a one-stop shop for managing your program. We will also discuss the relationship between Makefiles and CI/CD pipelines as well as alternatives to Makefiles.

Creating a Makefile for multi-git

Let’s start with the most basic target: build. The comment with the double hashtags will become a nice help message later. Note the tabbed indentation, which is an infamous gotcha of Makefiles. Another important issue is escaping the Dollar sign by doubling it. This is necessary because a single Dollar sign is used by Makefile variables. The command itself is using the -ldflags to inject the git tag and timestamp to the program and writes the executable to the current directory while adding the OS and architecture.

### Build multi-git and inject the git tag and build time to variables in main
build:
	go build -ldflags "-X 'main.gitTag=$$(git describe --tags)' \
                       -X 'main.buildTimestamp=$$(date -u)'" \
                       -o multi-git_$$(go env GOOS)_$$(go env GOARCH)

On my amd64 Mac the final executable is called multi-git_darwin_amd4 (Darwin is the original name of the macOS).

The install target depends on the build target as in install: build. This means that when you run make install, it will first run the build target and then execute the commands of the install target.

### Install multi-git into /usr/local/bin (avoid standard go install)
install: build
	mv multi-git_$$(go env GOOS)_$$(go env GOARCH) \
       /usr/local/bin/multi-git_$$(go env GOOS)_$$(go env GOARCH)
	rm /usr/local/bin/multi-git
	ln -s /usr/local/bin/multi-git_$$(go env GOOS)_$$(go env GOARCH) \
       /usr/local/bin/multi-git

Here are a few test targets. We can use the go test command to run just the unit tests, just the end-to-end tests, or all the tests. In addition, there is a Ginkgo-test target that runs the tests using Ginkgo:

### Run only the unit tests
unit-tests: build
	go test ./pkg/... -v

### Run only the end to end tests
e2e-tests: install
	go test ./e2e_tests/... -v

### Run all the tests
test: unit-tests e2e-tests

### Run all the tests with ginkgo
ginkgo-test: build
	ginkgo -r -v

Here is what it looks like to run the unit tests:

(πŸ™)/multi-git/
$ make unit-tests
?   	github.com/the-gigi/multi-git/pkg/helpers	[no test files]
=== RUN   TestRepoManager
Running Suite: RepoManager Suite
================================
Random Seed: 1594152750
Will run 7 of 7 specs

β€’β€’β€’β€’β€’β€’β€’
Ran 7 of 7 Specs in 0.233 seconds
SUCCESS! -- 7 Passed | 0 Failed | 0 Pending | 0 Skipped
--- PASS: TestRepoManager (0.23s)
PASS
ok  	github.com/the-gigi/multi-git/pkg/repo_manager	(cached)

Note that the unit-tests targets only depend on the build target. However, the e2e-tests target depends on the install target, which depends on the build target because the end-to-end tests expect multi-git to be available on the path, which the install takes care of.

We have a couple of Docker targets, too. The docker-build builds a Docker image and tags it with multi-git:latest. The docker-push target logs in to DockerHub and pushes the image.

### Dockerize multi-git
docker-build:
	docker build --build-arg -t g1g1/multi-git:latest .

### Push multi-git to DockerHub (requires DockerHub account)
docker-push: docker-build
	docker login -u g1g1
	docker push g1g1/multi-git:latest

Since, the same multi-git:latest tag is used by all images the last image always overwrites the previous one. If you want to go back to a previous image, then another tag is needed (e.g. the git tag).

Last, but not least I skipped the first command in the Makefile, which executes if you just type make:

## This Makefile saves some typing and groups some common commands
## # The fancy help is from here:
## https://gist.github.com/rcmachado/af3db315e31383502660#file-makefile
#

.SILENT:
.PHONY: help

### This help screen
help:
	printf "Available targets:\n\n"
	awk '/^[a-zA-Z\-\_0-9]+:/ { \
		helpMessage = match(lastLine, /^## (.*)/); \
		if (helpMessage) { \
			helpCommand = substr($$1, 0, index($$1, ":")-1); \
			helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
			printf "%-15s %s\n", helpCommand, helpMessage; \
		} \
	} \
	{ lastLine = $$0 }' $(MAKEFILE_LIST)

I will not attempt to explain it, as I borrowed it from the above-mentioned gist and just modified it slightly. The end result is a very nice help screen that lists all the targets in the Makefile with the double-hashtag comments as the description.

(πŸ™)/multi-git/
$ make
Available targets:

help            This help screen
build           Build multi-git and inject the git tag and build time to variables in main
install         Install multi-git into /usr/local/bin (avoid standard go install)
unit-tests      Run only the unit tests
e2e-tests       Run only the end to end tests
test            Run all the tests
ginkgo-test     Run all the tests with ginkgo
docker-build    Dockerize multi-git
docker-push     Push multi-git to DockerHub (requires DockerHub account)

Get hands-on with 1400+ tech skills courses.