Skip to content

fix: replace git push with GraphQL signed commits to satisfy required_signatures rulesets#21576

Merged
pelikhan merged 5 commits intomainfrom
copilot/implement-feature-plan
Mar 18, 2026
Merged

fix: replace git push with GraphQL signed commits to satisfy required_signatures rulesets#21576
pelikhan merged 5 commits intomainfrom
copilot/implement-feature-plan

Conversation

Copy link
Contributor

Copilot AI commented Mar 18, 2026

Commits created via git am + git push over the Git protocol are unverified because GitHub does not auto-sign them. This breaks repos with required_signatures branch protection rules (enterprise blocker).

Approach

Replace only the final git push step with a GraphQL replay that reads the locally-applied tree (from git am) and submits each commit via createCommitOnBranch — which GitHub auto-signs. The patch generation and git am pipeline is unchanged.

Changes

  • push_to_pull_request_branch.cjs — add pushSignedCommits() helper; replace git push call site
  • create_pull_request.cjs — same helper (deduped inline); replace both git push call sites (post-patch and allow-empty)

pushSignedCommits() behavior

git rev-list --reverse baseRef..HEAD   →  collect new commits (oldest-first)
  for each sha:
    git ls-remote                      →  fetch current remote HEAD OID
    git log -1 --format=%B             →  commit message (headline + body)
    git diff --name-status sha^  sha   →  file changes (A/M/D/R/C)
    githubClient.graphql(createCommitOnBranch) → signed commit on remote
  catch (GraphQL fails, e.g. GHES):
    git push origin <branch>           →  fallback with warning
  • Handles Added, Modified, Deleted, Renamed, and Copied files
  • gitAuthEnv passed through to fallback git push for cross-repo PAT auth
  • Updated push_to_pull_request_branch.test.cjs to mock the new GraphQL + fallback flow
Original prompt

Follow this plan:

#21572 (comment)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.



✨ PR Review Safe Output Test - Run 23245212334

💥 [THE END] — Illustrated by Smoke Claude ·



✨ PR Review Safe Output Test - Run 23249688423

💥 [THE END] — Illustrated by Smoke Claude ·

