ci: cut releases by bumping version.json - #16
Conversation
Add a GoReleaser-based release workflow that fires on `v*` tag pushes and publishes a GitHub Release with binaries for Linux, macOS and Windows on both amd64 and arm64. The dependency graph is free of cgo, so all six targets cross-compile from a single ubuntu-latest runner with CGO_ENABLED=0. Also add version reporting to the CLI: the release build stamps the version via -ldflags, with a runtime/debug.ReadBuildInfo() fallback so `go install` builds identify themselves too. Cobra exposes this as `ucantool --version`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j6gFX7vv3eYbmMn8NTQch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j6gFX7vv3eYbmMn8NTQch
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ee42c9e4d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Replace the tag-triggered release workflow with the version.json-driven process used by fil-forge/piri, so the tag is created by the automation rather than pushed by hand. Releases are now cut by bumping version.json in a PR: Release Checker validates the semver and pre-creates a draft release, Releaser publishes it on merge (creating the tag), and Binaries Releaser attaches the GoReleaser artifacts. Tag Push Checker nags on manually pushed tags. Because Releaser publishes with GITHUB_TOKEN, the new tag and release emit no workflow-triggering events, so Binaries Releaser listens on workflow_run from Releaser instead of on the tag. Version reporting moves to pkg/build, mirroring piri: a version subcommand reports version, commit, build date and builder, with the short VCS revision appended and version.json as the development fallback. Targets are now linux and darwin on amd64 and arm64, matching piri and the existing go-test-config.json which already skips Windows. Three hardenings over piri's workflow, all in release-binaries.yml: only run when Releaser succeeded, a concurrency group so a PAT-published release cannot race, and an explicit checkout of the tagged commit so a later push to main cannot be built by mistake. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j6gFX7vv3eYbmMn8NTQch
sha256sum is not part of a stock macOS install, so the verification instruction only worked on Linux. Document shasum alongside it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j6gFX7vv3eYbmMn8NTQch
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b29a589aab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| version, err = readVersionFromFile() | ||
| if err != nil { | ||
| // Use the default version | ||
| version = defaultVersion |
There was a problem hiding this comment.
Derive source-install versions from build metadata
When users follow the documented go install github.com/fil-forge/ucantool@latest path, no ldflag sets version, and the installed binary reads version.json from its runtime working directory rather than from its build source. Running it normally therefore reports v0.0.0-unknown (or even adopts an unrelated project's version.json) instead of the installed module version; use the version embedded in debug.ReadBuildInfo or another build-time value rather than a runtime-relative file.
Useful? React with 👍 / 👎.
| } | ||
| } | ||
|
|
||
| Version = fmt.Sprintf("%s-%s", version, revision.Revision) |
There was a problem hiding this comment.
Preserve the tagged version in release builds
When GoReleaser supplies a tagged version such as v0.1.0 through the configured ldflag, this unconditional suffix makes the official stable binary report v0.1.0-<revision>. Under SemVer that identifies a prerelease rather than the published v0.1.0 release, so version checks and upgrade logic can misclassify every release artifact; append the revision only for development/fallback builds, while the separate Commit field can retain release provenance.
Useful? React with 👍 / 👎.
| then extract the `ucantool` binary onto your `PATH`. Linux archives are named | ||
| `ucantool_<version>_linux_<arch>.tar.gz` and macOS archives | ||
| `ucantool_<version>_mac_os_<arch>.zip`, for both `amd64` and `arm64`. Each release | ||
| also publishes a `ucantool_<version>_checksums.txt` with SHA-256 checksums: |
There was a problem hiding this comment.
Match the documented checksum filename to GoReleaser
The GoReleaser configuration does not define checksum.name_template, so GoReleaser v2 emits its default checksums.txt, not the documented ucantool_<version>_checksums.txt. Consequently both verification commands point users at a file that is absent from the release; either configure that name template or document and pass the actual checksums.txt asset.
Useful? React with 👍 / 👎.
| concurrency: | ||
| # A release published with a PAT can fire both `release` and `workflow_run`. | ||
| # Queue rather than cancel, so an in-flight asset upload is never interrupted. | ||
| group: ${{ github.workflow }}-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref }} |
There was a problem hiding this comment.
Use one concurrency key for both release triggers
When Releaser publishes with a PAT and consequently emits both events described above, the release run uses a key ending in refs/tags/vX.Y.Z, while the workflow_run run uses one ending in the raw commit SHA. These are different concurrency groups, so the two GoReleaser jobs run simultaneously and race while uploading the same assets despite cancel-in-progress: false; derive both keys from the same tag or commit identity.
Useful? React with 👍 / 👎.
Installing
ucantoolrequires a Go toolchain, and the binary it produces reports no version, so there is no way to tell which build is sitting on a machine.Releases are cut by bumping
versioninversion.json. Open a pull request with that one change:vX.Y.Ztag, at the merge commit.amd64andarm64and attaches them, with a SHA-256 checksums file, to the release.A version with a pre-release suffix such as
v0.1.0-rc1is published as a pre-release.Nobody pushes a
v*tag by hand. If someone does, Tag Push Checker opens an issue asking them to reconcileversion.json.Release Checker, Releaser and Tag Push Checker are reusable workflows from ipdxco/unified-github-workflows, the release process fil-forge/piri already runs. Binaries Releaser is local and adds three things over piri's:
maincannot be built by mistake.Every action and reusable workflow is pinned to a commit SHA.
ucantool versionreports the version, commit, build date and builder.ucantool --versionprints the version alone. Release builds take the version from ldflags. Other builds fall back toversion.jsonand append the short VCS revision the Go toolchain embeds, so a development binary identifies itself too.To see what the binaries would look like without publishing anything:
The README covers downloading a binary, verifying the checksum, and cutting a release.