Generates a Makefile and optionally also GitHub workflows and a Dockerfile for your Go application:
- Makefile follows established Unix conventions for installing and packaging, and includes targets for vendoring, running tests and checking code quality.
- GitHub Workflows use GitHub Actions to lint, build, and test your code. Additionally, you can enable workflows for checking your codebase for security issues (e.g. CodeQL code scanning), spelling errors, and missing license headers.
The easiest way to get go-makefile-maker
is: go install github.com/sapcc/go-makefile-maker@latest
.
We also support the usual Makefile invocations: make
, make check
, and make install
. The latter understands the conventional environment variables for choosing install locations: DESTDIR
and PREFIX
.
You usually want something like make && sudo make install PREFIX=/usr/local
.
Put a Makefile.maker.yaml
file in your Git repository's root directory, then run the following to generate Makefile and GitHub workflows:
$ go-makefile-maker
go-makefile-maker
also generates a help
target for usage info:
$ make help
In addition to the Makefile
, you should also commit the Makefile.maker.yaml
file so that your users don't need to have go-makefile-maker
installed.
go-makefile-maker
requires a config file (Makefile.maker.yaml
) in the YAML format.
Take a look at go-makefile-maker
's own config file for an example of what a config could like.
The config file has the following sections:
- metadata
- makefile
- binaries
- testPackages
- coverageTest
- dockerfile
- variables
- golang
- golangciLint
- GoReleaser
- spellCheck
- renovate
- verbatim
- githubWorkflow
metadata:
url: https://github.com/foo/bar
metadata
contains information about the project which cannot be guessed consistently:
url
is the repository's remote URL.
makefile:
enabled: false
makefile
contains settings related to the higher level Makefile
generation.
enabled
is an optional setting to disable the Makefile
generation completely.
If not specified, the setting is treated as being set to true to maintain backwards compatibility with older configs.
binaries:
- name: example
fromPackage: ./cmd/example
installTo: bin/
- name: test-helper
fromPackage: ./cmd/test-helper
For each binary specified here, a target will be generated that builds it with go build
and puts it in build/$NAME
.
The fromPackage
is a Go module path relative to the directory containing the Makefile.
If installTo
is set for at least one binary, the install
target is added to the Makefile, and all binaries with installTo
are installed by it.
In this case, example
would be installed as /usr/bin/example
by default, and test-helper
would not be installed.
controllerGen:
crdOutputPath: config/crd/bases
objectHeaderFile: boilerplate.go.txt
rbacRoleName: manager-role crd webhook
Customization options for controller-gen.
crdOutputPath
allows changing the output:crd:artifacts:config
argument given to controller-gen rbac
. Defaults to crd
.
objectHeaderFile
allows changing the headerFile
argument given to controller-gen object
.
rbacRoleName
allows changing the roleName
argument given to controller-gen rbac. Defaults to the last element in the go module name.
testPackages:
only: '/internal'
except: '/test/util|/test/mock'
By default, all packages inside the repository are subject to testing, but this section can be used to restrict this.
The values in only
and except
are regexes for grep -E
.
Since only entire packages (not single source files) can be selected for testing, the regexes have to match package names, not on file names.
coverageTest:
only: '/internal'
except: '/test/util|/test/mock'
When make check
runs go test
, it produces a test coverage report.
By default, all packages inside the repository are subject to coverage testing, but this section can be used to restrict this.
The values in only
and except
are regexes for grep -E
.
Since only entire packages (not single source files) can be selected for coverage testing, the regexes have to match package names, not on file names.
dockerfile:
enabled: true
entrypoint: [ "/bin/bash", "--", "--arg" ]
extraDirectives:
- 'LABEL mylabel=myvalu'
extraIgnores:
- tmp
- files
extraPackages:
- curl
- openssl
runAsRoot: true
withLinkerdAwait: true
When enabled
, go-makefile-maker will generate a Dockerfile
and a .dockerignore
file.
The Dockerfile uses the Golang base image to run make install
, then copies all installed files into a fresh Alpine base image.
The image is provisioned with a dedicated user account (name appuser
, UID 4200, home directory /home/appuser
) and user group (name appgroup
, GID 4200) with stable names and IDs.
This user account is intended for use with all payloads that do not require a root user.
entrypoint
allows overwriting the final entrypoint.extraDirectives
appends additional directives near the end of the Dockerfile.extraIgnores
appends entries in.dockerignore
to the default ones.extraPackages
installs extra Alpine packages in the final Docker layer.ca-certificates
is always installed.runAsRoot
skips the privilege drop in the Dockerfile, i.e. theUSER appuser:appgroup
command is not added.withLinkerdAwait
whether to download the binary and prepend linkerd-await to the entrypoint. For more details see https://github.com/linkerd/linkerd-await.
variables:
GO_BUILDENV: ''
GO_BUILDFLAGS: '-mod vendor'
GO_LDFLAGS: ''
GO_TESTENV: ''
Allows to override the default values of Makefile variables used by the autogenerated recipes.
This mechanism cannot be used to define new variables to use in your own rules; use verbatim
for that.
By default, all accepted variables are empty.
The only exception is that GO_BUILDFLAGS
defaults to -mod vendor
when vendoring is enabled (see below).
A typical usage of GO_LDFLAGS
is to give compile-time values to the Go compiler with the -X
linker flag:
variables:
GO_LDFLAGS: '-X github.com/foo/bar.Version = $(shell git describe --abbrev=7)'
However, for this specific usecase, we suggest that your application use github.com/sapcc/go-api-declarations/bininfo
instead. When the respective module is present as a direct dependency in the go.mod
file, go-makefile-maker will
auto-generate suitable linker flags to fill the global variables in the bininfo
package.
GO_TESTENV
can contain environment variables to pass to go test
:
variables:
GO_TESTENV: 'POSTGRES_HOST=localhost POSTGRES_DATABASE=unittestdb'
GO_BUILDENV
can contain environment variables to pass to go build
:
variables:
GO_BUILDENV: 'CGO_CFLAGS=-D_LARGEFILE64_SOURCE'
golang:
enableVendoring: true
ldflags: -X "main.goversion={{.Env.GOVERSION}}"
setGoModVersion: true
Set golang.enableVendoring
to true
if you vendor all dependencies in your repository. With vendoring enabled:
- The default for
GO_BUILDFLAGS
is set to-mod vendor
, so that build targets default to using vendored dependencies. This means that building binaries does not require a network connection. - The
make tidy-deps
target is replaced by amake vendor
target that runsgo mod tidy && go mod verify
just likemake tidy-deps
, but also runsgo mod vendor
. This target can be used to get the vendor directory up-to-date before commits.
If golang.setGoModVersion
is set to true
then go.mod
will be automatically updated to the latest version.
The ldflags
option can be used to share flags between the Makefile and GoReleaser.
golangciLint:
createConfig: true
errcheckExcludes:
- io/ioutil.ReadFile
- io.Copy(*bytes.Buffer)
- io.Copy(os.Stdout)
- (*net/http.Client).Do
skipDirs:
- easypg/migrate/*
The make check
and make static-check
targets use golangci-lint
to lint your code.
If createConfig
is set to true
then go-makefile-maker
will create a
config file (.golangci.yaml
) for golangci-lint
and keep it up-to-date (in case of new changes).
This config file enables extra linters in addition to the default ones and configures various settings that can improve code quality.
Additionally, if createConfig
is true
, you can specify a list of files skipped entirely by golangci-lint in skipDirs
and a list of functions to be excluded from errcheck
linter in errcheckExcludes
field.
Refer to errcheck
's README for info on the format
for function signatures that errcheck
accepts.
Take a look at go-makefile-maker
's own golangci-lint
config file for an up-to-date example of what the generated config would look like.
goreleaser:
createConfig: true
binaryName: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
format: .tar.gz
nameTemplate: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
If goreleaser.createConfig
is set to true or it is unset but the release GitHub Workflow is enabled, a config file for goreleaser will be generated based on the metadata of the repository.
The format
option can be used to only upload binaries. It corresponds to the upstream archives[].format option. See https://goreleaser.com/customization/archive/ for more details.
The binaryName
option can be used to change the name of the compiled binaries. It corresponds to the upstream builds[].binary option. This is only really useful when format is set to binary. It defaults to name of the first entry in the binaries option.
The nameTemplate
option can be used to change the name of uploaded release artefacts. It corresponds to the upstream archives[].name_template option.
The files
option can be used to add extra files. For backwards compatibility it defaults to [ CHANGELOG.md, LICENSE, README.md ]
.
spellCheck:
ignoreWords:
- example
- exampleTwo
golangci-lint
(if golangciLint.createConfig
is true
) and the spell check GitHub workflow (githubWorkflow.spellCheck
) use misspell
to check for spelling errors.
If spellCheck.ignoreWords
is defined then both golangci-lint
and spell check workflow will give this word list to misspell
so that they can be ignored during its checks.
renovate:
enabled: true
assignees:
- devnull
- urandom
goVersion: 1.18
packageRules:
- matchPackageNames: []
matchUpdateTypes: []
matchDepTypes: []
matchFileNames: []
allowedVersions: ""
autoMerge: false
enabled: false
Generate RenovateBot config to automatically create pull requests weekly on Fridays with dependency updates.
To assign people to the PRs created by renovate, add their GitHub handle to the assignees
list.
Optionally overwrite go version with goVersion
, by default the Go version from go.mod
file will be used.
Additionally, you can also define packageRules
. Note that only the fields mentioned above are accepted when defining a packageRule
. The following package rules are defined by default:
packageRules:
# Group PRs for library dependencies together.
- matchPackageNames: [ "!/^github\\.com\\/sapcc\\/.*/", "/.*/" ]
groupName: "External dependencies"
automerge: false
- matchPackageNames: [ "/^github\\.com\\/sapcc\\/.*/" ]
groupName: "github.com/sapcc"
automerge: true
# This package rule will be added if go.mod file has a `k8s.io/*` dependency.
- matchPackagePrefixes: ["/^k8s.io\\//"]
allowedVersions: 0.28.x
# Restrict updates for versions managed by go-makefile-maker.
- matchPackageNames: [ golang ]
allowedVersions: $goVersion.x # only update within the same minor release
- matchDepTypes: [ action ]
enabled: false # see githubWorkflow config section below
- matchDepTypes: [ dockerfile ]
enabled: false # see docker config section above
verbatim: |
run-example: build/example
./build/example example-config.txt
This field can be used to add your own definitions and rules to the Makefile. The text in this field is copied into the Makefile mostly verbatim, with one exception: Since YAML does not like tabs for indentation, we allow rule recipes to be indented with spaces. This indentation will be replaced with tabs before writing it into the actual Makefile.
The githubWorkflow
section holds configuration options that define the behavior of various GitHub workflows.
Hint: You can prevent the workflows from running by including [ci skip]
in your commit message
(more info).
This section defines global settings that apply to all workflows. If the same setting is supported by a specific workflow and is defined then that will take override its global value.
global:
defaultBranch: dev
goVersion: 1.18
defaultBranch
specifies the Git branch on which push
actions will trigger the
workflows. This does not affect pull requests, they will automatically trigger all
workflows regardless of which branch they are working against. go-makefile-maker
will
automatically run git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'
and use its value by default.
goVersion
specifies the Go version that is used for jobs that require Go.
go-makefile-maker
will automatically retrieve the Go version from go.mod
file and use
that by default.
This workflow:
- checks your code using
golangci-lint
- ensures that your code compiles successfully
- runs tests and generates test coverage report
- uploads the test coverage report to Coveralls
ci:
enabled: true
runOn:
- macos-latest
- ubuntu-latest
- windows-latest
coveralls: true
ignorePaths: []
runOn
specifies a list of machine(s) to run the build
and test
jobs on (more
info). You can use this to ensure that your build compilation and tests are
successful on multiple operating systems. Default value for this is ubuntu-latest
.
If coveralls
is true
then your test coverage report will be uploaded to Coveralls. Make sure that you have enabled Coveralls for your GitHub repo beforehand.
ignorePaths
specifies a list of filename patterns. Workflows will not trigger if a path
name matches a pattern in this list. More info and filter pattern cheat
sheet. This option is not defined by default.
If your application depends on github.com/lib/pq
, the latest PostgreSQL server binaries will be available in the container when tests are executed.
This is intended for use with github.com/sapcc/go-bits/easypg
, which can launch a PostgreSQL server during func TestMain
; see documentation in package easypg for details.
If enabled
is set to true, the generated Dockerfile
is built for the platforms linux/amd64
and linux/arm64
and pushed to the repository path under ghcr.io
.
pushContainerToGhcr:
enabled: true
platforms: "linux/amd64,linux/arm64"
tagStrategy:
- edge
- latest
- semver
platforms
configures for which platforms the multi-arch docker image is built. Defaults to linux/amd64
. Note: emulation is provided by qemu and might take significant time.
tagStrategy
influences which container tags will be pushed. Currently edge
, latest
and semver
are supported.
If release
is enabled a workflow is generated which creates a new GitHub release using goreleaser when a git tag is pushed.
goreleaser.enabled
will be enabled automatically when the option isn't set yet.
If securityChecks
is enabled then it will generate the following workflows:
-
CodeQL workflow will run CodeQL, GitHub's industry-leading semantic code analysis engine, on your source code to find security vulnerabilities. You can see the security report generated by CodeQL under your repo's Security tab.
In addition to running the workflow when new code is pushed, this workflow will also run on a weekly basis (every Monday at 07:00 AM) so that existing code can be checked for new vulnerabilities.
-
dependency-review workflow will scan your pull requests for dependency changes and will raise an error if any new dependencies have existing vulnerabilities. It uses the GitHub Advisory Database as a source.
-
govulncheck workflow will scan your dependencies for vulnerabilities and will raise an error if any dependency has an existing vulnerability and the code path is in use. It uses the Go Vulnerability Database as a source.
securityChecks:
enabled: true
This workflow uses [addlicense
][addlicense] to ensure that all your Go source code files have a license header. If vendoring is enabled, the
vendor/` directory is always entirely ignored by this workflow.
license:
enabled: true
ignorePatterns:
- "vendor/**"
ignorePatterns
specifies a list of file patterns to check. You can use any pattern
supported by doublestar. See addlicense
's README for more info.
Hint: You can also use addlicense
to add license headers to all unignored Go files by running make license-headers
.