Collection of opinionated reusable Github Actions workflows for Go.
The workflows are optimized for execution speed and many are designed with supply chain security in mind. They use cache-go to cache Go modules and build artifacts separately. I use them both in both projects that are primarily CLI tools, and libraries used as a Go module.
The Go version used in the workflows is the Go version in go.mod. I find it helpful for CLI tool projects as
updating the Go version in go.mod means the same version of Go will be used locally and in CI. Likewise for
libraries linting and testing against the version in go.mod is useful, it will let you know if you are using
an outdated version of Go, and you're not releasing binaries so you don't need to use the latest version of Go.
These workflows are used in my projects and may be useful to others, though I'm aware that they're likely not customizable enough/too opinionated for many projects. If you'd like to use them but need more customization, feel free to open an issue or a PR that adds workflow inputs.
Run the latest versions of golangci-lint and staticcheck, and checks that go.mod is tidy.
Example usage:
name: Lint Go
on:
push:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/lint-go.yml"
pull_request:
branches:
- "*"
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/lint-go.yml"
workflow_dispatch: {}
jobs:
lint-go:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/lint-go.yml@masterRuns go test with the race detector, and optionally runs fuzz tests as well.
| Name | Description | Required | Default |
|---|---|---|---|
run-fuzz-tests |
Run fuzz tests recursively | false | true |
fuzz-with-race |
Run fuzz tests with race detector | false | false |
Example usage:
name: Test
on:
push:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/test.yml"
pull_request:
branches:
- "*"
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/test.yml"
workflow_dispatch: {}
jobs:
test:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/test.yml@masterChecks that running go generate does not change any files.
Example usage:
name: Check generated files
on:
push:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/check-generated.yml"
pull_request:
branches:
- "*"
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/check-generated.yml"
workflow_dispatch: {}
jobs:
check-generated:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/check-generated.yml@masterScans Go dependencies for known security vulnerabilities using govulncheck.
Example usage:
name: Vulnerability scan
on:
push:
branches:
- master
paths:
- "go.mod"
- "go.sum"
- "**.go"
- ".github/workflows/vuln.yml"
pull_request:
branches:
- "*"
paths:
- "go.mod"
- "go.sum"
- "**.go"
- ".github/workflows/vuln.yml"
schedule:
- cron: "0 0 * * *"
workflow_dispatch: {}
jobs:
vuln:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/vuln.yml@masterBuilds and releases Go binaries for multiple platforms using GoReleaser when a tag is pushed. Also signs the binaries with cosign.
Example usage:
name: Release binaries
on:
push:
tags:
- "v*.*.*"
jobs:
release-binaries:
permissions:
id-token: write
contents: write
uses: capnspacehook/go-workflows/.github/workflows/release-binaries.yml@masterChecks if binaries built by GoReleaser are reproducible using gorepro.
gorepro is a tool that extracts build information embedded in Go binaries since Go 1.18 and uses that to reproduce the binary.
There are currently some Go build flags that are currently not embedded (namely -ldflags) so gorepro needs to be informed of them by passing them as inputs.
| Name | Description | Required | Default |
|---|---|---|---|
extra-build-flags |
Build flags that aren't detected by gorepro | false | '' |
Example usage:
name: Reproduce binary
on:
push:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".goreleaser.yml"
- ".github/workflows/reproduce-binary.yml"
pull_request:
branches:
- "*"
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".goreleaser.yml"
- ".github/workflows/reproduce-binary.yml"
workflow_dispatch: {}
jobs:
reproduce-binary:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/reproduce-binary.yml@masterCreates a pull request to update the Go version in go.mod to the latest stable version.
Example usage:
name: Update Go version
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch: {}
jobs:
update-go-version:
permissions:
contents: write
pull-requests: write
uses: capnspacehook/go-workflows/.github/workflows/update-go-version.yml@masterAdds a comment to pull requests with info on how much the changes will affect binary sizes using go-size-tracker.
| Name | Description | Required | Default |
|---|---|---|---|
working-directory |
Relative path to the working directory that should be used to build your binary. | false | '.' |
build-environment |
Environment variables to set when building your binary. Must be separated by newlines. | false | '' |
build-arguments |
Arguments to 'go build' that will build your binary. The binary must be written to a file named 'out' in the working directory. | false | '-buildvcs=false -ldflags="-s -w" -trimpath -o out' |
Example usage:
name: Size report
on:
push:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/size-report.yml"
pull_request:
branches:
- "*"
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/size-report.yml"
workflow_dispatch: {}
jobs:
size-report:
permissions:
contents: write # so go-size-tracker can use git notes to cache size records
pull-requests: write # so go-size-tracker can comment on PRs
uses: capnspacehook/go-workflows/.github/workflows/size-report.yml@masterLints Dockerfiles using hadolint and verifies that Docker images build successfully.
Example usage:
name: Lint Dockerfiles
on:
push:
branches:
- master
paths:
- "**Dockerfile*"
- ".github/workflows/lint-docker.yml"
pull_request:
branches:
- "*"
paths:
- "**Dockerfile*"
- ".github/workflows/lint-docker.yml"
workflow_dispatch: {}
jobs:
lint-dockerfiles:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/lint-docker.yml@masterBuilds and pushes Docker images to GitHub Container Registry (GHCR), with semantic versioning for tags and branch/sha tags for other refs. Also signs the images with cosign.
Example usage:
name: Release image
on:
push:
branches:
- master
tags:
- "v*.*.*"
jobs:
release-image:
permissions:
contents: read
id-token: write
packages: write
uses: capnspacehook/go-workflows/.github/workflows/release-image.yml@masterLints GitHub Actions workflow files using actionlint.
Example usage:
name: Lint workflows
on:
push:
branches:
- master
paths:
- ".github/workflows/*"
pull_request:
branches:
- "*"
paths:
- ".github/workflows/*"
workflow_dispatch: {}
jobs:
lint-workflows:
permissions:
contents: read
uses: capnspacehook/go-workflows/.github/workflows/lint-actions.yml@masterRuns Github CodeQL analysis on Go code to detect security vulnerabilities and code quality issues. Skips execution for private repositories.
Example usage:
name: Run CodeQL
on:
push:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/codeql.yml"
pull_request:
branches:
- master
paths:
- "**.go"
- "go.mod"
- "go.sum"
- ".github/workflows/codeql.yml"
schedule:
- cron: "0 0 * * *"
workflow_dispatch: {}
jobs:
codeql:
permissions:
actions: write
contents: read
security-events: write
uses: capnspacehook/go-workflows/.github/workflows/codeql.yml@masterGo binaries and Docker images are both signed with cosign. Their signatures can can be verified and the binaries can be reproduced to verify that they were built from unmodified source code, by Github Actions.
Simply check the signature of the image with cosign:
cosign verify ghcr.io/<user>/<repo>:<version> | jqYou can verify the image was built by Github Actions by inspecting the Issuer and Subject fields of the output.
Download the checksums file, certificate, signature and the archive from a Github release to the same directory.
Extract the binary from the archive, verify the checksums file and verify the contents of the binary:
tar xfs whalewall_<version>_linux_amd64.tar.gz
cosign verify-blob --certificate checksums.txt.crt --signature checksums.txt.sig checksums.txt
sha256sum -c checksums.txtYou can also reproduce the released binaries to verify that they were built from unmodified source code. Verifying binaries requires gorepro.
First, download the release archive and extract it. Clone the source repro and cd into it.
Install gorepro and run it on the extracted release binary. gorepro will tell you if reproducing the binary was successful. Don't worry about checking out the correct tag or commit, gorepro will handle that for you.
If you don't trust gorepro you can run it again additionally passing the -d flag. This will print the commands gorepro generated to reproduce the release binary. You can run the printed commands and verify for yourself that the reproduced binary is bit for bit identical to the released one.
This example reproduces whalewall v0.2.3:
# attempt to reproduce whalewall
wget https://github.com/capnspacehook/whalewall/releases/download/v0.2.3/whalewall_0.2.3_linux_amd64.tar.gz
tar fxs whalewall_v0.2.3_linux_amd64.tar.gz
git clone https://github.com/capnspacehook/whalewall whalewall-src
cd whalewall-src
# reproduce binary
go install github.com/capnspacehook/gorepro@latest
gorepro -b="-ldflags=-s -w -X main.version v0.2.3" ../whalewall
# reproduce by manually running commands from gorepro
BUILD_CMD="$(gorepro -d -b='-ldflags=-s -w -X main.version v0.2.3' ../whalewall)"
echo "$BUILD_CMD"
"$BUILD_CMD"
sha256sum whalewall whalewall.repro