Repository navigation
Deploy mode: redeem the merged PR's approval on push (v2.3.0) - #69
Conversation
On push and workflow_dispatch the action now finds the pull request that merged the commit being deployed (merge_commit_sha on the pushed branch), verifies the receipt a signer approved for that PR's head SHA, and redeems it once. This lets a Vercel, Netlify, Railway or Fly.io deploy workflow sit behind the same approval as the PR gate. Before this, running the action on push crashed: the PR number is empty outside pull_request events and `jq --argjson prNumber ""` failed. - mode: auto | pr | deploy (auto picks pr on pull_request, deploy on push and workflow_dispatch, and errors on anything else) - deploy mode fails closed: no merged PR (direct push), PR merged into another branch, PR lookup failure, tags, pull_request events, redeem: false, fail-on-missing: false - RECEIPT_EXPIRED prints the renew link when PP says a signer can renew - new outputs: mode, pr-number, pr-head-sha - test/run-tests.sh runs the action script against stubbed gh and curl (69 checks), wired into a Test workflow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
✅ Permission Protocol: Approved |
🔒 Permission Protocol
Approval briefAffected flow: Production deployment gating on push or workflow_dispatch events. Why this change: The action previously crashed on push events because it lacked PR context; it now supports gating the actual deploy by redeeming merged PR approvals. What changes for users/operators: Users can now gate deployments on push to main. The action finds the merged PR, verifies its approval receipt, and redeems it to ensure one approval ships once. Blast radius:
|
PP rejects a prTitle longer than 300 UTF-16 units. The first version cut at 300 code points, and an emoji counts as two units, so a long emoji-heavy title could exceed the limit and fail the gate closed with a validation error. The title is now cut to 300 code points when every code point is a single unit, else to 150 (at most two units each). test/run-tests.sh (from #69) now covers the title: quotes, a newline and a $(...) round-trip as a JSON string without being run; an empty title omits the key; a long ASCII title is cut to 300; a 200-emoji title is cut to 150 code points, 300 UTF-16 units; a push-mode deploy sends no title. Reverting to the code-point cap fails the emoji case. 77 passed locally (bash 3.2, jq 1.7.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Send the PR title with the verify request The approval page shows a request's title from the request row when PP cannot read the pull request itself (no GitHub App access to the repository in that workspace). The verify route has accepted an optional prTitle for a while; this action never sent one, so those pages fell back to "Authorize production deploy" with no description, even for a PR with a full title. The title reaches the script through env, never interpolated into it, since a PR title is attacker-controlled text, and reaches jq through --arg. PP accepts at most 300 characters and rejects null, so the value is trimmed to 300 and the key is left out when the title is empty. Checked locally with jq: a title with quotes and a newline round-trips as a JSON string; an empty title omits the key; a 400-character title is sent as 300. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Cap the PR title by UTF-16 length; test the title in the harness PP rejects a prTitle longer than 300 UTF-16 units. The first version cut at 300 code points, and an emoji counts as two units, so a long emoji-heavy title could exceed the limit and fail the gate closed with a validation error. The title is now cut to 300 code points when every code point is a single unit, else to 150 (at most two units each). test/run-tests.sh (from #69) now covers the title: quotes, a newline and a $(...) round-trip as a JSON string without being run; an empty title omits the key; a long ASCII title is cut to 300; a 200-emoji title is cut to 150 code points, 300 UTF-16 units; a push-mode deploy sends no title. Reverting to the code-point cap fails the emoji case. 77 passed locally (bash 3.2, jq 1.7.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Rod <rod@Rods-MacBook-Air.local> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
What
Deploy Gate can now gate the deploy, not just the merge. On
push/workflow_dispatchit:GET /repos/{repo}/commits/{sha}/pulls, keeping only merged PRs whosemerge_commit_shais the pushed SHA and whose base is the pushed branch.repo,prNumber,headSha,env,capability) the PR gate used.This is the "protected CI workflow" the PP app tells Vercel, Netlify, Railway and Fly.io users to set up; until now none worked. Running the action on push crashed:
PP_PR_NUMBERis empty outsidepull_request, andjq --argjson prNumber ""errors.Fails closed
PP_NO_MERGED_PRpull-requests: read), or the response is unreadablePP_PR_LOOKUP_FAILEDmode: deployon a pull_request eventPP_UNSUPPORTED_EVENTPP_UNSUPPORTED_REFredeem: falseorfail-on-missing: falsein deploy modePP_INVALID_CONFIGmode: prwithout a PRPP_NO_PR_CONTEXT(previously a jq crash)None of these call PP. Receipt errors (
RECEIPT_NOT_FOUND,RECEIPT_ALREADY_REDEEMED,RECEIPT_EXPIRED) keep their existing handling, with deploy-specific messages.RECEIPT_EXPIREDprintsreapproval.approvalUrlwhen PP reportsautoRenewEligible.Compatibility
modedefaults toauto. Existingpull_requestworkflows run inprmode, which behaves as before.redeemnow defaults to'', which means false inprmode (same as the old default) and true indeploymode. Explicitredeem: truein a PR gate is unchanged.Known limit (server side, not this PR)
Production approvals expire 15 minutes after signing, and PP refuses to renew an approval once the PR is merged. A PR merged more than 15 minutes after approval therefore fails deploy with
RECEIPT_EXPIREDand has no renew path. Auto-merge avoids this. The fix belongs in the PP app's re-approval rules. INSTALL.md states the limit.Tests
test/run-tests.shextracts the action'srun:block and runs it against stubbedghandcurl, asserting exit codes, outputs and the exact request body sent to PP. It runs in the newTestworkflow.merge_commit_shamatch, or turning off redeem in deploy mode, makes the suite fail.Release
Not tagged. After merge: tag
v2.3.0and movev2. Note thatv2currently points at v2.1.0 (fbf265b), so moving it also ships v2.2.0's draft-PR skip to every@v2user.🤖 Generated with Claude Code