Refactoring Declarative Pipelines for Shared Library Use

Hey everyone,

I’m currently working on multiple pipelines across various applications and have noticed a considerable amount of duplicated code. My intention is to centralize this code into a shared library for better reuse.
While I’ve had success with extracting functions into the library, I’ve run into challenges when trying to move entire stages. From my research, it seems that constructs like when don’t function as expected outside the context of steps or script in declarative pipelines.

For clarity, here’s a snippet of one of the stages I’m dealing with:

stage('Precheck') {
      when {
        expression { IS_RELEASE == 'false' }
      }

      parallel {
        stage('Lint') {
          steps {
            sh 'npm run-script lint'
          }
        }

        stage('Typecheck (App)') {
          steps {
            sh 'npm run-script typecheck:app'
          }
        }

        stage('Typecheck (Api)') {
          steps {
            sh 'npm run-script typecheck:api'
          }
        }

        stage('Typecheck (Spec)') {
          steps {
            sh 'npm run-script typecheck:spec'
          }
        }

        stage('Typecheck (E2E)') {
          steps {
            sh 'npm run-script typecheck:e2e'
          }
        }

        stage('Security Audit') {
          steps {
            sh('npm run-script audit')
          }
        }
      }
    }

I’d appreciate any insights or best practices on how to efficiently refactor such stages for a shared library setup. Thanks in advance!

Hello @Gabriel, and welcome to this community! :wave:

Refactoring stages in Jenkins pipelines into a shared library is an excellent way to reduce duplicated code and improve maintainability. However, as you’ve mentioned, the when directive, typically used for conditionally executing stages, doesn’t function as expected outside of the primary pipeline script block.

To overcome this limitation and effectively refactor your stages, consider employing a combination of techniques:

  1. Utilize Function Parameters.
  2. Explore the Declarative Directive Generator.
  3. As a last resort, employ a Scripted Pipeline (which is not recommended, and is like opening a can of worms).

Thank you for the answer. I tried to refactor the stage mentioned above by removing the when and calling the functions inside a script tag and some other experiments, but nothing seems to work.

Shared library:

def call() {
  parallel {
    stage('Lint') {
      steps {
        sh 'npm run-script lint'
      }
    }

    stage('Typecheck (App)') {
      steps {
        sh 'npm run-script typecheck:app'
      }
    }

    stage('Typecheck (Spec)') {
      steps {
        sh 'npm run-script typecheck:spec'
      }
    }

    stage('Security Audit') {
      steps {
        sh('npm run-script audit --only=prod')
      }
    }
  }
}

Jenkinsfile:

stage('Precheck') {
    agent any
    when {
      expression { IS_RELEASE == 'false' }
    }

    steps {
      script {
        precheck()
      }
    }
  }

Error: ‘Jenkins’ is reserved for jobs with matching label expression

What would be the correct approach to extract this stage into a shared library (is it even recommended), or should I only extract the part that gets executed at the steps level as mentioned here?