One tag-triggered workflow proves the exact source, builds and smokes every archive, publishes the registries, and completes the GitHub release. A local green checkout is only a candidate.
- Confirm every runtime reports the same version with
mise run check:versions. - Review
docs/content/spec.mdand the matching shared conformance cases. - Move user-visible changes from
Unreleasedto the dated version inCHANGELOG.md. - Confirm package, file-format, event, snapshot, and minimum-runtime compatibility.
Benchmarks must identify a clean source commit. Use two commits because measuring necessarily changes the tracked benchmark outputs:
- Commit the source, dependency, tool, specification, and benchmark-harness candidate.
- From that clean commit, run
mise run all, thenmise run benchmark. - Review
benchmarks/latest.ndjson, both SVGs, and the README tables. Commit only those generated observations and documentation updates. - From a clean checkout of the final commit, run
mise run release:verify. - Push
mainand require green CI, Security, and Pages for that exact SHA.
Any source, dependency, tool, specification, or benchmark-harness change after measurement invalidates the benchmark digest and requires a new clean-source run.
- Keep the GitHub
releaseenvironment limited tov*tags. - Configure PyPI trusted publishing for project
fgraph, workflowrelease.yml, and environmentrelease. - Configure npm trusted publishing for package
@fmind-dev/fgraph, repositoryfmind/fgraph, workflowrelease.yml, and environmentrelease. - Require two-factor authentication and disallow token-based publication in the npm package settings.
Both registries exchange GitHub's short-lived OIDC identity for publication credentials. Do not add registry tokens to GitHub, the repository, or the workflow.
Create both annotated tags on the proved commit and push them atomically:
git tag -a v1.2.0 -m "fgraph v1.2.0"
git tag -a go/v1.2.0 -m "fgraph Go module v1.2.0"
candidate="$(git rev-parse HEAD)"
test "$(git rev-list -n 1 v1.2.0)" = "$candidate"
test "$(git rev-list -n 1 go/v1.2.0)" = "$candidate"
git push --atomic origin v1.2.0 go/v1.2.0The root tag starts .github/workflows/release.yml. The workflow:
- reruns the clean release gate and verifies both tags;
- builds deterministic Python, npm, and five native Go archives plus checksums;
- executes every Go archive on its native runner;
- enters the tag-limited
releaseenvironment; - creates a draft GitHub release with the exact assets;
- publishes npm and PyPI with Trusted Publishing, then makes the GitHub release public.
No approval or registry token is required after the two trusted publishers are configured.
Tags and registries are append-only. If a registry job fails after the draft is created, inspect that registry before retrying. When the version is definitely absent, rerun only failed jobs and their dependents with gh run rerun RUN_ID --failed; never rerun all jobs after draft creation because draft creation intentionally fails closed when the release already exists. If registry acceptance is partial or ambiguous, keep the draft, document the state, and fix forward with a patch version. Never republish immutable registry files or move a public tag.
Test outside the checkout:
uvx --from fgraph==1.2.0 fgraph version
go install github.com/fmind/fgraph/go/cmd/fgraph@v1.2.0
npx --yes @fmind-dev/fgraph@1.2.0 --versionDownload the release assets, verify SHA256SUMS, then run gh release verify v1.2.0 and gh release verify-asset v1.2.0 <artifact>. Create one database in each runtime, open it read-only from the other two, run doctor, and confirm the public docs and release links before announcing the release.