Search code examples
jenkinsjenkins-pipelinejenkins-job-dsl

How to pass and invoke a method utility to Jenkins template?


I have this template:

def call(body) {

    def pipelineParams= [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = pipelineParams
    body()

    pipeline {

        agent any

        ....

        stages {

            stage('My stages') {
                steps {

                    script {
                        pipelineParams.stagesParams.each { k, v ->
                            stage("$k") {
                                $v
                            }
                        }
                    }
                }
            }
        }

        post { ... }
    }
}

Then I use the template in a pipeline:

@Library('pipeline-library') _

pipelineTemplateBasic {

    stagesParams = [
        'First stage': sh "do something...",

        'Second stage': myCustomCommand("foo","bar")   
    ]
}

In the stagesParams I pass the instances of my command (sh and myCustomCommand) and they land in the template as $v. How can I then execute them? Some sort of InvokeMethod($v)? At the moment I am getting this error:

org.jenkinsci.plugins.workflow.steps.MissingContextVariableException: Required context class hudson.FilePath is missing
Perhaps you forgot to surround the code with a step that provides this, such as: node

The problem of using node is that it doesn't work in situations like parallel:

parallelStages = [:]
v.each { k2, v2 ->
     parallelStages["$k2"] = {
//       node {
             stage("$k2") {
                 notifySlackStartStage()
                     $v2
                      checkLog()
             }
//       }
     }
}

Solution

  • If you want to execute sh step provided with a map, you need to store map values as closures, e.g.

    @Library('pipeline-library') _
    
    pipelineTemplateBasic {
    
        stagesParams = [
            'First stage': {
                sh "do something..."
            }
    
            'Second stage': {
                myCustomCommand("foo","bar")   
            }
        ]
    }
    

    Then in the script part of your pipeline stage you will need to execute the closure, but also set the delegate and delegation strategy to the workflow script, e.g.

    script {
        pipelineParams.stagesParams.each { k, v ->
            stage("$k") {
                v.resolveStrategy = Closure.DELEGATE_FIRST
                v.delegate = this
                v.call()
            }
        }
    }