164 lines
7.8 KiB
Markdown
164 lines
7.8 KiB
Markdown
# ReleaseKit release procedure
|
|
|
|
ReleaseKit's deliverables are its source tag and a hosted Gitea page containing
|
|
the complete checked-in release note. The page is delivered asynchronously by
|
|
the prepared [Woodpecker workflow](../examples/woodpecker/releasekit-source-delivery.yml);
|
|
it is not a source-release completion check and carries no binary assets. The
|
|
authoritative repository is `origin` at
|
|
`https://gitea.maximumdirect.net/eric/ReleaseKit.git`, and the release branch
|
|
is `main`. A release completes when the intended annotated tag is successfully
|
|
pushed, or when one exact-object recovery query confirms that the push
|
|
succeeded. CI and consumer installation are not completion checks.
|
|
|
|
## Prepare a candidate
|
|
|
|
Use a clean clone on `main`. The chosen build toolchain is Go 1.26.8. Make that
|
|
local installation the active `go` command for preparation, building the release
|
|
executable, and candidate validation:
|
|
|
|
```sh
|
|
export GOTOOLCHAIN=local
|
|
go version
|
|
```
|
|
|
|
Record the reported version with the validation results. The
|
|
[Go support policy](policy/release.md#go-minimum-and-release-toolchain) owns
|
|
series selection, consumer minima, patch exceptions, and validation scope.
|
|
The maintained [source-delivery image](../examples/woodpecker/releasekit-source-delivery.yml)
|
|
and [consumer template image](../examples/woodpecker/tag-delivery.yml) follow the
|
|
chosen build toolchain; update them together when that selection changes.
|
|
|
|
The current consumer minimum is declared in [go.mod](../go.mod). When preparing
|
|
a release that raises it to Go 1.26, the normal declaration is:
|
|
|
|
```sh
|
|
go mod edit -go=1.26.0
|
|
```
|
|
|
|
Retain any higher patch minimum required by dependencies or a specific fix.
|
|
Make module changes before committing the candidate. A release preserving the
|
|
existing minimum keeps its current declaration. Minimum increases must fit the
|
|
[compatibility policy](policy/release.md#versioning-and-compatibility); before
|
|
v1.0.0, an increase belongs in a minor release.
|
|
|
|
Choose an unused stable `vMAJOR.MINOR.PATCH` version, and add
|
|
`docs/releases/<version>.md` with a first heading containing `ReleaseKit` and
|
|
the exact version. Summarize the change and any compatibility or upgrade impact.
|
|
End the note with the three license links at the immutable source tag:
|
|
|
|
```markdown
|
|
[COPYRIGHT](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/vX.Y.Z/COPYRIGHT) |
|
|
[LICENSE](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/vX.Y.Z/LICENSE) |
|
|
[ATTRIBUTION](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/vX.Y.Z/docs/oss/attribution.md)
|
|
```
|
|
|
|
Replace every `vX.Y.Z` with the selected version. These are links to the same
|
|
release note's immutable source tree and also work when the note is used as a
|
|
hosted release body.
|
|
|
|
Commit the note and all candidate changes on `main`, then publish that commit
|
|
to `origin/main`:
|
|
|
|
```sh
|
|
git push origin main
|
|
```
|
|
|
|
ReleaseKit checks that the worktree is clean, the candidate is on `main`, the
|
|
authoritative branch identifies that commit, and the version is unused locally
|
|
and on `origin`.
|
|
|
|
## Prepare dependencies, then publish
|
|
|
|
Dependency preparation is separate from the offline candidate validation. Run
|
|
it from the captured candidate source before the release command:
|
|
|
|
```sh
|
|
GOTOOLCHAIN=local go mod download
|
|
```
|
|
|
|
Then run the single publication command from the repository root:
|
|
|
|
```sh
|
|
./scripts/release vX.Y.Z
|
|
```
|
|
|
|
The command always runs the offline default test suite,
|
|
`go test -mod=readonly -count=1 ./...`, with toolchain and module network
|
|
access disabled. Any profile `checks` run afterward as additional product
|
|
checks; they should not repeat the standard suite. The command checks the
|
|
authoritative remote state, creates a local annotated tag from the captured
|
|
candidate and checked-in note, rechecks the tag and remote guards, and pushes
|
|
only that tag object. It prints the published version, commit, and tag object
|
|
on success. Exit status 0 means publication is confirmed; status 1 means a
|
|
known failure or conflict; status 2 means invalid input or profile; status 3
|
|
means the push outcome remains unknown and requires resolution.
|
|
|
|
The wrapper builds the native CLI into ignored `.releasekit/bin`, then invokes
|
|
it directly to preserve its exit status. The CLI comes from the source tree
|
|
being published, so this command also works
|
|
for ReleaseKit's first release before any released ReleaseKit executable
|
|
exists. It uses the local Go toolchain. Preparation must finish before the
|
|
offline validation begins.
|
|
|
|
## Resolve failures
|
|
|
|
A failed push triggers a URL-resolution check against the captured endpoint,
|
|
then at most one exact authoritative tag query. If the endpoint no longer
|
|
resolves to itself, recovery reports `unknown` without querying the rewritten
|
|
destination. An exact tag-object match confirms publication even when Git
|
|
reported an error. A proven absent tag is `not_published`; a different object
|
|
is a conflict; an unreadable query is `unknown`. The command retains its local
|
|
tag and reports the state, phase,
|
|
candidate commit, tag object, and captured publication endpoint when available.
|
|
Do not retry an unknown result until you resolve the authoritative ref at that
|
|
exact endpoint. Copy the reported endpoint value into the variable below using
|
|
normal shell quoting; retain the version and tag object from the same result.
|
|
The command checks that Git URL rewriting still resolves this value to itself;
|
|
the manual check below applies the same rule before querying:
|
|
|
|
```sh
|
|
releasekit_endpoint='COPY_CAPTURED_ENDPOINT'
|
|
releasekit_version='vX.Y.Z'
|
|
test "$(git ls-remote --get-url -- "$releasekit_endpoint")" = "$releasekit_endpoint" &&
|
|
git ls-remote --refs -- "$releasekit_endpoint" "refs/tags/$releasekit_version"
|
|
```
|
|
|
|
A failed URL-equality check or remote query leaves the outcome unresolved.
|
|
Correct transport configuration before continuing. The reported endpoint may
|
|
be different from `origin`'s fetch URL or its current push URL; querying either
|
|
of those does not establish absence at the captured endpoint.
|
|
|
|
Compare the returned object ID with the tag object reported by the command.
|
|
An exact match confirms publication. If the ref is absent, the version was not
|
|
published; only then may you deliberately remove the unpublished local tag
|
|
with `git tag -d vX.Y.Z` before correcting the candidate or retrying. Do not
|
|
move, overwrite, or delete a tag that exists remotely. If a different object
|
|
uses the version, choose a new version after resolving the conflict.
|
|
|
|
If validation or a guard fails before this invocation pushes, correct and
|
|
commit the candidate, publish `main`, and run the command again. A retained
|
|
local tag still requires the authoritative-state check above before removal;
|
|
a concurrent publisher may already have published the version. The prepared
|
|
Woodpecker workflow builds the CLI from the tagged checkout, then uses
|
|
`ci-deliver` for page-only delivery. To activate it after configuring the
|
|
destination and secret, copy it to `.woodpecker/releasekit-source-delivery.yml`
|
|
and add the Woodpecker repository secret `releasekit_gitea_token`. If page
|
|
delivery fails or is uncertain, inspect the hosted release and retry the same
|
|
immutable version from its tag:
|
|
|
|
```sh
|
|
git fetch origin "refs/tags/vX.Y.Z:refs/tags/vX.Y.Z"
|
|
git checkout --detach "vX.Y.Z"
|
|
GOTOOLCHAIN=local go mod download
|
|
mkdir -p .releasekit/bin
|
|
GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off go build -o .releasekit/bin/releasekit ./cmd/releasekit
|
|
RELEASEKIT_GITEA_TOKEN="$GITEA_TOKEN" .releasekit/bin/releasekit deliver "vX.Y.Z"
|
|
```
|
|
|
|
The root ignore file excludes `.releasekit/`. Delivery uses the committed note
|
|
and does not build consumer binaries, create a checksum manifest, or require a
|
|
previously released ReleaseKit executable. Keep source publication and hosted
|
|
page delivery status separate; retry only after resolving an uncertain remote
|
|
write. The workflow secret is named `releasekit_gitea_token` and is mapped to
|
|
`RELEASEKIT_GITEA_TOKEN` only in the delivery step.
|