Files
narratio/docs/release.md

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-amd64
  • narratio-vMAJOR.MINOR.PATCH-linux-arm64
  • narratio-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.