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
20
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.
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
20
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.
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}
10