16

想象一个 Jenkins 作业 A 需要 1 分钟才能运行,而作业 B 需要 5 分钟。

如果我们将作业 A 配置为触发作业 B,则作业 B 正在运行时,作业 A 可能会在 B 完成之前运行 5 次。然而,Jenkins 并没有将 5 个构建添加到作业 B 的队列中,这很好,因为否则快速的作业 A 会为缓慢的作业 B 创建不断增长的构建积压。

但是,现在我们希望使用参数化触发器插件将作业 A 触发器 B 作为参数化作业。参数化作业确实会排队积压,这意味着作业 A 正在为作业 B 愉快地创建大量构建,而这些构建可能跟不上。

每次触发时将新的参数化构建添加到队列中确实有意义,因为参数可能不同。Jenkins 不应该总是假设一个新的参数化构建使得以前排队的构建变得不必要。

然而,在我们的例子中,我们实际上想要这样。作业 A 构建并打包我们的应用程序,然后作业 B 将其部署到类似生产的环境并运行更重的集成测试集。我们还有一个构建 C,它部署到另一个环境并进行更多测试,所以这对我们来说是一个升级模式。

我们希望参数化作业 B 的队列只保留添加到其中的最后一个构建;每个新构建都将替换当前队列中的任何作业。

有什么好方法可以实现这一目标吗?

4

7 回答 7

8

将“System Groovy Script”预构建步骤添加到作业 B,以检查(较新)排队的同名作业,如果找到则退出:

def name = build.properties.environment.JOB_NAME
def queue = jenkins.model.Jenkins.getInstance().getQueue().getItems()
if (queue.any{ it.task.getName() == name }) {
  println "Newer " + name + " job(s) in queue, aborting"
  build.doStop()
} else {
  println "No newer " + name + " job(s) in queue, proceeding"
}
于 2016-05-27T07:03:08.523 回答
2

您可以摆脱参数化触发插件,而是使用传统的触发。正如您所说,这将防止作业 B 队列堆积。

那么如何将参数从A传递给B呢?使作业 A 在其控制台输出中产生参数。在作业 B 中,要获取这些构建参数,请检查最新 A 构建的控制台输出(也许是使用 Python 脚本?)。

于 2012-01-29T06:59:03.803 回答
1

这是一种解决方法:

于 2012-01-29T06:47:07.673 回答
1

如果您使用的是 Git,现在触发/参数/传递选项下的“组合排队的 git 哈希”支持此功能。第一个应该实际使用的 Git 插件版本是 1.1.27(参见Jenkins-15160

于 2013-02-23T23:52:31.350 回答
1

罗恩的解决方案对我有用。如果您不喜欢在构建历史记录中有大量已取消的构建,您可以在触发作业 B 之前将以下系统常规脚本添加到作业 A:

import hudson.model.*  
def q = jenkins.model.Jenkins.getInstance().getQueue()   
def items = q.getItems()  
for (i=0;i<items.length;i++){  
  if(items[i].task.getName() == "JobB"){  
    items[i].doCancelQueue()
  }   
}
于 2016-07-29T17:54:07.047 回答
0

如果您只关心几个参数匹配,这是一个更灵活的选择。这在外部触发作业(即来自 GitHub 或 Stash)并且某些参数不需要单独构建时特别有用。

如果检查的参数在当前构建和排队构建中的值和存在都匹配,则当前构建将被中止,并且描述将显示未来构建包含相同的检查参数(以及它们是什么)。

如果您不想让构建历史记录显示已中止的作业,则可以修改它以取消除最后一个以外的所有其他排队作业。

    checkedParams = [ 
    "PARAM1",
    "PARAM2",
    "PARAM3",
    "PARAM4",
]

def buildParams = null
def name = build.project.name
def queuedItems = jenkins.model.Jenkins.getInstance().getQueue().getItems()

yieldToQueuedItem = false
for(hudson.model.Queue.Item item : queuedItems.findAll { it.task.getName() == name }) {
    if(buildParams == null) {    
        buildParams = [:]
        paramAction = build.getAction(hudson.model.ParametersAction.class)
        if(paramAction) {
            buildParams = paramAction.getParameters().collectEntries {
                [(it.getName()) : it.getValue()]
            }
        }
    }
    itemParams = [:]
    paramAction = item.getAction(hudson.model.ParametersAction.class)
    if(paramAction) {
        itemParams = paramAction.getParameters().collectEntries {
            [(it.getName()) : it.getValue()]
        }
    }

    equalParams = true
    for(String compareParam : checkedParams) {
        itemHasKey = itemParams.containsKey(compareParam)
        buildHasKey = buildParams.containsKey(compareParam)
        if(itemHasKey != buildHasKey || (itemHasKey && itemParams[compareParam] != buildParams[compareParam])) {
            equalParams = false
            break;
        }
    }
    if(equalParams) {
        yieldToQueuedItem = true
        break
    }
}

if (yieldToQueuedItem) {
    out.println "Newer " + name + " job(s) in queue with matching checked parameters, aborting"
    build.description = "Yielded to future build with:"
    checkedParams.each {
        build.description += "<br>" + it + " = " + build.buildVariables[it]
    }

    build.doStop()
    return
} else {
    out.println "No newer " + name + " job(s) in queue with matching checked parameters, proceeding"
}
于 2016-08-25T17:06:25.463 回答
0

以下基于 Ron 的解决方案,但对我的 Jenkins 2 进行了一些修复,包括删除 java.io.NotSerializableException 异常和处理格式getName()有时与JOB_NAME

// Exception to distinguish abort due to newer jobs in queue
class NewerJobsException extends hudson.AbortException {
    public NewerJobsException(String message) { super(message); }
}

// Find jenkins job name from url name (which is the most consistently named
// field in the task object)
// Known forms:
//   job/NAME/
//   job/NAME/98/
@NonCPS
def name_from_url(url)
{
    url = url.substring(url.indexOf("/") + 1);
    url = url.substring(0, url.indexOf("/"));
    return url
}

// Depending on installed plugins multiple jobs may be queued. If that is the
// case skip this one.
// http://stackoverflow.com/questions/26845003/how-to-execute-only-the-most-recent-queued-job-in-jenkins
// http://stackoverflow.com/questions/8974170/jenkins-parameterized-job-that-only-queues-one-build
@NonCPS
def check_queue()
{
    def name = env.JOB_NAME
    def queue = jenkins.model.Jenkins.getInstance().getQueue().getItems()
    if (queue.any{ name_from_url(it.task.getUrl()) == name }) {
        print "Newer ${name} job(s) in queue, aborting"
        throw new NewerJobsException("Newer ${name} job(s) in queue, aborting")
    } else {
        print "No newer ${name} job(s) in queue, proceeding"
    }
}
于 2017-01-18T12:00:54.167 回答