Jenkins管道脚本 - 等待运行构建

时间:2017-06-19 14:59:37

标签: jenkins jenkins-pipeline

我有jenkins groovy管道触发其他构建。它按以下脚本完成:

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

问题是我的工作依赖于其他常见工作,即工作X触发工作Y而工作Z触发工作Y.我想要实现的是工作X触发工作Y并且工作Z等待结果Y由X触发。

我想我需要迭代所有正在运行的构建,并检查是否有任何相同类型的构建正在运行。如果是,那么等待它。以下代码可以等待构建完成:

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

我的问题是我需要告诉我需要等待哪个正在运行的工作。为此,我需要检查:

  • 构建参数
  • 构建环境变量
  • 职位名称

我如何检索这类数据?

我知道詹金斯有静默期特征,但在期满后会触发新工作。

EDIT1

只是为了澄清为什么我需要这个功能。我有构建应用程序和库的作业。应用程序依赖于libs和libs依赖于其他libs。当触发构建时,它会触发下游作业(它所依赖的库)。

示例依赖关系树:

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

所以当我触发A然后B,C,D,E被触发并且F也被触发(4次)。我只想触发一次F。

我有beta / PoC解决方案(下面)几乎可以工作。现在我对此代码有以下问题:

  • 回显文本“找到已经运行的作业”在job.future.get()结束前不会刷新到屏幕
  • 我有这个丑陋的“等待”(对于(i = 0; i&lt; 1000; ++ i){})。这是因为get方法返回时没有设置结果字段

    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
    }
    

1 个答案:

答案 0 :(得分:0)

我有类似的问题需要解决。但是,我正在做的是迭代作业(因为活动作业可能还没有在执行程序上执行)。

触发在我的解决方案中如下所示:

  • 如果手动或通过VCS触发作业,则会触发所有(递归)下游作业
  • 如果作业已被另一个上游作业触发,则不会触发任何内容

这样,作业按其触发原因分组,可以使用

检索
@NonCPS
def getTriggerBuild(currentBuild)
{
    def triggerBuild = currentBuild.rawBuild.getCause(hudson.model.Cause$UpstreamCause)
    if (triggerBuild) {
        return [triggerBuild.getUpstreamProject(), triggerBuild.getUpstreamBuild()]
    }
    return null
}

我给每个工作提供了它所拥有的直接上游工作清单。然后,作业可以检查上游作业是否已使用

在同一组中完成构建
@NonCPS
def findBuildTriggeredBy(job, triggerJob, triggerBuild)
{
    def jobBuilds = job.getBuilds()
    for (buildIndex = 0; buildIndex < jobBuilds.size(); ++buildIndex)
    {
        def build = jobBuilds[buildIndex]
        def buildCause = build.getCause(hudson.model.Cause$UpstreamCause)
        if (buildCause)
        {
            def causeJob   = buildCause.getUpstreamProject()
            def causeBuild = buildCause.getUpstreamBuild()
            if (causeJob == triggerJob && causeBuild == triggerBuild)
            {
                return build.getNumber()
            }
        }
    }
    return null
}

从那里开始,一旦上传构建列表完成,我就等着它们。

def waitForUpstreamBuilds(upstreamBuilds)
{
    // Iterate list -- NOTE: we cannot use groovy style or even modern java style iteration
    for (upstreamBuildIndex = 0; upstreamBuildIndex < upstreamBuilds.size(); ++upstreamBuildIndex)
    {
        def entry = upstreamBuilds[upstreamBuildIndex]
        def upstreamJobName = entry[0]
        def upstreamBuildId = entry[1]
        while (true)
        {
            def status = isUpstreamOK(upstreamJobName, upstreamBuildId)
            if (status == 'OK')
            {
                break
            }
            else if (status == 'IN_PROGRESS')
            {
                echo "waiting for job ${upstreamJobName}#${upstreamBuildId} to finish"
                sleep 10
            }
            else if (status == 'FAILED')
            {
                echo "${upstreamJobName}#${upstreamBuildId} did not finish successfully, aborting this build"
                return false
            }
        }
    }
    return true
}

如果其中一个上游构建失败,则中止当前构建(这很好地转换为“中止构建”而不是“构建失败”)。

完整的代码在那里:https://github.com/doudou/autoproj-jenkins/blob/use_autoproj_to_bootstrap_in_packages/lib/autoproj/jenkins/templates/library.pipeline.erb

我的解决方案的主要缺点是,当有大量构建等待时,等待是昂贵的CPU。有内置的waitUntil,但它导致了死锁(我没有尝试过最后一个版本的管道插件,可能已经解决了)。我正在寻找解决这个问题的方法 - 这就是我找到你问题的方法。