4

We have many, many projects which all have their own Jenkinsfile which simply executes a pipeline defined in our shared library. The pipeline ensures that all projects are built, packaged, and installed in the same exact way.

project-a/Jenkinsfile

library 'the-shared-library'

buildProject name: 'project-a', buildApi: true, ...

the-shared-library/vars/buildProject.groovy

def call(Map config) {
  pipeline {
    // standard stages go here
  }
}

We want to extend this to allow for an additional stage to be executed during the pipeline, for certain projects (e.g. 1 out of the many). I was thinking of doing it as follows, if possible:

  1. pass a config param which is a Stage, into buildProject
  2. in buildProject.call, if a custom stage was provided, tack it on to the end of the pipeline, or perhaps between two (known) stages, and run it

Something like this ...

project-a/Jenkinsfile

library 'the-shared-library'

def myCustomStage = ... // not sure how

buildProject name: 'project-a', buildApi: true, ..., customStage: myCustomStage

the-shared-library/vars/buildProject.groovy

def call(Map config) {
  def customStage = config.customStage

  pipeline {
    // standard stages 1 through 3
    // if customStage provided, it goes here
    // standard stages 5 through 5
  }
}

I'm not sure what is a correct solution here.

Josh M.
  • 26,437
  • 24
  • 119
  • 200
  • Is the question that you've tried this and it's not working, or that you don't know if this is the right way to go? If it's the latter, consider that conditionally injecting a custom stage in the middle of a pipeline will break whatever comfort you get from ensuring `all projects are built, packaged, and installed in the same exact way.` – thehole May 22 '19 at 21:55
  • It's not about comfort, it's about maintenance. I don't want to maintain 20 Jenkinsfiles all with the same pipeline, so we have one which they all use directly. The question is "how do I do this" -- I'm not sure how to even implement it in a way that would work. – Josh M. May 23 '19 at 12:16
  • understood. Does the `customStage` have to be dynamic? that is, would `projectA` have a different `customStage` than `projectB`, potentially? – thehole May 23 '19 at 12:39
  • Seems fine as long as you deal with `customStage == null` appropriately. You might also be able to do something clever here with object deferal/yielding for the custom stage for funsies. – Matthew Schuchard May 23 '19 at 12:46
  • Yes, the custom stage would be defined in the project's Jenkinsfile and passed into the shared pipeline. For a given project it would either a) not exist at all or b) exist and be unique to that project. – Josh M. May 23 '19 at 13:08

1 Answers1

3

I haven't tested, but something like this looks like it should work:

//project-a/Jenkinsfile
library 'the-shared-library'

def myCustomStage = { echo 'Hello' }

buildProject name: 'project-a', buildApi: true, ..., myCustomStage
//the-shared-library/vars/buildProject.groovy
def call(Map config, Closure customStage=null) {
  def customStage = config.customStage

  pipeline {
    // standard stages 1 through 3
    // if customStage provided, it goes here
    stage('Conditional'){
      when{
        expression { customStage }
      }
      steps { script {customStage()} }
    // standard stages 5 through 5
  }
}

see Can I use a Closure to define a stage in a Jenkins Declarative Pipeline?

thehole
  • 501
  • 4
  • 14