Defining the version of node used in a node-based repository can be a task that is overlooked.
On a few occasions, I have come across a scenario similar to the following:
- I am running a node-based application locally without any problems
- a new developer joins the team, and tries to run it locally too
- but they discover there is some obscure error occurs
After spending hours trying to find the cause of the issue, we finally discovered that the only difference beetween our developer setup was that our node versions were different, and only by a minor release.
It takes hours to discover, because it's probably one of the last things you'd expect – but it happens.
Another scenario:
- one developer uses node v14.x is install packages initially
- which uses npm v6.x, which generates a lock file using the version 1 lock file format
- another developer is using node v16.x, and adds or updates a package
- and that node version uses npm v8.x, which generates a lock file using the version 2 lock file format
- the
package-lock.json
ends up having thousands of lines changed, but nobody notices - the first developer then has no idea what is going on when they get the latest code and run
npm ci
To avoid misaligned node version pain, we need to ensure the following:
- all developers are using the same version of
node
andnpm
- and the CI environment is also using that same version
For local development, there is more than one option; the one I have found most useful so far is nvm
.
This not only specifies the node
version, but also the npm
version associated with it.
The simplest implementation is to add a .nvmrc
file that contains the required node version – best to specify it to the patch level.
However, just adding this file does not enforce use of that node version, meaning that a developer still won't know if/when they are using the wrong version of node.
To make the environment setup more watertight, I have recently implemented a solution that checks the developer's node version at critical points in the development process, and won't allow the developer to continue if they have the wrong version.
It's not as light-weight as I would like, but once it is in place in one repository, it's easy enough to implement in other repos as well.
- create a script that checks the node version in use against the version specified in
.nvmrc
- install
husky
to leverage git lifecycle hooks - add relevant git lifecycle hooks that run the script – I have found that the
pre-commit
,pre-merge
,pre-push
andpre-rebase
hooks are the most useful
Create a shell script similar to this:
#!/bin/bash -eu
EXPECTED_NODE_VERSION=$(cat .nvmrc)
ACTUAL_NODE_VERSION=$(node --version)
if [[ ${ACTUAL_NODE_VERSION} != ${EXPECTED_NODE_VERSION} ]] ; then
echo "ERROR: the version of node you are using is incorrect."
echo " You have: ${ACTUAL_NODE_VERSION}"
echo " You need: ${EXPECTED_NODE_VERSION}"
echo "Please run 'nvm install' to fix this issue."
echo
exit 1
fi
echo "Node version: ${ACTUAL_NODE_VERSION}"
Add a scripts
entry in your package.json
file that calls your script:
"checkNodeVersion": "./check-node-version.sh"
Follow husky's Usage installation steps.
Use those instructions to call your script with npm run checkNodeVersion
for the relevant git hooks.
Don't forget to specify the same version of node in your CI setup.
And run npm run checkNodeVersion
as part of the CI tests.
It's a good idea to update the project's documentation to describe what to do when you want to change the node version 🤓