Document the source release workflow
This commit is contained in:
133
docs/release.md
Normal file
133
docs/release.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# CommandKit release procedure
|
||||
|
||||
## Scope and prerequisites
|
||||
|
||||
CommandKit publishes source-only Go module releases: an immutable annotated
|
||||
version tag for one committed candidate and its matching checked-in release
|
||||
note. This procedure does not publish a hosted release page, binaries,
|
||||
checksums, or other delivery assets, and it requires no delivery credentials.
|
||||
|
||||
Use branch `main`, remote `origin`, and the expected effective push endpoint
|
||||
`https://gitea.maximumdirect.net/eric/CommandKit.git`. The profile enforces that
|
||||
endpoint during publication. The maintainer needs a local Go toolchain
|
||||
compatible with the module's Go 1.25.5 minimum, Git with push access to `origin`,
|
||||
and a POSIX shell.
|
||||
|
||||
This procedure follows the [release policy](policy/release.md); the release-note
|
||||
footer follows the [OSS discoverability policy](policy/oss.md#discoverability).
|
||||
|
||||
ReleaseKit v0.2.0 owns candidate validation, publication guards, annotated tag
|
||||
creation, tag publication, and outcome reporting. The repository profile selects
|
||||
only its default candidate suite. The launcher bootstraps the module-selected
|
||||
native executable into ignored `.releasekit/` storage and invokes its `publish`
|
||||
command. The upstream [CLI reference](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/cli.md),
|
||||
[profile reference](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/config.md),
|
||||
and [publication guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/consumers/publish.md)
|
||||
own detailed command, profile, and status contracts.
|
||||
|
||||
## Prepare and publish a candidate
|
||||
|
||||
Choose a fresh, unused stable Semantic Version tag in the form
|
||||
`vMAJOR.MINOR.PATCH`, with no leading zeroes. Before `v1.0.0`, a minor release
|
||||
may change a documented contract if its note explains the impact and consumer
|
||||
action; a patch release must not intentionally break contracts within its
|
||||
minor line. Never reuse a version previously published through any release
|
||||
channel. The first version is selected during release preparation, not embedded
|
||||
in the tool or profile.
|
||||
|
||||
Start from `main` and prepare a note at `docs/releases/<version>.md`. Begin with
|
||||
a heading containing `CommandKit` and the exact version. Include a useful
|
||||
summary and any compatibility impact or upgrade instructions. End it with the
|
||||
license and attribution links at that version's immutable source tag, replacing
|
||||
`VERSION` with the selected tag:
|
||||
|
||||
```markdown
|
||||
[COPYRIGHT](https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/COPYRIGHT) |
|
||||
[LICENSE](https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/LICENSE) |
|
||||
[ATTRIBUTION](https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/docs/oss/attribution.md)
|
||||
```
|
||||
|
||||
Review these paths in the candidate; the future tag URLs do not need to resolve
|
||||
before publication.
|
||||
|
||||
Prepare dependencies separately from the offline candidate suite:
|
||||
|
||||
Run these commands in the same shell session so the version variable remains
|
||||
set:
|
||||
|
||||
```sh
|
||||
commandkit_version=vX.Y.Z
|
||||
GOWORK=off GOTOOLCHAIN=local go mod download
|
||||
```
|
||||
|
||||
If dependency preparation changes `go.mod` or `go.sum`, review and commit those
|
||||
changes with the candidate. Commit the note and all candidate changes on `main`,
|
||||
then publish that commit to `origin`:
|
||||
|
||||
```sh
|
||||
git push origin HEAD:refs/heads/main
|
||||
./scripts/release "$commandkit_version"
|
||||
```
|
||||
|
||||
The launcher builds the ReleaseKit version selected by the module in readonly
|
||||
mode. After preflight, `publish` runs the offline default Go suite once, checks
|
||||
the candidate and remote guards, creates an annotated tag from the committed
|
||||
note, and pushes only that tag object. The launcher does not push `main`; push
|
||||
the candidate first. Do not require a separate `check` before `publish` or repeat
|
||||
the same suite. Successful source publication does not wait for CI, module
|
||||
availability, or consumer installation.
|
||||
|
||||
## Completion and statuses
|
||||
|
||||
Publication is complete when the intended annotated tag is successfully pushed
|
||||
to the expected endpoint, or when an exact tag-object query confirms that push
|
||||
after an error. Status 0 reports confirmed publication. Status 1 reports a
|
||||
known failure or conflict; status 2 reports invalid input or profile; status 3
|
||||
means the push outcome remains unresolved. See the upstream [CLI status
|
||||
contract](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/cli.md).
|
||||
|
||||
For a failed or ambiguous push, use the exact endpoint, version, and annotated
|
||||
tag object reported by the invocation. Confirm that Git still resolves the
|
||||
captured endpoint to itself before querying only that tag ref:
|
||||
|
||||
```sh
|
||||
releasekit_endpoint='COPY THE CAPTURED ENDPOINT EXACTLY'
|
||||
releasekit_version='vX.Y.Z'
|
||||
releasekit_tag_object='COPY THE REPORTED TAG OBJECT ID'
|
||||
releasekit_ref="refs/tags/$releasekit_version"
|
||||
|
||||
releasekit_resolved_endpoint=$(git ls-remote --get-url -- "$releasekit_endpoint") || {
|
||||
printf '%s\n' 'publication state is unresolved: endpoint resolution failed' >&2
|
||||
exit 1
|
||||
}
|
||||
if [ "$releasekit_resolved_endpoint" != "$releasekit_endpoint" ]; then
|
||||
printf '%s\n' 'publication state is unresolved: endpoint resolution changed' >&2
|
||||
exit 1
|
||||
fi
|
||||
releasekit_remote_tag=$(git ls-remote --refs -- "$releasekit_endpoint" "$releasekit_ref") || {
|
||||
printf '%s\n' 'publication state is unresolved: remote query failed' >&2
|
||||
exit 1
|
||||
}
|
||||
printf '%s\n' "$releasekit_remote_tag"
|
||||
```
|
||||
|
||||
`--refs` returns the annotated tag object's ID, not its peeled commit. Compare
|
||||
that ID with `releasekit_tag_object`: an exact match confirms publication; a
|
||||
different object is a conflict; successful output with no matching ref proves
|
||||
absence at the captured endpoint. A failed query or changed URL resolution is
|
||||
unresolved, never evidence of absence. Do not query a different fetch URL or
|
||||
follow a rewritten endpoint to infer the captured destination's state.
|
||||
|
||||
ReleaseKit retains a local tag after a failed push, so the tag blocks an ordinary
|
||||
retry. After proving the tag absent at the captured endpoint,
|
||||
confirm that it was never published or used by another release channel before
|
||||
deliberately removing the unpublished local tag:
|
||||
|
||||
```sh
|
||||
git tag -d "$releasekit_version"
|
||||
```
|
||||
|
||||
Do not move, overwrite, recreate, or delete a published tag. If the exact tag
|
||||
object is present remotely, keep it. Resolve conflicts with a fresh version;
|
||||
corrections to published source require a new version. A transport failure
|
||||
alone does not require a new version.
|
||||
Reference in New Issue
Block a user