Simplify local release validation and publication
This commit is contained in:
173
docs/release.md
173
docs/release.md
@@ -12,21 +12,24 @@ binary, container image, generated package, or separate registry publication.
|
||||
- Authoritative remote: `origin`, expected to fetch from
|
||||
`https://gitea.maximumdirect.net/eric/ConfigKit.git`
|
||||
- Tags: stable Semantic Versions such as `v0.1.0`; prerelease tags are not supported
|
||||
- Minimum Go language/toolchain line: Go 1.25, tested with a supported patched
|
||||
Go 1.25 toolchain and one newer supported toolchain
|
||||
- Minimum Go language/toolchain line: Go 1.25, release-validated with one maintained
|
||||
patch of that line; newer-toolchain testing is optional and non-gating
|
||||
- Release-gated build targets: Linux on amd64 and arm64
|
||||
- Best-effort targets: macOS on amd64 and arm64, under the
|
||||
[platform support policy](policy/release.md#platform-support)
|
||||
|
||||
The automated checker cross-builds the Linux targets. Before release, the maintainer
|
||||
must also obtain native test, race, symlink, and permission-fixture evidence on Linux;
|
||||
cross-compilation is not native execution evidence. Native macOS checks and Darwin
|
||||
cross-builds are optional and do not gate publication.
|
||||
The local Linux checker includes native race tests and CGO-disabled cross-builds.
|
||||
Its output reports skipped filesystem cases; no separate evidence record or
|
||||
human sign-off is required. Native macOS checks and Darwin cross-builds are
|
||||
optional and do not gate publication.
|
||||
|
||||
CI is never a release gate. Do not wait for it, poll its status, or delay reporting
|
||||
completion because it is pending, unavailable, or failed.
|
||||
|
||||
## 1. Prepare access and tools
|
||||
|
||||
The release maintainer owns this procedure. Work in a dedicated clone with Git, Bash 3.2 or newer,
|
||||
and two supported Go toolchains available. The checker supports the stock macOS
|
||||
The release maintainer owns this procedure. Use a clean checkout (a dedicated clone is optional) with Git, Bash 3.2 or newer,
|
||||
a maintained Go 1.25 patch, and the compiler needed for native race tests. The checker supports the stock macOS
|
||||
Bash; its filename enumeration is null-delimited, including names with whitespace. Configure private-host Git and Go module
|
||||
access through the normal credential manager before starting. Keep tokens, passwords,
|
||||
SSH material, and environment dumps outside the repository and command transcript.
|
||||
@@ -37,23 +40,24 @@ git remote get-url origin
|
||||
git ls-remote origin HEAD
|
||||
```
|
||||
|
||||
The remote URL must equal the authoritative URL above. Select the latest supported
|
||||
patch in the Go 1.25 line and a newer supported toolchain. The values below are the
|
||||
toolchains used while this runbook was introduced; update them to supported patch
|
||||
releases during candidate preparation and record the actual versions in the release
|
||||
note or validation record.
|
||||
The remote URL must equal the authoritative URL above. Acquire the selected
|
||||
toolchain and dependencies explicitly. For example, with the maintained patch
|
||||
used when this procedure was simplified:
|
||||
|
||||
```sh
|
||||
export CONFIGKIT_BASELINE_TOOLCHAIN=go1.25.5
|
||||
export CONFIGKIT_NEWER_TOOLCHAIN=go1.26.7
|
||||
export CONFIGKIT_BASELINE_TOOLCHAIN=go1.25.14
|
||||
GOTOOLCHAIN="$CONFIGKIT_BASELINE_TOOLCHAIN" go version
|
||||
GOTOOLCHAIN="$CONFIGKIT_NEWER_TOOLCHAIN" go version
|
||||
GOTOOLCHAIN="$CONFIGKIT_BASELINE_TOOLCHAIN" go mod download
|
||||
GOTOOLCHAIN="$CONFIGKIT_NEWER_TOOLCHAIN" go mod download
|
||||
```
|
||||
|
||||
Those commands explicitly acquire toolchains and dependencies. The checker itself
|
||||
sets `GOPROXY=off` and fails if its required cache is incomplete.
|
||||
Keep toolchains maintained, but do not require latest-version discovery for each
|
||||
release. The checker sets `GOPROXY=off` and fails if the cache is incomplete.
|
||||
Toolchain and YAML-dependency update research belongs to periodic maintenance or
|
||||
relevant dependency changes. No renewed approval of an unchanged pin is required.
|
||||
|
||||
Use Bash with `set -euo pipefail` for the command blocks below so any failed gate
|
||||
stops the sequence. Publication requires user authorization; the agent policy's
|
||||
local-commit authorization is not permission to push or release.
|
||||
|
||||
## 2. Select the version and review compatibility
|
||||
|
||||
@@ -63,7 +67,7 @@ For the first release, review the whole public surface and current contracts.
|
||||
```sh
|
||||
release_version=v0.1.0
|
||||
printf '%s\n' "$release_version" | grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'
|
||||
git show-ref --verify --quiet "refs/tags/$release_version" && exit 1 || :
|
||||
if git show-ref --verify --quiet "refs/tags/$release_version"; then exit 1; fi
|
||||
if git ls-remote --exit-code --tags origin "refs/tags/$release_version" >/dev/null; then
|
||||
printf 'remote tag already exists: %s\n' "$release_version" >&2
|
||||
exit 1
|
||||
@@ -79,16 +83,9 @@ intentional incompatible minor-release change must be explained with consumer ac
|
||||
patch releases must remain compatible within their minor line. Review the maintained
|
||||
consumer examples and the migration guide even when signatures did not change.
|
||||
|
||||
Reassess `go.yaml.in/yaml/v4` rather than using `@latest` implicitly:
|
||||
|
||||
```sh
|
||||
go list -m -versions go.yaml.in/yaml/v4
|
||||
go list -m -json go.yaml.in/yaml/v4@latest
|
||||
```
|
||||
|
||||
If changing the explicit pin, make that a reviewed dependency change and rerun the
|
||||
complete checker, especially parser policy, limits, diagnostics, and rendering tests.
|
||||
If retaining a prerelease pin, record that decision in Compatibility.
|
||||
When dependencies change, review parser behavior and migration impact as part of
|
||||
that change. Keep Compatibility accurate about the dependency pin, but do not
|
||||
perform an external version survey for every patch release.
|
||||
|
||||
## 3. Write the release note
|
||||
|
||||
@@ -100,55 +97,46 @@ release note until there is a real candidate and reviewed content.
|
||||
|
||||
## 4. Validate the source candidate
|
||||
|
||||
Run the complete checker on Linux from the repository root after dependencies have been
|
||||
acquired. It validates module manifests in a disposable copy, both supported Go
|
||||
toolchains, offline default tests, the native race detector, vet, native builds,
|
||||
CGO-disabled Linux cross-builds, formatting and whitespace (including untracked files),
|
||||
examples, package isolation, and local documentation links.
|
||||
Before release validation, finish compatibility review, inspect the diff and file
|
||||
list for unintended content or credentials, retire completed roadmaps, repair
|
||||
links, and review the release note. These are ordinary preparation tasks the
|
||||
executing agent may perform, not separate human approvals or evidence records.
|
||||
Ask the maintainer only for an unresolved product decision or missing authority.
|
||||
|
||||
Commit the candidate, staging only its intended changes, then run the checker
|
||||
once against that clean commit. Do not run both everyday and release checkers to
|
||||
repeat the same gates.
|
||||
|
||||
```sh
|
||||
./scripts/release-check.sh
|
||||
```
|
||||
|
||||
`go vet` is the repository's static-analysis baseline. No additional linter is
|
||||
claimed. Links to current ConfigKit files and headings are strict. Recorded links to
|
||||
sibling consumer source are checked when those checkouts exist and are otherwise
|
||||
inapplicable in an isolated clone; their immutable revisions remain recorded in the
|
||||
extraction inventory.
|
||||
|
||||
Complete these manual gates and record their evidence with the candidate:
|
||||
|
||||
- The maintainer reviews compatibility and downstream migration impact against
|
||||
`docs/config.md`, Go API comments, and the consumer guide.
|
||||
- The maintainer inspects `git status --short`, `git diff`, and the candidate file
|
||||
list for credentials, private data, generated output, unintended files, and
|
||||
unrelated changes.
|
||||
- The maintainer records a successful native checker run on Linux, including
|
||||
filesystem capability tests. Missing required Linux evidence blocks release;
|
||||
no macOS run, sign-off, or cross-build result is required.
|
||||
- The maintainer removes completed, obsolete documents under `docs/roadmap/`, repairs
|
||||
inbound links, and reruns the checker. ADR rationale remains historical.
|
||||
- The maintainer reviews the exact version-matched release note for accuracy and
|
||||
confirms it describes only deliverables that will actually be published.
|
||||
|
||||
## 5. Commit, push, and record the candidate
|
||||
|
||||
Commit all candidate source, contract, example, roadmap-retirement, and release-note
|
||||
changes, rerun the checker against that clean commit, then push only `main` and record
|
||||
the exact commit in Git metadata local to the clone:
|
||||
|
||||
```sh
|
||||
git add .
|
||||
git status --short
|
||||
# Stage the specific files belonging to the candidate, then review:
|
||||
git diff --staged
|
||||
git commit --message "Prepare ConfigKit $release_version"
|
||||
./scripts/release-check.sh
|
||||
test -z "$(git status --porcelain=v1 --untracked-files=all)"
|
||||
git push origin HEAD:refs/heads/main
|
||||
git rev-parse HEAD | tee .git/configkit-release-candidate
|
||||
candidate_commit=$(cat .git/configkit-release-candidate)
|
||||
candidate_commit=$(git rev-parse HEAD)
|
||||
./scripts/release-check.sh
|
||||
test "$(git rev-parse HEAD)" = "$candidate_commit"
|
||||
test -z "$(git status --porcelain=v1 --untracked-files=all)"
|
||||
printf '%s\n' "$candidate_commit" > "$(git rev-parse --git-path configkit-release-candidate)"
|
||||
```
|
||||
|
||||
If review changes the source after this point, create and validate a new candidate;
|
||||
do not reuse the recorded commit.
|
||||
If the final candidate is already committed, omit the staging and commit commands;
|
||||
do not create an empty commit. The checker validates module manifests in a
|
||||
disposable copy, formatting, whitespace, the full offline suite once with the race
|
||||
detector and verbose skip reporting, vet once, and CGO-disabled Linux amd64/arm64
|
||||
builds. The suite includes examples, consumer fixtures, documentation links, and
|
||||
package boundaries; no separate reruns are required. No additional linter is
|
||||
claimed. Sibling-consumer links are checked when those checkouts exist and are
|
||||
otherwise inapplicable in an isolated checkout.
|
||||
|
||||
A changed candidate requires validation again. Do not reuse a recorded candidate
|
||||
from an older revision. The record is written only after successful local checks.
|
||||
|
||||
## 5. Prepare publication
|
||||
|
||||
The following guarded sequence pushes only `main` and the selected tag. Local
|
||||
validation failures block it; CI does not. Keep the selected `release_version`
|
||||
from step 2 in the same shell. If starting a new shell, set it explicitly again.
|
||||
|
||||
## 6. Run publication guards
|
||||
|
||||
@@ -156,14 +144,12 @@ Immediately before tagging, fetch the authoritative branch and tags, restore the
|
||||
recorded variables if this is a new shell, and execute every guard:
|
||||
|
||||
```sh
|
||||
release_version=v0.1.0
|
||||
candidate_commit=$(cat .git/configkit-release-candidate)
|
||||
candidate_commit=$(cat "$(git rev-parse --git-path configkit-release-candidate)")
|
||||
git fetch --prune origin main
|
||||
git fetch --tags origin
|
||||
test "$(git branch --show-current)" = main
|
||||
test -z "$(git status --porcelain=v1 --untracked-files=all)"
|
||||
test "$(git rev-parse HEAD)" = "$candidate_commit"
|
||||
test "$(git rev-parse refs/remotes/origin/main)" = "$candidate_commit"
|
||||
test "$(git remote get-url origin)" = 'https://gitea.maximumdirect.net/eric/ConfigKit.git'
|
||||
printf '%s\n' "$release_version" | grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'
|
||||
test -f "docs/releases/$release_version.md"
|
||||
@@ -171,17 +157,22 @@ test "$(sed -n '1p' "docs/releases/$release_version.md")" = "# ConfigKit $releas
|
||||
for heading in Summary Compatibility Upgrade Changes; do
|
||||
grep -Fxq "## $heading" "docs/releases/$release_version.md"
|
||||
done
|
||||
git show-ref --verify --quiet "refs/tags/$release_version" && exit 1 || :
|
||||
if git show-ref --verify --quiet "refs/tags/$release_version"; then exit 1; fi
|
||||
if git ls-remote --exit-code --tags origin "refs/tags/$release_version" >/dev/null; then
|
||||
printf 'remote tag already exists: %s\n' "$release_version" >&2
|
||||
exit 1
|
||||
else
|
||||
test $? -eq 2
|
||||
fi
|
||||
git push origin HEAD:refs/heads/main
|
||||
git fetch origin main
|
||||
test "$(git rev-parse refs/remotes/origin/main)" = "$candidate_commit"
|
||||
test "$(git rev-parse HEAD)" = "$candidate_commit"
|
||||
test -z "$(git status --porcelain=v1 --untracked-files=all)"
|
||||
```
|
||||
|
||||
Confirm the recorded automated and manual approvals apply to exactly
|
||||
`$candidate_commit`. A successful older run is not evidence for a changed commit.
|
||||
The recorded local validation must apply to exactly `$candidate_commit`. No
|
||||
separate human sign-off, CI status, or manual evidence document is required.
|
||||
|
||||
## 7. Tag and publish the exact ref
|
||||
|
||||
@@ -194,15 +185,17 @@ test "$(git rev-list -n 1 "$release_version")" = "$candidate_commit"
|
||||
git push origin "refs/tags/$release_version:refs/tags/$release_version"
|
||||
```
|
||||
|
||||
The pushed tag completes the declared source-only publication. No Gitea release
|
||||
object or binary upload is required.
|
||||
A successful push of the intended tag completes the release. Report success now;
|
||||
do not wait for CI, module propagation, or any post-publication check. No Gitea
|
||||
release object or binary upload is required.
|
||||
|
||||
## 8. Verify publication and clean consumer retrieval
|
||||
## 8. Optional post-publication checks
|
||||
|
||||
Verify the authoritative tag and then retrieve that exact version into a new module
|
||||
without a local `replace` directive. Private-host credentials must already be
|
||||
configured. Use direct origin retrieval unless an explicitly approved internal Go
|
||||
proxy is the publication channel.
|
||||
Only when useful or requested, read back the remote tag or retrieve the exact
|
||||
version into a new module without a local `replace` directive. These checks are
|
||||
optional and non-gating; their failure or unavailability does not delay release
|
||||
completion. Do not poll CI or wait for module propagation. Private-host credentials
|
||||
must already be configured. Example direct-origin verification:
|
||||
|
||||
```sh
|
||||
remote_tag_commit=$(git ls-remote origin "refs/tags/$release_version^{}" | awk '{print $1}')
|
||||
@@ -229,13 +222,13 @@ EOF
|
||||
rm -rf -- "$consumer_dir"
|
||||
```
|
||||
|
||||
The final `go list` output must show the exact module path and `$release_version`.
|
||||
Record the result with the release evidence.
|
||||
If performed, the final `go list` output should show the exact module path and
|
||||
`$release_version`. Report optional verification separately from publication success.
|
||||
|
||||
## 9. Handle failures without replacement
|
||||
|
||||
Before publication, fix the candidate, rerun every gate, commit, push, and record a
|
||||
new exact candidate. An unpublished local tag may be deleted only after verifying it
|
||||
Before publication, fix and commit the candidate, validate it, record the new
|
||||
exact candidate, and repeat the guarded publication sequence. An unpublished local tag may be deleted only after verifying it
|
||||
was never pushed or used by another release channel.
|
||||
|
||||
After publication, never move, delete, or recreate the tag and never replace its
|
||||
|
||||
Reference in New Issue
Block a user