Files
notarius/docs/roadmap/implementation.md

429 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Source-Only Release Implementation Plan
## Purpose
Implement the target state defined by
[Source-Only Releases](source-releases.md): immutable source tags with checked-in
release notes, a diagnostic version interface, strong shared candidate checks,
validation-only tag CI, Linux support, best-effort macOS compilation, and no
packaged binaries or Windows support.
This plan is ordered. Each numbered stage is one implementation prompt for a
gpt-5.6-terra coding agent. Complete and validate one stage before beginning
the next. Preserve all unrelated worktree changes, follow every policy under
`docs/policy/`, and update current-behavior documentation in the same stage as
the behavior it describes.
Do not create or push a release tag while implementing this plan. Do not invent
a release note for `v0.1.0`, `v0.2.0`, or `v0.3.0`. The first real release
under the completed procedure will add its own note in a separate release
operation.
## Decisions Fixed For Implementation
- Releases are stable `vMAJOR.MINOR.PATCH` source tags on `main`; prereleases
are unsupported initially.
- Tags are lightweight and immutable after publication.
- No release binaries, archives, checksums, signatures, containers,
package-manager entries, or Gitea release objects are produced.
- Linux is supported. Release checks compile Linux `amd64` and `arm64` with
`CGO_ENABLED=0`.
- macOS is best-effort. Release checks compile Darwin `amd64` and `arm64` with
`CGO_ENABLED=0`, without promising runtime CI or packaged output.
- Windows is unsupported and must not be added to build checks.
- Release notes begin with the first release made under the new procedure;
historical tags are left untouched.
- `notarius --version` is informational. Receipt and artifact contracts remain
authoritative for downstream compatibility.
- One checked-in POSIX shell command owns substantive source-candidate checks.
The release procedure and tag CI call it rather than maintaining duplicate
test/build matrices.
- Tag CI validates only. Pre-publication local guards remain mandatory because
tag CI cannot prevent an already-pushed tag.
## Stage 1: Add Build Version Resolution And `--version` ✅
### Goal
Add a small, testable build-information boundary and expose the public root
version flag without affecting existing command behavior.
### Implementation
1. Create `internal/buildinfo` with an exported link-time string variable named
`Override` and an exported resolver such as `Version() (string, error)`.
Keep this package independent of CLI and application packages.
2. Resolve the displayed value using this precedence:
1. a nonempty `Override`;
2. the main-module version returned by `runtime/debug.ReadBuildInfo`; then
3. the literal `development`.
3. Accept a release value only when it matches the complete stable SemVer tag
form `^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$`.
Whitespace, prerelease/build suffixes, pseudo-versions, and arbitrary text
are not release versions. A nonempty invalid linker override is an error;
an empty, `(devel)`, pseudo-version, or otherwise non-release main-module
version falls back to `development`.
4. Add `--version` to the root dispatch in `internal/cli`. It is valid only as
the sole argument, writes exactly `notarius <resolved-version>\n` to stdout,
writes nothing to stderr, and exits zero. Additional arguments are a syntax
error using the existing exit-2 and stderr conventions. An invalid linker
override is a runtime/build error using exit 1 and stderr.
5. Add the flag to root usage without changing the existing behavior of empty
arguments, help spellings, or subcommands.
6. Update `docs/cli.md` as the canonical public contract and
`docs/internal/cli.md` as the implementation owner. State that tagged
`go install` builds can obtain the main-module tag from Go build information,
controlled builds may inject
`gitea.maximumdirect.net/eric/notarius/internal/buildinfo.Override`, and an
ordinary unversioned checkout reports `development`.
### Tests
- Add table-driven `internal/buildinfo` tests for valid stable versions,
leading-zero rejection, whitespace, prerelease/build suffixes, pseudo-
versions, override precedence, invalid nonempty override, and development
fallback. Test the pure resolution decision rather than trying to mutate
process build information.
- Extend the CLI command-contract tests to cover exact stdout/stderr/exit
behavior for `--version`, extra arguments, and unchanged help/unknown-command
behavior.
- Do not snapshot the whole usage document solely for the new line; assert the
stable semantic fragments already owned by the CLI contract tests.
### Validation
```sh
go test ./internal/buildinfo ./internal/cli
go test ./...
go vet ./...
go build ./cmd/notarius
go run ./cmd/notarius --version
```
Build a temporary Linux host binary with:
```sh
go build -trimpath \
-ldflags '-X gitea.maximumdirect.net/eric/notarius/internal/buildinfo.Override=v0.0.0' \
-o /path/to/temp/notarius ./cmd/notarius
```
and verify that its output is exactly `notarius v0.0.0`.
### Acceptance Criteria
- The public and internal documentation matches the implemented version
behavior.
- Ordinary builds print `notarius development`.
- A valid linker override prints the exact stable tag.
- Invalid linker content cannot masquerade as a release version.
- Existing CLI commands, help, streams, and exit classes remain unchanged.
## Stage 2: Add One Reusable Source-Candidate Checker ✅
### Goal
Create one repository-owned, offline validation command used identically by a
maintainer and release CI.
### Implementation
1. Create executable POSIX shell script `scripts/check-release-source.sh`.
Require exactly one positional argument containing a stable SemVer tag. The
script must locate and enter the repository root from its own checked-in
path so callers cannot accidentally validate another working directory.
2. Use `set -eu`, quote all expansions, reject invalid versions before using
them in paths or linker arguments, and use a freshly created temporary
directory for cross-build output. Install a cleanup trap scoped only to that
resolved temporary directory.
3. Keep release-note, branch, remote, clean-worktree, and tag-existence guards
out of this script. Those publication-specific checks belong in
`docs/release.md` and tag CI. This script owns the substantive source checks
that both workflows share.
4. Implement these checks in a clear fail-fast order:
- require the expected Notarius module path in `go.mod`;
- reject tracked `go.work` or `go.work.sum`, an existing `vendor` directory,
and any `replace` directive in `go.mod`;
- `GOWORK=off go test -count=1 ./...`;
- `GOWORK=off go test -race -count=1 ./...`;
- `GOWORK=off go vet ./...`;
- `GOWORK=off go build ./...`;
- `GOWORK=off go mod tidy -diff`;
- require no output from `gofmt -l` for tracked Go files;
- `git diff --check` and `git diff --cached --check`;
- validate `examples/dnd-minimal.config.yml` and
`examples/dnd-complete.config.yml` for pipeline `dnd-session` using the
built or `go run` Notarius command;
- build `./cmd/notarius` with `CGO_ENABLED=0` for Linux `amd64` and `arm64`
and Darwin `amd64` and `arm64`; and
- inject the supplied tag through `internal/buildinfo.Override` in every
cross-build.
5. Execute the built command and verify exact `--version` output when the
current host GOOS/GOARCH matches one of the four targets. Do not attempt to
execute a foreign target.
6. Do not compile for Windows, write output beneath the repository, contact an
LLM provider, require credentials, or mutate tracked files.
### Tests And Validation
- Run the script with `v0.0.0` as a synthetic build version. It does not require
or create a corresponding release note or Git tag.
- Exercise its cheap argument guards separately with missing, extra, malformed,
prerelease, and leading-zero versions. These failures must occur before Go
tests or builds begin.
- Confirm temporary outputs are removed on success and ordinary command
failure. Do not add a large shell-test framework solely for this script;
retain focused automated tests only if they protect a realistic failure that
is not more clearly covered by executing the checker itself.
```sh
./scripts/check-release-source.sh v0.0.0
git status --short
```
### Acceptance Criteria
- One command runs every substantive source-candidate check required by the
feature roadmap.
- The command is deterministic, offline, credential-free, POSIX-compatible,
fail-fast, and safe with temporary paths.
- The Linux and Darwin target matrix succeeds and Windows is absent.
- Version injection and host-binary reporting are checked as part of the same
matrix.
- Successful execution leaves the worktree and index unchanged.
## Stage 3: Add Validation-Only Tag CI ✅
### Goal
Independently validate every newly pushed release tag without publishing or
mutating release state.
### Implementation
1. Add `.woodpecker/release.yml` triggered only by tag events.
2. Use the Go 1.25.5 container image to match the version currently declared
by `go.mod`. When the declared Go version changes in a future release, the
release pipeline image and release documentation must be reviewed in the
same change.
3. In one validation step:
- read the candidate version only from `CI_COMMIT_TAG`;
- require the stable SemVer form fixed above;
- require a nonempty `docs/releases/$CI_COMMIT_TAG.md`;
- require the exact heading `# Notarius $CI_COMMIT_TAG`;
- require exact `## Summary`, `## Compatibility`, `## Upgrade`, and
`## Changes` headings; and
- invoke `./scripts/check-release-source.sh "$CI_COMMIT_TAG"`.
4. Do not include a release plugin, API token, artifact upload, Gitea release
creation, archive/checksum step, Windows target, tag mutation, or retry that
could overwrite published state.
5. Keep the CI file thin: note/tag guards belong in it, while the substantive
source checks remain in the shared script.
### Validation
- Review the YAML trigger and commands against the repository's Woodpecker
syntax and the established Weatherreporter tag pipeline structure.
- Confirm every invoked path exists and the script is executable.
- Run the shared checker locally with `v0.0.0`.
- Search the new pipeline for release-plugin configuration, upload commands,
secrets, Windows targets, and mutation commands; none may be present.
- Run `git diff --check`.
Do not push a synthetic tag merely to test this stage. The first real release
will exercise the remote trigger; local source validation and review provide
the pre-release confidence boundary.
### Acceptance Criteria
- Every stable release tag triggers the validation pipeline.
- Missing or malformed version-matched release notes fail before the expensive
source checks.
- CI calls the same substantive checker used locally.
- The pipeline cannot publish binaries, releases, checksums, or other assets
and requires no release secret.
## Stage 4: Establish The Canonical Release Procedure And Documentation Policy ✅
### Goal
Make the complete source-release workflow executable by a maintainer without
undocumented knowledge, and give release documentation an explicit canonical
home.
### Implementation
1. Create `docs/release.md`, adapted to Notarius's source-only model. It must
define:
- stable SemVer selection and the pre-`v1` compatibility policy;
- the exact `docs/releases/<tag>.md` template and validation guards;
- invocation of `./scripts/check-release-source.sh "$RELEASE_VERSION"`;
- manual review of changed Markdown links and unintended repository files;
- committing and pushing the release note and current documentation before
tagging;
- recording `RELEASE_COMMIT` from `HEAD^{commit}`;
- a copyable guard that requires `main`, a clean worktree/index, disabled Go
workspace use, an exact match between `RELEASE_COMMIT` and
`origin/main`, a matching note, and an unused local and remote tag;
- explicit lightweight-tag creation against `RELEASE_COMMIT`;
- pushing only
`refs/tags/$RELEASE_VERSION:refs/tags/$RELEASE_VERSION`;
- remote tag and tagged-note verification;
- a fresh `go install ...@"$RELEASE_VERSION"` or fresh exact-tag checkout
verification, including exact `--version` output; and
- immutable-tag failure and correction policy.
2. The procedure must say that the tag and checked-in note are the release and
that tag CI is validation-only. It must explicitly exclude binaries,
archives, checksums, signatures, containers, package-manager publication,
Gitea release objects, Windows, and retrospective notes for existing tags.
3. Document private-module installation through standard `GOPRIVATE` and Git
authentication mechanisms without including credentials or private
environment dumps. Do not make one maintainer's credential setup part of
the release contract.
4. Update `docs/policy/documentation.md`:
- add canonical-owner rows for `docs/release.md` and `docs/releases/`;
- state that release notes are historical summaries, not current-state
contract owners;
- state that the checked-in note at the immutable tag is the release record;
and
- require current canonical docs to change with behavior rather than using
release notes as substitutes.
5. Update `docs/development.md` with a release-preparation/tagging/verification
routing row pointing to `docs/release.md` and the relevant policies.
6. Do not create an empty placeholder release note or a retrospective note.
`docs/releases/` first becomes tracked when the next actual release note is
prepared.
### Validation
- Follow every local Markdown link added or changed in this stage.
- Execute every non-destructive candidate-validation command that does not
require an actual new release note, remote tag, or publication.
- Compare the procedure line by line with the shared checker and CI so their
tag syntax, note headings, target matrix, and validation ownership agree.
- Confirm the procedure never uses `git push --tags`, moves a published tag,
uploads an asset, or embeds credentials.
- Run `git diff --check`.
### Acceptance Criteria
- `docs/release.md` is sufficient to prepare, guard, tag, publish, verify, and
recover from a source release.
- Release procedure and release-note ownership are explicit and nonduplicative.
- The procedure calls the shared checker rather than restating its full command
matrix.
- No historical or placeholder release note is introduced.
## Stage 5: Align Platform, Installation, And Operational Documentation ✅
### Goal
Make the supported-platform and source-installation story discoverable in the
canonical current-state documents without duplicating the release procedure.
### Implementation
1. Update `README.md` with a concise source-installation section. Show
`go install gitea.maximumdirect.net/eric/notarius/cmd/notarius@<tag>` as a
version-pinned pattern and retain the existing minimal product quickstart.
Link release maintainers to `docs/release.md` rather than reproducing its
guards.
2. Update `docs/operations.md` with operator-facing source deployment facts:
Linux support, the Go version declared by `go.mod`, version pinning, exact
tag builds, and `notarius --version` as a diagnostic. Link command semantics
to `docs/cli.md` and maintainer publication mechanics to `docs/release.md`.
3. Update `docs/policy/architecture.md` with the durable platform and
distribution invariants: supported Linux deployment, best-effort macOS
development, unsupported Windows, and source-only distribution. Keep tag
commands and release mechanics out of architecture.
4. Review `docs/internal/overview.md` navigation after adding
`internal/buildinfo`. Add only the smallest component entry needed if the
current inventory would otherwise omit a meaningful implemented boundary;
do not inflate build information into a subsystem.
5. Confirm `docs/roadmap/future.md` no longer lists the active documented
release-process work. Retain packaged alpha artifacts as deferred work; the
source-only release feature does not permanently reject reconsideration.
6. Review the completed feature roadmap against the implementation and correct
only genuine target-state inconsistencies. Do not convert it into a
changelog or duplicate `docs/release.md`.
### Validation
- Verify all added and changed local Markdown links.
- Confirm commands agree with the implemented CLI and declared module path.
- Confirm no current-state document claims that Notarius publishes binary
assets or supports Windows.
- Run:
```sh
go run ./cmd/notarius --version
go run ./cmd/notarius help
git diff --check
```
### Acceptance Criteria
- Users can discover how to install a pinned source release.
- Operators can identify the deployed version and understand the support
boundary.
- Architecture records durable platform/distribution policy without owning
maintainer release commands.
- Packaged artifacts remain clearly deferred rather than accidentally promised
or permanently prohibited.
## Stage 6: Final Release-System Verification
### Goal
Review the implemented feature as one system and prove that code,
documentation, shared validation, and CI converge on the same release model.
### Verification Work
1. Inspect all commits associated with Stages 15 and compare the result with
`docs/roadmap/source-releases.md` and this plan.
2. Run the shared candidate checker with synthetic version `v0.0.0`. This is a
build identity only; do not create a note or tag for it.
3. Independently run the repository-wide baseline checks if any are not already
performed by the shared checker:
```sh
go test ./...
go vet ./...
go build ./cmd/notarius
```
4. Verify exact development and injected release output:
- ordinary checkout: `notarius development`;
- injected `v0.0.0`: `notarius v0.0.0`;
- invalid linker override: nonzero exit, no false release identity.
5. Confirm both maintained D&D configurations validate offline.
6. Verify all added or changed local Markdown links and run
`git diff --check`.
7. Audit `.woodpecker/release.yml` and `docs/release.md` for agreement on tag
form, note location/headings, Go image/version expectations, and shared
checker use.
8. Confirm the repository contains no generated release binary, distribution
directory, checksum file, release credential, Windows target, release
plugin, synthetic release note, or new tag.
9. Review tests under the repository testing policy. Retain behavior-level
coverage for the public version contract and important validation guards;
remove redundant tests that merely duplicate the shared checker or CI text.
10. Report any remaining divergence as a concrete finding. Fix only in-scope
release-feature defects discovered during this verification; do not expand
into packaged distribution or unrelated cleanup.
### Acceptance Criteria
- All feature-roadmap acceptance criteria are met except creation of the first
real post-procedure release, which is intentionally a separate operator
action.
- Local validation and tag CI share one substantive source checker.
- Version reporting, source installation, platform policy, documentation
ownership, tag guards, and immutable failure handling are internally
consistent.
- The full test suite, vet, build, configuration validation, four-target cross-
build matrix, Markdown link review, and whitespace checks pass.
- The worktree contains no release side effects beyond the intended source,
automation, and documentation changes.