Replace the release procedure with ReleaseKit
This commit is contained in:
292
docs/release.md
292
docs/release.md
@@ -1,7 +1,7 @@
|
|||||||
# ConfigKit Release Procedure
|
# ConfigKit Release Procedure
|
||||||
|
|
||||||
This is the maintainer runbook for ConfigKit's [release policy](policy/release.md).
|
This is the maintainer runbook for ConfigKit's [release policy](policy/release.md).
|
||||||
ConfigKit publishes one source-only Go module. The immutable annotated tag and
|
ConfigKit publishes a source-only Go module. The immutable annotated tag and
|
||||||
matching checked-in release note are the deliverables; there is no binary,
|
matching checked-in release note are the deliverables; there is no binary,
|
||||||
container image, generated package, or separate registry publication.
|
container image, generated package, or separate registry publication.
|
||||||
|
|
||||||
@@ -9,183 +9,215 @@ container image, generated package, or separate registry publication.
|
|||||||
|
|
||||||
- Module: `gitea.maximumdirect.net/eric/ConfigKit`
|
- Module: `gitea.maximumdirect.net/eric/ConfigKit`
|
||||||
- Primary branch: `main`
|
- Primary branch: `main`
|
||||||
- Authoritative remote: `origin`, with fetch and push destination
|
- Authoritative remote: `origin`
|
||||||
`https://gitea.maximumdirect.net/eric/ConfigKit.git`
|
- Authoritative push URL: `https://gitea.maximumdirect.net/eric/ConfigKit.git`
|
||||||
- Tags: stable Semantic Versions such as `v0.5.0`; no prereleases
|
- Tags: stable Semantic Versions such as `v0.5.0`; no prereleases
|
||||||
- Minimum Go line: Go 1.25, validated with one maintained patch
|
- Minimum Go version: the first stable patch of the selected Go series, or a
|
||||||
|
higher patch when required by a dependency or specific correctness fix
|
||||||
- Release-gated targets: Linux amd64 and arm64
|
- Release-gated targets: Linux amd64 and arm64
|
||||||
- Best-effort targets: macOS amd64 and arm64, under the
|
- Best-effort targets: macOS amd64 and arm64, under the
|
||||||
[platform support policy](policy/release.md#platform-support)
|
[platform support policy](policy/release.md#platform-support)
|
||||||
|
|
||||||
The checker runs one offline race suite, one full vet pass, and CGO-disabled
|
Release validation runs one offline race-enabled Go suite, one full vet pass,
|
||||||
builds for both Linux targets. It reports skipped filesystem cases. macOS checks
|
and CGO-disabled package compilation for both Linux targets. macOS and CI are
|
||||||
and newer-toolchain tests are optional. CI is never a readiness or completion
|
not release gates; do not wait for or poll CI.
|
||||||
gate; do not wait for or poll it.
|
|
||||||
|
|
||||||
## 1. Prepare access and tools
|
## 1. Select Go and prepare the tool
|
||||||
|
|
||||||
Use a clean checkout with Git, Bash 3.2 or newer, a maintained Go 1.25 patch,
|
Use a clean checkout with Git, Bash 3.2 or newer, and a native compiler for race
|
||||||
and a native compiler for race tests. Configure private-host access through the
|
tests. Select the newest stable Go series available in the development
|
||||||
normal credential manager. Keep credentials and environment dumps outside the
|
environment and put its latest locally available compatible patch first on
|
||||||
repository and command transcript.
|
`PATH`. The same `go` executable must build ReleaseKit and run its child
|
||||||
|
validation commands. Set local toolchain selection and verify it:
|
||||||
Verify access without displaying credentials:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git remote get-url origin
|
export GOTOOLCHAIN=local
|
||||||
git ls-remote origin HEAD
|
go version
|
||||||
|
go env GOVERSION
|
||||||
|
command -v go
|
||||||
```
|
```
|
||||||
|
|
||||||
The fetch URL and all configured push destinations must resolve to the
|
At release preparation, record the selected series' initial stable release in
|
||||||
authoritative URL above. The publication command checks this again. Acquire the
|
`go.mod`. These commands derive that minimum from the selected executable. The
|
||||||
selected toolchain and dependencies explicitly before offline validation:
|
calculation preserves a higher patch minimum already recorded within that same
|
||||||
|
series; retain it only when dependency or correctness review confirms it is
|
||||||
|
still required. Do not keep a higher patch solely because it was previously
|
||||||
|
recorded:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export CONFIGKIT_BASELINE_TOOLCHAIN=go1.25.14
|
selected_go=$(GOTOOLCHAIN=local go env GOVERSION)
|
||||||
GOTOOLCHAIN="$CONFIGKIT_BASELINE_TOOLCHAIN" go version
|
selected_series=${selected_go#go}
|
||||||
GOTOOLCHAIN="$CONFIGKIT_BASELINE_TOOLCHAIN" go mod download
|
selected_series=${selected_series%.*}
|
||||||
|
current_minimum=$(go list -m -f '{{.GoVersion}}')
|
||||||
|
release_minimum=$(awk -F. -v current="$current_minimum" -v series="$selected_series" 'BEGIN {
|
||||||
|
split(current, current_parts, "[.]")
|
||||||
|
split(series, selected_parts, "[.]")
|
||||||
|
if (current_parts[1] == selected_parts[1] && current_parts[2] == selected_parts[2] && current_parts[3] + 0 > 0) {
|
||||||
|
print current
|
||||||
|
} else {
|
||||||
|
print series ".0"
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
go mod edit -go="$release_minimum"
|
||||||
|
go mod download
|
||||||
```
|
```
|
||||||
|
|
||||||
A maintained patch may replace the example pin. No per-release latest-version
|
For example, Go 1.26.8 records `go 1.26.0` unless a higher minimum is required.
|
||||||
survey is required. Dependencies must be cached; the checker sets `GOPROXY=off`.
|
The `go` directive is ConfigKit's consumer minimum, not the toolchain pin. A
|
||||||
Publication requires user authorization. Standing authorization for local
|
minimum increase requires consumers to use the newer Go version; before v1.0.0,
|
||||||
commits does not authorize a push or release.
|
an intentional compatibility increase requires a minor release under the
|
||||||
|
[versioning policy](policy/release.md#versioning-and-compatibility). Review any
|
||||||
|
required higher patch minimum as part of compatibility preparation.
|
||||||
|
|
||||||
## 2. Select the version and review compatibility
|
Build the tool selected by `go.mod` into temporary storage outside the checkout.
|
||||||
|
Ensure `$TMPDIR` names a system temporary location, not the repository. These
|
||||||
|
commands never install another ReleaseKit version:
|
||||||
|
|
||||||
Choose an unused stable version. Compare the candidate with the previous tag.
|
```sh
|
||||||
Review exported Go signatures and ownership, YAML and scalar semantics,
|
releasekit_tmp=$(mktemp -d "${TMPDIR:-/tmp}/configkit-releasekit.XXXXXX")
|
||||||
precedence, diagnostic codes, minimum Go version, and platform support. A
|
releasekit_bin="$releasekit_tmp/releasekit"
|
||||||
signature-preserving change may still affect consumers. Before `v1.0.0`,
|
go build -o "$releasekit_bin" gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit
|
||||||
intentional incompatible minor-release changes need a note explaining impact
|
go list -m gitea.maximumdirect.net/eric/ReleaseKit
|
||||||
and consumer action; patch releases remain compatible within their minor line.
|
"$releasekit_bin" version
|
||||||
Review the maintained consumer examples and migration guide.
|
```
|
||||||
|
|
||||||
When dependencies change, review parser behavior and migration impact as part
|
The module-selected executable is ReleaseKit v0.2.0. Do not use `go run` or
|
||||||
of that change. Module tidiness belongs to dependency-change review, not the
|
`go tool` for publication: the native executable preserves its exit status and
|
||||||
release checker. Do not repeat a dependency-version survey for every release.
|
the publication-state diagnostics. ReleaseKit's child Go commands use the
|
||||||
|
`go` found on `PATH` with `GOTOOLCHAIN=local`; the existing
|
||||||
|
`CONFIGKIT_BASELINE_TOOLCHAIN` selector for ConfigKit's local release scripts
|
||||||
|
does not select their compiler.
|
||||||
|
|
||||||
## 3. Write and commit the release note
|
Verify access to the authoritative remote without displaying credentials:
|
||||||
|
|
||||||
Create `docs/releases/<version>.md` for the selected version. Its first line
|
```sh
|
||||||
must be `# ConfigKit <version>`. Give a meaningful summary and include
|
git ls-remote https://gitea.maximumdirect.net/eric/ConfigKit.git HEAD refs/heads/main
|
||||||
compatibility impact or upgrade instructions when applicable. Link to current
|
```
|
||||||
contracts for exact behavior. Review the note and affected canonical documents
|
|
||||||
as ordinary preparation, then commit the complete candidate.
|
|
||||||
|
|
||||||
End every new note with these links, replacing `VERSION` with the complete tag:
|
Publication requires explicit authorization. Local implementation commits do
|
||||||
|
not authorize a push or release.
|
||||||
|
|
||||||
|
## 2. Prepare and commit the candidate
|
||||||
|
|
||||||
|
Choose an unused stable version locally and on the authoritative remote. Compare
|
||||||
|
the candidate with the previous tag and review exported Go signatures and
|
||||||
|
ownership, YAML and resolution semantics, diagnostic contracts, minimum Go
|
||||||
|
version, and platform support. Review maintained consumer examples and the
|
||||||
|
migration guidance. Before v1.0.0, a minimum-Go increase that changes the
|
||||||
|
compatibility contract requires a minor release and an explanation of consumer
|
||||||
|
impact in the release note; patch releases remain compatible within their minor
|
||||||
|
line.
|
||||||
|
|
||||||
|
Create `docs/releases/<version>.md`. Its first line must be exactly
|
||||||
|
`# ConfigKit <version>`, and it must end with the license and attribution footer:
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/VERSION/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/VERSION/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/VERSION/docs/oss/attribution.md)
|
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/VERSION/COPYRIGHT) | [LICENSE](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/VERSION/LICENSE) | [ATTRIBUTION](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/VERSION/docs/oss/attribution.md)
|
||||||
```
|
```
|
||||||
|
|
||||||
Confirm the three target files exist in the candidate and review the version in
|
Replace `VERSION` with the full tag and confirm the three target files exist in
|
||||||
the URLs. The future tag need not resolve remotely yet; this review adds no
|
the candidate. Retire completed roadmaps, preserve unfinished and deferred
|
||||||
network gate. ConfigKit uses checked-in notes without hosted release objects.
|
work, and repair affected links. Review the complete candidate for unintended
|
||||||
If a hosted page is used later, reuse the note and preserve its footer. Do not
|
files and credentials, then commit it on `main`. These are ordinary preparation
|
||||||
rewrite historical notes or add downloadable license assets.
|
tasks, not separate approval gates. The `go.mod` change belongs in this
|
||||||
|
candidate.
|
||||||
|
|
||||||
|
## 3. Push the candidate to `main`
|
||||||
|
|
||||||
|
Push only that committed candidate to the literal authoritative URL. This does
|
||||||
|
not follow local tags:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git push --no-follow-tags https://gitea.maximumdirect.net/eric/ConfigKit.git HEAD:refs/heads/main
|
||||||
|
```
|
||||||
|
|
||||||
|
ReleaseKit requires authoritative `main` to equal this candidate before it
|
||||||
|
validates. A failure before this push means ReleaseKit publication has not
|
||||||
|
started. If the push result is uncertain, inspect authoritative `main` and
|
||||||
|
continue only when it is confirmed at the candidate. If validation later fails,
|
||||||
|
no release tag is published; correct the issue in a new commit, push the new
|
||||||
|
candidate to `main`, and invoke validation again.
|
||||||
|
|
||||||
## 4. Publish the committed candidate
|
## 4. Publish the committed candidate
|
||||||
|
|
||||||
Before publication, review compatibility, release-note accuracy, and the file
|
With the authorized candidate already on authoritative `main`, invoke the
|
||||||
list for unintended content or credentials. Retire completed roadmaps as part of
|
module-selected native executable exactly once:
|
||||||
release preparation, preserving unfinished and deferred work and repairing
|
|
||||||
links. These are ordinary preparation tasks, not separate human sign-offs.
|
|
||||||
|
|
||||||
With the release explicitly authorized, run the one publication command.
|
|
||||||
The script resolves the repository root from its own path, so it may be invoked
|
|
||||||
from another directory by using its path there. Replace the sample version with
|
|
||||||
the selected version:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./scripts/release.sh v0.5.0
|
"$releasekit_bin" publish vMAJOR.MINOR.PATCH
|
||||||
```
|
```
|
||||||
|
|
||||||
The command requires `main`, a clean index/worktree, an unused version locally
|
Replace the placeholder with the selected version. ReleaseKit checks the
|
||||||
and on the authoritative remote, and a matching note in the captured commit.
|
effective push URL against `expected_push_url`, verifies the candidate on
|
||||||
Ignored local files do not block it. It checks remote ancestry, runs
|
`main`, and checks the release-note H1 for the product and version. It then
|
||||||
[`release-check.sh`](../scripts/release-check.sh) once against that commit,
|
runs the selected race-enabled standard suite once with test-time vet disabled,
|
||||||
rechecks candidate and version state, pushes only that commit to `main`, then
|
one full vet pass, and CGO-disabled package compilation for Linux amd64 and
|
||||||
creates and pushes one annotated tag. The tag message is the checked-in note.
|
arm64. It publishes only the annotated tag for that candidate. Confirmed
|
||||||
It never commits or repairs the checkout.
|
publication of the intended tag completes the release. The tag points to the
|
||||||
|
captured commit and uses the complete checked-in note as its annotation message.
|
||||||
A successful intended tag push completes publication. The command reports the
|
The optional `check`
|
||||||
version and commit immediately. No CI, module retrieval, remote read-back, or
|
command is available for diagnosis but is not a separate required pass:
|
||||||
hosted release object is required. The checker may be run separately for
|
|
||||||
diagnosis, but it is not a prerequisite: a fresh publication invocation
|
```sh
|
||||||
validates its candidate once.
|
"$releasekit_bin" check vMAJOR.MINOR.PATCH
|
||||||
|
```
|
||||||
## 5. Recover from a failed or uncertain push
|
|
||||||
|
Status 0 confirms publication, status 1 reports a known publication failure or
|
||||||
The command performs one bounded authoritative-state check after a failed push.
|
conflict, status 2 reports usage, profile, or version errors, and status 3 means
|
||||||
If the main push failed but authoritative main equals the candidate, it proceeds.
|
the push outcome remains unknown. Preserve both the native exit status and its
|
||||||
If the tag push failed but its exact tag object reached the remote, it reports
|
diagnostics.
|
||||||
success. Otherwise it reports branch-only, conflicting, or unknown state and
|
|
||||||
retains any local tag.
|
The command does not push or repair `main`, wait for CI, or publish binaries or
|
||||||
|
a hosted release page. A successful intended-tag publication does not require
|
||||||
Inspect exact refs before any retry. Set `release_version` to the attempted
|
remote read-back or consumer retrieval; those checks remain optional.
|
||||||
version and compare main with the candidate from the command's report:
|
|
||||||
|
## 5. Recover from a failed or uncertain tag push
|
||||||
|
|
||||||
|
The executable reports the publication state, phase, version, candidate commit,
|
||||||
|
tag-object ID, effective endpoint, and error on standard error. It performs one
|
||||||
|
bounded authoritative reconciliation after a failed tag push. Exit status 3
|
||||||
|
means the tag outcome remains unknown; do not blindly retry or move a tag.
|
||||||
|
|
||||||
|
For an uncertain result, use the exact endpoint, candidate commit, version, and
|
||||||
|
tag-object ID from the report. Query the authoritative refs once:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
release_version=v0.5.0
|
|
||||||
authoritative=https://gitea.maximumdirect.net/eric/ConfigKit.git
|
authoritative=https://gitea.maximumdirect.net/eric/ConfigKit.git
|
||||||
if remote_main=$(git ls-remote --refs --exit-code "$authoritative" refs/heads/main); then
|
release_version=vMAJOR.MINOR.PATCH
|
||||||
printf 'remote main: %s\n' "$remote_main"
|
git ls-remote --refs "$authoritative" refs/heads/main "refs/tags/$release_version"
|
||||||
else
|
|
||||||
result=$?
|
|
||||||
if [ "$result" -eq 2 ]; then
|
|
||||||
printf 'authoritative main confirmed absent\n' >&2
|
|
||||||
else
|
|
||||||
printf 'remote main state unknown; inspect before retrying\n' >&2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
ref="refs/tags/$release_version"
|
|
||||||
if remote_tag=$(git ls-remote --refs --exit-code "$authoritative" "$ref"); then
|
|
||||||
printf 'remote tag: %s\n' "$remote_tag"
|
|
||||||
else
|
|
||||||
result=$?
|
|
||||||
if [ "$result" -eq 2 ]; then
|
|
||||||
printf 'remote tag confirmed absent\n'
|
|
||||||
else
|
|
||||||
printf 'remote tag state unknown; do not reuse the version\n' >&2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if local_tag=$(git rev-parse --verify "refs/tags/$release_version" 2>/dev/null); then
|
|
||||||
printf 'local tag object: %s\n' "$local_tag"
|
|
||||||
else
|
|
||||||
printf 'no local tag object\n'
|
|
||||||
fi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
A query failure is not proof that the tag is absent. Compare the remote tag
|
Compare the `main` object ID with the reported candidate commit and the tag
|
||||||
object ID with the local annotated tag object ID; its peeled commit alone is
|
object ID with the reported tag-object ID. `--refs` returns the annotated tag
|
||||||
insufficient to establish identical publication. A conflicting published tag
|
object itself, not merely its peeled commit. If the tag object matches, the
|
||||||
must not be moved or replaced. If remote main advanced but the tag was not
|
release is published. If the query fails, the result is conflicting, or the
|
||||||
published, report that partial progress. Only after confirming the local tag
|
endpoint or candidate differs, stop and resolve the state before retrying. If
|
||||||
was never published or used by another channel may a maintainer remove it:
|
the endpoint is reachable, the tag is confirmed absent, and `main` still equals
|
||||||
|
the reported candidate, remove a retained local tag only after confirming it
|
||||||
|
was not published or used by another channel; then rerun `publish`, which
|
||||||
|
revalidates the candidate:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git tag -d "$release_version"
|
git tag -d "$release_version"
|
||||||
|
"$releasekit_bin" publish "$release_version"
|
||||||
```
|
```
|
||||||
|
|
||||||
A subsequent invocation revalidates the candidate; a correctly published tag
|
A validation failure after the candidate reached `main` leaves an untagged
|
||||||
needs no rerun. Do not roll back main or automatically delete a tag. Transport
|
candidate there. Correct it with a new commit rather than rewriting or rolling
|
||||||
or authentication failure alone does not require a new version. A defect in
|
back `main`. A conflicting published tag must never be moved or reused. A
|
||||||
published source requires a corrected commit, a new semantically appropriate
|
defect in published source requires a corrected commit, a new semantically
|
||||||
version and note, and the release procedure again.
|
appropriate version, and a matching release note.
|
||||||
|
|
||||||
## 6. Optional consumer verification
|
## 6. Optional consumer verification
|
||||||
|
|
||||||
Only when useful or requested, read back the remote tag or retrieve the exact
|
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
|
version into a new module without a local `replace` directive. These checks are
|
||||||
are optional and non-gating. Do not poll CI or wait for module propagation.
|
optional and non-gating. Do not poll CI or wait for module propagation. For
|
||||||
Private-host credentials must already be configured. For direct-origin
|
direct-origin verification, use the exact published version in a temporary
|
||||||
verification, set the version to the release just published (replace the sample
|
consumer module:
|
||||||
below), then use a temporary consumer module and check the exact module path
|
|
||||||
and version reported by `go list -m`:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
release_version=v0.5.0
|
release_version=vMAJOR.MINOR.PATCH
|
||||||
consumer_dir=$(mktemp -d)
|
consumer_dir=$(mktemp -d)
|
||||||
(
|
(
|
||||||
cd "$consumer_dir"
|
cd "$consumer_dir"
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ Completion evidence: policy and roadmap agree on order, gates, compatibility,
|
|||||||
and completion; no policy passage still describes the retired script or its
|
and completion; no policy passage still describes the retired script or its
|
||||||
stronger automated guards as current behavior.
|
stronger automated guards as current behavior.
|
||||||
|
|
||||||
## Stage 4: Replace the maintainer release runbook
|
## Stage 4: Replace the maintainer release runbook — Complete
|
||||||
|
|
||||||
1. Revise [the runbook](../release.md) into copyable steps for the adopted
|
1. Revise [the runbook](../release.md) into copyable steps for the adopted
|
||||||
process. During this cutover, move `go.mod` from its current minimum to the
|
process. During this cutover, move `go.mod` from its current minimum to the
|
||||||
|
|||||||
Reference in New Issue
Block a user