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

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.

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.

{
    "name": "some-name",
    "main": "index.js",
    ...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.

https://media.giphy.com/media/BpGWitbFZflfSUYuZ9/giphy.gif

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 CHANGELOG.md 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.

{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/changelog",
      {
        "changelogFile": "CHANGELOG.md"
      }
    ],
    "@semantic-release/npm",
    "@semantic-release/github",
    [
      "@semantic-release/git",
      {
        "assets": ["package.json", "CHANGELOG.md"],
        "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
      }
    ]
  ]
}

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. 🤯

https://media.giphy.com/media/mIVTwFK45J19KPVSsS/giphy-downsized-large.gif

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.

name: Release
on:
  push:
    branches:
      - main

jobs:
  release:
    name: Release
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: "lts/*"

      - name: Install dependencies
        run: npm ci

      - name: Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        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 CHANGELOG.md 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 https://www.npmjs.com/package/YOUR_PACKAGE_NAME_HERE 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.

https://media.giphy.com/media/Nnhw4G2p926eCa6mAl/giphy.gif

Conclusion

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](https://github.com/semantic-release/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.

Coner


🔔 This article was originally published on my blog 🔔