Automating npm package releases with GitHub Actions
Tags: ci, githubactions, npm, javascript
Published: 2024-02-01 04:09:24

I should have written this last year, after my talk at [DevFest Ibadan 2023](https://gdg.community.dev/events/details/google-gdg-ibadan-presents-devfest-ibadan-2023/). But, I just couldn't get the GitHub workflow to run successfully. It was terrible. 

So, recently, I finally figured it out by getting it to work in [metadata](https://github.com/kaf-lamed-beyt/metadata)


![GitHub actions tab of the metadata package showing a successful workflow run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7rbkfvka1e6d80cexi7.png)

### Getting started

To cut through the chase, the first thing you need to do is install the `semantic-release` package with your preferred JavaScript package manager.  

```sh
yarn add semantic-release
```

Semantic-release combs through all your commit messages, analyzes them with the angular commit convention, and bumps your package version number with semantic-versioning in mind.

More on that [here](https://github.com/semantic-release/semantic-release/tree/master?tab=readme-ov-file#commit-message-format)

This is where you reap the benefits of writing good/semantic commit messages.

### Creating a workflow

The snippet below is the basic setup of what you'd need to automate the releases of your npm package.

```yml
name: release

on:
  push:
    branches:
      - master

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout current branch
        uses: actions/checkout@v2

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: yarn

      - name: Build package
        run: yarn build

      - name: Semantic Release
        run: npx semantic-release
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
permissions:
  contents: write
  pages: write
  id-token: write
``` 

In your project directory, create a directory and name it `.github`, move into it, and create another one called `workflows`, then create a `<workflow.yml>` file. 

In this case, `releases.yml`. You can call it anything you want. But I'd recommend that you give it a name that encapsulates what it is meant to accomplish.

You may not exactly need to specify any permissions since the GitHub Personal Access Token you'll generate has the `repo_scope`, so all these permissions could be enabled by default.

```yml 
on:
  push:
    branches:
      - master
``` 

In the snippet above. This step of the workflow controls when and where the workflow runs. In this case, it only runs when a push event in the `master` branch occurs in your repository. 

Make sure to change your branch name if it isn't `master`.

### Creating environment variables 

The important part of getting this to work is creating environment variables. This part stressed me so much. 

For semantic-release to work it requires two environment variables, `GH_TOKEN`, short for GitHub Token, and `NPM_TOKEN`. Although, `NPM_TOKEN` can be optional, if you don't want it to publish your package to npm for you. 

You can omit it if you want to take charge of doing `npm publish` by yourself. But, note that the workflow run would still fail on GitHub because the CI environment expects that token. 

To bypass this, you can modify the **Semantic Release** step in the workflow to run in dry mode like so: 

```yml
      - name: Semantic Release
        run: npx semantic-release --dry-run
```

> The objective of the dry-run mode is to get a preview of the pending release. Dry-run mode skips the following steps: prepare, publish, addChannel, success and fail. In addition to this it prints the next version and release notes to the console.

To get your `GH_TOKEN` go to your account settings and generate a [Personal Acess Token](https://github.com/settings/tokens?type=beta). When you're done with that, go to the repository housing your package, click on the settings tab, in the sidebar, click on the **"Secrets and variables"** dropdown, then click on **"Actions"**

![Repository settings on GitHub showing various options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vtqbltzsxorxq1aukcqr.png)

You should be on this screen by now.

![Environment secret settings tab in a GitHub repository](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fegweyhdlu4cjk9e2b0j.png)

When you're here, click on the green button to create a new repository secret and paste the PAT (Personal Access Token) you generated previously into the textaraea.

Make sure the name matches what you've set in the workflow file and vice versa.

To create an `NPM_TOKEN` log in to your npm account, if you have one, or create one by visiting [npmjs.com/signup](https://www.npmjs.com/signup). 

Click on **"Access Token"** from your avatar dropdown 

![Npmjs dot com dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4e35qyq4as7oyitemccg.png)

You should be here by now. Click on **"Generate New Token"**, it should open a dropdown menu with two options. 

For the new security features npm released, it is recommended that you select the **"Granular Access Token"**

![Access Token UI on npm dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmnkto1dy7a0prr5mm23.png)

Provide the required details in the next screen. For more info about what to fill in or stuff about NPM access tokens, [see this](https://docs.npmjs.com/about-access-tokens)

### Testing 

When you're done with creating your tokens, make sure you add them as environment secrets in the settings tab of your repository.

Now that you're done with these processes, try creating a pull request or pushing directly to your default branch, you'll find out that a new tag has been generated in the repo. 

You can click on the tags element.

![metadata repository on GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8q4alxirj6u1mn0k84u.png)


It'll take you to this UI.

![Repository tags](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0ux8qc771tp0g4lfxj5.png)

Click on your most recent tag. It'll take you to a new screen where you can **"generate a release note"** from the tag.

This is what a typical release note would look like when it is generated. I made some tweaks though. 

![Release note from the metadata npm package repository](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slfu65o8lpmpb9r4o89b.png)

### Wrapping up.

Automating repeated processes can be fun, but no one tells you about the stress you'd have to go through to get them working sometimes. 

It is a good investment if you get it right and worth the stress. 

To see another example of automating workflows, you can take a look at [how I moved a Husky pre-commit hook into GitHub Actions](https://www.meje.dev/blog/husky-precommit-into-github-actions) which in turn saved a lot of time while working in dev mode, it also covers a bit on how to deploy on Vercel with GitHub Actions.