2026-10-05 19:04:55 +00:00
|
|
|
# ReleaseKit release procedure
|
|
|
|
|
|
2026-10-05 20:56:57 +00:00
|
|
|
ReleaseKit's deliverables are its source tag and a hosted Gitea page containing
|
2026-11-01 23:41:51 +00:00
|
|
|
the complete checked-in release note. Deliver the page locally after publication
|
|
|
|
|
or asynchronously through the prepared
|
|
|
|
|
[Woodpecker workflow](../examples/woodpecker/releasekit-source-delivery.yml).
|
|
|
|
|
Hosted-page delivery has a separate status from source-release completion and
|
|
|
|
|
carries no binary assets. The authoritative repository is `origin` at
|
2026-11-01 14:37:41 +00:00
|
|
|
`ssh://git@forge.maximumdirect.net:22222/base-layer/releasekit.git`, and the release
|
|
|
|
|
branch is `main`. The hosted Gitea destination is
|
|
|
|
|
`https://forge.maximumdirect.net/base-layer/releasekit`. A release completes
|
|
|
|
|
when the intended annotated tag is successfully
|
2026-10-05 20:56:57 +00:00
|
|
|
pushed, or when one exact-object recovery query confirms that the push
|
|
|
|
|
succeeded. CI and consumer installation are not completion checks.
|
2026-10-05 19:04:55 +00:00
|
|
|
|
|
|
|
|
## Prepare a candidate
|
|
|
|
|
|
2026-11-01 15:39:36 +00:00
|
|
|
For local hosted-page delivery, save a Gitea personal access token in
|
|
|
|
|
`.releasekit/gitea-token` with owner-only file permissions. The file contains
|
|
|
|
|
only the token. ReleaseKit's profile selects this file as a fallback to
|
|
|
|
|
`RELEASEKIT_GITEA_TOKEN`; `.releasekit/` is already ignored. Its optional
|
2026-11-01 16:02:51 +00:00
|
|
|
`validation.forbidden_files` selection checks that this path is absent from the
|
2026-11-01 15:39:36 +00:00
|
|
|
committed candidate. Source publication requires no delivery token. The
|
|
|
|
|
[configuration reference](config.md) owns credential selection and validator
|
|
|
|
|
semantics.
|
|
|
|
|
|
2026-11-01 14:44:40 +00:00
|
|
|
Use a clean clone on `main`. The chosen build toolchain is Go 1.26.10. Make that
|
2026-10-08 16:29:48 +00:00
|
|
|
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
|
2026-10-09 03:29:37 +00:00
|
|
|
[Go support policy](policy/release.md#consumer-minimum-and-release-toolchain) owns
|
2026-10-08 16:29:48 +00:00
|
|
|
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.
|
|
|
|
|
|
2026-10-10 04:03:42 +00:00
|
|
|
The current consumer minimum is Go 1.26.0, declared in [go.mod](../go.mod).
|
|
|
|
|
Keep it for a release unless release preparation deliberately selects a newer
|
|
|
|
|
series under the [Go support policy](policy/release.md#consumer-minimum-and-release-toolchain).
|
|
|
|
|
When increasing it, update `go.mod` to the first stable version of the selected
|
|
|
|
|
series, or to a higher patch when a dependency or specific fix requires one.
|
|
|
|
|
Make module changes before committing the candidate. Minimum increases must fit the
|
2026-10-08 16:29:48 +00:00
|
|
|
[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
|
2026-10-05 19:04:55 +00:00
|
|
|
`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
|
2026-11-01 14:37:41 +00:00
|
|
|
[COPYRIGHT](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/vX.Y.Z/COPYRIGHT) |
|
|
|
|
|
[LICENSE](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/vX.Y.Z/LICENSE) |
|
|
|
|
|
[ATTRIBUTION](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/vX.Y.Z/docs/oss/attribution.md)
|
2026-10-05 19:04:55 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
2026-10-06 17:15:46 +00:00
|
|
|
The command always runs the offline default test suite,
|
2026-10-05 19:04:55 +00:00
|
|
|
`go test -mod=readonly -count=1 ./...`, with toolchain and module network
|
2026-10-06 17:15:46 +00:00
|
|
|
access disabled. Any profile `checks` run afterward as additional product
|
|
|
|
|
checks; they should not repeat the standard suite. The command checks the
|
2026-10-05 19:04:55 +00:00
|
|
|
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.
|
|
|
|
|
|
2026-10-06 01:03:18 +00:00
|
|
|
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
|
2026-10-05 19:04:55 +00:00
|
|
|
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.
|
|
|
|
|
|
2026-11-01 23:41:51 +00:00
|
|
|
## Deliver the hosted release page
|
|
|
|
|
|
|
|
|
|
After source publication succeeds, deliver the hosted page locally when
|
|
|
|
|
Woodpecker automation is inactive. From the same repository root, keep the
|
|
|
|
|
worktree clean and `HEAD` at the published tag's commit. Reuse the executable
|
|
|
|
|
built by the publication command:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
.releasekit/bin/releasekit deliver vX.Y.Z
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The command uses the configured Gitea destination and token source to deliver
|
|
|
|
|
the complete checked-in release note. Report its result separately from the
|
|
|
|
|
completed source release. For failed or uncertain delivery, follow the recovery
|
|
|
|
|
instructions below using the same immutable version.
|
|
|
|
|
|
2026-11-02 19:37:29 +00:00
|
|
|
## Resolve publication failures
|
|
|
|
|
|
|
|
|
|
Each publication invocation attempts to save its captured identity before the
|
|
|
|
|
tag push. The CLI reports the saved record path separately from publication
|
|
|
|
|
success or failure and, after a push or immediate-reconciliation error, prints a
|
2026-11-02 22:34:19 +00:00
|
|
|
copyable command that selects that exact record. Run that command, or use the
|
|
|
|
|
source-built executable prepared above, to make a separate later observation:
|
2026-10-05 19:04:55 +00:00
|
|
|
|
|
|
|
|
```sh
|
2026-11-02 22:34:19 +00:00
|
|
|
./.releasekit/bin/releasekit reconcile --record '/absolute/path/to/attempt-record.json'
|
2026-10-05 19:04:55 +00:00
|
|
|
```
|
|
|
|
|
|
2026-11-02 19:37:29 +00:00
|
|
|
The saved evidence fixes the original endpoint and tag identity. Later recovery
|
|
|
|
|
uses current supported transport settings for the recorded remote name, but it
|
|
|
|
|
does not substitute the current remote URL, inspect the local tag or current
|
|
|
|
|
HEAD, load a profile, rerun checks, or change repository state. A completed
|
|
|
|
|
observation reports `published`, `not_published`, or `conflict`; an incomplete
|
|
|
|
|
observation reports `unknown` with diagnostic context. Resolve an unknown result
|
|
|
|
|
by correcting the identified endpoint rewrite, transport configuration, or
|
|
|
|
|
missing evidence. For incomplete saved identity, select another record with the
|
|
|
|
|
required fields or use complete typed evidence through the Go API.
|
|
|
|
|
|
|
|
|
|
If command output was lost, list local attempts and explicitly select the
|
|
|
|
|
matching record by its module, full tag, and saved identity:
|
2026-10-06 01:05:31 +00:00
|
|
|
|
2026-11-02 19:37:29 +00:00
|
|
|
```sh
|
2026-11-02 22:34:19 +00:00
|
|
|
./.releasekit/bin/releasekit publication-attempts --repo "$PWD"
|
|
|
|
|
./.releasekit/bin/releasekit reconcile --record '/absolute/path/from-the-list/attempt-record.json'
|
2026-11-02 19:37:29 +00:00
|
|
|
```
|
2026-10-05 19:04:55 +00:00
|
|
|
|
2026-11-02 19:37:29 +00:00
|
|
|
Discovery only reads the local store. It neither queries remotes nor chooses an
|
2026-11-02 22:34:19 +00:00
|
|
|
attempt automatically. If the original recovery entry point is unavailable,
|
|
|
|
|
select another supported ReleaseKit CLI and pass the same record path explicitly.
|
|
|
|
|
An older record may lack the endpoint, tag-object ID, candidate commit, or
|
|
|
|
|
another field required for a conclusive result.
|
2026-11-02 19:37:29 +00:00
|
|
|
|
|
|
|
|
A later observation that the tag is absent describes the remote at that time;
|
|
|
|
|
it does not prove the version was never published. Never reuse a version known
|
|
|
|
|
to have been published, and preserve published tags and contents as immutable.
|
|
|
|
|
Keep removal of an unpublished local tag, candidate correction, and retry as
|
|
|
|
|
explicit operator decisions after reviewing the record and authoritative
|
|
|
|
|
remote state. A conflicting object requires resolving the conflict and selecting
|
|
|
|
|
an unused version when appropriate; do not move, overwrite, or delete a
|
|
|
|
|
published tag.
|
|
|
|
|
|
|
|
|
|
If record storage fails, ReleaseKit reports the recording warning separately;
|
|
|
|
|
the warning does not change the actual publication outcome. The saved handoff is
|
|
|
|
|
then unavailable. A Go caller that retained `PublicationResult.Evidence` can
|
|
|
|
|
still pass that typed evidence directly to `ReconcilePublication`. Process
|
|
|
|
|
termination before the record is written, removed storage, and older incomplete
|
|
|
|
|
records can leave no sufficient saved evidence for later recovery.
|
|
|
|
|
|
|
|
|
|
Later reconciliation adds no successful-push read-back, polling, CI, consumer
|
|
|
|
|
installation, or hosted-delivery gate. Hosted page delivery remains separate.
|
2026-11-02 03:20:13 +00:00
|
|
|
|
2026-10-06 01:05:31 +00:00
|
|
|
If validation or a guard fails before this invocation pushes, correct and
|
|
|
|
|
commit the candidate, publish `main`, and run the command again. A retained
|
2026-11-02 19:37:29 +00:00
|
|
|
local tag still requires reviewing the selected record and authoritative state
|
|
|
|
|
before deciding whether to remove it. The prepared
|
2026-10-06 19:49:19 +00:00
|
|
|
Woodpecker workflow builds the CLI from the tagged checkout, then uses
|
|
|
|
|
`ci-deliver` for page-only delivery. To activate it after configuring the
|
2026-10-05 20:56:57 +00:00
|
|
|
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"
|
2026-10-08 12:31:56 +00:00
|
|
|
GOTOOLCHAIN=local go mod download
|
2026-10-05 20:56:57 +00:00
|
|
|
mkdir -p .releasekit/bin
|
|
|
|
|
GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off go build -o .releasekit/bin/releasekit ./cmd/releasekit
|
2026-11-01 15:39:36 +00:00
|
|
|
.releasekit/bin/releasekit deliver "vX.Y.Z"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For an older tag whose CLI predates `token_file`, supply the token through its
|
|
|
|
|
environment variable when retrying that tagged tool:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
RELEASEKIT_GITEA_TOKEN="$(cat .releasekit/gitea-token)" \
|
|
|
|
|
.releasekit/bin/releasekit deliver "vX.Y.Z"
|
2026-10-05 20:56:57 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|