198 lines
8.7 KiB
Markdown
198 lines
8.7 KiB
Markdown
# ConfigKit Release Procedure
|
|
|
|
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
|
|
matching checked-in release note are the deliverables; there is no binary,
|
|
container image, generated package, or separate registry publication.
|
|
|
|
## Release identity and support
|
|
|
|
- Module: `gitea.maximumdirect.net/eric/ConfigKit`
|
|
- Primary branch: `main`
|
|
- Authoritative remote: `origin`, with fetch and push destination
|
|
`https://gitea.maximumdirect.net/eric/ConfigKit.git`
|
|
- Tags: stable Semantic Versions such as `v0.5.0`; no prereleases
|
|
- Minimum Go line: Go 1.25, validated with one maintained patch
|
|
- Release-gated targets: Linux amd64 and arm64
|
|
- Best-effort targets: macOS amd64 and arm64, under the
|
|
[platform support policy](policy/release.md#platform-support)
|
|
|
|
The checker runs one offline race suite, one full vet pass, and CGO-disabled
|
|
builds for both Linux targets. It reports skipped filesystem cases. macOS checks
|
|
and newer-toolchain tests are optional. CI is never a readiness or completion
|
|
gate; do not wait for or poll it.
|
|
|
|
## 1. Prepare access and tools
|
|
|
|
Use a clean checkout with Git, Bash 3.2 or newer, a maintained Go 1.25 patch,
|
|
and a native compiler for race tests. Configure private-host access through the
|
|
normal credential manager. Keep credentials and environment dumps outside the
|
|
repository and command transcript.
|
|
|
|
Verify access without displaying credentials:
|
|
|
|
```sh
|
|
git remote get-url origin
|
|
git ls-remote origin HEAD
|
|
```
|
|
|
|
The fetch URL and all configured push destinations must resolve to the
|
|
authoritative URL above. The publication command checks this again. Acquire the
|
|
selected toolchain and dependencies explicitly before offline validation:
|
|
|
|
```sh
|
|
export CONFIGKIT_BASELINE_TOOLCHAIN=go1.25.14
|
|
GOTOOLCHAIN="$CONFIGKIT_BASELINE_TOOLCHAIN" go version
|
|
GOTOOLCHAIN="$CONFIGKIT_BASELINE_TOOLCHAIN" go mod download
|
|
```
|
|
|
|
A maintained patch may replace the example pin. No per-release latest-version
|
|
survey is required. Dependencies must be cached; the checker sets `GOPROXY=off`.
|
|
Publication requires user authorization. Standing authorization for local
|
|
commits does not authorize a push or release.
|
|
|
|
## 2. Select the version and review compatibility
|
|
|
|
Choose an unused stable version. Compare the candidate with the previous tag.
|
|
Review exported Go signatures and ownership, YAML and scalar semantics,
|
|
precedence, diagnostic codes, minimum Go version, and platform support. A
|
|
signature-preserving change may still affect consumers. Before `v1.0.0`,
|
|
intentional incompatible minor-release changes need a note explaining impact
|
|
and consumer action; patch releases remain compatible within their minor line.
|
|
Review the maintained consumer examples and migration guide.
|
|
|
|
When dependencies change, review parser behavior and migration impact as part
|
|
of that change. Module tidiness belongs to dependency-change review, not the
|
|
release checker. Do not repeat a dependency-version survey for every release.
|
|
|
|
## 3. Write and commit the release note
|
|
|
|
Create `docs/releases/<version>.md` for the selected version. Its first line
|
|
must be `# ConfigKit <version>`. Give a meaningful summary and include
|
|
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:
|
|
|
|
```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)
|
|
```
|
|
|
|
Confirm the three target files exist in the candidate and review the version in
|
|
the URLs. The future tag need not resolve remotely yet; this review adds no
|
|
network gate. ConfigKit uses checked-in notes without hosted release objects.
|
|
If a hosted page is used later, reuse the note and preserve its footer. Do not
|
|
rewrite historical notes or add downloadable license assets.
|
|
|
|
## 4. Publish the committed candidate
|
|
|
|
Before publication, review compatibility, release-note accuracy, and the file
|
|
list for unintended content or credentials. Retire completed roadmaps as part of
|
|
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
|
|
./scripts/release.sh v0.5.0
|
|
```
|
|
|
|
The command requires `main`, a clean index/worktree, an unused version locally
|
|
and on the authoritative remote, and a matching note in the captured commit.
|
|
Ignored local files do not block it. It checks remote ancestry, runs
|
|
[`release-check.sh`](../scripts/release-check.sh) once against that commit,
|
|
rechecks candidate and version state, pushes only that commit to `main`, then
|
|
creates and pushes one annotated tag. The tag message is the checked-in note.
|
|
It never commits or repairs the checkout.
|
|
|
|
A successful intended tag push completes publication. The command reports the
|
|
version and commit immediately. No CI, module retrieval, remote read-back, or
|
|
hosted release object is required. The checker may be run separately for
|
|
diagnosis, but it is not a prerequisite: a fresh publication invocation
|
|
validates its candidate once.
|
|
|
|
## 5. Recover from a failed or uncertain push
|
|
|
|
The command performs one bounded authoritative-state check after a failed push.
|
|
If the main push failed but authoritative main equals the candidate, it proceeds.
|
|
If the tag push failed but its exact tag object reached the remote, it reports
|
|
success. Otherwise it reports branch-only, conflicting, or unknown state and
|
|
retains any local tag.
|
|
|
|
Inspect exact refs before any retry. Set `release_version` to the attempted
|
|
version and compare main with the candidate from the command's report:
|
|
|
|
```sh
|
|
release_version=v0.5.0
|
|
authoritative=https://gitea.maximumdirect.net/eric/ConfigKit.git
|
|
if remote_main=$(git ls-remote --refs --exit-code "$authoritative" refs/heads/main); then
|
|
printf 'remote main: %s\n' "$remote_main"
|
|
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
|
|
object ID with the local annotated tag object ID; its peeled commit alone is
|
|
insufficient to establish identical publication. A conflicting published tag
|
|
must not be moved or replaced. If remote main advanced but the tag was not
|
|
published, report that partial progress. Only after confirming the local tag
|
|
was never published or used by another channel may a maintainer remove it:
|
|
|
|
```sh
|
|
git tag -d "$release_version"
|
|
```
|
|
|
|
A subsequent invocation revalidates the candidate; a correctly published tag
|
|
needs no rerun. Do not roll back main or automatically delete a tag. Transport
|
|
or authentication failure alone does not require a new version. A defect in
|
|
published source requires a corrected commit, a new semantically appropriate
|
|
version and note, and the release procedure again.
|
|
|
|
## 6. Optional consumer verification
|
|
|
|
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. Do not poll CI or wait for module propagation.
|
|
Private-host credentials must already be configured. For direct-origin
|
|
verification, use a temporary consumer module and check the exact module path
|
|
and version reported by `go list -m`:
|
|
|
|
```sh
|
|
consumer_dir=$(mktemp -d)
|
|
(
|
|
cd "$consumer_dir"
|
|
go mod init example.invalid/configkit-release-check
|
|
GOPRIVATE=gitea.maximumdirect.net GOPROXY=direct go get "gitea.maximumdirect.net/eric/ConfigKit@$release_version"
|
|
go list -m -f '{{.Path}} {{.Version}}' gitea.maximumdirect.net/eric/ConfigKit
|
|
)
|
|
```
|
|
|
|
The output should show the exact module path and selected version. Report this
|
|
optional check separately from publication success.
|