Skip to content

Using semantic-release with an SSH deploy key in GitHub Actions

We use semantic-release to release new versions of one of our JavaScript-based web applications. semantic-release can help with various release-based activities, such as figuring out the version bump based on the commit history using conventional commits, updating the changelog, pushing a new version tag, and so on.

We recently migrated our repositories to GitHub and have a ruleset enabled for the main (default) branch to protect this branch. Using a ruleset is basically the new way of protecting a branch. For a single developer or a very small team this might be overkill and slow you down. For bigger teams definitely it makes sense to ensure that certain practices are adhered to. For example, you can enforce that force pushes are getting blocked, or that a pull request is required before merging.

This is where we ran into issues where the release commit by semantic-release could not be pushed directly to main due to this rule. GitHub allows you to grant bypass permissions for your ruleset. Unfortunately, you cannot add a single user to this bypass list, and the GITHUB_TOKEN secret is associated with the (special) github-actions[bot] user.

So, how were we able to accomplish this?

You could use another account, or a bot user with their own personal access token (PAT), or, create a dedicated GitHub app. What we ended up using is a deploy key.

The advantage of using a deploy key is that it is tied to a specific repository whereas a user account might have access to more repositories.

To use and set up a deploy key for a repository, follow GitHub's instructions. Ensure that the deploy key has write access to the repository.

Then, add "Deploy Keys" to the bypass list and set this specific bypass permission to "Always allow".

Now, to make use of this deploy key in your release workflow, you need to check out the repository using the SSH private key. Add the private key as an actions secret on the repository.

Then, update your workflow as follows:

Release workflow file
[...]
    steps:
      - uses: actions/checkout@v6.0.1
        with:
          ssh-key: ${{ secrets.DEPLOY_KEY }}
          # Persist credentials so that semantic-release will use them
          persist-credentials: true
      - name: Run semantic-release
        run: npx semantic-release
[...]
Could this be abused?

Like me, you are probably asking yourself whether someone could abuse the secret. Basically, someone could adjust the workflow in a PR, use of the secret and push something directly to main from the workflow 🤔

Definitely.

As the GitHub docs state:

Deploy keys with write access can perform the same actions as an organization member with admin access, or a collaborator on a personal repository.

Unfortunately, GitHub (unlike GitLab) is missing restrictions for secrets to make them only available to protected branches or certain environments.

The semantic-release note about pushing to your repository applies here.

Essentially, this should only be done in trusted environments and you really need to be aware of the risks and be willing to accept them.

At least for situations where someone raises a PR from a fork it is not possible. This is because secrets are not passed to workflows triggered from forks.

And, you need to ensure that the semantic-release/git plugin will use the SSH protocol. Add the SSH repository URL to your semantic-release configuration in the repositoryUrl option:

.releaserc
{
    "repositoryUrl": "git@github.com/owner/repo.git"
}

Improving the semantic-release documentation

Part of this was not immediately clear from the semantic-release documentation. I raised a PR to improve the SSH key documentation. Hopefully it will get accepted.

Comments

Comments are currently not supported. For the time being, please send me an email.