GitHub: signing commit in a workflow

0xdbe · April 4, 2024

Committing in your workflow can normally be done using git commands or other actions that perform commits for you. However, if your repository requires commit signing, it is difficult to manage securely a GPG keys and set up GitHub Runner to sign your commit. Fortunately, this can be done through the GitHub GraphQL API.

GraphQL Mutation

Github provides a GraphQL Mutation createcommitonbranch which creates a commit. Commits made using this mutation are automatically signed by GitHub and will be marked as verified in the user interface.

For better reusability, we can define a GraphQL mutation in a file .github/api/createCommitOnBranch.gql:

mutation (
    $githubRepository: String!,
    $branchName: String!,
    $expectedHeadOid: GitObjectID!
    $commitMessage: String!
    $files: [FileAddition!]!
) {
  createCommitOnBranch(
    input: {
    branch:
    {
        repositoryNameWithOwner: $githubRepository,
        branchName: $branchName
    },
    message: {headline: $commitMessage},
    fileChanges: {
        additions: $files
    }
    expectedHeadOid: $expectedHeadOid
    }
  ){
    commit {
    url
    }
  }
}

The following input fields must be set when you call this mutation:

  • githubRepository
  • branchName
  • expectedHeadOid
  • commitMessage
  • files

Usage with gh CLI

The mutation can now be called using gh CLI:

gh api graphql \
  -F $githubRepository="owner/repo" \
  -F branchName="main" \
  -F expectedHeadOid=$(git rev-parse HEAD) \
  -F commitMessage="chore: commit file" \
  -F files[][path]="README.md" -F files[][contents]=$(base64 -w0 README.md) \
  -F files[][path]="HELLO.md" -F files[][contents]=$(base64 -w0 HELLO.md) \
  -F 'query=@.github/api/createCommitOnBranch.gql'

Warning: files is an array of FileAddition, but fortunately, the gh CLI allows defining nested objects in fields.

Usage in a GitHub Workflow

In order to sign a new commit in your workflow, you can add a step to call the mutation using the gh CLI:

permissions:
  checks: write
  statuses: write
  contents: write

jobs:

  committing:
    runs-on: ubuntu-latest
    steps:

    - name: checkout
      uses: actions/checkout@v4

    - name: edit file
      run: |
        echo "hello" >> README.md

    - name: commit
      run: |
        gh api graphql \
          -F $githubRepository=$GITHUB_REPOSITORY \
          -F branchName=$BRANCH \
          -F expectedHeadOid=$(git rev-parse HEAD) \
          -F commitMessage="chore: commit file" \
          -F files[][path]="README.md" -F files[][contents]=$(base64 -w0 README.md) \
          -F files[][path]="HELLO.md" -F files[][contents]=$(base64 -w0 HELLO.md) \
          -F 'query=@.github/api/createCommitOnBranch.gql'
      env:
        GH_TOKEN: $
        BRANCH: "main"

This commit will be signed by github-actions[bot].

Twitter, Facebook