Skip to content

Build and Release

Jake Paine edited this page May 23, 2026 · 4 revisions

Build and Release

Local build

make build

Produces ./bin/keyseal. The Makefile injects version metadata via -ldflags:

VERSION  ?= $(shell git describe --tags --abbrev=0 --match 'v*' || echo dev)
COMMIT   ?= $(shell git rev-parse --short HEAD || echo unknown)
DATE     ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ" || echo unknown)

On a clean tagged commit, VERSION resolves to something like v1.0.0. On a development build without a tag, it falls back to dev. The commit hash and build date are embedded unconditionally.

The build cache is written to .cache/go-build/ by default (controlled by the exported GOCACHE variable).

Makefile targets

Target What it does
build Compiles to ./bin/keyseal with ldflags
test Runs go test ./...
fmt Formats all Go files under cmd/ and internal/ with gofmt -w
fmt-check Checks formatting without modifying; fails if any file is unformatted
check Runs fmt-check, then test, then build (in that order)
run go run ./cmd/keyseal - no ldflags, version will be dev
tidy Runs go mod tidy
dist Builds release archives for all four platforms

check is the gate before a release. It must pass cleanly.

Cross-platform dist

make dist

Builds for:

  • linux/amd64
  • linux/arm64
  • darwin/amd64
  • darwin/arm64

Each platform produces a .tar.gz archive containing:

  • The keyseal binary
  • README.md
  • LICENSE

Archives land in dist/ with names like keyseal_v1.0.0_linux_amd64.tar.gz.

A checksum file is generated alongside the archives:

dist/keyseal_v1.0.0_checksums.txt

This uses sha256sum on Linux or shasum -a 256 on macOS. The checksum file covers all .tar.gz archives in dist/.

The dist/ directory is removed and recreated at the start of each dist run, so you always get a clean set of archives.

You can override the version, commit, and date:

make dist VERSION=v1.0.0 COMMIT=abc1234 DATE=2026-04-24T00:00:00Z

Version stamping

Three variables in internal/buildinfo/buildinfo.go are injected at build time:

var (
    Version = "dev"
    Commit  = "unknown"
    Date    = "unknown"
)

These default to "dev", "unknown", and "unknown" for builds that do not set them via ldflags (e.g. go build without make, or go run).

The keyseal version output reflects whatever was stamped at build time. To verify you are running a properly stamped release binary:

keyseal version
# should show tag, commit, and date - not "dev" or "unknown"

CI workflow

.github/workflows/ci.yml runs on every push and pull request:

  1. make fmt-check - formatting gate
  2. make test - full test suite
  3. make build VERSION=ci COMMIT=${GITHUB_SHA} DATE=... - compilation check

This uses Go 1.25 on ubuntu-latest.

Release workflow

.github/workflows/release.yml runs on pushes of tags matching v*:

  1. make check - fmt + test + build
  2. Extract the matching CHANGELOG.md section for the tag (fails the job if not found)
  3. make dist VERSION=${GITHUB_REF_NAME} COMMIT=${GITHUB_SHA} DATE=... - all four platform archives
  4. Publishes the changelog section, archives, and checksums file to a GitHub Release via softprops/action-gh-release

To cut a release:

  1. Add a ## vX.Y.Z section to CHANGELOG.md and commit it.
  2. Tag and push:
git tag v1.0.0
git push origin v1.0.0

The release workflow runs automatically. The resulting GitHub Release body is the ## v1.0.0 section from CHANGELOG.md. The release will have four .tar.gz archives and a checksums file attached.

Release notes

Release notes are extracted directly from CHANGELOG.md. The workflow looks for a heading that exactly matches the tag:

## v1.0.0

Everything between that heading and the next ## heading becomes the release body. The heading itself is not included.

If the tag has no matching section, the release job fails with a clear error pointing at the missing entry. Do not push a tag without a corresponding changelog section.

The heading format must be ## vX.Y.Z — no brackets, no extra text on the heading line.

Development builds

For development work where you just need to test behavior:

make run -- doctor
# equivalent to: go run ./cmd/keyseal doctor
# version will show as "dev"

Or build once and use the binary directly:

make build
./bin/keyseal doctor

The .cache/go-build/ directory speeds up repeated builds. It is excluded from Git via .gitignore.

Clone this wiki locally