在我们当前的项目设置中,我们有作业A,B,C。
当成功构建A,B或C时,我们希望将生成的工件部署到我们的开发服务器。因此我们使用工作D.
由于部署重新创建了开发数据库,因此我们只希望每隔一小时运行一次该作业。我们的测试人员熟悉这个时间表并以这种方式工作。
实际上当D被A,B或C触发时,会启动一个脚本,等待服务器分钟为00.如果其中一个触发器作业在等待时再次触发D,则以前等待的脚本将被取消并重新启动。如果脚本达到小时的00,则进行部署。
主要问题是,在最坏的情况下,作业D会阻止建筑物插槽59分钟。
默认情况下,每小时运行一次作业不是一种选择,因为即使没有任何改变,也会发生部署。
在更改后运行作业也很糟糕,因为测试人员习惯于每小时部署。
我知道有一个安静的时间'选项,但这只能让我设置相对于触发时间的等待时间。我需要的是一个安静的时间'延迟工作直到达到某个时间。 有人就如何实现这一目标提出建议吗?
答案 0 :(得分:2)
我开发了以下解决方案:
1)使用作业A,B和C中的Conditional BuildStep Plugin在作业D config.xml
中设置cron计划:
#!/bin/bash
echo " This script is a placeholder to represent a build step that can succeed or not succeed"
true # to test build success → Groovy script should be executed
#false # to test not successful build → nothing should be done
// From: How to delay Jenkins job until a certain time was reached?
// http://stackoverflow.com/questions/27952216/1744774
// -----------------------------------------------------------
// Adapt these according to your environment
final String DOWNSTREAM_NAME = 'SO-27952216-Downstream-job'
final String CRON_SCHEDULE = '0 * * * *'
// -----------------------------------------------------------
final String SEPARATOR = new String(new char[8]).replace('\0', '-')
println(" ${SEPARATOR} Adapting configuration of ${DOWNSTREAM_NAME} ${SEPARATOR}")
import jenkins.model.*
import hudson.model.*
final Project DOWNSTREAM_JOB = Jenkins.instance.getItem(DOWNSTREAM_NAME)
final String DOWNSTREAM_CONFIG = DOWNSTREAM_JOB.getRootDir().getPath() + "/config.xml"
//import hudson.triggers.*
//DOWNSTREAM_JOB.getTrigger(TimerTrigger.class).spec = CRON_SCHEDULE
// leads to:
// ERROR: Build step failed with exception groovy.lang.ReadOnlyPropertyException:
// Cannot set readonly property: spec for class: hudson.triggers.TimerTrigger
import org.w3c.dom.*
import javax.xml.parsers.*
import javax.xml.xpath.*
println(" Reading ${DOWNSTREAM_CONFIG}")
Document doc =
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(DOWNSTREAM_CONFIG)
XPathExpression expr = XPathFactory.newInstance().newXPath()
.compile("/project/triggers/hudson.triggers.TimerTrigger/spec");
final org.w3c.dom.Node SCHEDULE_NODE = expr.evaluate(doc, XPathConstants.NODE)
println(String.format(
" Changing Build Triggers → Build periodically → Schedule from '%s' to ' %s'",
SCHEDULE_NODE.getTextContent(), CRON_SCHEDULE))
SCHEDULE_NODE.setTextContent(CRON_SCHEDULE)
import javax.xml.transform.*
import javax.xml.transform.dom.*
import javax.xml.transform.stream.*
println(" Writing ${DOWNSTREAM_CONFIG}")
Transformer transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.transform(new DOMSource(doc), new StreamResult(new File(DOWNSTREAM_CONFIG)))
println(" ${SEPARATOR} Adapted configuration of ${DOWNSTREAM_NAME} ${SEPARATOR}")
2)在作业D中使用构建后操作→ Groovy Postbuild 重置其config.xml
中的cron计划:
#!/bin/bash
echo " This script is a placeholder to represent a build step"
// From: How to delay Jenkins job until a certain time was reached?
// http://stackoverflow.com/questions/27952216/1744774
// -----------------------------------------------------------
// Adapt these according to your environment
final String THIS_JOB_NAME = 'SO-27952216-Downstream-job'
final String CRON_SCHEDULE = ''
// -----------------------------------------------------------
final Object LOG = manager.listener.logger
final String SEPARATOR = new String(new char[8]).replace('\0', '-')
LOG.println(" ${SEPARATOR} Adapting configuration of ${THIS_JOB_NAME} ${SEPARATOR}")
import jenkins.model.*
import hudson.model.*
final Project THIS_JOB = Jenkins.instance.getItem(THIS_JOB_NAME)
final String THIS_JOB_CONFIG = THIS_JOB.getRootDir().getPath() + "/config.xml"
//import hudson.triggers.*
//THIS_JOB.getTrigger(TimerTrigger.class).spec = CRON_SCHEDULE
// leads to:
// ERROR: Build step failed with exception groovy.lang.ReadOnlyPropertyException:
// Cannot set readonly property: spec for class: hudson.triggers.TimerTrigger
import org.w3c.dom.*;
import javax.xml.parsers.*
import javax.xml.xpath.*
LOG.println(" Reading ${THIS_JOB_CONFIG}")
Document doc =
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(THIS_JOB_CONFIG)
XPathExpression expr = XPathFactory.newInstance().newXPath()
.compile("/project/triggers/hudson.triggers.TimerTrigger/spec")
final org.w3c.dom.Node SCHEDULE_NODE = expr.evaluate(doc, XPathConstants.NODE)
LOG.println(String.format(
" Changing Build Triggers → Build periodically → Schedule from '%s' to ' %s'",
SCHEDULE_NODE.getTextContent(), CRON_SCHEDULE))
SCHEDULE_NODE.setTextContent(CRON_SCHEDULE)
import javax.xml.transform.*
import javax.xml.transform.dom.*
import javax.xml.transform.stream.*
LOG.println(" Writing ${THIS_JOB_CONFIG}")
Transformer transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.transform(new DOMSource(doc), new StreamResult(new File(THIS_JOB_CONFIG)))
LOG.println(" ${SEPARATOR} Adapted configuration of ${THIS_JOB_NAME} ${SEPARATOR}")
欢迎提示,println()
在构建后操作中不会向Jenkin的控制台输出打印任何内容。
更新:明白了!根据{{3}},它是manager.listener.logger.println()
,而不仅仅是println()
。
应该注意的是,这种解决方案有三种情况会导致问题:
config.xml
,用户在同一时刻通过UI保存D的配置→并发写访问。
config.xml
,我们可以通过异常处理和在我们的脚本中几秒钟后重试来解决此问题。config.xml
我们运气不好→用户界面可能会在“恶魔”詹金斯政权下出现错误:config.xml
,反之亦然→我们可以通过异常处理和在我们的脚本中几秒钟后重试来覆盖此内容。config.xml
,Jenkins'[JENKINS-18651 – Enable correct writing to the job's log from a post build script]同时读取文件(每分钟执行一次)→由于写入尚未读取错误数据完成了。我将此作为挑战,让读者避免在多个上游作业的情况下重复代码:
http://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
http://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
不幸构建后操作→ Groovy Postbuild 不支持 Groovy脚本文件,因此在这方面仍存在代码重复(以及记录,见上文,无论如何都是不同的。)