GitHub Actions, CI/CD, Continuous Integration, NPM, Version Increment

Using Github Actions to Automatically Increment NPM Version before Deployments

Automatically incrementing the version number before releasing an application using GitHub actions.

Author Alex
Alex11th March 2021

Lately, we switched to GitHub actions to test and deploy our libraries and applications. One of our prerequisites before releasing a web application is, that we always want to increment automatically the application version number. We show this version number to the user on the website, and it helps us for debugging purposes and bug reports.

Automating Manual Steps

To avoid that developers forget to increment the version number by hand, we automated this process especially for web applications by using the built-in command from NPM called npm version. This command increments the currently given version number inside the package.json file and commits the modified file with a Git commit.

However, this brings also some problems with it. Since the npm version command makes changes to the codebase and creates a new commit, we have to deal with two separate commits that run both their own GitHub action jobs.

In order to overcome this problem, we create two separate GitHub action workflow files with two distinct responsibilities.

Workflow 1 - Increment Version and Push Changes

First, we create a GitHub action file called .github/workflows/version.yml that increments the version number:

1name: Version Increment
2
3on:
4  push:
5    branches:
6      - main
7
8jobs:
9  version:
10    runs-on: ubuntu-latest
11    if: "!startsWith(github.event.head_commit.message, '[RELEASE]')"
12    steps:
13      - uses: actions/checkout@v2
14        with:
15          token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
16      - run: git config --global user.name 'AboutBits Tech'
17      - run: git config --global user.email 'tech@aboutbits.it'
18      - run: npm version patch -m "[RELEASE] %s"
19      - run: git push

As you can see from the code, the steps get only executed for commits on the main branch that do not start with the prefix "[RELEASE]". By checking every commit message for the prefix, we can make sure that we only increment the version number for commits generated by a user.

In addition, you have to adjust the checkout action to clone the code using a personal GitHub access token that has repo permissions. If we would not provide a personal access token for the checkout action, then the GitHub actions would not be started for the commit that is pushed by this GitHub action. The token must be stored as a secret in the GitHub repository. In this example we used the variable name GH_PERSONAL_ACCESS_TOKEN to store and reference the token.

We also tell the npm version command to only make a patch release and so only increment the last number of the version string. Furthermore, using the parameter -m we can define the custom prefix for the commit message generated by this command. This helps us to determine that this new commit is created by the pipeline and not by a user.

Note: We internally as a team are working with the feature branching approach as a pattern for managing the source code. This is also known as the pull request model. So we are not always directly committing and pushing to the main branch. That's why this approach works very well because then the CI pipelines are not incrementing the version number on every little change, but rater when we release bug fixes and features or in other words when we merge a pull request.

Commit History

Workflow 2 - Deploying the Application

Next, we create another GitHub action file called .github/workflows/deploy.yml that deploys the application:

1name: Deploy
2
3on:
4  push:
5    branches:
6      - main
7
8jobs:
9  deploy:
10    runs-on: ubuntu-latest
11    if: "startsWith(github.event.head_commit.message, '[RELEASE]')"
12    steps:
13      - uses: actions/checkout@v2
14      - uses: actions/setup-node@v1
15        with:
16          node-version: '12.x'
17          registry-url: 'https://registry.npmjs.org'
18      - run: npm ci
19      - run: npm run build

The important parts of this code snippet are, that the workflow only gets executed for commits on the main branch whose commit message starts with "[RELEASE]". Basically, this workflow should pick up the commits generated by the previous workflow, that sets the right version number, and then build and deploy the application. For simplicity, we only include the build step in this example.

Actions History

Passing the Version Number to the Code

Last, we have to somehow pass the version number defined inside the package.json file to the source code. This can be done using a build command that you can define in the scripts section of the package.json file.

Therefore, you can just use the build in variable $npm_package_version that represents the version specified and pass it as an environment variable to the build script.

1{
2  "name": "project-name",
3  "version": "0.1.1",
4  "private": true,
5  "scripts": {
6    "build": "APP_VERSION=$npm_package_version react-scripts build"
7  },
8  ...
9}