Jenkins pipeline script - wait for running build
08:37 19 Jun 2017

I have jenkins groovy pipeline which triggers other builds. It is done in following script:

for (int i = 0; i < projectsPath.size(); i++) {
    stepsForParallel[jenkinsPath] = {
        stage("build-${jenkinsPath}") {
            def absoluteJenkinsPath = "/${jenkinsPath}/BUILD"
            build job: absoluteJenkinsPath, parameters: [[$class: 'StringParameterValue', name: 'GIT_BRANCH', value: branch],
                                                         [$class: 'StringParameterValue', name: 'ROOT_EXECUTOR', value: rootExecutor]]
        }
    }
}
parallel stepsForParallel

The problem is that my jobs depend on other common job, i.e. job X triggers job Y and job Z triggers job Y. What I'd like to achieve is that the job X triggers job Y and job Z waits for result of Y triggered by X.

I suppose I need to iterate over all running builds and check if any build of the same type is running. If yes then wait for it. Following code could wait for build to be done:

def busyExecutors = Jenkins.instance.computers
                        .collect { 
                          c -> c.executors.findAll { it.isBusy() }
                        }
                        .flatten()
busyExecutors.each { e -> 
    e.getCurrentWorkUnit().context.future.get()
}

My problem is that I need to tell which running job I need to wait. To do so I need to check:

  • build parameters
  • build environments variables
  • job name

How can i retreive this kind of data?

I know that jenkins have silent period feature but after period expires new job will be triggered.

EDIT1

Just to clarify why I need this function. I have jobs which builds applications and libs. Applications depend on libs and libs depend on other libs. When build is triggered then it triggers downstream jobs (libs on which it depends).

Sample dependency tree:

A -> B,C,D,E
B -> F
C -> F
D -> F
E -> F

So when I trigger A then B,C,D,E are triggered and F is also triggered (4 times). I'd like to trigger F only once.

I have beta/PoC solution (below) which almost work. Right now I have following problems with this code:

  • echo with text "found already running job" is not flushed to the screen until job.future.get() ends
  • I have this ugly "wait" (for(i = 0; i < 1000; ++i){}). It is because result field isn't set when get method returns

    import hudson.model.*
    
    def getMatchingJob(projectName, branchName, rootExecutor){
    
        result = null
    
        def busyExecutors = []
        for(i = 0; i < Jenkins.instance.computers.size(); ++i){
            def computer = Jenkins.instance.computers[i]
            for(j = 0; j < computer.getExecutors().size(); ++j){
                def executor = computer.executors[j]
                if(executor.isBusy()){
                    busyExecutors.add(executor)
                }
            }
        }
    
        for(i = 0; i < busyExecutors.size(); ++i){
            def workUnit = busyExecutors[i].getCurrentWorkUnit()
            if(!projectName.equals(workUnit.work.context.executionRef.job)){
                continue
            }
            def context = workUnit.context
            context.future.waitForStart()
    
            def parameters
            def env
            for(action in context.task.context.executionRef.run.getAllActions()){
                if(action instanceof hudson.model.ParametersAction){
                    parameters = action
                } else if(action instanceof org.jenkinsci.plugins.workflow.cps.EnvActionImpl){
                    env = action
                }
            }
    
            def gitBranchParam = parameters.getParameter("GIT_BRANCH")
            def rootExecutorParam = parameters.getParameter("ROOT_EXECUTOR")
    
            gitBranchParam = gitBranchParam ? gitBranchParam.getValue() : null
            rootExecutorParam = rootExecutorParam ? rootExecutorParam.getValue() : null
    
            println rootExecutorParam
            println gitBranchParam
    
            if(
                branchName.equals(gitBranchParam)
                && (rootExecutor == null || rootExecutor.equals(rootExecutorParam))
            ){
                result = [
                    "future" : context.future,
                    "run" : context.task.context.executionRef.run,
                    "url" : busyExecutors[i].getCurrentExecutable().getUrl()
                ]
            }
        }
        result
    }
    
    job = getMatchingJob('project/module/BUILD', 'branch', null)
    if(job != null){
        echo "found already running job"
        println job
        def done = job.future.get()
        for(i = 0; i < 1000; ++i){}
        result = done.getParent().context.executionRef.run.result
        println done.toString()
        if(!"SUCCESS".equals(result)){
            error 'project/module/BUILD: ' + result
        }
        println job.run.result
    }
    
jenkins jenkins-pipeline