<onWebFocus />

Knowledge is only real when shared.

GitHub npm Release Action

updated

October 18, 2021

GitHub action to automate publishing to npm.

Publishing npm plugins is usually done from the computer of a developer with the contents built locally and then published straight to npm. This poses some security risks especially when publishing credentials are shared between several users and the published files aren't necessarly built from source.

Luckily, this process can be automated with a GitHub action. This post discusses a GitHub action based on semantic-release which calculates the next version based on annotated commits that have been described in a previous Commit post and then generates a release with the release notes and publishes the package to npm.

On the plugin side the package doesn't require any setup. Not even a version field in package.json needs to be maintained as it will automatically be generated and stored in the form of git tags.

The Action

Make sure to run the action only on the branch where releases should be made from.

name: release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: tobua/release-npm-action@v3
        with:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

The release will always be made for the current branch. To request the next commit to be released add release-npm somewhere in the commit message or trigger a manual release as described under Manually Triggered Workflow. The next version will be calculated based on the annotated commits since the last tagged release, then a git tag with that version along with the release notes is created and at last the artifacts are published to npm under the same version.

The action is found on GitHub Marketplace as release-npm-action.

Authorization

The action requires an NPM_TOKEN for publish authorization to npm. This npm token can be generated on npm under Access Tokens when logged in. The action obviously cannot enter a 2 Factor Authentication code and therefore requires an Automation token that can bypass this type of additional authentication. Without 2FA a regular Publish token will also work.

npm Provenance

Update With version v9.5 npm released a very interesting security feature. Generally, a package author can publish arbitrary contents to the registry no matter if they match the linked source repository or not. The provenance feature which only works when publishing through an action from a participating service like GitHub will ensure the published contents match the state of the repository at a certain commit which is then linked on npm.

Provenance feature flag

Green flag

Versions signed with provenance are marked with a green flag next to the version as well as a detailed banner with links to the source commit as well as the action at the bottom. To publish a package with provenance a few things are required. First is to add a flag in the package.json as seen below.

{
  "publishConfig": {
    "provenance": true
  }
}

The id-token permission has to be set as this will allow provenance to mint a signed token. Since, this will reset other permissions the contents permission to create the GitHub tag and release has to be added again.

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      id-token: write # Required to mint token for npm package provenance
      contents: write # Required to publish regular GitHub release
    steps:
      - uses: actions/checkout@v3
      # ... build, test, etc.
      - uses: tobua/release-npm-action@v3
        with:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Caveats

The actions has some limitations inherited from the semantic-release implementation. First, for unpublished plugins the initial version will always be 1.0.0. Similarly, the next version is always calculated based on the annotated commits therefore it's very important to note all breaking changes there and ensure to only add breaking changes when one is willing to publish a new major release. As a workaround to publish the first release below create an empty tag with a version below the one desired. So, to start publishing at 0.1.0 create a v0.0.1tag on an earlier commit after which there are commits with feature which bumps the minor. Note that this will not work when adding a tag and release trigger to the first commit as the tool will assume the commit has already been released and there are no new changes.

Versioning doesn't require a version field explicitly in package.json. However, for some tasks (like installing the package as a tar-ball) the version is validated. In this case a dummy version like 0.0.0-development can be added without affecting how the action works.