fix(tag-release): use Git Data API so commits and tags auto-sign#56
Open
j7an wants to merge 10 commits into
Open
fix(tag-release): use Git Data API so commits and tags auto-sign#56j7an wants to merge 10 commits into
j7an wants to merge 10 commits into
Conversation
These tests are intentionally failing — they pin the contract that the Git Data API rewrite of tag-release.yml will consume. The manifest must be a unique path set so POST /git/trees does not receive duplicate tree[] entries. Refs cross-agent-reviews#12.
…phase pass
Code-quality review found that Test 4 (no cross-run pollution) and Test 2
(unique-paths in mixed run) could pass for the wrong reason during the
red phase if /tmp/bump.modified didn't exist:
- Test 4: `! grep -q server.json /tmp/bump.modified` returned true on a
missing file (grep fails, ! flips), masking that the script wasn't
creating the manifest at all. Add explicit `[ -f /tmp/bump.modified ]`
guard before the negated grep.
- Test 2: `actual=$(sort /tmp/bump.modified)` swallowed the missing-file
error into an empty `actual`, producing a content-mismatch message
that hid the real problem. Switch to `< /tmp/bump.modified` so the
shell-level open fails loudly with No such file or directory.
Also clarify Test 3's confusing inline comment about idempotent rerun.
Refs cross-agent-reviews#12.
Manifest of modified paths consumed by tag-release.yml's Git Data API rewrite. De-duped via sort -u so multi-entry-same-file configs (two path_expr entries against one file) produce one path, not two — which would create duplicate tree[] entries in POST /git/trees. Refs cross-agent-reviews#12.
Mirror the manifest-emission changes from scripts/bump-version-files.sh into the inline copy embedded in tag-release.yml, satisfying the inline-sync invariant validated by scripts/check-inline-sync.sh. Refs cross-agent-reviews#12.
Static checks that the rewritten Bump and Tag steps do not regress to local-state reads (which would reintroduce the silent-overwrite race the API rewrite is designed to prevent) and that bot-signing-disabling identity fields stay out of API payloads. Phase B-only checks (GITHUB_SHA usage, no identity fields) skip on the current Phase A baseline and activate after the workflow rewrite lands. Refs cross-agent-reviews#12.
Replace `git commit && git push origin HEAD:main` with a sequence of gh api calls: blobs → tree → commit → PATCH ref. The commit's parent is BASE_SHA=$GITHUB_SHA (the runner's checkout SHA), not a fresh read of main, to avoid the silent-overwrite race documented in spec §2. The commit payload omits author/committer fields so GitHub auto-signs with its bot key (spec §3.3) — satisfying the required_signatures branch ruleset on consumer repos like cross-agent-reviews without needing bypass-actor entries. The exit-2 branch (no bump created) reads main HEAD via API and exports TAG_TARGET_SHA for the tag step. Fixes cross-agent-reviews#12.
Code-quality review against GitHub's REST API docs revealed the plan and
spec had inherited a wrong jq path: GET /repos/.../git/commits/{sha}
returns the raw Git commit object with `verification` at the TOP level,
not under `.commit.verification` (that nesting only exists on the higher-
level GET /repos/.../commits/{sha} endpoint).
With the wrong path, $VERIFIED would always be the literal string "null"
and the acceptance-criterion-#2 check would always fail with exit 1
AFTER the bump commit had already been PATCH'd onto main — leaving a
dangling commit and no tag.
Fix: keep the low-level /git/commits/{sha} endpoint (consistent with the
PARENT_TREE_SHA call earlier in the same step) but use .verification.*
jq paths instead. Add a comment documenting the gotcha so future
maintainers don't reintroduce it.
Refs cross-agent-reviews#12.
Replace `git tag -a && git push origin <tag>` with POST /git/tags + POST /git/refs. Tag points at TAG_TARGET_SHA exported by the prior Bump step (new commit SHA in the happy path; current main HEAD via API in the no-bump case). Tag payload omits the tagger field so GitHub auto-signs the tag object with its bot key when possible. The verification.verified status is reported in the workflow step summary so the canary run can record acceptance criterion #3 (signed) vs #4 (unsigned-fallback) per spec §6. Fixes cross-agent-reviews#12.
Three review findings on the signed-commit release path: - Finding 2: the case-0 PATCH to refs/heads/main now runs *after* the verification check, not before. A commit object is addressable by SHA the instant POST /git/commits returns, so verification needs no ref update first. Verifying first means a verification failure leaves main untouched — no dangling unverified commit for a later rerun to pick up. - Finding 1: the no-bump (exit 2) path now tags GITHUB_SHA — the commit "Compute release" semver-analyzed — instead of a live GET /git/ref/heads/main read. The live read raced with concurrent pushes (spec §2) and could tag an unanalyzed commit. GITHUB_SHA also covers rerun-after-partial-failure: a fresh dispatch resolves it to current main HEAD anyway. - Finding 4: manifest path is now configurable via BUMP_MODIFIED_FILE (default unchanged) so concurrent script/test runs don't clobber a shared /tmp file. The bats suite pins a per-test path; the inline copy stays in sync.
Finding 3: the parent-SHA invariant test previously only checked that GITHUB_SHA appeared somewhere in the bump step — a stray echo would satisfy it. It now asserts the full load-bearing chain: GITHUB_SHA -> BASE_SHA -> jq --arg parent -> commit payload. Adds a companion guard (Finding 1 regression): the bump step must not perform a live `GET .../git/ref/heads/` read. The singular-`ref` GET form is forbidden; the legitimate fast-forward PATCH uses the plural `git/refs/heads/main` and is unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tag-release.yml's bump-commit step to use the GitHub Git Data API (POST /git/blobs→POST /git/trees→POST /git/commits→PATCH /git/refs/heads/main). The commit's parent isBASE_SHA=$GITHUB_SHA, not a fresh read ofmain, to avoid the silent-overwrite race documented in the spec.POST /git/tags+POST /git/refsagainst either the new bump commit's SHA ormain's current HEAD (via API, never local checkout state).author/committer/taggerfields so GitHub auto-signs with its bot/web-flow key — satisfying therequired_signaturesbranch ruleset on consumer repos without bypass-actor entries./tmp/bump.modifiedmanifest emission (unique path set) toscripts/bump-version-files.shso the API path knows which files to put in the tree.The verification check uses the low-level
GET /git/commits/{sha}endpoint with jq path.verification.verified(top-level), not the higher-level/commits/{sha}endpoint with.commit.verification.verified— a distinction that bit us during review and is now documented in-line.See the design notes in the spec authored alongside this PR (kept locally per the project's docs/ workflow; not committed) for the full reasoning behind the design invariant, race-detection backstop, and acceptance criteria.
Test plan
bash scripts/check-inline-sync.shpassesbash scripts/lint-workflow-call.shpassesbats tests/bump-version-files.bats— 50/50 (existing 46 + 4 new manifest tests)bats tests/tag-release-invariants.bats— 4/4bats tests/suite — 92/92python3 yaml.safe_load(...)git config user.*,git tag -a, orgit push originpatterns remain intag-release.ymlfix:+ 3 ×test:, 0 ×feat:(no inadvertent minor-bump trigger)v2.5.3-rc1(manual —release.ymlisworkflow_callonly, not push-driven), pincross-agent-reviewsto RC SHA, run Tag Release oncross-agent-reviewsviaworkflow_dispatch, verify acceptance criteria deps: bump step-security/harden-runner from 2.16.0 to 2.16.1 #1–fix: use PR author instead of github.actor for bot detection #7 from spec §6v2.5.3from RC SHA (manual replay ofrelease.yml's two steps — floatingv2/v2.5tag updates +gh release create v2.5.3), bumpcross-agent-reviewstov2.5.3finalFixes j7an/cross-agent-reviews#12.