Versioning
Fracta follows Semantic Versioning 2.0. Tags are prefixed withv:
| Tag pattern | Examples | When to use |
|---|---|---|
vMAJOR.MINOR.PATCH | v0.1.0, v1.2.3 | Stable releases |
vMAJOR.MINOR.PATCH-PRERELEASE | v0.2.0-rc1, v1.0.0-beta.2 | Pre-releases |
vMAJOR.MINOR.PATCH-PRERELEASE+METADATA | v0.2.0-rc1+sha.abc123 | Pre-release with build metadata |
latest Docker tag — only stable tags do.
How a release happens
The workflow is defined in.github/workflows/release.yml.
Cutting a release
1. Make sure CI is green
Releases should be cut from a known-good main. Verify the latest CI run passed:completed success.
2. Decide the version
For a stable release, bump from the previous tag according to semver rules:- MAJOR: breaking changes (CLI flag removed, public Go API broken, config format changed incompatibly)
- MINOR: backwards-compatible feature additions
- PATCH: backwards-compatible bug fixes
-rc1 / -beta.1 / etc.
3. Create and push the tag
4. Watch the workflow
- Build job: ~2 minutes per platform (4 platforms run in parallel)
- Package scaffolds: under 10 seconds
- Release job: under 30 seconds (waits on build + scaffolds, then attaches artifacts; doesn’t wait on Docker)
- Docker job: ~25-30 minutes —
node:20-slimbase + apt-get + npm install of three CLIs + uv sync of Python deps, all re-run under emulated arm64. This is the long pole.
Build and push sitting at 15+ minutes, that is expected — see baseline timings in past Release runs (gh run list --workflow=release.yml).
5. Verify the release
After the workflow finishes:Release artifacts
Each release publishes:Platform binaries
Attached to the GitHub Release page:| File | Platform |
|---|---|
fracta-vX.Y.Z-linux-amd64 | Linux x86_64 |
fracta-vX.Y.Z-linux-arm64 | Linux ARM64 (Raspberry Pi, AWS Graviton, etc.) |
fracta-vX.Y.Z-darwin-amd64 | macOS Intel |
fracta-vX.Y.Z-darwin-arm64 | macOS Apple Silicon |
.sha256 checksum file.
Build details (applied uniformly to all four binaries):
CGO_ENABLED=0— static binary, no glibc dependency-trimpath— reproducible builds, no developer paths leaked-ldflags "-s -w -X main.version=vX.Y.Z"— strip debug info, embed version
Docker image
Pushed to GitHub Container Registry (ghcr.io). Multi-arch (linux/amd64 + linux/arm64) — the right architecture is selected automatically when youdocker pull.
Tags published per release:
ghcr.io/darkquasar/fracta:vX.Y.Z— exact versionghcr.io/darkquasar/fracta:X.Y.Z— without thevprefixghcr.io/darkquasar/fracta:X.Y— minor-version alias (auto-bumped)ghcr.io/darkquasar/fracta:X— major-version alias (auto-bumped)ghcr.io/darkquasar/fracta:latest— only for stable releases (no pre-release tag)
v0.2.0-rc1) skip latest so consumers pulling latest always get a stable version.
GitHub Release notes
Auto-generated from commit history since the previous tag. Includes:- List of merged PRs with author attribution
- Categorised changes (GitHub groups by conventional-commit prefixes if present; fracta uses domain prefixes like
cli:,mcpcatalog:,docs:instead — these still appear in the list but won’t be auto-categorised into release-notes sections) - Comparison link to the previous release
gh release edit vX.Y.Z.
What to do if a release fails
Build job failed
Most likely a platform-specific issue (CGO accidentally re-enabled, syscall import that doesn’t exist on darwin, etc.). Check the workflow log for the failing platform:main, delete the broken tag, re-tag:
Release job failed (artifacts didn’t attach)
Usually a permissions issue (contents: write not set) or a transient GitHub API error. The build job already produced the binaries — they’re in the workflow run artifacts. Re-run just the failed job:
Docker job failed
Most common cause: ghcr.io permissions. Confirm the workflow haspackages: write permission and that the repository’s “Manage Actions access” allows GITHUB_TOKEN to write packages.
If the binary release succeeded but Docker didn’t, you can re-trigger just the Docker job:
workflow_dispatch by default. To enable manual re-runs, add workflow_dispatch: under on: in the workflow file.)
Backports and hotfixes
For a critical bug in v0.2.0 that needs a v0.2.1 release while v0.3.0 work continues on main:- Branch from the v0.2.0 tag:
git checkout -b release/v0.2 v0.2.0 - Cherry-pick the fix from main:
git cherry-pick FIX_SHA - Push the branch:
git push origin release/v0.2 - Tag from the branch:
git tag -a v0.2.1 -m "v0.2.1 — fix X" && git push origin v0.2.1
Quick reference card
The whole release flow boiled down to copy-pasteable commands. Useful when you’ve done it before and just need a refresher.Cut a stable release
Cut a pre-release
Same flow, but use a pre-release tag (-rc1, -beta.1, etc.). Pre-releases skip the latest Docker tag.
Inspect / debug
Recover from a botched release
Download a published binary to test
Yanking a release
GitHub Releases can be deleted, but tags persist in the repo’s history and in any clones. To “yank” a release:gh api:
Future enhancements
Things not yet in the release workflow that may be added later:- Homebrew tap — auto-update a Homebrew formula on release.
- Code signing — sign macOS binaries (requires Apple Developer ID + secrets management).
- SBOM generation — produce a Software Bill of Materials per release for supply-chain transparency.
- Reproducible builds verification — a separate job that rebuilds and confirms binary hash equality.

