7.0 KiB
Releasing Narratio
This is the maintainer runbook for the Release Policy. Its explicit exceptions preserve the successful tag push as the automated responsibility boundary. Binary publication is asynchronous; later consumer-facing inspection is optional.
Prerequisites and publication scope
Use a checkout on main with Git authentication permitting fetches and pushes
to origin, the authoritative Git remote. Install Git, a POSIX shell, standard
Unix build tools, and the Go toolchain below. Make required modules available
before offline validation. No model/provider credentials are needed. Gitea
publication credentials belong to CI's secret store, not the local command.
Synchronous guards inspect local tags and origin, not every configured remote
or Gitea release entry. Do not select a version known to have been published in
any channel, even if these guards cannot see it.
Go prerequisite
Release preparation requires the maintained Go 1.26 baseline. Tag CI currently
uses the explicitly pinned golang:1.26.7 image for validation and asset builds.
That asynchronous work does not change the tag-push completion boundary.
Choose a version and write its note
Select an unused stable version under the versioning rules. Narratio is past v1.0.0; intentional CLI/configuration breaks require a major release.
Create docs/releases/vMAJOR.MINOR.PATCH.md using the
required note structure.
Describe CLI, configuration, artifact, integration, and operating-contract
changes, and the required operator action. Link to current canonical
documentation for exact behavior.
Prepare and record the candidate
Before committing, perform these manual reviews in addition to automated checks:
- Confirm compatibility/version selection and update affected canonical documentation and maintained examples.
- Remove completed roadmap documents covering work included in this release; retain pending work. Their history remains in Git. Do not delete historical release notes.
- Review the diff, staged changes, and untracked files for credentials, private data, generated output, unintended files, and unrelated changes.
- Review changed documentation links and behavior-bearing commands. Automated link checks supplement rather than replace review.
Commit the note and candidate changes, then push that commit to main through
the ordinary development workflow. Record the exact candidate:
git rev-parse HEAD
Do not create a tag until origin/main points to that same commit. If the
candidate changes, repeat the reviews and validation.
Validate the candidate
From the repository root, run:
scripts/check-release-candidate.sh vMAJOR.MINOR.PATCH
The script also works when invoked by its absolute path from another directory.
It validates the version and matching note, module hygiene, formatting,
whitespace, uncached tests, race tests, static checks, documentation, examples,
and three cross-build assets. It uses GOWORK=off, does not contact application
services, CI, or Gitea, and does not create tags or modify tracked source.
Fix failures on main, commit, push normally, and rerun validation.
The macOS cross-build proves compilation, not native runtime behavior.
Declared deliverables and automation
The source release is the exact version tag at the validated commit.
The tag-triggered .woodpecker/release.yml validates the tagged source,
runs scripts/build-release-assets.sh, and publishes these Gitea assets:
narratio-vMAJOR.MINOR.PATCH-linux-amd64narratio-vMAJOR.MINOR.PATCH-linux-arm64narratio-vMAJOR.MINOR.PATCH-darwin-arm64(best effort)SHA256SUMS
The builder embeds the version, disables ambient Go workspaces, and checks a
host binary's version output. The release publisher uses the matching
checked-in note and generates SHA-256 checksums. Existing files are skipped and
overwrite is disabled. No image or package-registry deliverable is declared.
CI does not choose versions, modify source candidates, or move tags.
Publish the tag
From a clean checkout on main whose HEAD equals origin/main, run:
scripts/release.sh vMAJOR.MINOR.PATCH
The command fetches and checks origin/main, re-runs candidate validation,
then fetches and checks again before creating an explicitly unsigned lightweight
tag for the originally recorded commit. It refuses dirty, divergent, changed,
or already-tagged candidates. It verifies the local tag target and pushes only:
refs/tags/vMAJOR.MINOR.PATCH:refs/tags/vMAJOR.MINOR.PATCH
It never commits changes, pushes main, force-pushes, moves tags, or pushes all
tags. Successful publication of that ref completes the command; it prints the
tag and commit and returns without waiting for CI, querying Gitea, downloading
assets, or checking checksums.
Failure handling
Before tag creation, correct the candidate on main and repeat validation.
If a push fails after local tag creation, the local tag is retained for inspection.
Do not retry blindly or assume the remote received nothing: a connection failure
can leave publication uncertain. Inspect the exact remote ref before taking any
local-tag cleanup action. This is recovery from a failed command, not a
post-success publication gate.
After publication, follow the immutability and correction rules. Correct defects or failed asynchronous publication with a new semantically appropriate version, matching note, and the complete procedure. Never replace existing tagged assets in place.
Optional asynchronous inspection
A human may later inspect the tag-triggered Woodpecker job and Gitea release. This is optional follow-up, never part of the automated releaser's completion criteria. Do not wait for or poll CI/Gitea from the release command.
For optional source verification, substitute the released version below and compare the remote target to the recorded candidate hash:
git ls-remote --tags origin refs/tags/vMAJOR.MINOR.PATCH
git rev-parse 'refs/tags/vMAJOR.MINOR.PATCH^{commit}'
Download the three named binaries and SHA256SUMS from the matching Gitea release
into an empty inspection directory. In that directory, verify on Linux:
sha256sum --check SHA256SUMS
On macOS, use shasum -a 256 --check SHA256SUMS. After successful verification,
make the matching host binary executable and run its version command, for example
on Linux amd64 (replace the version in both commands):
chmod +x ./narratio-vMAJOR.MINOR.PATCH-linux-amd64
./narratio-vMAJOR.MINOR.PATCH-linux-amd64 version
Expected output is narratio vMAJOR.MINOR.PATCH. Use only the matching architecture
on other supported hosts. Checksums establish consistency with the downloaded
checksum file, not a new signature or independent authenticity guarantee.
Missing assets are an asynchronous publication problem, not grounds to move or
reuse the successfully published source tag.