Skip to content

Instantly share code, notes, and snippets.

@0xernesto
Last active August 28, 2025 12:28
Show Gist options
  • Save 0xernesto/a8065cce55940e6ccc523664a87ee9bc to your computer and use it in GitHub Desktop.
Save 0xernesto/a8065cce55940e6ccc523664a87ee9bc to your computer and use it in GitHub Desktop.
How to set up an automated release flow using semantic-release and Github Actions.

Semantic Release bot flow

Overview

This semantic-release publishing flow consists of using a bot for publishing releases. Note that commits by this bot will not be signed/verfied. If this is a requirement for you, refer to this guide. This setup requires creating a Github App and adding the following Github Actions secrets to the repo it is being used in:

  • NPM_TOKEN
    • Refer to “Generating an NPM token” section.
  • MY_RELEASER_ID
    • Refer to “Creating a GitHub App” section
  • MY_RELEASER_PRIVATE_KEY
    • Refer to “Creating a GitHub App” section

Generating an NPM token

  1. Go to npmjs.com and create an account, if necessary.
  2. Click “Access Tokens” from the menu.
  3. Click “Generate New Token”
  4. Click “Granular Access Token”
  5. Give the token a descriptive name that differentiates it from the rest of the tokens in your account, such as “My Releaser Bot”
  6. Provide a description, such as “Token for My Releaser Bot”
  7. Provide an expiration date, such as “12/31/2100”
  8. Set permissions to “Read and write”
  9. Select the package
  10. Click “Generate token”
  11. Copy the “Token”
    • ⚠️ This token will never be visible on the NPM website again.
  12. Create a secret in the Github repo this will be used and name it NPM_TOKEN
    • Paste the token value from the previous step

Create a new GitHub app

We need to create an internal GitHub app to act as the release bot with very strict permissions. If the setting is not specified here, it should NOT be checked or filled in when creating the app.

  1. Got to your Github org homepage
  2. Click “Settings”
  3. Click “Developer settings”
  4. Click the “GitHub Apps” tab
  5. Click “New GitHub App”
  6. Configure the app as follows:
    • GitHub App name:
      • My Releaser (results in a my-releaser slug)
    • Description:
      • Internal bot to handle automatic releases of packages.
    • Homepage URL:
    • Callback URL:
    • Repository permissions:
      • Checks - read/write
      • Contents - read/write
      • Issues - read/write
      • Metadata - read
      • Pull requests - read/write
      • Secrets - read
    • Where can this GitHub App be installed?
      • Only on this account (Only allow this GitHub App to be installed on the @your-org account)
  7. Click “Create GitHub App”
  8. Make note of the “App ID” to reference later.
  9. Click “generate a private key”
    • This will create a private key and trigger a download of a .pem file to your machine
    • The .pem must be stored securely.
    • If this key is lost, you will have to create a new private key in the Github app settings (you won’t have to create a new app)
  10. Click the “Install App” tab and install the app in your organization
    • Select the repos that this internal app should have access to.
      • Since this is is an internal app with very limited permissions, it should be safe to give it access to all the the org’s repos. However, it probably makes sense to gradually give the app access to more repos as necessary.
  11. In the repo that this bot will be used in, add the following GitHub Actions secrets
    • MY_RELEASER_ID
      • Paste the App ID noted earlier as the value
    • MY_RELEASER_PRIVATE_KEY
      • Paste the contents of the .pem file downloaded earlier as the value
  12. If the repo has the following protections, we will have to modify the settings so that the my-releaser Github app can bypass one of the protections
    • Protections:
      • ☑ Require a pull request before merging
      • ☑ Require approvals
    • Modify with:
      • ☑ Allow specified actors to bypass the required pull requests - Search for and select my-releaser as an actor that can bypass this requirement - Repeat the above for every protected branch in the repo (e.g., main, beta, …)

Github Actions Workflow

The following YAML code should be placed in the projects .github/workflows/release.yml file of the repo you're using this bot in.

Note that this file makes use of all the environment variables described in the “Overview” section, so it is crucial that they’re all defined in the Github Actions secrets of the repo you're using this bot in.

The only environment variable in this file that does not need to be defined as a Github Actions secret is GITHUB_TOKEN.

name: release

on:
  push:
    branches:
      - main
      - beta
  workflow_dispatch:

permissions:
  contents: write
  issues: write
  pull-requests: write

jobs:
  publish:
    runs-on: ubuntu-latest

    steps:
      - name: Generate bot app token
        id: generate_token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.RELEASER_ID }}
          private-key: ${{ secrets.RELEASER_PRIVATE_KEY }}

      - name: Checkout
        uses: actions/checkout@v4
        with:
          token: ${{ steps.generate_token.outputs.token }}
          fetch-depth: 0
          persist-credentials: false

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20.x"

      - name: Get bot user ID
        id: bot-user-id
        run: |
          echo "user-id=$(gh api "/users/${{ steps.generate_token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
        env:
          GH_TOKEN: ${{ steps.generate_token.outputs.token }}
      
      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm run test

      - name: Build package
        run: npm run-script build
        env:
          NODE_ENV: production

      - name: Publish package
        run: npx semantic-release
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
          GIT_AUTHOR_EMAIL: "${{ steps.bot-user-id.outputs.user-id }}+${{ steps.generate_token.outputs.app-slug }}[bot]@users.noreply.github.com"
          GIT_COMMITTER_EMAIL: "${{ steps.bot-user-id.outputs.user-id }}+${{ steps.generate_token.outputs.app-slug }}[bot]@users.noreply.github.com"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment