Skip to content

Enterprise blocker: create-pull-request safe output fails with org-level required_signatures ruleset #21562

@mason-tim

Description

@mason-tim

Enterprise blocker: create-pull-request safe output fails with org-level required_signatures ruleset

Problem

When an organisation enforces a required_signatures ruleset (common in enterprise environments), the create-pull-request safe output fails because git am + git push produces unsigned commits. The push is rejected with:

! [remote rejected] branch -> branch (push declined due to repository rule violations)
error: failed to push some refs

This blocks any workflow using create-pull-request in organisations with signed commit requirements.

Analysis

Root cause is in actions/setup/js/create_pull_request.cjs:

  • Line 694: git am --3way ${patchFilePath} — creates a local unsigned commit
  • Line 742: git push origin ${branchName} — pushes the unsigned commit, rejected by ruleset

The same issue affects push_to_pull_request_branch.cjs.

Other safe outputs (create-issue, dispatch-workflow, add-comment, etc.) work correctly because they use the GitHub API, which signs operations automatically.

GitHub Apps DO sign commits when using the API — the createCommitOnBranch GraphQL mutation produces verified commits. The problem is specifically the git CLI path.

Related Issues

Workaround

Add the GitHub App as a bypass actor on the org's required_signatures ruleset, which undermines the purpose of the ruleset.

Implementation Plan

Replace the git am + git push pipeline with the GraphQL createCommitOnBranch mutation:

  1. Parse the patch to extract file changes (additions, deletions, modifications)

    • Already partially done — patch content is validated and parsed for file counts in enforcePullRequestLimits()
    • Need to extract per-file diffs into FileAddition and FileDeletion objects
  2. Create branch via API (POST /repos/{owner}/{repo}/git/refs)

    • Create the branch ref pointing at the base branch HEAD
    • Replaces git checkout -b ${branchName}
  3. Commit via GraphQL (createCommitOnBranch mutation)

    mutation($input: CreateCommitOnBranchInput!) {
      createCommitOnBranch(input: $input) {
        commit { oid, url }
      }
    }
    • Input: branch, expectedHeadOid, message, fileChanges (additions + deletions)
    • GitHub signs the commit automatically
    • The App token already has contents: write permission
  4. Create PR via API (already done — line 1079 uses GraphQL)

    • No changes needed here
  5. Update push_to_pull_request_branch.cjs with the same approach

  6. Handle edge cases:

    • Binary files (base64 encode for FileAddition)
    • File renames (deletion + addition)
    • Large patches — GraphQL has a payload size limit; may need chunked commits
    • Empty commits (allow_empty config option)
  7. Add tests in create_pull_request.test.cjs:

    • Test patch-to-FileChanges conversion
    • Test GraphQL commit creation
    • Test with file additions, deletions, and modifications
    • Test binary file handling
  8. Retain git am + git push as fallback for GHES environments where GraphQL createCommitOnBranch may not be available (check API version)

Impact

This is a significant blocker for enterprise adoption. Organisations with signed commit requirements (which is increasingly standard for compliance) cannot use any workflow that creates PRs via safe outputs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions