Skip to content

fix: pushSignedCommits fails on new branches with "Require signed commits" ruleset#22012

Merged
pelikhan merged 7 commits intomainfrom
copilot/fix-create-pull-request-signed-commits
Mar 20, 2026
Merged

fix: pushSignedCommits fails on new branches with "Require signed commits" ruleset#22012
pelikhan merged 7 commits intomainfrom
copilot/fix-create-pull-request-signed-commits

Conversation

Copy link
Contributor

Copilot AI commented Mar 20, 2026

createCommitOnBranch requires a valid expectedHeadOid, but git ls-remote returns empty for branches that don't yet exist on the remote. The code threw an error and fell back to plain git push, which is rejected (GH013) by repos with "Require signed commits" rulesets.

Changes

  • New branch support: When git ls-remote returns empty, resolve expectedHeadOid via git rev-parse sha^ (parent of the first commit). createCommitOnBranch will create the branch automatically at the correct base.
  • OID chaining: After each successful mutation, reuse the returned OID as expectedHeadOid for the next commit instead of re-querying git ls-remote — eliminates redundant network calls and is correct by construction for both new and existing branches.
// Before: throws "Could not resolve remote HEAD OID" → falls back to unsigned git push
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}`);
}

// After: resolves parent OID when branch is new; chains GraphQL-returned OIDs for subsequent commits
if (lastOid) {
  expectedHeadOid = lastOid;                              // reuse previous mutation result
} else {
  expectedHeadOid = oidOut || parentOfFirstCommit;        // ls-remote or git rev-parse sha^
}

Two new integration tests cover single-commit and multi-commit pushes to a branch that has never been pushed.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/github/gh-aw/contents/.github%2Fworkflows%2Faudit-workflows.md
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --conditions node --conditions development --experimental-import-meta-resolve --require /home/REDACTED/work/gh-aw/gh-aw/actions/setup/js/node_modules/vitest/suppress-warnings.cjs /home/REDACTED/work/gh-aw/gh-aw/actions/setup/js/node_modules/vitest/dist/workers/forks.js (http block)
  • invalid.example.invalid
    • Triggering command: /usr/lib/git-core/git-remote-https /usr/lib/git-core/git-remote-https origin https://invalid.example.invalid/nonexistent-repo.git git init�� de_modules/.bin/-u lure tions/setup/node_modules/.bin/git user.email feature branch ache/node/24.14.-b git bran�� -M main 0/x64/lib/node_modules/npm/node_/home/REDACTED/work/gh-aw/gh-aw/actions/setup/js/node_modules/vite--verify /tmp/bare-incremgit gin/feature-branadd odules/npm/node_agent-change.txt git (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits March 20, 2026 18:41
When `git ls-remote` returns empty (branch doesn't exist on remote),
use `git rev-parse sha^` to get the parent commit OID as
`expectedHeadOid` for the `createCommitOnBranch` GraphQL mutation.
This allows the mutation to create the branch automatically.

After each successful mutation, reuse the returned OID for the next
iteration instead of re-querying `git ls-remote`.

Fixes: create-pull-request signed commits fail when branch does not
yet exist on remote (GH013 rejection with signed commits ruleset).

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/9a1cfa2e-dbc6-4899-8ea8-77ba1711969f
Copilot AI changed the title [WIP] Fix create-pull-request signed commits when branch does not exist on remote fix: pushSignedCommits fails on new branches with "Require signed commits" ruleset Mar 20, 2026
Copilot AI requested a review from pelikhan March 20, 2026 18:48
@pelikhan pelikhan marked this pull request as ready for review March 20, 2026 18:50
Copilot AI review requested due to automatic review settings March 20, 2026 18:50
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

Fixes pushSignedCommits so it can push signed commits to branches that don’t yet exist on the remote (avoiding fallback to unsigned git push that is rejected by “Require signed commits” rulesets), and reduces redundant remote OID lookups by chaining GraphQL-returned commit OIDs.

Changes:

  • Resolve expectedHeadOid for new (unpushed) branches using the parent OID of the first commit when git ls-remote returns empty.
  • Chain createCommitOnBranch’s returned commit OID across multiple commits instead of re-running git ls-remote each iteration.
  • Add integration tests covering single-commit and multi-commit pushes to a branch that has never been pushed.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
actions/setup/js/push_signed_commits.cjs Implements new-branch expectedHeadOid resolution and OID chaining for multi-commit GraphQL replay.
actions/setup/js/push_signed_commits.test.cjs Adds integration tests validating new-branch parent-OID behavior and multi-commit OID chaining.
.github/workflows/smoke-codex.lock.yml Updates generated safe-output tool schema/step inputs for add_smoked_label action usage.
Comments suppressed due to low confidence (3)

actions/setup/js/push_signed_commits.cjs:71

  • The thrown error Could not resolve OID for new branch ${branch} drops important context for debugging (which SHA was being processed / which command failed). Including the SHA (and/or the failing rev-parse target ${sha}^) in the error message would make failures actionable, especially since this path only triggers when ls-remote is empty.
          core.info(`pushSignedCommits: branch ${branch} not yet on the remote, resolving parent OID for first commit`);
          const { stdout: parentOut } = await exec.getExecOutput("git", ["rev-parse", `${sha}^`], { cwd });
          expectedHeadOid = parentOut.trim();
          if (!expectedHeadOid) {
            throw new Error(`Could not resolve OID for new branch ${branch}`);
          }

.github/workflows/smoke-codex.lock.yml:457

  • Grammar in the tool description: "if there're multiple labels" should be corrected (e.g., "if there are multiple labels"). This string is user-facing in the generated tool metadata.
                      "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.",
                      "type": "string"

.github/workflows/smoke-codex.lock.yml:458

  • The PR description focuses on fixing pushSignedCommits behavior, but this PR also changes the generated smoke-codex.lock.yml safe-output action schema/step inputs. If this workflow change is intentional, consider noting it in the PR description; otherwise, it may be an unrelated regenerated diff that can be reverted to keep the PR scoped.
              {
                "description": "Add the 'smoked' label to the current pull request (can only be called once)",
                "inputSchema": {
                  "additionalProperties": false,
                  "properties": {
                    "labels": {
                      "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.",
                      "type": "string"
                    },

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

);
const oid = result?.createCommitOnBranch?.commit?.oid;
core.info(`pushSignedCommits: signed commit created: ${oid}`);
lastOid = result?.createCommitOnBranch?.commit?.oid;
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

lastOid is assigned from the GraphQL response but never validated. If createCommitOnBranch returns an unexpected shape (or oid is null), the loop will continue and log signed commit created: undefined, and subsequent commits may be based on an incorrect/stale HEAD. Consider asserting that the mutation returned a non-empty OID (and throwing if not) before continuing/chaining.

This issue also appears on line 66 of the same file.

Suggested change
lastOid = result?.createCommitOnBranch?.commit?.oid;
const newOid = result && result.createCommitOnBranch && result.createCommitOnBranch.commit
? result.createCommitOnBranch.commit.oid
: undefined;
if (typeof newOid !== "string" || newOid.length === 0) {
throw new Error("pushSignedCommits: GraphQL createCommitOnBranch did not return a valid commit OID");
}
lastOid = newOid;

Copilot uses AI. Check for mistakes.
}
},
"required": [
"labels"
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The generated tool schema marks only labels as required, but the workflow step always passes both labels and number to actions-ecosystem/action-add-labels. If the agent calls add_smoked_label using the documented payload (labels only), .number will be null/empty and the action may fail outside pull_request-triggered runs (e.g. schedule/workflow_dispatch). Consider either making number required in the schema or defaulting number to the current PR number when available.

This issue also appears in the following locations of the same file:

  • line 456
  • line 450
Suggested change
"labels"
"labels",
"number"

Copilot uses AI. Check for mistakes.
@pelikhan
Copy link
Contributor

@copilot review comments

…d_label

- Validate createCommitOnBranch OID before chaining; throw if null/empty
- Add number to required fields for add_smoked_label in smoke-codex.lock.yml
- Update smoke-codex.md agent instructions to include number in the call

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c7d3c322-e1bd-4ec2-bf01-fdf30ae9571b
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot review comments

Addressed both review comments in 93aa792:

  1. OID validation (push_signed_commits.cjs): Now explicitly validates the createCommitOnBranch response and throws "did not return a valid commit OID" if the OID is missing or not a non-empty string, preventing silent chaining of bad state.

  2. add_smoked_label required fields (smoke-codex.lock.yml): Added "number" to the required array alongside "labels". Also updated the agent instructions in smoke-codex.md to include "number": "${{ github.event.pull_request.number }}" in the example payload.

@pelikhan
Copy link
Contributor

@copilot add logging to help diagnostics

Log per-commit SHA and progress, OID source (chained/ls-remote/parent),
commit headline, file change counts, and mutation invocation details

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/fe3acff2-47c1-4f2f-b977-d1b6b832cd69
Copy link
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot add logging to help diagnostics

Added in a645698. The following diagnostic log lines are now emitted during pushSignedCommits:

  • Startup: commit count, branch, and repo
  • Per commit: ${i+1}/${total} sha=…
  • OID source: using chained OID, using remote HEAD OID from ls-remote, or using parent OID for new branch
  • Commit headline
  • File change counts: N addition(s), M deletion(s)
  • Before mutation: calling createCommitOnBranch mutation (expectedHeadOid=…)
  • After mutation: signed commit created: <oid>

@pelikhan pelikhan merged commit ef31e5b into main Mar 20, 2026
48 checks passed
@pelikhan pelikhan deleted the copilot/fix-create-pull-request-signed-commits branch March 20, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

create-pull-request signed commits fail when branch does not yet exist on remote

3 participants