Align release policy and operational documentation

This commit is contained in:
2026-09-28 01:13:09 +00:00
parent 299ebc21a9
commit 3d0093e1fd
7 changed files with 242 additions and 177 deletions

View File

@@ -64,7 +64,8 @@ git show cb5b435:docs/roadmap/remediation.md
```
Release-candidate, publication, and verification work follows the operational
[release procedure](release.md).
[release procedure](release.md). Its single publication command validates the
committed candidate and publishes only the intended refs.
The completed string-and-integer schema-version implementation and remediation
records are preserved in Git history at `244104f`. The
@@ -116,9 +117,9 @@ Use focused checks while iterating, with validation proportionate to the change
under the testing policy. Documentation-only work normally needs the local-link
check below and whitespace review; behavior-bearing examples and commands also
need execution. Use `go test -race ./...` for ownership or concurrency changes.
The [release checker](release.md#4-publish-the-committed-candidate) performs one
minimum-toolchain offline race-suite run, one full vet pass, and Linux
cross-builds using readonly module mode.
The [publication command](release.md#4-publish-the-committed-candidate) invokes
the release checker once. It performs one minimum-toolchain offline race-suite
run, one full vet pass, and Linux cross-builds using readonly module mode.
It already covers examples, links, and package boundaries; do not additionally
run the everyday checker just to repeat those checks. CI never gates a release.
@@ -139,7 +140,8 @@ code changes. Use the component overview to find the corresponding implementatio
| Secrets, bootstrap, redaction, explanation | [References](../internal/engine/secret_reference_test.go), [bootstrap](../internal/engine/secrets_test.go), [rendering](../internal/engine/render_test.go), [explanation](../internal/engine/explain_test.go) | `go test ./internal/engine -run 'Secret\|Bootstrap\|Effective\|Explain\|Explanation\|History\|Sensitivity'` |
| Consumer examples and migration fixtures | [Consumer tests](../examples/consumers/consumers_test.go), [API examples](../example_test.go) | `go test ./examples/consumers` and `go test . -run '^Example'` |
| Documentation links and dependency isolation | [Link checks](../documentation_test.go), [import boundary](../import_boundary_test.go), [public boundary](../public_boundary_test.go) | `go test . -run 'TestLocalDocumentationLinks\|TestDependencyImportBoundary\|TestPublicFacadeBoundary'` |
| Check-command behavior | [Shell regression](../release_check_test.go) | `go test . -run '^TestCheckScripts'` |
| Development and release checker behavior | [Shell regression](../release_check_test.go) | `go test . -run 'TestCheckScripts\|TestDevelopmentCheck'` |
| Guarded release publication and recovery | [Publication regression](../release_publish_test.go) | `go test . -run '^TestReleasePublication'` |
## Fuzz validation

View File

@@ -6,9 +6,6 @@
**Upstream Source:** [Standard Repository Policies](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/release.md)
ConfigKit's [release procedure](../release.md) uses one publication command
with the selected local checker and explicit main-then-tag pushes.
## Purpose
This policy defines a proportionate workflow for validating and publishing an
@@ -19,28 +16,21 @@ The repository-specific commands belong in `docs/release.md`. Historical release
summaries belong in `docs/releases/`. The procedure must agree with the adopted
policy and link to it rather than introduce additional release requirements.
## ConfigKit applicability
## ConfigKit Applicability
ConfigKit adopts a source-only Go module release model. The source tag and
version-matched release note are the declared deliverables. Standalone binaries,
container images, and separate binary/package-registry publication are not
required. This does not waive source compilation, dependency maintenance, testing, or
local target-build validation. Post-publication consumer retrieval is optional,
as described under Release Model.
ConfigKit publishes a source-only Go module. Its declared deliverables are the
immutable annotated source tag and version-matched checked-in release note.
Standalone binaries, container images, hosted release objects, and separate
registry publication are not required. Additional channels require a deliberate
update to the policy and procedure.
The release procedure must define the module path, authoritative remote,
supported Go versions and operating-system/architecture targets, prerequisite
dependency acquisition, and the exact verification commands once established.
The default tests themselves remain deterministic and offline. Additional
publication channels require documented deliverables and verification before use.
The Go API, documented YAML/resolution semantics, and structured diagnostic
contracts all participate in compatibility review. A signature-preserving change
The Go API, documented YAML and resolution semantics, and structured diagnostic
contracts participate in compatibility review. A signature-preserving change
can still break consumers. Human diagnostic wording is stable only where a
contract expressly promises it. Minimum Go version and platform support changes
also require an explicit compatibility assessment.
contract expressly promises it. Minimum Go version and platform support
changes also require an explicit compatibility assessment.
### Platform support
### Platform Support
Linux is the release-validated platform. macOS has best-effort support: keeping
it working remains desirable, but neither native macOS validation nor Darwin
@@ -49,50 +39,59 @@ does not by itself block a release. Voluntary checks and fixes are welcome; ther
is no guarantee that each release has been validated on macOS.
The [release procedure](../release.md#release-identity-and-support) owns target
architectures and required Linux checks. This support change applies to upcoming
releases; historical release notes and their validation records remain unchanged.
The v0.3.0 release note must explain the change in Compatibility, with version
selection following this policy's existing compatibility rules.
architectures and required Linux checks. Historical release notes and their
validation records remain unchanged.
## Release Model
## Release Model And Completion
A source release identifies one exact committed source revision through an
immutable version tag and a version-matched, checked-in release note. ConfigKit's
primary release branch is `main`; its tags are annotated. The
[release procedure](../release.md) names the authoritative publication remote.
immutable version tag and a version-matched, checked-in release note. The
primary release branch is `main` unless the adopted policy specifies otherwise.
The procedure must name the authoritative publication remote.
A successful push of the intended tag, with its source commit available on the
authoritative primary branch, completes the release. ConfigKit publishes the
committed candidate to `main` before pushing only the intended tag.
authoritative primary branch, completes the release. The source commit may be
pushed before the tag or published together with it through an atomic push.
The procedure must choose and document the supported approach.
ConfigKit uses sequential explicit pushes: first the captured candidate to
authoritative `main`, then the annotated tag. Its
[release procedure](../release.md) names `origin` and checks that its fetch and
push destinations resolve to the authoritative URL.
Release commands must finish at successful publication. They must not wait for
or poll CI, require successful CI status, inspect downloadable artifacts, or
check consumer installation or Go module availability as completion gates.
Optional remote tag read-back and fresh consumer retrieval do not delay release
completion.
A failed or ambiguous push requires resolving the authoritative remote state
before declaring success or retrying; successful pushes do not require a
separate remote read-back gate.
ConfigKit publishes source only. If additional deliverables are deliberately
adopted, they must be traceable to the tagged source, identify the same version,
and have documented publication responsibilities and verification.
Remote tag read-back and fresh consumer retrieval are optional and non-gating
after successful publication. A failed push receives one bounded check of the
authoritative ref; do not poll or blindly retry.
Release automation may validate or publish a release only when its exact
responsibility is documented. Automation must not silently change the source
candidate, move a published tag, replace a published deliverable, or repair a
release in place.
Repositories may publish source only. Additional binaries, images, packages,
checksums, or other declared deliverables may be published asynchronously from
the tagged source. The procedure must identify those deliverables and the
responsibility of their automation. Published deliverables must be traceable
to that source and identify the same version.
ConfigKit currently declares no additional deliverables. Its source-tag push
is the completion point.
## Versioning And Compatibility
Stable releases use Semantic Versioning in the form `vMAJOR.MINOR.PATCH`, with
no leading zeroes. The selected version must be unused locally and on the
authoritative remote. A version known to have been published elsewhere must
not be reused.
no leading zeroes. Prereleases require an explicit adopted policy defining
their format and use.
Unless an adopted policy explicitly supports and defines prereleases,
prerelease versions are not permitted.
ConfigKit does not support prereleases.
The selected version must be unused locally and on the authoritative remote.
Do not require queries to every configured remote or publication service.
A version known to have already been published must not be reused, even if the
normal guards do not inspect that channel. Adding a distribution channel
requires a deliberate update to the policy and procedure where needed.
For releases at or after `v1.0.0`:
@@ -105,89 +104,123 @@ configuration, data, integration, or compatibility contract when the release
note explains the impact and required consumer action. A patch release must
not intentionally break those contracts within its minor line.
A repository with additional compatibility promises must define them in its
adopted policy or canonical contract documentation. A release note may explain
a compatibility change, but it must not redefine the current compatibility
contract.
Additional compatibility promises and deliberate exceptions belong in the
adopted policy or canonical contract documentation. Release notes explain
changes; they do not redefine current compatibility contracts.
## Release Documentation
## Release Documentation And Preparation
Every release made under this policy must include
`docs/releases/<version>.md` in the tagged source tree. The filename must use
the complete version tag, and the document must begin with a title containing
the product name and that exact version.
Every new release must include `docs/releases/<version>.md` in the tagged
source tree. Use the complete version tag as the filename and begin with a
title containing the product name and that exact version.
ConfigKit requires the first line `# ConfigKit <version>`.
Include a short summary and any relevant compatibility impact or upgrade
instructions. Choose sections suited to the release and keep the note
proportionate to the change. The annotated tag's message is the checked-in note.
proportionate to the change.
Release notes are immutable historical summaries. They should link to current
canonical documentation for exact behavior. Every affected canonical document
must be updated in the same candidate; a release note must not substitute for
current documentation or defer required documentation changes.
Keep exact current contracts in their canonical documents and update affected
documentation with the behavior change. Link to those documents from the note
where useful. If annotated tags are used, derive their summary from the
checked-in note.
Each new release note must end with the release navigation defined by the
[OSS policy](oss.md#discoverability). Preserve those links on a hosted download
page if one is used. The release procedure owns URL construction and channel
treatment. This is ordinary preparation, not a new network check or approval
gate; historical release notes are unchanged.
ConfigKit uses the complete checked-in note as the annotated tag message.
## Candidate Preparation And Validation
Include the release footer defined by the [OSS policy](oss.md#discoverability)
in each new release document. The release procedure defines the concrete URLs
and publication mechanics. Prepare these links with the release note; they do
not add a network check or approval gate. If a hosted download page is used in
a future channel, preserve the footer there.
The publication command validates the final committed candidate once with
ConfigKit's local release checker, using one maintained patch of the minimum
supported Go line on Linux. The checker runs one full offline race suite, one
full vet pass, and CGO-disabled Linux amd64/arm64 builds. The race suite
protects library behavior, ownership,
and concurrent use; it includes examples, documentation links, consumer fixtures,
and package boundaries without separate runs. Full vet checks more than the
subset normally run by tests, so test-time vet is disabled. The two cross-builds
cover supported configurations that native race compilation does not. A second
toolchain is optional and non-gating.
Handle version selection, release-note content, and retirement of completed
roadmaps during preparation. Preserve planned and deferred work and repair
affected links when retiring roadmaps. Reuse ordinary change review for
unintended files, credentials, and private data. Scope release-content review
to the tagged source.
All Go checks use readonly module mode and an offline dependency cache.
Formatting and whitespace remain ordinary development checks. Review module
tidiness when dependencies change, rather than auditing it on every release.
## Candidate Validation
Commit the release note and associated changes before release validation. The
publication command captures the exact candidate and rechecks it after the
validator. A changed candidate requires a fresh invocation and validation.
Do not publish uncommitted or unvalidated content. Local check failures still
block publication. Skipped test cases are reported, not separate human sign-off
requirements.
Provide one publication command that validates the final committed candidate,
checks publication state, and publishes the intended refs. A validation-only
mode or command may be provided for diagnosis, but must not be a required step
before publication repeats the same checks.
Compatibility review, release-note accuracy, removal of completed roadmaps, and
review for unintended files, credentials, private data, and unrelated changes
are ordinary preparation. The executing agent may perform them without routine
human approval or separate manual evidence records. Ask the maintainer only for
genuine unresolved decisions or missing authority. A clean existing checkout is
sufficient; a dedicated clone is optional. Dependency and toolchain update
research belongs to maintenance or relevant changes, not every release.
ConfigKit uses `./scripts/release.sh vMAJOR.MINOR.PATCH`. The separate
`scripts/release-check.sh` remains an optional diagnostic command; the
publication command invokes it once and does not use a persistent candidate
record.
Run the complete deterministic, offline default test suite once for the final
candidate, following the [testing policy](testing.md). For repositories that
publish builds, verify the required build targets. Select those targets for
the application's actual support commitments; native testing on each platform
and access to particular hardware are not default release prerequisites.
ConfigKit's one maintained Go 1.25 baseline runs the full suite with the race
detector and test-time vet disabled. This protects library behavior, ownership,
and concurrent use; the suite already includes examples, consumer fixtures,
documentation links, and package boundaries. One full vet pass adds static
checks beyond the test-time subset. CGO-disabled Linux amd64 and arm64 builds
cover supported configurations distinct from native race compilation. macOS
and newer-toolchain checks are optional.
Additional gates must address a concrete risk that the existing checks do not
adequately cover. Do not automatically require linting, race testing, module
hygiene audits, documentation audits, credential scans, example validation, or
expanded platform matrices solely because they are possible. Retain such
checks when their distinct protection justifies their cost. A one-time migration
check must not silently become a permanent release requirement.
The selected Go commands use readonly module mode with `GOPROXY=off`; acquire
the baseline toolchain and dependencies before validation. Formatting and
whitespace remain ordinary development checks in `scripts/check.sh`.
Review module tidiness when dependencies change, not as a release gate.
Release tests must not require real credentials, mutable application services,
paid APIs, provider probes, or inference calls. The procedure may describe dependency
preparation separately from offline validation.
Avoid duplicate validation across release phases. Successful checks for the
same final candidate need not be repeated solely to satisfy another phase;
any reuse must clearly apply to that exact candidate. Persistent validation
caches are not required. If the candidate changes, validate the new candidate.
Cheap publication-state checks may be repeated immediately before pushing
because local or remote state may have changed during validation.
Automate mechanical guards. Human review gates are strongly disfavored: add one
only through an explicit project-specific policy decision explaining the
concrete risk and why ordinary change review or automation is insufficient.
Do not add routine confirmations for checks the command can perform itself.
Compatibility review, release-note accuracy, completed-roadmap retirement, and
review for unintended files or credentials remain ordinary preparation. The
executing agent may perform them without routine human approval or separate
manual evidence records. Publication itself requires authorization.
## Publication Guards
Publication requires authorization before invoking the command. The command
must verify all of the following:
Automate the checks needed to publish the intended source under a fresh version:
- the stable version has the required format and is unused locally and on the
- the version has the adopted format and is unused locally and on the
authoritative remote;
- the current branch is `main`, and the worktree and index are clean;
- the committed source and matching release note identify the exact validated
candidate;
- the authoritative remote main can advance to that candidate without
discarding history;
- the local automated gates succeeded for that commit; and
- the annotated tag identifies that candidate and only the intended refs are
- the worktree and index are clean on the primary release branch;
- the source being published is the exact candidate that passed validation,
including its matching release note;
- the remote primary branch can be advanced to that candidate without
discarding remote history, and is published before the tag; and
- the tag identifies the validated candidate and only the intended refs are
published.
Push the exact candidate to authoritative main, then create and verify the
annotated tag before pushing only its ref. Do not use `git push --tags` or
implicit follow-tags publication. A failed or ambiguous push receives one
authoritative-state check before success, rejection, or unknown status is
reported.
ConfigKit also checks that `origin` has exactly the authoritative fetch and
push destination, including configured push URLs. The command checks the
candidate and unused version again after validation, and checks local state
before creating the tag. Ignored local files do not block publication.
The repository-specific procedure must make these guards executable or give
copyable commands for performing them.
Fetch the authoritative branch and tag state as needed for these checks.
Do not force branch or tag updates to bypass a failed guard. Publish explicit
refs rather than all local tags. Do not require CI success, publication-service
availability checks, or a separate manual confirmation for each guard.
## Immutability And Correction
@@ -201,33 +234,33 @@ rerun the publication command. If a push fails or its outcome is ambiguous,
inspect the authoritative remote before retrying. Remove an unpublished local
tag only after confirming it was never published or used by a release channel.
An unavailable remote leaves publication status unknown; do not overwrite a tag
to resolve uncertainty. A transient transport failure alone does not require
a new version.
to resolve uncertainty.
If publication or post-publication verification reveals a defect, leave the
published release intact. Correct the defect on the primary branch, choose a
new patch version or other semantically appropriate new version, write a new
matching release note, and perform the complete release procedure again.
Transient network, authentication, CI, or consumer-availability failures do not
by themselves require a new version.
A distribution channel's supported yank, deprecation, or advisory mechanism
may warn consumers away from a defective release, but it must not replace that
release's contents or reuse its version.
A defect requiring changes to published source or contents requires a corrected
commit, a new semantically appropriate version, a matching release note, and
the release procedure again. Leave the original release intact. A channel's
yank, deprecation, or advisory mechanism may warn consumers away from a defective
release without replacing its contents or reusing its version.
## Repository Release Procedure
Maintain `docs/release.md` as the executable maintainer runbook. It identifies
prerequisites, version and note preparation, the single publication command,
declared deliverables and build targets, successful-tag-push completion, and
partial or unknown-state recovery. Keep optional diagnostics and consumer
verification clearly optional. The procedure must agree with this policy.
Maintain `docs/release.md` as a short, executable maintainer runbook. It must
identify prerequisites, the primary branch and authoritative remote, version
and note preparation, one publication command with local checks and guards,
source-only deliverables, the successful-tag-push completion point, and
partial or unknown-state recovery.
Use copyable commands where practical. Keep optional diagnostics and consumer
verification clearly optional. The procedure must not turn preparation work,
ordinary change review, or post-publication investigation into additional
release gates.
## Policy Exceptions
Any exception to this policy must be explicitly documented in this section,
including the repository constraint requiring it, its rationale, scope, and
compensating controls. Customizing release mechanics must not silently weaken
the policy's integrity, validation, documentation, verification, or immutability
requirements.
The following historical exceptions do not change the current release workflow.
For v0.1.1 only, the maintainer approved renaming the reserved `_configkit` key to
`configkit` as an error-correcting point release because there are no current

View File

@@ -124,12 +124,21 @@ 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 printed or recorded from the run:
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
git ls-remote --refs --exit-code "$authoritative" refs/heads/main
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"
@@ -141,7 +150,11 @@ else
printf 'remote tag state unknown; do not reuse the version\n' >&2
fi
fi
git rev-parse --verify "refs/tags/$release_version"
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
@@ -168,5 +181,17 @@ 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`. Report optional verification separately
from publication success.
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.

View File

@@ -1,6 +1,6 @@
# Release Workflow Implementation Plan
Status: ready for implementation; no stages completed by this planning pass.
Status: implemented on 2026-09-28. All stages are complete.
Implement the [release roadmap](release.md) in the numbered order below. Each
stage is a bounded work unit for a gpt-5.6-terra coding agent; finish its tests
@@ -177,6 +177,12 @@ no longer require a manually recorded candidate before single-command publicatio
## Stage 3 — Complete documentation alignment and integrated validation
**Completed 2026-09-28.** Reconciled the adopted release policy with its pinned
upstream source and ConfigKit decisions; aligned the runbook, development guide,
and roadmap records with the implemented command. Bash syntax, focused release
tests, local documentation links, whitespace review, and the final real baseline
release checker passed on Linux. No publication or push was performed.
Reconcile `docs/policy/release.md` with the pinned upstream release policy. Adopt
shared paragraphs verbatim except for substantive local decisions. Remove the
temporary deferral, all-remotes freshness rule, and stale prospective v0.3.0 note

View File

@@ -1,7 +1,7 @@
# Standard Policy Documentation Alignment
Status: implemented on 2026-09-28. The [implementation record](implementation.md)
contains the completed stages and validation evidence.
Status: implemented on 2026-09-28. The completed implementation record and
validation evidence are preserved at `faf5ff8:docs/roadmap/implementation.md`.
ConfigKit's seven [adopted policies](../policy/meta.md) now identify the pinned
Standard Repository Policies revision
@@ -11,10 +11,9 @@ applicable policies. [COPYRIGHT](../../COPYRIGHT) and the
[attribution notices](../oss/attribution.md) identify the separately licensed
adapted policy and orientation material.
The [release policy](../policy/release.md) retains ConfigKit's existing
operational requirements. The [maintainer runbook](../release.md) remains their
command owner. The separate [release-workflow roadmap](release.md) owns future
publication automation and gate review; this policy alignment did not implement
that workflow.
The [release policy](../policy/release.md) and
[maintainer runbook](../release.md) own current release requirements and
operations. The separate [release-workflow roadmap](release.md) records the
subsequent publication-automation work.
These completed records remain until normal release preparation retires them.

View File

@@ -1,7 +1,7 @@
# Single-Command Release Workflow and Gate Review
Status: accepted target; execution order and acceptance checks are defined in the
[implementation plan](implementation.md).
Status: implemented on 2026-09-28. The
[implementation plan](implementation.md) records the completed work and checks.
## Intent and baseline
@@ -15,14 +15,15 @@ The upstream reference is release policy v1.4 at
`d84a38ebab26f048cafbe59a2246700c35d29843`:
[standard release policy](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/release.md).
Today, `scripts/release-check.sh` performs local validation; the runbook separately
At the planning baseline, `scripts/release-check.sh` performed local validation; the runbook separately
records a candidate commit and executes shell blocks for guards, branch push,
annotated tagging, and tag push. The checker runs module hygiene, formatting and
whitespace checks, one race-enabled suite, vet, and Linux amd64/arm64 cross-builds
using a maintained Go 1.25 patch. Publication completes on successful tag push.
These remain the current requirements until deliberately revised. This roadmap
does not itself waive existing gates.
The implementation plan records the subsequent gate selection and publication
command. Current requirements belong to the
[release policy](../policy/release.md) and [runbook](../release.md).
## Target release contract
@@ -40,7 +41,7 @@ authorization, not the standing local-commit policy.
The command validates the exact candidate once. An optional validation-only mode
may support diagnosis but is not a prerequisite followed by duplicate validation.
Do not require both everyday and release checkers. An implementation need not
introduce a persistent validation cache merely to replace today's manual record.
introduce a persistent validation cache merely to replace the former manual record.
Mechanical guards ensure a clean worktree/index on the primary branch, an unused
valid version locally and on the authoritative remote, a matching release note,
@@ -54,12 +55,11 @@ consumer installation, or optional verification. Do not add a successful-push
remote read-back gate. If future artifact channels are adopted, delivery status
must remain separate; this task introduces none.
## Gate review target
## Selected gate rationale
Keep the full deterministic offline suite as the core check. The selected gates
below provide distinct protection without repeating validation. Their rationale
belongs in the adopted release policy, with commands in the runbook and tooling
once implemented.
belongs in the adopted release policy, with commands in the runbook and tooling.
| Check | Selected end state and rationale |
| --- | --- |

View File

@@ -12,13 +12,13 @@ fail() {
}
check_candidate() {
local current status
current=$(git branch --show-current) || fail "cannot read the current branch"
[[ "$current" == main ]] || fail "release requires the main branch"
status=$(git status --porcelain=v1 --untracked-files=all) || fail "cannot inspect the worktree"
[[ -z "$status" ]] || fail "release requires a clean index and worktree"
current=$(git rev-parse --verify HEAD) || fail "cannot identify the candidate"
[[ "$current" == "$candidate" ]] || fail "candidate changed; commit and validate it again"
local context=$1 current status
current=$(git branch --show-current) || fail "$context: cannot read the current branch"
[[ "$current" == main ]] || fail "$context: release requires the main branch"
status=$(git status --porcelain=v1 --untracked-files=all) || fail "$context: cannot inspect the worktree"
[[ -z "$status" ]] || fail "$context: release requires a clean index and worktree"
current=$(git rev-parse --verify HEAD) || fail "$context: cannot identify the candidate"
[[ "$current" == "$candidate" ]] || fail "$context: candidate changed; commit and validate it again"
}
check_unused_version() {
@@ -59,7 +59,7 @@ push_urls=$(git remote get-url --push --all origin 2>/dev/null) || fail "origin
fail "origin must have exactly the authoritative fetch and push destination"
candidate=$(git rev-parse --verify HEAD) || fail "cannot identify the candidate"
check_candidate
check_candidate "before validation"
check_unused_version
note_path="docs/releases/$version.md"
@@ -85,7 +85,7 @@ git merge-base --is-ancestor "$fetched_main" "$candidate" ||
fail "candidate cannot advance authoritative main without discarding history"
./scripts/release-check.sh || fail "release validation failed; no refs were published"
check_candidate
check_candidate "after validation"
check_unused_version
if git push --no-follow-tags "$remote_url" "$candidate:refs/heads/main"; then
@@ -93,37 +93,37 @@ if git push --no-follow-tags "$remote_url" "$candidate:refs/heads/main"; then
else
if observed_main=$(read_remote_ref refs/heads/main); then
[[ "$observed_main" == "$candidate" ]] ||
fail "main push did not publish; authoritative main differs from the candidate"
fail "main push did not publish candidate $candidate; authoritative main differs"
else
result=$?
((result == 2)) && fail "main push failed; authoritative main is absent"
fail "main push outcome is unknown; inspect authoritative main before retrying"
((result == 2)) && fail "main push failed for $candidate; authoritative main is absent"
fail "main push outcome is unknown for $candidate; inspect authoritative main before retrying"
fi
fi
check_candidate
check_candidate "main at $candidate; tag not yet published"
git tag -a -F "$note_file" --cleanup=verbatim "$version" "$candidate" ||
fail "could not create the local annotated tag"
fail "main is at $candidate; could not create local tag $version"
tag_target=$(git rev-parse --verify "refs/tags/$version^{}") ||
fail "cannot verify the local tag target"
[[ "$tag_target" == "$candidate" ]] || fail "local tag does not identify the candidate"
fail "main is at $candidate; cannot verify local tag $version"
[[ "$tag_target" == "$candidate" ]] || fail "main is at $candidate; local tag $version targets another commit"
tag_type=$(git cat-file -t "refs/tags/$version") ||
fail "cannot inspect the local tag"
[[ "$tag_type" == tag ]] || fail "release tag must be annotated"
fail "main is at $candidate; cannot inspect local tag $version"
[[ "$tag_type" == tag ]] || fail "main is at $candidate; local tag $version is not annotated"
tag_oid=$(git rev-parse --verify "refs/tags/$version") ||
fail "cannot identify the annotated tag"
fail "main is at $candidate; cannot identify local tag $version"
if git push --no-follow-tags "$remote_url" "refs/tags/$version:refs/tags/$version"; then
:
else
if observed_tag=$(read_remote_ref "refs/tags/$version"); then
[[ "$observed_tag" == "$tag_oid" ]] ||
fail "authoritative tag conflicts with the local tag; never replace it"
fail "authoritative tag $version conflicts with the local tag at $candidate; never replace it"
else
result=$?
((result == 2)) &&
fail "tag push failed; main is published, but the local tag is unpublished"
fail "tag push outcome is unknown; retain the local tag and inspect authoritative state"
fail "tag push failed; main is at $candidate, but local tag $version is unpublished"
fail "tag $version push outcome is unknown for $candidate; retain the local tag and inspect authoritative state"
fi
fi