Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.PHONY: all help fmt test policy build build-linux-arm64 gitleaks provability drift-check generated-real-check contract check-all clean
.PHONY: release-control-test release-guard-signing-env release-guard-tag-version release-guard-tag-ruleset release-tag release-push-tag release-verify-tag release-verify-remote-tag

BINARY_NAME := synthcorpus-gen
BINARY_EXT :=
Expand All @@ -25,6 +26,14 @@ help:
' generated-real-check Property-only dogfood check (needs pinned decernor + sidecars)' \
' contract Run both decernor consumer-contract lanes' \
' check-all fmt + tests + build + scanners + proofs + contract + diff --check' \
' release-control-test Hermetic negative tests for release tooling' \
' release-guard-signing-env Validate the operator-private signing environment' \
' release-guard-tag-version Verify release tag and commit inputs' \
' release-guard-tag-ruleset Verify the live version-tag publication policy' \
' release-tag Create and verify the signed release tag locally' \
' release-push-tag Recheck and push the signed release tag' \
' release-verify-tag Verify an existing signed release tag' \
' release-verify-remote-tag Verify GitHub tag signature state and target' \
' clean Remove local build artifacts'

fmt:
Expand All @@ -33,6 +42,10 @@ fmt:
test:
go test ./...

release-control-test:
@bash -n scripts/*.sh
@./scripts/test-release-controls.sh

policy:
go test ./internal/repopolicy/ -count=1

Expand Down Expand Up @@ -62,10 +75,31 @@ generated-real-check:

contract: drift-check generated-real-check

check-all: fmt test policy build gitleaks provability contract
check-all: fmt test policy release-control-test build gitleaks provability contract
@git diff --check
@git diff --check $(DIFF_BASE)...HEAD
@echo 'check-all ok'

release-guard-signing-env:
@./scripts/release-guard-signing-env.sh

release-guard-tag-version:
@./scripts/release-guard-tag-version.sh

release-guard-tag-ruleset:
@./scripts/release-guard-tag-ruleset.sh

release-tag:
@./scripts/release-tag.sh

release-push-tag:
@./scripts/release-push-tag.sh

release-verify-tag:
@./scripts/release-verify-tag.sh

release-verify-remote-tag:
@./scripts/release-verify-remote-tag.sh

clean:
rm -rf bin
112 changes: 84 additions & 28 deletions RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,38 @@ committed-synthetic corpus, and no generated-real material. This intentionally
follows
[`docs/decisions/ADR-0002-no-publish-no-worktree-generator.md`](docs/decisions/ADR-0002-no-publish-no-worktree-generator.md).

## Release environment

The maintainer loads the signing identity from the operator-private release
environment and sets the release tag and literal check-verified commit. The
dedicated keyring must remain outside every Git worktree.

- `THREELEAPS_SYNTHCORPUS_RELEASE_TAG`: the intended `vMAJOR.MINOR.PATCH` tag;
- `THREELEAPS_SYNTHCORPUS_RELEASE_COMMIT`: the full lowercase 40-hex commit SHA
on which the release gates and hosted checks passed;
- `THREELEAPS_SYNTHCORPUS_GPG_HOMEDIR`: the dedicated release-signing keyring;
- `THREELEAPS_SYNTHCORPUS_GPG_SIGNING_FINGERPRINT`: the full uppercase 40-hex
primary GPG fingerprint authorized to sign the Git tag;
- `THREELEAPS_SYNTHCORPUS_TAGGER_NAME`: the tagger name associated with the
signing identity;
- `THREELEAPS_SYNTHCORPUS_TAGGER_EMAIL`: a tagger email present on the signing
key and verified for the publishing account.

Git release tags carry GPG signatures. This no-asset release creates no
minisign signature or uploaded key; minisign material is not part of this
ceremony.

## 1. Quality gates

- [ ] In the shell that will perform the release, confirm all release changes
are merged to `main`; fetch and record the intended release commit as a
read-only value; and confirm the clean checkout matches it:
- [ ] Confirm all release changes are merged to `main`; fetch and record the
intended release commit; and confirm the clean checkout matches it:

```sh
git fetch origin main
release_commit="$(git rev-parse origin/main)"
readonly release_commit
printf 'release commit: %s\n' "$release_commit"
test "$(git rev-parse HEAD)" = "$release_commit"
gate_verified_commit="$(git rev-parse origin/main)"
readonly gate_verified_commit
printf 'release commit: %s\n' "$gate_verified_commit"
test "$(git rev-parse HEAD)" = "$gate_verified_commit"
test -z "$(git status --porcelain)"
```

Expand Down Expand Up @@ -88,44 +108,80 @@ follows
- [ ] Confirm the tag matches `VERSION`:

```sh
release_version="$(tr -d '\n' < VERSION)"
test "v${release_version}" = "v0.1.0"
export THREELEAPS_SYNTHCORPUS_RELEASE_TAG="v$(tr -d '\n' < VERSION)"
export THREELEAPS_SYNTHCORPUS_RELEASE_COMMIT='<literal full SHA from the check-verified handoff>'
readonly THREELEAPS_SYNTHCORPUS_RELEASE_TAG
readonly THREELEAPS_SYNTHCORPUS_RELEASE_COMMIT
make release-guard-tag-version
```

- [ ] As the maintainer, load the operator-private release environment and
validate all required variables and out-of-band paths without copying key
material into the repository:

```sh
source <operator-private-release-env>
make release-guard-signing-env
```

- [ ] Confirm the active tag-protection ruleset covers only `refs/tags/v*`,
blocks creation, update, deletion, and non-fast-forward changes, and
permits only the authorized organization-administrator bypass:

```sh
make release-guard-tag-ruleset
```

- [ ] Confirm the active tag-protection ruleset covers `refs/tags/v*`, blocks
creation, update, deletion, and non-fast-forward changes, and permits only
the authorized organization-administrator bypass.
- [ ] Confirm the three required hosted checks are green on the recorded
`release_commit`. Keep the same release shell open through signing; the
tag command fails closed if that verified value is unavailable.
- [ ] As an authorized organization administrator, create and push the signed
tag through the ruleset bypass:
`THREELEAPS_SYNTHCORPUS_RELEASE_COMMIT`. This literal full SHA is the
verifier-to-maintainer handoff; never replace it with a newly resolved
`HEAD` or `origin/main`. Keep the checkout unchanged through signing.
- [ ] As an authorized organization administrator, create the GPG-signed tag
with out-of-band key material. The target validates the signing
environment and live ruleset, embeds the ruleset-policy fingerprint,
explicitly tags the check-verified commit, and verifies the annotated
object directly targets that commit with type `commit`, plus the exact
signer fingerprint, tagger identity, policy attestation, and peeled
target:

```sh
test -n "${release_commit:-}"
git fetch origin main
current_main="$(git rev-parse origin/main)"
printf 'current main: %s\n' "$current_main"
test "$current_main" = "$release_commit"
git tag -s -m "v0.1.0 — synthetic fixtures with provable boundaries" \
v0.1.0 "$release_commit"
git tag -v v0.1.0
test "$(git rev-parse 'v0.1.0^{}')" = "$release_commit"
git push origin v0.1.0
make release-tag
```

- [ ] Push the signed tag through the protected-tag bypass. The push target
rechecks the signature and signer, tagger identity, policy attestation,
peeled target, live ruleset, clean checkout, and `origin/main` equality
immediately before publication. This guarded split is intentional: it
preserves the check-verified SHA across local signing and publication.

```sh
make release-push-tag
```

- [ ] After the maintainer reports a successful push, return control to the
release operator. Confirm GitHub reports the annotated tag signature as
verified and its target as the literal check-verified release commit:

```sh
export THREELEAPS_SYNTHCORPUS_RELEASE_TAG="v$(tr -d '\n' < VERSION)"
export THREELEAPS_SYNTHCORPUS_RELEASE_COMMIT='<same literal full SHA used by the maintainer>'
make release-verify-remote-tag
```

- [ ] Create the GitHub release from `RELEASE_NOTES.md` without uploading files:

```sh
gh release create v0.1.0 --title "synthcorpus v0.1.0" \
gh release create "$THREELEAPS_SYNTHCORPUS_RELEASE_TAG" \
--title "synthcorpus $THREELEAPS_SYNTHCORPUS_RELEASE_TAG" \
--notes-file RELEASE_NOTES.md
```

- [ ] Confirm the release has zero uploaded assets. GitHub's automatic source
archives are not included in the `assets` API field:

```sh
test "$(gh release view v0.1.0 --json assets --jq '.assets | length')" -eq 0
test "$(gh release view "$THREELEAPS_SYNTHCORPUS_RELEASE_TAG" \
--json assets --jq '.assets | length')" -eq 0
```

## 5. Post-release housekeeping
Expand Down
Loading