Files
narratio/docs/roadmap/release-procedure.md

304 lines
13 KiB
Markdown

# Release Procedure Upgrade
## Status
Accepted target state. This document owns the intended release-process scope,
maintainer policy, and target end state until the work is implemented.
## Goal
Give Narratio one reproducible, guarded minor/patch release procedure that
validates a source candidate before publication, creates the existing binary
release inputs consistently, and makes the exact upstream tag push the clear
completion boundary.
The procedure should borrow WeatherReporter's binary-publication model and
Notarius's centralized source-candidate validation and immutable-tag guards,
while retaining Narratio's own repository-wide checks and six supported build
targets.
## Responsibility Boundary
A Narratio release is complete when the selected tag has been successfully
pushed to its exact tag ref in the upstream Git repository. The release command
must then return successfully without polling Woodpecker, waiting for a
pipeline, querying a Gitea release, downloading assets, or otherwise making its
result depend on CI availability.
The tag event may asynchronously cause Woodpecker to validate the tagged tree,
build binaries and checksums, and create a Gitea release. That work is outside
the synchronous release procedure. CI failure or delay does not change whether
the Git tag was successfully published, and release tooling must never wait
indefinitely for infrastructure outside the upstream Git push.
An operator may inspect CI or published assets later. Such inspection is an
optional asynchronous follow-up, not a release completion condition and not a
step that an automated coding agent must perform after pushing the tag.
## Release Model
- Releases use unused stable semantic-version tags in the form
`vMAJOR.MINOR.PATCH`; prereleases are outside this feature.
- Narratio is past `v1.0.0`, so a minor release adds backward-compatible
functionality, a patch release contains backward-compatible fixes, and an
intentional breaking change to a documented public contract requires a new
major version.
- Release tags remain lightweight, matching Narratio's established tag
history, and are bound explicitly to a guarded commit on `main`.
- Every new release has a nonempty checked-in note at
`docs/releases/<tag>.md` in the tagged tree.
- Published tags are immutable. They are never moved, reused, overwritten, or
deleted, even when asynchronous CI fails to create a complete binary
release.
- The Gitea release, when asynchronous publication succeeds, remains the
canonical source for downloadable binaries and checksums. The Git tag and
checked-in note remain valid source-release history independently of CI.
- Existing historical tags and notes are not retroactively rewritten or
backfilled to satisfy the new procedure.
## Canonical Documentation
Add `docs/release.md` as the canonical maintainer procedure. Update
`docs/development.md` to route release preparation, tagging, and publication to
it, and update the documentation policy so it assigns ownership as follows:
- `docs/release.md` owns version selection, candidate preparation, validation,
tag guards, tag publication, the responsibility boundary, and failure
recovery;
- `docs/releases/<tag>.md` owns the immutable historical summary for one
release;
- `docs/releases/README.md` owns the release-note index and points to the Gitea
release collection for downloadable assets; and
- current CLI, configuration, operation, integration, and internal contracts
remain in their existing canonical documents rather than being restated in
release notes.
The maintainer procedure must be written as a complete safe workflow suitable
for either a human maintainer or a coding agent. Commands must be explicit,
non-interactive, and scoped to the selected version and tag ref.
## Release Notes
Require each future note to use this minimum structure:
```markdown
# Narratio vMAJOR.MINOR.PATCH
This release ...
## Summary
## Compatibility
## Upgrade
## Changes
```
The compatibility section must identify changes to documented CLI,
configuration, durable artifact, integration, or operating contracts. The
upgrade section must state required operator actions or explicitly say that no
special action is required. When the Notarius consumer contract changes, the
note should identify the supported/tested contract version and any required
Notarius upgrade.
Release notes summarize a historical candidate and link to current canonical
documentation for exact behavior. They must not substitute for updating that
documentation, include credentials or private infrastructure details, or make
claims that have not been validated against the candidate.
## Shared Release Scripts
### Source-candidate checker
Add the POSIX-shell checker:
```text
scripts/check-release-candidate.sh vMAJOR.MINOR.PATCH
```
It must be deterministic, non-interactive, safe to run repeatedly, and usable
both by maintainers before tagging and by tag CI in a detached checkout. It
must not create or push Git tags, modify tracked source, contact CI, or require
live WhisperX, LLM, Notarius, Scriptorium, object-storage, or notification
services.
The checker must fail clearly unless all of the following hold:
- the argument is a stable semantic version;
- `go.mod` declares Narratio's expected module path;
- the matching release note exists, has the exact Narratio/version heading,
and contains the required sections;
- Go workspace use is disabled for candidate commands;
- tracked `go.work`/`go.work.sum`, vendoring, and `go.mod` replacement
directives are absent;
- uncached repository tests, race tests, vet, build, documentation checks, and
maintained-example validation succeed;
- `go mod tidy -diff` reports no module-file change;
- every tracked Go source file is `gofmt` clean;
- working-tree and cached whitespace checks succeed; and
- all supported release binaries cross-compile with the selected version
embedded, with the host-platform binary reporting exactly
`narratio <tag>` through `narratio version`.
The checker must preserve the distinction already documented by Narratio:
cross-compilation is compilation evidence, not native macOS or Windows runtime
evidence.
### Asset builder
Add `scripts/build-release-assets.sh`, used by both the candidate checker and
release CI. It must build exactly these CGO-disabled targets:
- Linux `amd64` and `arm64`;
- macOS `amd64` and `arm64`; and
- Windows `amd64` and `arm64`.
Asset names must remain deterministic and include the Narratio version,
operating system, architecture, and `.exe` suffix for Windows. Build output
must use a caller-supplied, narrowly validated staging directory and must not
perform broad or ambiguous deletion. Official assets use `-trimpath` and the
established release version linker override.
### Tag-publication command
Add the narrowly scoped release command:
```text
scripts/release.sh vMAJOR.MINOR.PATCH
```
The command owns local validation, publication guards, lightweight tag
creation, local tag verification, and pushing only the selected tag ref. It
must not commit candidate changes or push `main`; the candidate must already be
committed and present on upstream `main`.
Before creating a tag, the command must:
- run the shared candidate checker;
- require the current branch to be `main`;
- require a clean worktree and index;
- fetch upstream `main` and tags;
- record the exact candidate commit and require local `HEAD` to equal
`origin/main`;
- require the version-matched release note from that commit; and
- reject an existing local or upstream tag of the selected name.
It must create an explicitly lightweight tag against the recorded commit,
verify that the local tag resolves directly to that commit, and push only:
```text
refs/tags/<version>:refs/tags/<version>
```
It must never use `git push --tags`. If the tag push succeeds, the command must
report the version and commit and exit successfully immediately. In
particular, it must not invoke a CI client, poll a build status, wait for a
Gitea release, or verify release assets.
If a failure occurs before the upstream tag is published, no release has
completed. An unpublished local tag may be removed only after the maintainer
has inspected the failure. The script should not silently delete it. A
successful upstream push is final even if later asynchronous work fails.
## Woodpecker Release Workflow
Retain tag-triggered asynchronous release automation, but make it consume the
same checked-in contracts as local preparation:
- validate the tag syntax and matching release note before asset work;
- call the shared candidate checker rather than maintaining an independent
drifting validation list;
- use the shared asset builder for the six official binaries;
- pin the Go builder image to `golang:1.25.5`, an explicit patch version
compatible with the current `go.mod` language version;
- pin the release plugin to the reviewed explicit version
`woodpeckerci/plugin-release:0.3.1` rather than a floating image;
- set the Gitea release title explicitly to `Narratio <tag>`;
- use `docs/releases/<tag>.md` from the tagged tree as the release body;
- generate a flattened `SHA256SUMS` file for all official binaries;
- keep prerelease publication disabled and overwrite disabled; and
- keep validation, asset building, and publication dependency-ordered so a
failed CI validation cannot publish assets.
The workflow must not require a callback to, acknowledgement from, or status
update in the release command. The two sides have intentionally separate
responsibilities:
```text
local release command asynchronous infrastructure
--------------------- ---------------------------
validate candidate
guard main and exact commit
create lightweight tag
push exact upstream tag ────────► validate tagged tree
return success immediately build assets and checksums
create Gitea release
```
## Failure And Correction Policy
Failures before tag publication are corrected on `main`, followed by complete
candidate revalidation and fresh guards.
After a tag has been pushed, neither CI failure nor a defect permits changing
that tag. Correct the cause on `main`, choose a new patch version, write a new
release note, and repeat the complete release procedure. Do not move or delete
the published tag, manually overwrite generated assets, or make the release
command wait for repaired infrastructure.
A maintainer may later inspect the remote tag, Gitea note, assets, checksums,
and host binary. Documentation may provide these as optional asynchronous
inspection commands, clearly outside the release responsibility boundary. No
automated release instruction may require them after a successful tag push.
## Security And Operational Constraints
- Local scripts never read or print the Gitea release token; the token remains
a Woodpecker-owned secret supplied only to the release plugin.
- Candidate validation remains offline with respect to paid APIs, mutable
application services, and private user data.
- Temporary build output is confined to a validated temporary or staging
directory and cleaned safely.
- Scripts avoid environment dumps and never record credentials in release
notes, logs, repository files, or command output.
- The release command does not edit tracked files, create commits, or push
branches. Apart from fetching refs for its guards, its only release-state
mutations are creating the selected local tag and pushing that exact tag.
## Target End State
When this feature is complete:
1. A maintainer or coding agent prepares and commits a standardized release
note plus any final canonical-documentation updates.
2. The candidate is pushed to `main` through the ordinary development
workflow.
3. One shared checker proves the exact tagged source is clean, self-contained,
formatted, tested, race-tested, statically checked, documented, module
tidy, cross-buildable, and version-correct.
4. One guarded release command refuses ambiguous or unpublished candidates,
creates the established lightweight tag, and pushes only that tag.
5. The command completes successfully at the upstream tag push and performs no
CI or Gitea status wait.
6. Woodpecker independently reuses the shared checks and asset builder and may
publish six versioned binaries, release notes, and SHA-256 checksums.
7. Published tags remain immutable, and failures after tag publication are
corrected only through a new version.
8. The release procedure is documented in one canonical location, linked from
contributor guidance, enforced by scripts and CI, and clear about the
boundary between guaranteed Git publication and best-effort asynchronous
binary publication.
## Non-Goals
This feature does not:
- repair, replace, monitor, or improve Woodpecker reliability;
- wait for or synchronously verify CI or Gitea release completion;
- introduce prereleases, signed tags, containers, package-manager publication,
installers, auto-updaters, or native macOS/Windows test runners;
- publish `main` automatically or create release-note content automatically;
- run live external adapters or paid model calls during release validation; or
- rewrite historical tags or manufacture retrospective release notes.