Cutting a release
This is the maintainer-facing walkthrough for publishing a new
GopherTrunk version. The release workflows
(.github/workflows/release.yml, .github/workflows/installer.yml)
do almost all of the heavy lifting — this doc covers what humans
need to do before, during, and after.
Pre-release checklist
Run these on a clean checkout of main at the commit you intend to
tag. Anything failing means the tag isn’t ready.
git statusis clean andgit pull --ff-only origin mainis a no-op (you’re at the tip ofmain).gofmt -l .prints nothing. CI gates this since v0.1.5; if it fails, rungofmt -w .and commit before tagging.make lint(=go vet ./...) clean.go test -race -count=1 ./...green. Slow but matches CI.make integrationgreen (the dispatch table of per-protocol control-channel integration tests; seeMakefile).make release-dry-run VERSION=vX.Y.Zproduces a Linux binary underdist/dry-run/gophertrunkandgophertrunk versionprints the expected version / commit / build time.make version-refs-checkpasses (the same check CI runs as theversion-refsjob): CHANGELOG.md / README / docs fallback all still name the newest stable tag, so you are starting from a consistent state.CHANGELOG.md[Unreleased]section covers every merged PR since the previous tag. Cross-check with:sh git log <prev-tag>..main --onelineAny PR mentioned in that log but not in the CHANGELOG needs a bullet before tagging.- No open issues blocking release on reference hardware (NESDR Smart v5, RTL-SDR Blog v3 / v4) — see hardware.md. If something’s broken on the reference hardware, document it in the release notes or hold the release.
Tagging
- Promote
[Unreleased]and bump every hand-maintained version reference in one go:make release-prep VERSION=vX.Y.ZThis runs
scripts/release-refs.py bump, which renames## [Unreleased]to## [vX.Y.Z] — YYYY-MM-DD(inserting a fresh empty## [Unreleased]above it, or a placeholder heading if nothing was recorded), and pointsREADME.md’s Quick StartVERSION=line and the docs site’s fallback tag (docs/_includes/latest-version.html) at the new version. It is idempotent and verifies itself. Commit and push:git add CHANGELOG.md README.md docs/_includes/latest-version.html git commit -m "release: vX.Y.Z" git push origin mainIf you skip this step, the release workflow’s
version-refsjob does the same bump after publishing and opens arelease/refs-vX.Y.ZPR for it — merge that promptly, because theversion-refsCI job is red onmainuntil the references catch up with the tag. If the repository does not allow GitHub Actions to create pull requests, the job still pushes the branch and leaves a warning with a compare link in the run summary; open the PR from that link (the release run stays green). - Create and push the annotated tag. Annotated (not
lightweight) so
git describeproduces a useful version string inside the binary’s ldflags:git tag -a vX.Y.Z -m "vX.Y.Z" git push origin vX.Y.Z - Watch the release workflow. The push triggers
.github/workflows/release.yml, which:- builds
gophertrunk.exe(amd64 + arm64) and the Windows installer (Zadig-bundled, Inno Setup), - builds Linux + macOS tarballs for amd64 and arm64,
- builds all three web consoles (standard + Signal Lab + Config
Builder) and stages them under
gophertrunk-web/alongside every artifact, - computes
SHA256SUMSover every file, - attaches everything to a new GitHub Release whose body is
the fixed per-platform install blurb from
release.ymlplus GitHub’s auto-generated notes (generate_release_notes: true, i.e. the merged-PR list since the previous tag). The release body does not readCHANGELOG.md, which is why step 1 above has to happen by hand.
If any job fails, the release won’t publish. Fix and re-tag with a fresh patch number — do not delete-and-recreate a tag that ever went public, since GitHub’s tag deletion is not retroactive for clones.
- builds
Post-release
- Merge the
release/refs-vX.Y.ZPR if the workflow opened one (i.e. you tagged without runningmake release-prep). The README Quick StartVERSION=snippet and the docs-site fallback indocs/_includes/latest-version.htmlare covered by that step;downloads.mdis fully ``-templated and needs nothing. - Sanity-check the published
gophertrunk-<ver>-windows-amd64-setup.exeon a real Windows 11 machine — install, run Zadig from the Start Menu, rungophertrunk sdr list, uninstall and confirm the cleanup prompt. The Windows installer has the most moving parts (Inno Setup script + Zadig bundle + uninstaller cleanup), so smoke-testing it post-release catches regressions the headless CI can’t. - Announce in whatever channels the project uses.
Patch releases
If a fix needs to ship without all of main’s unreleased feature
work:
- Branch off the previous tag:
git checkout -b release/vX.Y vX.Y.0. - Cherry-pick the fix commits onto the release branch.
- Bump the patch component in
CHANGELOG.mdand tag from the release branch (git tag -a vX.Y.Z release/vX.Y). - Push the branch + tag.
Don’t tag patch releases off main if main contains unreleased
feature work — the patch would silently ship every unreleased
feature too.
Rolling back
If a published release has to be pulled:
- Don’t delete the tag or the GitHub Release. Edit the release description to mark it superseded and point to the replacement version.
- Tag a fresh patch release with the fix.
- Update
README.mdanddocs/downloads.mdto the new patch.