Coner Murphy

Published on 25 Sept 2022 | 7 Minute Read

How to Automatically Publish and Maintain an NPM Package Using `semantic-release`

Publishing and maintaining NPM packages can involve manual administrative tasks but it doesn't need to. semantic-release can automate it for us, here's how.

Photo from Unsplash
How to Automatically Publish and Maintain an NPM Package Using `semantic-release`

Creating NPM packages is an easy and convenient way to share reusable snippets of code for both yourself and others to consume in future projects. They could be as simple as an ESLint configuration or as detailed as a data fetching package or something else entirely. But, for the purposes of this post, the content within the package isn’t important, what is, is the ability to package this code up quickly and easily and publish it to NPM for others to consume.

By the end of this post, we’re going to have created and published an NPM package automatically from a GitHub repository by using a great package called `semantic-release`. In their own words, `semantic-release` “automates the whole package release workflow including: determining the next version number, generating the release notes, and publishing the package. In short, it takes all of the administrative parts of creating and maintaining an NPM package and automates them for us.

Repository Setup

For this tutorial, I’ll be using a custom ESLint configuration I created which you can find on GitHub here if you’re interested. But, it doesn’t need to be this, the package you publish can be pretty much anything you want. For the purposes of this tutorial, it can even be a project generated from running `npm init -y`. Really all we need is an `index.js` file in the root of the project that has some code exported from it and then have this file referenced in the `main` property of the `package.json` file like below.

Node.js logopackage.json
2 "name": "some-name",
3 "main": "index.js",
4 ...other properties

This property is important as it’s the entry point to the module that your `package.json` file is describing. Also, make sure to set a name that you’re happy with as this is the name it’ll be published to NPM with. So, with your repository created, populated with some code, and your `package.json` configured we’re ready to get started.

`semantic-release` Setup

To get started with the setup of `semantic-release`, you first need to install it by running the command `npm install --save-dev semantic-release` which will install the `semantic-release` package in your repository. Then we need to install some plugins for `semantic-release`, these are.

  • `@semantic-release/changelog` - this plugin automatically creates and/or updates the changelog in our repository based on the commit messages of each release.
  • `@semantic-release/git` - this plugin allows us to commit files we specify into our repository after they have been changed by the release workflow like `` and `package.json`.

To install these plugins run `npm install @semantic-release/changelog @semantic-release/git -D`. With these plugins installed we can move on to creating the configuration file for `semantic-release` so in the root of your repository create a new file called `.releaserc.json` and paste the below into it.

2 "branches": ["main"],
3 "plugins": [
4 "@semantic-release/commit-analyzer",
5 "@semantic-release/release-notes-generator",
6 [
7 "@semantic-release/changelog",
8 {
9 "changelogFile": ""
10 }
11 ],
12 "@semantic-release/npm",
13 "@semantic-release/github",
14 [
15 "@semantic-release/git",
16 {
17 "assets": ["package.json", ""],
18 "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
19 }
20 ]
21 ]

What this configuration does is tell `semantic-release` to only run on the `main` branch and then configure a bunch of plugins for us. The reason you’re seeing more plugins defined here than what we installed above is because the core `semantic-release` package already comes with some plugins installed like `commit-analzyer`, `release-notes-generator`, `npm`, and `github` so we just needed to install the ones that aren’t pre-installed.

These plugins are what will perform the “magic” for us. When we push to `main` or merge in a new PR, a GitHub action will run (we’ll configure this in a moment) and our commit messages will be analyzed, the correct new version determined using semantic versioning and then a new git tag, GitHub release and NPM package will be issued for that version. 🤯

GitHub Action Configuration

To configure the GitHub Action I just mentioned, create a `.github` folder at the root of your repository and then a `workflows` folder within that. Inside the `workflows` folder, create a new file called `release.yml` and paste the following code into it.

1name: Release
3 push:
4 branches:
5 - main
8 release:
9 name: Release
10 runs-on: ubuntu-latest
12 steps:
13 - name: Checkout
14 uses: actions/checkout@v2
15 with:
16 fetch-depth: 0
18 - name: Setup Node.js
19 uses: actions/setup-node@v2
20 with:
21 node-version: "lts/*"
23 - name: Install dependencies
24 run: npm ci
26 - name: Release
27 env:
28 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
30 run: npx semantic-release

This action is triggered on pushes to the main branch (PR merges are technically pushes so don’t worry it covers them too). Then the workflow checks out the latest code and configures Node.js and installs our dependencies using `npm ci`. Then finally, it runs the `semantic-release` package using `npx semantic-release`. We pass it our `GITHUB_TOKEN` and our `NPM_TOKEN` as environmental variables so it can perform the required actions we described above.

The `GITHUB_TOKEN` is automatically created for us in GitHub Actions so we don’t need to do anything to configure that. But, we do need to add the `NPM_TOKEN` to our repository secrets so it can pull that into the action when it runs so let’s configure that now.

NPM Setup and Token

For this, you will need an NPM account so head over to their website and sign in if you already have an account or create a new one if you don’t. Once you’re logged into your account, head to your “Access Tokens” menu by clicking on your avatar/icon in the top right and selecting it from the dropdown.

Then you want to press the “Generate New Token” button followed by selecting the “Automation” option, then name your token and then press “Generate Token”. Then copy the token you just created and head back over to your GitHub repository. On your repository page, head to the settings page and then under “Secrets > Actions”, click on “New repository secret”, name it `NPM_TOKEN` and then paste in the token you just created in the “Secret” field. Your `NPM_TOKEN` is now configured and ready to be used.

The Final Push

With our NPM token, `semantic-release` package, and GitHub Action all configured we are ready for the big moment; releasing `v1.0.0` of our new package to GitHub and NPM. So, if you’ve been working locally on the `main` branch commit it using the Conventional Commits Specification and then push it to GitHub. And, if you’ve been working on a separate branch, commit it the same way and then push it to GitHub and then PR it into `main`.

Once your code is on `main`, the GitHub action should be automatically triggered and upon the successful completion of that our `v1.0.0` should be released which you can confirm on the sidebar of the “Code” tab on GitHub. Under “Releases” you should see `1` as the total and your latest release as `v1.0.0`. You should also now have a `` in the root of your repository which contains an automatically generated changelog from the commit messages and your `package.json` version number should have been bumped to `1.0.0`.

Then, finally the moment you’ve been waiting for. Head to your package on NPM by going to `` and check out your new NPM package published for the world to see. Now, whenever you commit to your repository using a `fix`, `feat` or breaking change commit; a new patch, minor, or major version (the version bump is controlled by the commit type) will be released, tagged and published to NPM without you needing to do a single thing.


In this post we’ve covered how to take a GitHub repository and have it automatically tagged, released and published to NPM with an automatic changelog and version bumps. All without you needing to manually do anything besides commit your work and merge PRs all thanks to the `semantic-release` package.

If you’re interested in seeing the package I created for this post, you can see it on NPM and GitHub and if you’d like to see more content from me, please consider heading over to my Twitter account where I share more developer-related content and what I’m working on. If you’re interested in working with me or seeing more of my blog posts, consider viewing my portfolio.

Thank you for reading.