DEV Community

Edward Chopuryan
Edward Chopuryan

Posted on

Jenkins + npm package is [not] a good idea

Prologue

Automating npm package deployment (Continues Deployment) is a good practice but not an easy one especially the combination of jenkins and npm package.
During my latest project I was developing an npm package that was going to be used by multiple teams and projects across the entire company. After a week or two of development we decided that we are ready to publish our first version and that we are going to need a CI/CD pipeline to push the package into our local registery. Our pipeline was very simple and had only 4 stages:

  • Initialization - In this stage we simply build our application: npm ci
  • Unit Tests - In this is stage we run the tests and check the coverage
  • Build - This builds the project and prepares it for a publish(npm run build).
  • Publish - This stage simply runs the following command: npm publish

Problem

If you are anything like me you are going to forget to update the version of your package and push your changes and since our Jenkins doesn't check the version it will give a green light to merge your branch and when you do it is going to fail. Why? because we forgot to update the version and when npm wants to perform publish it fails.
I personally encountered this many times and it was getting very frustrating and expensive because I broke 2 keyboards, 3 mugs and someone's skull (apparently mine). Anyway there are many solutions to this problem here is one that I really liked and implemented personally.

Solution

Idea is to check the version before Jenkins reaches that last Publish stage.
Jenkinsfile

   stage('Version Check') {
      steps {
        script {
          sh "chmod 777 config/version_check.sh"
          sh "config/version_check.sh"
        }
      }
    }

config/versoin_check.sh

#!/bin/bash
set -exuo pipefail

LOCAL_VERSION=$(node -p -e "require('./package.json').version");
REMOTE_VERSION=$(npm view . version);

if [ "${LOCAL_VERSION}" == "${REMOTE_VERSION}" ]
then
    echo "Package with v$LOCAL_VERSION already exists"
    exit 1;
else
    exit 0;
fi

This new stage is coming right after the very first Initialization stage so now our Jenkins pipeline has this 5 stages:

  • Initialization - In this stage we simply build our application: npm ci
  • Version Check - In this stage we are verifying our version
  • Unit Tests - In this is stage we run the tests and check the coverage
  • Build - This builds the project and prepares it for a publish(npm run build).
  • Publish - This stage simply runs the following command: npm publish

The script that performs the version check is quite simple just make sure that Jenkins is using a container that has node installed or use node agent for this stage and don't forget to make the script executable (sh "chmod 777 config/version_check.sh")

Limitations & Further Improvements

This solution is not perfect just like any other "solution". First things first it doesn't check all remote versions only the latest one which is not ideal we want to be sure our local version is unique across all published versions. Fortunately it is possible to get all version numbers of published package and check against the list.

Yet another issue is that our code doesn't perform any kind of validation to make sure that our version number is compliant to Semantic v2.0.0 standards. Fortunately there are some good npm packages to perform those validations.

Thank you

Thank you for reading this article. Hope it can help someone and if you have any questions or suggestions let's talk about it in the comments below.

Top comments (1)

Collapse
 
pranav_bhatia profile image
Pranav Bhatia

Great post!
If you could answer my query - If you run the application through Jenkins pipeline (npm run), how do you manage to get the final output because npm will run it and keep on running it until we manually shut it.

I guess one solution could be running the app through Pm2?

What I am trying to achieve through pipeline - Take pull from git, npm install and then npm run so that my application runs on the server where Jenkins is present and then the automation test suit runs.