Align release publication policy and runbook
This commit is contained in:
217
docs/release.md
217
docs/release.md
@@ -1,167 +1,114 @@
|
||||
# Releasing Narratio
|
||||
|
||||
This is the maintainer runbook for the [Release Policy](policy/release.md).
|
||||
Its [explicit exceptions](policy/release.md#policy-exceptions) preserve the
|
||||
successful tag push as the automated responsibility boundary. Binary publication
|
||||
is asynchronous; later consumer-facing inspection is optional.
|
||||
The source release completes when the intended tag is successfully pushed to
|
||||
`origin`; binary delivery is a separate asynchronous status.
|
||||
|
||||
## Prerequisites and publication scope
|
||||
## Prepare the candidate
|
||||
|
||||
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.
|
||||
Use `main` and the authoritative Git remote `origin`. Install Git, a POSIX
|
||||
shell, standard Unix build tools, and Go 1.26. Have the selected Go modules
|
||||
available before offline validation. Git access must permit fetch and tag push.
|
||||
Application-provider credentials are not required; Gitea delivery credentials
|
||||
belong to CI.
|
||||
|
||||
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.
|
||||
Choose an unused stable `vMAJOR.MINOR.PATCH` under the
|
||||
[compatibility rules](policy/release.md#versioning-and-compatibility). Narratio
|
||||
is past v1.0.0, so intentional CLI or configuration breaks require a major
|
||||
release. Update affected canonical documentation with the change. Write the
|
||||
matching `docs/releases/vMAJOR.MINOR.PATCH.md` with a short summary and any
|
||||
relevant compatibility impact or upgrade action; choose sections to suit the
|
||||
release.
|
||||
|
||||
## Go prerequisite
|
||||
End that checked-in note with these three links, replacing the version in
|
||||
each URL with the intended tag. The tag need not exist remotely yet. Check that
|
||||
`COPYRIGHT`, `LICENSE`, and `docs/oss/attribution.md` are present in the
|
||||
candidate. The tag CI reuses the checked-in note as the hosted release body.
|
||||
|
||||
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.
|
||||
```markdown
|
||||
[COPYRIGHT](https://gitea.maximumdirect.net/eric/narratio/src/tag/vMAJOR.MINOR.PATCH/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/narratio/src/tag/vMAJOR.MINOR.PATCH/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/narratio/src/tag/vMAJOR.MINOR.PATCH/docs/oss/attribution.md)
|
||||
```
|
||||
|
||||
## Choose a version and write its note
|
||||
|
||||
Select an unused stable version under the
|
||||
[versioning rules](policy/release.md#versioning-and-compatibility). 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](policy/release.md#release-documentation).
|
||||
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:
|
||||
Retire completed roadmaps included in the release while preserving planned or
|
||||
deferred work and repairing affected links. Review the change through the
|
||||
ordinary development workflow for unintended files, credentials, private data,
|
||||
and documentation accuracy. Commit and push the candidate to `origin/main`
|
||||
before tagging; record its exact commit if needed:
|
||||
|
||||
```sh
|
||||
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.
|
||||
## Publish
|
||||
|
||||
## Validate the candidate
|
||||
|
||||
From the repository root, run:
|
||||
From a clean `main` checkout at the pushed candidate, run one command:
|
||||
|
||||
```sh
|
||||
scripts/check-release-candidate.sh vMAJOR.MINOR.PATCH
|
||||
scripts/release.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.
|
||||
It fetches `origin/main` and tags; requires local `HEAD` to equal
|
||||
`origin/main`; checks the committed note and unused version; runs the
|
||||
candidate checker **once**; then repeats cheap mutable-state guards before
|
||||
creating an unsigned lightweight tag at the recorded commit. It verifies the
|
||||
tag target and pushes only
|
||||
`refs/tags/vMAJOR.MINOR.PATCH:refs/tags/vMAJOR.MINOR.PATCH`. It does not
|
||||
commit, push `main`, force updates, or publish all tags.
|
||||
|
||||
The macOS cross-build proves compilation, not native runtime behavior.
|
||||
The checker runs `go mod tidy -diff`, tracked Go formatting and diff whitespace
|
||||
checks, one `go test -mod=readonly -race -count=1 ./...` full suite, and
|
||||
`go vet -mod=readonly ./...`. Its asset builder cross-builds Linux amd64,
|
||||
Linux arm64, and best-effort macOS arm64, then checks the embedded version
|
||||
on a host binary. The macOS cross-build proves compilation, not native
|
||||
runtime behavior. Owned Go commands run with `GOWORK=off`; tests, vet, and
|
||||
builds use readonly module mode. The deterministic suite includes documentation
|
||||
and maintained-example checks. The command does not contact application
|
||||
services or Gitea and does not wait for CI.
|
||||
|
||||
## Declared deliverables and automation
|
||||
A successful tag push is the source-release completion point. The command
|
||||
prints the tag and commit and exits without a separate network read-back.
|
||||
For optional diagnosis before publication, run
|
||||
`scripts/check-release-candidate.sh vMAJOR.MINOR.PATCH`; publication will
|
||||
validate the candidate again, so this is not a required preparation step.
|
||||
|
||||
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:
|
||||
## Artifact delivery
|
||||
|
||||
Tag-triggered `.woodpecker/release.yml` independently validates the tagged
|
||||
source, builds the same three targets, and publishes these Gitea downloads:
|
||||
|
||||
- `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.
|
||||
The builder embeds the tag version and checks host `version` output. CI
|
||||
generates checksums and uses non-overwriting publication settings. It does
|
||||
not select versions, change the source, or move tags. No image or package
|
||||
registry output is declared. Tag publication is not proof that these
|
||||
asynchronous downloads are available.
|
||||
|
||||
## Publish the tag
|
||||
## Failure and recovery
|
||||
|
||||
From a clean checkout on `main` whose `HEAD` equals `origin/main`, run:
|
||||
If validation fails before tagging, correct and commit the candidate, push
|
||||
it to `main`, and rerun the publication command. If the tag push reports
|
||||
failure, its outcome may be ambiguous: keep the local tag and inspect the
|
||||
exact `refs/tags/vMAJOR.MINOR.PATCH` at `origin` before declaring success
|
||||
or retrying. A reachable matching remote tag at the recorded commit establishes
|
||||
publication; a confirmed absent tag permits investigation of the transport
|
||||
failure. If `origin` cannot be inspected, publication status is unknown.
|
||||
Do not move, overwrite, or automatically retry the tag. Remove a local tag
|
||||
only after confirming it was never published or used by a release channel.
|
||||
|
||||
```sh
|
||||
scripts/release.sh vMAJOR.MINOR.PATCH
|
||||
```
|
||||
A transient CI or delivery failure can be retried from the same immutable
|
||||
tag. Missing assets may be added later if they match the tagged source and
|
||||
any published checksum manifest; do not overwrite existing binaries,
|
||||
checksums, or release-note contents. A source/content defect, or delivery
|
||||
that cannot be corrected without replacement, requires a new suitable
|
||||
version and matching note. Leave the original release intact.
|
||||
|
||||
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:
|
||||
|
||||
```text
|
||||
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](policy/release.md#immutability-and-correction).
|
||||
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:
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
```sh
|
||||
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):
|
||||
|
||||
```sh
|
||||
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.
|
||||
Optional consumer inspection may compare the tag's remote commit with the
|
||||
recorded candidate, check the Woodpecker delivery job and hosted Gitea note,
|
||||
download the assets into an empty directory, run `sha256sum --check
|
||||
SHA256SUMS` (or `shasum -a 256 --check SHA256SUMS` on macOS), and run
|
||||
the matching host binary's `version` command. This is separate delivery
|
||||
observation, never a source-release gate.
|
||||
|
||||
Reference in New Issue
Block a user