Signing commits from bot accounts and automation scripts in Github Actions
⋅ 2 min readIt is very important to sign your Git commits.
Although it is quite easy to generate your own GPG key and use it to auto-sign all your Git commits, it is difficult to sign commits coming from automation scripts, bot accounts and CI steps. For example, for many implicit git
commits on Github Actions, the default github-actions
bot user account is used for (unsigned) commits.
I recently ran into a compliance requirement that needed every single commit in the main branch to be signed and verified. We use semantic-release to make releases and commit back the latest version to our package.json
version. With the signing requirement, we had to remove semantic-release from our CI workflow, as it uses its own bot account for commits – which are unsigned.
However, I was determined to bring this step back while remaining compliant, so I did just that.
Step 1 – Set up your GPG keys
Follow Github's guide on adding a new GPG key to your account to first set up your keys.
Personally, I did not want to use my personal GPG keys to sign commits at work, so I created a dedicated bot account on Github. Once I did that, I generated new GPG keys for the bot account. I finally added the GPG keys to the bot's Github account as explained above. Make sure to securely note down the passphrase (you should use one and not leave it blank!) and the private & public keys. You'll need these for the next step.
Step 2 – Set up GPG passphrase & private key as secret
The GPG passphrase and the private key need to be set up as encrypted secrets in the Github repository of your choice. I named them BOT_GPG_PASSPHRASE
and BOT_GPG_PRIVATE_KEY
.
Step 3 – Configure commits to be signed with the GPG key
Adjust your Github Actions workflow to import the GPG key and use it to sign your commits.
name: Release
on:
push:
branches:
- main
jobs:
release:
name: release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
persist-credentials: false # This is important if you have branch protection rules!
- name: Import bot's GPG key for signing commits
id: import-gpg
uses: crazy-max/ghaction-import-gpg@v4
with:
gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }}
git_config_global: true
git_user_signingkey: true
git_commit_gpgsign: true
- name: Change some files
run: echo 'adding a new commit now' >> README.md
- name: Commit changes to README.md file
run: git commit -m "this is bot" README.md
env:
GITHUB_TOKEN: ${{ secrets.OSLASH_BOT_GITHUB_TOKEN }}
GIT_AUTHOR_NAME: ${{ steps.import-gpg.outputs.name }}
GIT_AUTHOR_EMAIL: ${{ steps.import-gpg.outputs.email }}
GIT_COMMITTER_NAME: ${{ steps.import-gpg.outputs.name }}
GIT_COMMITTER_EMAIL: ${{ steps.import-gpg.outputs.email }}
Here's a working example of such a workflow – getoslash/eslint-plugin-tap, and a commit that was made from an automated CI step.