Push backport branches via GraphQL createCommitOnBranch (signed commits)#1937
Push backport branches via GraphQL createCommitOnBranch (signed commits)#1937TooTallNate merged 1 commit intomainfrom
Conversation
The repo has an enterprise-level branch ruleset requiring verified
signatures on every ref (`~ALL`), so a normal `git push` of a locally
cherry-picked commit is rejected ("Commits must have verified
signatures"). Replace the `git push` step with a GraphQL
`createCommitOnBranch` mutation, which signs commits automatically
with GitHub's internal key (the same way commits made via the web UI
are signed).
Walks the cherry-pick's diff against the parent (`stable` HEAD),
collects file additions (with binary-safe base64 contents read via
`git cat-file blob`) and deletions, ensures the backport branch
exists on the remote, then runs the mutation. Also updates the manual
conflict-resolution instructions in the failure comment to mention
that local cherry-picks must be signed (`git cherry-pick -S`)
because of the same ruleset.
Caveats:
- Authorship is lost — `createCommitOnBranch` always attributes
commits to the token owner (`github-actions[bot]`). The original
commit SHA is still referenced in the PR body.
- Non-regular files (executable bit, symlinks, submodules) are not
supported by the mutation; the step warns and proceeds with mode
100644 for affected paths.
|
🧪 E2E Test Results✅ All tests passed Summary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
|
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.race with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 10 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 25 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 50 sequential data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 10 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 25 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro workflow with 50 concurrent data payload steps (10KB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) stream pipeline with 5 transform steps (1MB)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) 10 parallel streams (1MB each)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) fan-out fan-in 10 streams (1MB each)💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
|
No backport to This commit only modifies To override, add the |
There was a problem hiding this comment.
Pull request overview
This PR updates the backport.yml workflow to avoid branch ruleset rejections that require verified-signed commits on all refs. Instead of pushing a locally cherry-picked commit (unsigned in CI), it replays the cherry-pick via GitHub’s GraphQL createCommitOnBranch mutation so GitHub produces a verified-signed commit on the backport branch.
Changes:
- Replace
git pushwith a GitHub API–based branch update using GraphQLcreateCommitOnBranch(auto-signed commits). - Improve robustness of PR creation by reusing an existing open backport PR for the same branch instead of failing.
- Update manual conflict-resolution instructions to require signed local cherry-picks (
git cherry-pick -S) due to repo rulesets.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const [, mode, type, blobSha] = match; | ||
| if (type !== 'blob') { | ||
| throw new Error( | ||
| `Unsupported tree entry for ${path}: type=${type} mode=${mode}. ` + | ||
| `GraphQL createCommitOnBranch only supports regular files.`, | ||
| ); | ||
| } |
| const diffOutput = git( | ||
| 'diff', | ||
| '--name-status', | ||
| '-z', | ||
| parentSha, |
* origin/main: feat: serializable AbortController/AbortSignal (#1301) Auto-remove workflow packages from serverExternalPackages (#1481) Add missing changeset for Zod 4.4.x compatibility fix in @workflow/world (#1939) Push backport branch via GraphQL createCommitOnBranch for signed commits (#1937) Fix backport workflow opencode permission and surface AI failures (#1936) Restructure backport workflow with AI-driven decisions (#1934)
Summary
Backport of PR #1902 (label-triggered) failed with:
The repo has an enterprise-level branch ruleset matching
~ALLthat requires verified signatures on every ref, so a normalgit pushof a locally cherry-picked commit is rejected because CI doesn't have a signing key.This replaces the
git pushstep with a GraphQLcreateCommitOnBranchmutation, which signs commits automatically with GitHub's internal key (the same way commits made via the web UI or bychangesets/actionwithcommitMode: github-apiare signed).Implementation
After the local cherry-pick (with optional AI conflict resolution) succeeds, a new
Push backport branch via GitHub APIstep:git diff --name-status -zbetween the parent (stableHEAD) and the cherry-pick HEAD.git cat-file bloband base64-encodes them into aFileAddition. For each deletion, emits aFileDeletion. Renames are handled asadd(new) + delete(old).backport/<...>branch exists on the remote and points at thestableHEAD (the mutation requires the branch to already exist with the expectedexpectedHeadOid).createCommitOnBranchmutation. GitHub creates a verified-signed commit and updates the branch ref atomically.The subsequent
Create backport PRstep is unchanged.Caveats
createCommitOnBranchalways attributes commits to the token owner (github-actions[bot]), so we lose original-author attribution in the git log. The original commit SHA is still referenced in the PR body.100644for affected paths. This matches the documented limitations of@changesets/ghcommit, which therelease.ymlworkflow uses for its owncommitMode: github-api. Backports involving these file types will need manual signed pushes (the failure-comment instructions now mentiongit cherry-pick -Sfor that).Manual fallback
The
Comment on conflict failurestep's manual instructions are also updated to mention that local cherry-picks must be signed (git cherry-pick -S) because of the same ruleset, and to push to abackport/<...>branch and open a PR rather than pushing directly tostable.