274 lines
13 KiB
Markdown
274 lines
13 KiB
Markdown
# ConfigKit Release Procedure
|
|
|
|
This is the maintainer runbook for ConfigKit's [release policy](policy/release.md).
|
|
A release publishes the Go source under a new Git tag, with a matching release
|
|
note committed in the repository. The tag is annotated: it includes the release
|
|
note text and points to the released commit. Published tags and contents are
|
|
immutable. There is no binary, container image, generated package, or separate
|
|
registry publication.
|
|
|
|
A **candidate** is the committed revision you intend to release. The
|
|
**authoritative remote** is the repository where ConfigKit publishes releases;
|
|
its exact push URL is listed below. The **consumer minimum** is the oldest Go
|
|
version supported for applications using ConfigKit.
|
|
|
|
## Release identity and support
|
|
|
|
- Module: `go.maximumdirect.net/configkit`
|
|
- Primary branch: `main`
|
|
- Authoritative remote: `origin`
|
|
- Authoritative push URL: `ssh://git@forge.maximumdirect.net:22222/base-layer/configkit.git`
|
|
- Tags: stable Semantic Versions such as `v0.5.0`; no prereleases
|
|
- 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, as
|
|
recorded in `go.mod`; see the
|
|
[consumer minimum policy](policy/release.md#consumer-minimum-and-release-toolchain)
|
|
- Release-gated targets: Linux amd64 and arm64
|
|
- Best-effort targets: macOS amd64 and arm64, under the
|
|
[platform support policy](policy/release.md#platform-support)
|
|
|
|
Release validation runs the offline Go test suite once with the race detector,
|
|
then a full vet pass and package compilation for both Linux targets with cgo
|
|
disabled (`CGO_ENABLED=0`). macOS and CI are
|
|
not release gates; do not wait for or poll CI.
|
|
|
|
The vanity module path is the name applications put in Go imports. Source/tag
|
|
browser links and public Go retrieval use the Forge HTTPS repository
|
|
independently of SSH publication.
|
|
|
|
## 1. Select Go and prepare the tool
|
|
|
|
Use a clean checkout with Git, Bash 3.2 or newer, and a native compiler for race
|
|
tests. Choose the release type before selecting Go. For a minor or major release,
|
|
select the newest stable Go series available in the development environment.
|
|
For a patch release, retain the series recorded in `go.mod` and leave its consumer
|
|
minimum unchanged. Follow the
|
|
[toolchain selection policy](policy/release.md#configkit-toolchain-selection).
|
|
|
|
Choose the latest compatible patch installed locally in that Go series. Put
|
|
its `go` executable first on `PATH` and ensure it meets the minimum in
|
|
`go.mod`. The same `go` executable must build ReleaseKit and run its child
|
|
validation commands. Set local toolchain selection and verify it:
|
|
|
|
```sh
|
|
export GOTOOLCHAIN=local
|
|
go version
|
|
go env GOVERSION
|
|
command -v go
|
|
```
|
|
|
|
Only when a minor or major release advances the consumer minimum to a newer Go
|
|
series, derive that series' initial stable release from the selected executable:
|
|
|
|
```sh
|
|
selected_go=$(GOTOOLCHAIN=local go env GOVERSION)
|
|
selected_series=${selected_go#go}
|
|
selected_series=${selected_series%.*}
|
|
release_minimum="$selected_series.0"
|
|
go mod edit -go="$release_minimum"
|
|
```
|
|
|
|
If a dependency or identified correctness fix requires a higher patch minimum,
|
|
use that required version instead and document the reason. When the series is
|
|
unchanged, leave the minimum unchanged unless a minor or major release
|
|
deliberately changes it under the
|
|
[consumer minimum policy](policy/release.md#consumer-minimum-and-release-toolchain).
|
|
For example:
|
|
|
|
| Release situation | Go used for validation | Consumer minimum |
|
|
| --- | --- | --- |
|
|
| Patch release in the 1.26 series | Latest compatible patch installed locally, such as 1.26.8 | Keep the existing `go.mod` minimum unchanged. |
|
|
| Minor/major release staying in 1.26 | Latest compatible patch installed locally | Normally retain the minimum; any deliberate change follows the linked policy. |
|
|
| Minor/major release adopting a newer installed stable series | Latest compatible patch installed locally in that series | Normally declare that series' initial stable patch, or the higher patch required by a dependency or identified fix. |
|
|
|
|
Using Go 1.26.8 for validation can therefore leave `go 1.26.0` unchanged. Users
|
|
need the declared minimum, while maintainers validate with their selected patch.
|
|
There is no separate requirement to validate with Go 1.26.0.
|
|
|
|
Prepare dependencies whether or not the minimum changes:
|
|
|
|
```sh
|
|
go mod download
|
|
```
|
|
|
|
Build the ReleaseKit version recorded in `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:
|
|
|
|
```sh
|
|
releasekit_tmp=$(mktemp -d "${TMPDIR:-/tmp}/configkit-releasekit.XXXXXX")
|
|
releasekit_bin="$releasekit_tmp/releasekit"
|
|
go build -o "$releasekit_bin" gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit
|
|
go list -m gitea.maximumdirect.net/eric/ReleaseKit
|
|
"$releasekit_bin" version
|
|
```
|
|
|
|
This repository selects ReleaseKit v0.4.2. Use the compiled program above for
|
|
publication so its exit status and publication report reach you unchanged.
|
|
`go run` maps a nonzero child exit status to 1. ReleaseKit's child Go commands
|
|
use the `go` found on `PATH` with `GOTOOLCHAIN=local`; keep the selected
|
|
executable first on `PATH` through the publication command.
|
|
|
|
Verify access to the authoritative remote without displaying credentials:
|
|
|
|
```sh
|
|
git ls-remote ssh://git@forge.maximumdirect.net:22222/base-layer/configkit.git HEAD refs/heads/main
|
|
```
|
|
|
|
Publication requires explicit authorization. Local implementation commits do
|
|
not authorize a push or release.
|
|
|
|
## 2. Prepare and commit the candidate
|
|
|
|
Choose a stable version that has not been used locally or on the publication
|
|
remote. Compare the candidate with the previous tag. Review public Go APIs and
|
|
value ownership, YAML and resolution rules, diagnostic contracts, the minimum
|
|
Go version, and platform support. Review maintained consumer examples and the
|
|
migration guidance. Explain any consumer-minimum increase and required consumer
|
|
action under the
|
|
[consumer minimum policy](policy/release.md#consumer-minimum-and-release-toolchain).
|
|
|
|
Create `docs/releases/<version>.md`. Its first line must be exactly
|
|
`# ConfigKit <version>`, and it must end with the license and attribution footer:
|
|
|
|
```markdown
|
|
[COPYRIGHT](https://forge.maximumdirect.net/base-layer/configkit/src/tag/VERSION/COPYRIGHT) | [LICENSE](https://forge.maximumdirect.net/base-layer/configkit/src/tag/VERSION/LICENSE) | [ATTRIBUTION](https://forge.maximumdirect.net/base-layer/configkit/src/tag/VERSION/docs/oss/attribution.md)
|
|
```
|
|
|
|
Replace `VERSION` with the full tag and confirm the three target files exist in
|
|
the candidate. Retire completed roadmaps, preserve unfinished and deferred
|
|
work, and repair affected links. Review the complete candidate for unintended
|
|
files and credentials, then commit it on `main`. These are ordinary preparation
|
|
tasks, not separate approval gates. Any `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 ssh://git@forge.maximumdirect.net:22222/base-layer/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
|
|
|
|
Once publication is authorized and the candidate is on the publication remote's
|
|
`main`, run the compiled ReleaseKit program once to validate and publish it:
|
|
|
|
```sh
|
|
"$releasekit_bin" publish vMAJOR.MINOR.PATCH
|
|
```
|
|
|
|
Replace the placeholder with the selected version. ReleaseKit checks the
|
|
push URL Git will actually use, after URL rewrites, against `expected_push_url`.
|
|
It verifies the candidate on
|
|
`main`, and checks the release-note H1 for the product and version. It then
|
|
runs the selected race-enabled standard suite once with test-time vet disabled,
|
|
one full vet pass, and CGO-disabled package compilation for Linux amd64 and
|
|
arm64. It publishes only the annotated tag for that candidate. Confirmed
|
|
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.
|
|
Use the optional `check` command for diagnosis when useful. It validates without
|
|
publishing and is not required before `publish`, which performs its own checks:
|
|
|
|
```sh
|
|
"$releasekit_bin" check vMAJOR.MINOR.PATCH
|
|
```
|
|
|
|
For `publish`, interpret the program's exit status as follows:
|
|
|
|
| Status | Meaning | Next action |
|
|
| --- | --- | --- |
|
|
| `0` | Publication succeeded. | The release is complete. Optional verification can follow. |
|
|
| `1` | A known publication failure or conflict was reported. | Read the report and correct the stated problem. Preserve any published tag. |
|
|
| `2` | The command, profile, or version is invalid. | Correct the command or configuration before trying again. |
|
|
| `3` | The tag push outcome is still unknown. | Follow the recovery procedure below before retrying or declaring success. |
|
|
|
|
Keep both the program's exit status and its diagnostic report. Using `go run`
|
|
would lose the distinction between its nonzero exit statuses.
|
|
|
|
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
|
|
remote read-back or consumer retrieval; those checks remain optional.
|
|
|
|
## 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. After a failed
|
|
tag push, it makes one bounded check of the exact publication remote to find out
|
|
whether the tag arrived. Exit status 3
|
|
means the tag outcome remains unknown; do not blindly retry or move a tag.
|
|
|
|
An annotated tag is a Git object containing the release note and a pointer to
|
|
the source commit. Its tag-object ID identifies that complete object. Checking
|
|
only the commit ID would not establish that the intended tag object arrived.
|
|
|
|
For an uncertain result, use the exact endpoint (remote URL), candidate commit,
|
|
version, and tag-object ID from the report. Copy the captured endpoint into the variable
|
|
below using normal shell quoting. Check that Git still resolves it to itself,
|
|
then query the authoritative refs once:
|
|
|
|
```sh
|
|
authoritative='COPY_CAPTURED_ENDPOINT'
|
|
release_version='vMAJOR.MINOR.PATCH'
|
|
test "$(git ls-remote --get-url -- "$authoritative")" = "$authoritative" &&
|
|
git ls-remote --refs -- "$authoritative" refs/heads/main "refs/tags/$release_version"
|
|
```
|
|
|
|
A failed URL-equality check or remote query leaves the outcome unresolved.
|
|
Correct transport configuration before continuing; a rewritten destination
|
|
cannot establish absence at the captured endpoint.
|
|
|
|
Compare the `main` object ID with the reported candidate commit and the tag
|
|
object ID with the reported tag-object ID. `--refs` returns the annotated tag
|
|
object itself. A peeled commit is the source commit reached by following that
|
|
tag; it is a different identity. If the tag object matches, the
|
|
release is published. If the query fails, the result is conflicting, or the
|
|
endpoint or candidate differs, stop and resolve the state before retrying. If
|
|
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
|
|
git tag -d "$release_version"
|
|
"$releasekit_bin" publish "$release_version"
|
|
```
|
|
|
|
A validation failure after the candidate reached `main` leaves an untagged
|
|
candidate there. Correct it with a new commit rather than rewriting or rolling
|
|
back `main`. A conflicting published tag must never be moved or reused. A
|
|
defect in published source requires a corrected commit, a new semantically
|
|
appropriate version, and a matching release note.
|
|
|
|
## 6. Optional consumer verification
|
|
|
|
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. Do not poll CI or wait for module propagation. For
|
|
direct-origin verification, use the exact published version in a temporary
|
|
consumer module:
|
|
|
|
```sh
|
|
release_version=vMAJOR.MINOR.PATCH
|
|
consumer_dir=$(mktemp -d)
|
|
(
|
|
cd "$consumer_dir"
|
|
go mod init example.invalid/configkit-release-check
|
|
GOWORK=off GOPROXY=direct go get "go.maximumdirect.net/configkit@$release_version"
|
|
GOWORK=off go list -m -f '{{.Path}} {{.Version}}' go.maximumdirect.net/configkit
|
|
)
|
|
```
|
|
|
|
The output should show the exact module path and selected version. Report this
|
|
optional check separately from publication success.
|