Copilot AI and others added 2 commits March 18, 2026 12:20
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Add pushSignedCommits() helper to push_to_pull_request_branch.cjs and
create_pull_request.cjs that replays local commits via the GitHub
GraphQL createCommitOnBranch mutation, producing cryptographically
signed commits. Falls back to plain git push on GHES or when GraphQL
fails. Handles added/modified/deleted/renamed/copied files.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement feature based on provided plan fix: replace git push with GraphQL signed commits to satisfy required_signatures rulesets Mar 18, 2026
Copilot AI requested a review from pelikhan March 18, 2026 12:30
* @param {object} [opts.gitAuthEnv] - Environment variables for git push fallback auth
* @returns {Promise<void>}
*/
async function pushSignedCommits({ githubClient, owner, repo, branch, baseRef, cwd, gitAuthEnv }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot move to helper file, add integration test into a test branch (reuse test branch) . Reuse with create pr safe output

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ The smoke test agent confirms: pushSignedCommits has been moved to push_signed_commits.cjs and is reused by both create_pull_request.cjs and push_to_pull_request_branch.cjs as requested! The new helper file also includes push_signed_commits.test.cjs for testing.

📰 BREAKING: Report filed by Smoke Copilot

Copy link
Contributor Author

Copilot AI commented Mar 18, 2026

@pelikhan I've opened a new pull request, #21584, to work on those changes. Once the pull request is ready, I'll request review from you.

@github-actions
Copy link
Contributor

Hey @Copilot 👋 — great work tackling the required_signatures enterprise blocker! Replacing git push with a createCommitOnBranch GraphQL replay is the right architectural move, and the graceful GHES fallback is a nice touch. A few things to clean up before this is ready:

  • Unrelated go.mod change — the diff promotes github.com/charmbracelet/lipgloss from // indirect to a direct dependency in go.mod. None of the changed .cjs files are Go code; this change is entirely orthogonal to the signed-commits feature. Please revert it (or explain why it belongs here) to keep the PR focused.

  • Duplicated pushSignedCommits helper — the function is copied verbatim into both create_pull_request.cjs and push_to_pull_request_branch.cjs. The project already has git_helpers.cjs as a shared utility module. Extracting pushSignedCommits there would eliminate the duplication and make future changes easier to maintain.

  • Missing tests for create_pull_request.cjspush_to_pull_request_branch.test.cjs is updated with mocks for the new GraphQL + fallback flow, but the corresponding test file for create_pull_request.cjs does not include equivalent coverage for the two new pushSignedCommits call sites added in that module.

If you'd like a hand addressing these, here's a ready-to-run prompt for your coding agent:

In the branch `copilot/implement-feature-plan` of `github/gh-aw`, clean up PR #21576 as follows:

1. **Revert the unrelated `go.mod` change**: the diff moves `github.com/charmbracelet/lipgloss` from `// indirect` to a direct dependency. Revert it so `go.mod` is unchanged from the base branch for this entry.

2. **Extract `pushSignedCommits` to a shared module**: the function is currently duplicated in both `actions/setup/js/create_pull_request.cjs` and `actions/setup/js/push_to_pull_request_branch.cjs`. Move it into `actions/setup/js/git_helpers.cjs` (which already exports shared git utilities), export it with `module.exports.pushSignedCommits = pushSignedCommits`, and replace both inline definitions with a `require('./git_helpers.cjs')` import.

3. **Add tests for `create_pull_request.cjs`**: in the test file for `create_pull_request.cjs`, add at minimum two test cases covering the two new `pushSignedCommits` call sites:
   a. Happy path — GraphQL `createCommitOnBranch` succeeds and the signed OID is logged.
   b. Fallback path — GraphQL fails and `git push origin (branch)` is called instead.
   Mirror the mock pattern already used in `push_to_pull_request_branch.test.cjs`.

Generated by Contribution Check ·

@pelikhan pelikhan added the smoke label Mar 18, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2026

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2026

✅ All tools validated successfully! Agent Container Smoke Test confirms agent container is ready.

@github-actions github-actions bot removed the smoke label Mar 18, 2026
@github-actions
Copy link
Contributor

Agent Container Tool Check

Tool Status Version
bash 5.2.21
sh available
git 2.53.0
jq 1.7
yq 4.52.4
curl 8.5.0
gh 2.87.3
node 20.20.1
python3 3.12.3
go 1.24.13
java 21.0.10 (Temurin)
dotnet 10.0.102

Result: 12/12 tools available ✅

Overall Status: PASS

🔧 Tool validation by Agent Container Smoke Test ·

@github-actions
Copy link
Contributor

Commit pushed: 2e57b3b

Generated by Changeset Generator

@github-actions
Copy link
Contributor

Smoke Test: Codex Engine Validation
PR titles:

🔮 The oracle has spoken through Smoke Codex ·

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

@github-actions
Copy link
Contributor

🤖 Smoke test §23249688412⚠️ 11/12 pass

Test Result
GitHub MCP
MCP Scripts GH CLI
Serena MCP
Playwright
Web Fetch
File Writing
Bash Tool
Discussion Interaction
Build gh-aw
Discussion Creation
Workflow Dispatch
PR Review

Status: PARTIAL PASS — Serena MCP tools not available
Author: @copilot-swe-agent | Assignees: @pelikhan @Copilot

📰 BREAKING: Report filed by Smoke Copilot ·

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smoke test review of PR #21576 — replacing git push with GraphQL createCommitOnBranch for signed commits is a solid approach to satisfy required-signature rulesets. Two minor observations: (1) exec needs an explicit require, and (2) the per-commit ls-remote loop could be optimized. Overall the implementation looks correct with good fallback handling.

📰 BREAKING: Report filed by Smoke Copilot

* @param {object} opts
* @param {any} opts.githubClient - Authenticated Octokit client with .graphql()
* @param {string} opts.owner - Repository owner
* @param {string} opts.repo - Repository name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exec module is used here but doesn't appear to be declared with a require at the top of the file. Consider adding const exec = require("@actions/exec"); to the imports section to make the dependency explicit.

const expectedHeadOid = oidOut.trim().split(/\s+/)[0];
if (!expectedHeadOid) {
throw new Error(`Could not resolve remote HEAD OID for branch ${branch}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ls-remote call on every iteration of the loop (for (const sha of shas)) makes an extra network round-trip per commit. For repos with many new commits, this could be slow. Consider fetching the initial remote HEAD OID once before the loop and updating it locally after each successful GraphQL mutation.

@github-actions
Copy link
Contributor

Smoke Test Results — Run §23249688423

Test Result
1 GitHub MCP
2 GH CLI Scripts
3 Serena MCP
4 Make Build
5 Playwright
6 Tavily Search
7 File Writing
8 Bash Tool
9 Discussion Comment
10 AW MCP Status
11 Update PR
12 PR Review Comments
13 Submit PR Review
14 Resolve Thread ⚠️
15 Add Reviewer
16 Push to Branch
17 Close PR ⚠️

Overall: PARTIAL (2 skipped)

💥 [THE END] — Illustrated by Smoke Claude ·

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💥 Automated smoke test review - all systems nominal!

💥 [THE END] — Illustrated by Smoke Claude

* @param {object} [opts.gitAuthEnv] - Environment variables for git push fallback auth
* @returns {Promise<void>}
*/
async function pushSignedCommits({ githubClient, owner, repo, branch, baseRef, cwd, gitAuthEnv }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function signature destructures all options inline — consider defining a JSDoc typedef for the options object to improve IDE autocomplete and make it clearer what callers must provide.

const { stdout: revListOut } = await exec.getExecOutput("git", ["rev-list", "--reverse", `${baseRef}..HEAD`], { cwd });
const shas = revListOut.trim().split("\n").filter(Boolean);

if (shas.length === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good early-exit guard. The shas.length === 0 check prevents unnecessary GraphQL calls when there are no new commits to push.

@github-actions
Copy link
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@pelikhan pelikhan marked this pull request as ready for review March 18, 2026 15:08
Copilot AI review requested due to automatic review settings March 18, 2026 15:08
@pelikhan pelikhan merged commit 6c1b6bd into main Mar 18, 2026
1 check passed
@pelikhan pelikhan deleted the copilot/implement-feature-plan branch March 18, 2026 15:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces direct git push usage with a GraphQL-based commit replay (createCommitOnBranch) so commits created by the workflow are GitHub-signed and comply with required_signatures branch protection rules, with a fallback to git push when GraphQL isn’t available.

Changes:

  • Introduces pushSignedCommits() helper to replay commits via GraphQL createCommitOnBranch and fall back to git push.
  • Updates push_to_pull_request_branch.cjs and create_pull_request.cjs to use the new helper instead of direct git push.
  • Adds integration tests for the helper and updates existing tests to cover the new flow.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
actions/setup/js/push_signed_commits.cjs Adds GraphQL-based signed commit replay helper with git-push fallback.
actions/setup/js/push_to_pull_request_branch.cjs Switches push step from git push to pushSignedCommits().
actions/setup/js/create_pull_request.cjs Switches branch push steps from git push to pushSignedCommits().
actions/setup/js/push_signed_commits.test.cjs Adds integration tests for commit enumeration, payload construction, and fallback behavior.
actions/setup/js/push_to_pull_request_branch.test.cjs Updates mocks to account for the new signed-push flow and fallback.
go.mod Promotes github.com/charmbracelet/lipgloss to a direct dependency.
.changeset/patch-use-signed-commit-pushes.md Declares a patch release for signed-commit push behavior change.
Comments suppressed due to low confidence (1)

actions/setup/js/create_pull_request.cjs:919

  • Same issue as the earlier pushSignedCommits call: if the branch doesn’t already exist on the remote, pushSignedCommits() will fall back to git push (unsigned) because git ls-remote returns no OID. For the allow-empty branch creation path, this likely happens every time; consider creating the remote ref first via GraphQL (or in the helper) so createCommitOnBranch is used and commits are GitHub-signed under required_signatures.
          await pushSignedCommits({
            githubClient,
            owner: repoParts.owner,
            repo: repoParts.repo,
            branch: branchName,
            baseRef: `origin/${baseBranch}`,
            cwd: process.cwd(),
          });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +76 to +85
} else if (status.startsWith("R") || status.startsWith("C")) {
// Rename or Copy: parts[1] = old path, parts[2] = new path
deletions.push({ path: parts[1] });
const content = fs.readFileSync(path.join(cwd, parts[2]));
additions.push({ path: parts[2], contents: content.toString("base64") });
} else {
// Added or Modified
const content = fs.readFileSync(path.join(cwd, parts[1]));
additions.push({ path: parts[1], contents: content.toString("base64") });
}
Comment on lines +51 to +56
// Get the current remote HEAD OID (updated each iteration)
const { stdout: oidOut } = await exec.getExecOutput("git", ["ls-remote", "origin", `refs/heads/${branch}`], { cwd });
const expectedHeadOid = oidOut.trim().split(/\s+/)[0];
if (!expectedHeadOid) {
throw new Error(`Could not resolve remote HEAD OID for branch ${branch}`);
}
Comment on lines +96 to +112
const result = await githubClient.graphql(
`mutation($input: CreateCommitOnBranchInput!) {
createCommitOnBranch(input: $input) { commit { oid } }
}`,
{ input }
);
const oid = result?.createCommitOnBranch?.commit?.oid;
core.info(`pushSignedCommits: signed commit created: ${oid}`);
}
core.info(`pushSignedCommits: all ${shas.length} commit(s) pushed as signed commits`);
} catch (graphqlError) {
core.warning(`pushSignedCommits: GraphQL signed push failed, falling back to git push: ${graphqlError instanceof Error ? graphqlError.message : String(graphqlError)}`);
await exec.exec("git", ["push", "origin", branch], {
cwd,
env: { ...process.env, ...(gitAuthEnv || {}) },
});
}
Comment on lines +744 to +751
await pushSignedCommits({
githubClient,
owner: repoParts.owner,
repo: repoParts.repo,
branch: branchName,
baseRef: `origin/${baseBranch}`,
cwd: process.cwd(),
});
lpcox added a commit that referenced this pull request Mar 18, 2026
* docs: add GH_HOST and RUNNER_TEMP to glossary (daily scan 2026-03-18) (#21553)

* docs: update GHES CLI docs and add Copilot GHES troubleshooting guide (#21528)

* Fix list height not updating on terminal resize (#21555)

* fix(daily-workflow-updater): unblock PR creation by setting protected-files: allowed (#21554)

* chore: bump MCP Gateway v0.1.15→v0.1.17 (#21552)

* feat: custom Huh theme mapped from pkg/styles Dracula palette (#21557)

* Add top-level `github-app` frontmatter as universal fallback for token minting (#21510)

* feat: add GitHub App-only permissions support (#21511)

* fix: fall back to existing remote tracking ref when incremental patch fetch fails (#21568)

* Add weekly blog post writer agentic workflow (#21575)

* ci: add timeout-minutes to all 25 jobs lacking explicit limits (#21601)

* fix: add setupGlobals in generate_aw_info step to fix staged mode ReferenceError (#21602)

* Disable lockdown mode for weekly blog post generator (#21598)

* fix: replace git push with GraphQL signed commits to satisfy required_signatures rulesets (#21576)

* docs: add weekly update blog post for 2026-03-18 (#21608)

Covers v0.58.0 through v0.61.0 (7 releases this week),
notable PRs, and auto-triage-issues as Agent of the Week.

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix failing CI: merge main to include setupGlobals in generate_aw_info step and update golden files

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
lpcox added a commit that referenced this pull request Mar 18, 2026
…ains on data residency (#21527)

* Initial plan

* Fix threat detection AWF run missing --copilot-api-target and GHE domains on data residency

When engine.api-target is configured for GHE Cloud with data residency,
propagate the APITarget from the main engine config to the detection
engine config so the threat detection AWF invocation receives the same
--copilot-api-target flag and GHE-specific domains in --allow-domains.

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Add GITHUB_COPILOT_BASE_URL env var support and fix integration test api-target

- Fix TestAPITargetDomainsInThreatDetectionStep to use api.contoso-aw.ghe.com
  (copilot-api. prefix does not trigger base-domain derivation in GetAPITargetDomains)
- Add GetCopilotAPITarget() helper: resolves --copilot-api-target from engine.api-target
  (explicit) or GITHUB_COPILOT_BASE_URL in engine.env (fallback), mirroring the
  OPENAI_BASE_URL/ANTHROPIC_BASE_URL pattern for Codex/Claude
- Update awf_helpers.go, copilot_engine_execution.go, and domains.go to use the helper
- Add unit tests for GetCopilotAPITarget and engine execution step
- Add integration test TestGitHubCopilotBaseURLInCompiledWorkflow
- Update engines.md docs to document GITHUB_COPILOT_BASE_URL

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

* Update DefaultMCPGatewayVersion to v0.1.17 and regenerate golden/lock files (#21609)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

* fix: merge main to resolve failing build-wasm and test CI jobs (#21612)

* docs: add GH_HOST and RUNNER_TEMP to glossary (daily scan 2026-03-18) (#21553)

* docs: update GHES CLI docs and add Copilot GHES troubleshooting guide (#21528)

* Fix list height not updating on terminal resize (#21555)

* fix(daily-workflow-updater): unblock PR creation by setting protected-files: allowed (#21554)

* chore: bump MCP Gateway v0.1.15→v0.1.17 (#21552)

* feat: custom Huh theme mapped from pkg/styles Dracula palette (#21557)

* Add top-level `github-app` frontmatter as universal fallback for token minting (#21510)

* feat: add GitHub App-only permissions support (#21511)

* fix: fall back to existing remote tracking ref when incremental patch fetch fails (#21568)

* Add weekly blog post writer agentic workflow (#21575)

* ci: add timeout-minutes to all 25 jobs lacking explicit limits (#21601)

* fix: add setupGlobals in generate_aw_info step to fix staged mode ReferenceError (#21602)

* Disable lockdown mode for weekly blog post generator (#21598)

* fix: replace git push with GraphQL signed commits to satisfy required_signatures rulesets (#21576)

* docs: add weekly update blog post for 2026-03-18 (#21608)

Covers v0.58.0 through v0.61.0 (7 releases this week),
notable PRs, and auto-triage-issues as Agent of the Week.

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix failing CI: merge main to include setupGlobals in generate_aw_info step and update golden files

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Co-authored-by: Landon Cox <landon.cox@microsoft.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants