Дженкинс параметризует задание, которое ставит только одну строчку
Представьте себе работу Дженкинса A, которая занимает 1 минуту, а работа B занимает 5 минут.
Если мы сконфигурируем задание A для запуска задания B, в то время как задание B работает, задание A может выполняться 5 раз до завершения B. Тем не менее, Дженкинс не добавляет 5 строчек к задаче B, что отлично, потому что иначе быстрая работа A создавала бы постоянно растущее отставание сборок для плохой работы B.
Однако теперь мы хотим иметь задание A trigger B как параметризованное задание, используя параметризованный триггерный плагин. Параметрированные задания помещают в очередь на отставание, что означает, что работа A с радостью создает огромную кучу сборок для задания B, которые не могут не отставать.
Имеет смысл добавлять новую параметризованную сборку в очередь каждый раз, когда она запускается, поскольку параметры могут быть разными. Дженкинс не должен всегда предполагать, что новая параметризованная сборка делает ненужные ненужные очереди.
Однако в нашем случае мы действительно хотели бы этого. Job A создает и обрабатывает наше приложение, затем Job B разворачивает его в производственную среду и запускает более тяжелый набор тестов интеграции. У нас также есть сборка C, которая развертывается в другой среде и делает еще больше тестирования, поэтому для нас это является эскалацией.
Нам нужна очередь для нашего параметризованного задания B, чтобы сохранить только добавленную к нему последнюю сборку; каждая новая сборка заменит любое задание, находящееся в очереди.
Есть ли хороший способ достичь этого?
Ответы
Ответ 1
Добавьте шаг предварительной настройки "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"
}
Ответ 2
Вы можете избавиться от параметра Parameterized Trigger Plugin, а вместо этого использовать традиционный запуск. Как вы сказали, это предотвратит наложение очереди B.
Как передать параметры от A до B? Сделайте задание A, чтобы получить параметры в нем вывод консоли. В задании B, чтобы получить эти параметры сборки, просмотрите вывод консоли из последней сборки A (с Python script, возможно?).
Ответ 3
Здесь один способ:
- Создайте задание A2B между заданиями A и B
- Добавьте шаг сборки в задание A2B, определяющее, работает ли B. Для этого проверьте:
- Наконец, активируйте задание B из A2B только в том случае, если в очереди или запуске нет B-строчек (с переносом параметров)
Ответ 4
В случае, если вы используете Git, теперь это поддерживается с помощью хешей Combine Queued git в параметре Triggering/Parameters/Pass-through.
Первая версия плагина git, которая должна фактически работать с этим: 1.1.27 (см. Jenkins-15160)
Ответ 5
Решение Рона работало для меня. Если вам не нравится иметь кучу отложенных сборок в истории сборки, вы можете добавить следующую систему groovy script к заданию A перед запуском задания B:
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()
}
}
Ответ 6
Здесь приведен более гибкий вариант, если вы заботитесь только о нескольких параметрах. Это особенно полезно, когда задание запускается извне (т.е. Из 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"
}
Ответ 7
Ниже приведено решение 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/info/26845003/how-to-execute-only-the-most-recent-queued-job-in-jenkins
// http://stackoverflow.com/info/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"
}
}