Adopt ReleaseKit 0.8 and document publication recovery

This commit is contained in:
2026-11-03 00:55:19 +00:00
parent 795cc2f379
commit 43ac34d10e
3 changed files with 65 additions and 38 deletions

View File

@@ -106,7 +106,7 @@ go list -m go.maximumdirect.net/releasekit
"$releasekit_bin" version
```
This repository selects ReleaseKit v0.6.0. Use the compiled program above for
This repository selects ReleaseKit v0.8.0. 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
@@ -269,53 +269,80 @@ version through the normal release procedure.
## 6. 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.
ReleaseKit attempts to save publication evidence before pushing a tag and
reports the absolute record path, or a recording warning, separately from the
publication outcome. A recording failure does not undo a successful publication
or change its exit status. Confirmed publication still completes the source
release; hosted delivery remains a separate operation.
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.
Records default to a private store under the repository's Git common directory,
shared by linked worktrees. `publish --record-dir DIR` can select another
location; relative paths resolve from the source repository root. Records can
contain exact endpoints, including credentials. Keep them private and remove
them when their recovery value expires. There is no new tracked storage,
credential-preflight gate, or required recording-success check.
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:
After a failed push, ReleaseKit makes one bounded observation of the original
publication endpoint. If the outcome remains unknown, use the explicitly saved
record rather than rerunning publication. Copy the recovery command printed by
ReleaseKit, or use the same compiled executable:
```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"
publication_record='/absolute/path/from/the/publication/report.json'
"$releasekit_bin" reconcile --record "$publication_record"
```
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:
If the path was lost, discover local attempts without contacting a remote:
```sh
"$releasekit_bin" publication-attempts
```
Select the record for the intended attempt from its reported version, candidate,
and tag evidence; discovery does not automatically choose the latest attempt.
For an alternate store, supply `publication-attempts --record-dir DIR`.
Both commands support optional `--json` output. Treat saved records as private
regardless of the CLI's credential-redacted diagnostic output.
Reconciliation observes the captured endpoint and exact tag object using current
supported Git transport settings. It does not load the current profile, require
the original `HEAD`, rerun candidate validation, change tags, or contact Gitea
hosted delivery. It needs no clean candidate checkout. Use ReleaseKit v0.8.0 or
another CLI supporting the saved record format, including format 2 where exact
non-UTF-8 endpoint/path bytes were retained.
| Reconciliation status | Meaning and next action |
| --- | --- |
| `0` | The intended tag object is published. The source release is complete; continue with hosted delivery from its clean tagged checkout. |
| `1` | The tag is currently absent or conflicting. Read the reported state. Investigate conflicts; absence alone does not prove that the version was never published. |
| `2` | The record cannot be read/decoded, or the invocation is invalid. Correct the path, access, CLI compatibility, or command. |
| `3` | Observation remains unresolved, including insufficient evidence or cancellation. Correct the transport or other reported cause before another explicit observation; do not blindly republish. |
These are recovery actions after failure, not read-back requirements after a
successful push. A failed observation leaves the result unresolved. Do not poll
CI or move a tag to settle uncertainty.
When no usable record exists (for example, storage failed or an older CLI made
the attempt), use the captured endpoint and exact tag-object ID from the original
report for a manual authoritative observation. Checking only the peeled commit
would not establish that the intended annotated tag object arrived. Do not
substitute current profile or local tag state for missing original evidence.
Only after establishing that a retained local tag was never published or used
by another release channel may it be removed before retrying publication:
```sh
release_version='vMAJOR.MINOR.PATCH'
git tag -d "$release_version"
"$releasekit_bin" publish "$release_version"
```
Replace the placeholder with the original attempt's version.
The next `publish` revalidates the candidate and publication guards. A conflicting
published tag must never be moved or reused. A defect in published source
requires a corrected commit, a new appropriate version, and a matching note.
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.
candidate there; correct it in a new commit rather than rewriting `main`.
## 7. Optional consumer verification

2
go.mod
View File

@@ -6,7 +6,7 @@ require go.yaml.in/yaml/v4 v4.0.0-rc.6
require (
github.com/yuin/goldmark/v2 v2.1.6 // indirect
go.maximumdirect.net/releasekit v0.6.0 // indirect
go.maximumdirect.net/releasekit v0.8.0 // indirect
golang.org/x/mod v0.40.0 // indirect
)

4
go.sum
View File

@@ -1,7 +1,7 @@
github.com/yuin/goldmark/v2 v2.1.6 h1:igy0q9JcqovoFXFx/XjtU8sHVhcUHavPj2sA3EoKJUE=
github.com/yuin/goldmark/v2 v2.1.6/go.mod h1:G6M4/qOFtfn01/o14BU1UR2Lo5N3S9Qo7xCuz/sHjGQ=
go.maximumdirect.net/releasekit v0.6.0 h1:wbzMeQGeLjXigBSN9j9iJLf/CC3htjzt9iEwubezgy0=
go.maximumdirect.net/releasekit v0.6.0/go.mod h1:QE4BLrAhVJTzi+/YTcVl21K/qZp9ojRLg5kuoy3Zb44=
go.maximumdirect.net/releasekit v0.8.0 h1:hSaHPbUI7tAhnolfbu7UMj2fvmm9FGBY+jhkygdbxR0=
go.maximumdirect.net/releasekit v0.8.0/go.mod h1:QE4BLrAhVJTzi+/YTcVl21K/qZp9ojRLg5kuoy3Zb44=
go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=