304 lines
13 KiB
Markdown
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.
|