The 3.0.9 release preparation omitted stable from the initial push instructions and left the local branch on the previous release. Add an agent reminder and a release checklist covering both local and remote stable refs. Document fast-forward preparation, publishing master/stable/the version tag together, and comparing remote branch tips with the peeled tag. Keep stable on release commits rather than later development commits, exclude prereleases, and prohibit moving published tags. This documentation change does not publish or move release refs. Validation: reviewed commands against the repository branch and annotated-tag workflow; git diff --check passed. No runtime code changed. Assisted-by: GPT-6
7.2 KiB
Publish releases with native Nix CI
All CI workflows and their support files live in .ngit/nix/workflows/.
default.nix groups commands by workflow and job; the root flake exports them
as ngit-ci.commands, so YAML selects each command by its job and step names. A v* tag push runs
release.yaml: a pure static-musl archive build and a container layout build,
followed by OCI publication, a pull-and-run verification, and NIP-82 publication.
The publisher copies the exact predecessor archive/layout; it does not rebuild
release products. The container builder preserves the repository Dockerfile
and scripts/build-container-layout.sh. Its base-image pulls retain that
Dockerfile's existing update policy.
Publisher and container jobs begin with explicit tool builds. Their closures
are shared through the provider cache before runtime commands start; credentials
are used only by the publisher step. These commands pin Git, Python, ngit, Cargo
and container tooling. The separate release-ngit input must support both release and container
publication; checks.x86_64-linux.release-cli verifies its required flags.
Checks and publishers share .ngit/nix/workflows/release-ngit.nix: on x86_64 Linux it installs
the SHA-256-pinned static v3.0.3 release archive, avoiding a lengthy LTO rebuild
in every fresh guest. The upstream source package disables its own tests, so
recompiling it added no CLI test coverage. Other platforms retain the pinned
source package. The archive pin trusts the upstream release build; it is not a
claim of locally reproduced binaries. Keep the archive version/hash and source
fallback revision aligned when updating this tooling.
Container build and verification jobs reuse the deployment test's job-owned
Docker daemon. They require a disposable root VM with the kernel facilities
listed in their workflow requirements, and clean up the daemon on exit.
Rust CI, archive builds, container builds and Cargo publication require at least 4 GiB of guest RAM, including container verification. These are provider-selection floors, not memory caps.
Review the provider summary at the top of the final job log when tuning these minimums: observed guest RAM includes the guest OS, and swap activity or OOM kills signal pressure. Compare cold dependency builds with cached runs and leave headroom; a successful run alone does not measure peak memory.
Configure the repository NGIT_PUBLISHER_NBUNKSEC secret. Only the OCI and
NIP-82 publisher jobs request it, and pass its private
$NGIT_CI_SECRETS_DIR/NGIT_PUBLISHER_NBUNKSEC file directly to ngit. No secret
value enters a Nix derivation, output artifact, or command argument. The
existing .ngit/release.yaml and .ngit/containers.yaml retain the publication
metadata, relays and Blossom servers. Archive contents are reproducible with
a fixed tar timestamp; the NIP-82 release date comes from the actual Git tag.
Every release command requires an explicit tag context and fetches that tag from
the Nostr repository. Ordinary tag pushes check the Cargo version and both
the checkout and peeled tag against the signed NGIT_CI_COMMIT_SHA. A moved tag fails before
publication. Branch or pull-request contexts cannot publish.
Stable release checklist
- Finish implementation changes and their changelog entries before the release commit. Run CI on the exact implementation commit and require successful Rust CI and container deployment checks; a successful push is not a CI result.
- Prepare the final version commit: update
Cargo.toml, the root package entry inCargo.lock, andnix/module.nix; the flake derives its version from Cargo. Move Unreleased notes into a dated release section, with Security before Fixed and any operator security guidance under Security. Validate locked Cargo metadata, Nix module parsing, formatting, and the diff. - Prepare an annotated version tag and fast-forward the local
stablebranch to that same commit. Inspect branch/worktree state first; ifstablehas diverged, resolve that explicitly instead of forcing it. If further fixes are needed before publication, rerun CI and keep the release commit last. - When publication is authorized, push all three refs:
master,stable, and the exact version tag. A tag push starts release publication. - Verify the remote branches and peeled tag point to the intended release commit, then check the release and crate publication workflows separately. Do not report publication complete just because Git accepted the refs.
For a new stable release, with a clean tree on master at the reviewed release
commit, replace vX.Y.Z with the version being released:
release_tag=vX.Y.Z
# Check local and remote branch state before preparing the refs.
git fetch origin
git status -sb
git worktree list
git tag -a "$release_tag" -m "ngit-grasp $release_tag"
git switch stable
git merge --ff-only master
git switch master
# All three must resolve to the same commit before publication.
git rev-parse master stable "${release_tag}^{commit}"
# Run only when publication is authorized.
git push origin master stable "refs/tags/$release_tag"
git ls-remote origin refs/heads/master refs/heads/stable \
"refs/tags/$release_tag" "refs/tags/$release_tag^{}"
An annotated tag's direct ref identifies the tag object; compare its peeled
^{} result with the branch commit IDs. If the local tag was already prepared,
verify it instead of recreating it. Never move a published version tag. This is
one push invocation, not a guarantee of atomic updates across Nostr relays and
Git servers: inspect any partial failure and finish only the missing updates.
stable tracks the latest stable release commit, not subsequent development or
documentation commits on master. Prerelease tags do not advance stable.
Manual container backfill
Replay .ngit/nix/workflows/container_backfill.yaml from a commit containing
this workflow with an explicit refs/tags/v<version> context. The current
reviewed command fetches only that historical tag, validates its Cargo version,
and extracts its source into the job's private workspace. It builds and
publishes only the exact OCI version tag, preserving latest and prerelease
aliases, then pulls and verifies the image. It does not publish a crate or
NIP-82 release.
This is the deliberate exception to ordinary release source matching: the signed allocation commit supplies the workflow, tools and publisher while the named historical tag supplies the Dockerfile and build context. A separate predecessor provenance artifact records the version, ref, historical source commit and allocation commit. The publisher refetches the tag and checks that record before copying the layout and publishing. Historical source never supplies the credentialed publisher code. Tags are neither moved nor created.
Crates.io publication remains independent; see publishing the crate. For nonpublishing validation run:
nix build .#checks.x86_64-linux.release-cli .#checks.x86_64-linux.release-scripts
These checks inspect CLI compatibility and exercise release guards, secret handling and artifact fan-in using fake external commands. They do not publish or perform a full Docker release run. Providers must allow enough predecessor artifact bytes for the OCI layout (including extracted archive transport).