Makefile
2024-10-08
This Makefile serves two purposes. The first is to automate common admin tasks (like running tests, checking for vulnerabilities, pushing changes to a remote repository, and deploying to production), and the second is to provide short aliases for Go commands that are long or difficult to remember.
The exact contents of the Makefile changes from project to project. It’s generic enough that you should be able to use it as-is for almost all projects.
1. Gist
# Change these variables as necessary. main_package_path = ./cmd/example binary_name = example # =========================================================================== # # HELPERS # =========================================================================== # ## help: print this help message .PHONY: help help: @echo 'Usage:' @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' .PHONY: confirm confirm: @echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ] .PHONY: no-dirty no-dirty: @test -z "$(shell git status --porcelain)" # =========================================================================== # # QUALITY CONTROL # =========================================================================== # ## audit: run quality control checks .PHONY: audit audit: test go mod tidy -diff go mod verify test -z "$(shell gofmt -l .)" go vet ./... go run honnef.co/go/tools/cmd/staticcheck@latest -checks=all,-ST1000,-U1000 ./... go run golang.org/x/vuln/cmd/govulncheck@latest ./... ## test: run all tests .PHONY: test test: go test -v -race -buildvcs ./... ## test/cover: run all tests and display coverage .PHONY: test/cover test/cover: go test -v -race -buildvcs -coverprofile=/tmp/coverage.out ./... go tool cover -html=/tmp/coverage.out # =========================================================================== # # DEVELOPMENT # =========================================================================== # ## tidy: tidy modfiles and format .go files .PHONY: tidy tidy: go mod tidy -v go fmt ./... ## build: build the application .PHONY: build build: # Include additional build steps, like TypeScript, SCSS or Tailwind compilation here... go build -o=/tmp/bin/${binary_name} ${main_package_path} ## run: run the application .PHONY: run run: build /tmp/bin/${binary_name} ## run/live: run the application with reloading on file changes .PHONY: run/live run/live: go run github.com/cosmtrek/air@v1.43.0 \ --build.cmd "make build" --build.bin "/tmp/bin/${binary_name}" --build.delay "100" \ --build.exclude_dir "" \ --build.include_ext "go, tpl, tmpl, html, css, scss, js, ts, sql, jpeg, jpg, gif, png, bmp, svg, webp, ico" \ --misc.clean_on_exit "true" # =========================================================================== # # OPERATIONS # =========================================================================== # ## push: push changes to the remote Git repository .PHONY: push push: confirm audit no-dirty git push ## production/deploy: deploy the application to production .PHONY: production/deploy production/deploy: confirm audit no-dirty GOOS=linux GOARCH=amd64 go build -ldflags='-s' -o=/tmp/bin/linux_amd64/${binary_name} ${main_package_path} upx -5 /tmp/bin/linux_amd64/${binary_name} # Include additional deployment steps here...
The Makefile is organized into several sections, each with its own set of targets:
1.1. Helpers
-
help
: Prints a help message for the Makefile, including a list of available targets and their descriptions. -
confirm
: Prompts the user to confirm an action with a "y/N" prompt. -
no-dirty
: Checks that there there are no untracked files or uncommitted changes to the tracked files in the current git repository.
1.2. Quality Control
-
audit
: Runs quality control checks on the codebase, including usinggo mod tidy -diff
to check that thego.mod
andgo.sum
files are up-to-date and correctly formatted, verifying the dependencies withgo mod verify
, runningtest -z "$(shell gofmt -l .)"
to check that all.go
files are correctly formatted, running static analysis with govet
andstaticcheck
, checking for vulnerabilities usinggovulncheck
, and running all tests. Note that it usesgo run
to execute the latest versions of the remotestaticcheck
andgovulncheck
packages, meaning that you don’t need to install these tools first. -
test
: Runs all tests. Note that we enable the race detector and embed build info in the test binary. -
test/cover
: Runs all tests and outputs a coverage report in HTML format.
1.3. Development
-
tidy
: Updates the dependencies and formats thego.mod
and go.sum usinggo mod tidy
, and formats all.go
files usinggo fmt
. -
build
: Builds the package atmain_package_path
and outputs a binary at/tmp/bin/{binary_name}
. -
run
: Calls thebuild
target and then runs the binary. Note that my main reason for not usinggo run
here is thatgo run
doesn’t embed build info in the binary. -
run/live
: Use theair
tool to run the application with live reloading enabled. When changes are made to any files with the specified extensions, the application is rebuilt and the binary is re-run. -
Depending on the project you might want to add more to this section, such as targets for connecting to a development database instance and managing SQL migrations.
1.4. Operations
-
push
: Push changes to the remote Git repository. This asks for y/N confirmation first, and automatically runs theaudit
andno-dirty
targets to make sure that all audit checks are passing and there are no uncommitted changes in the repository before the push is executed. -
production/deploy
: Builds the a binary for linux/amd64 architecture, compress it usingupx
, and then run any deployment steps. Note that this target asks for y/N confirmation before anything is executed, and also runs theaudit
andno-dirty
checks too. -
Depending on the project I often add more to this section too. For example, a
staging/deploy
rule for deploying to a staging server,production/connect
for SSHing into a production server,production/log
for viewing production logs,production/db
for connecting to the production database, andproduction/upgrade
for updating and upgrading software on a production server.
2. Usage
Each of these targets can be executed by running make
followed by the target
name in your terminal. For example:
make tidy
go mod tidy -v go fmt ./...
If you run make help
(or the naked make
command without specifiying a
target) then you’ll get a description of the available targets.
make help
Usage: help print this help message tidy tidy modfiles and format .go files audit run quality control checks test run all tests test/cover run all tests and display coverage build build the application run run the application run/live run the application with reloading on file changes push push changes to the remote Git repository production/deploy deploy the application to production