在我们当前的项目设置中,我们有作业 A、B、C。
成功构建 A、B 或 C 后,我们希望将生成的工件部署到我们的开发服务器。因此我们使用作业 D。
因为部署会重新创建开发数据库,所以我们只想每整整一小时运行一次作业。我们的测试人员熟悉这个时间表并以这种方式工作。
实际上,当 D 被 A、B 或 C 触发时,将启动一个脚本,等待服务器分钟为 00。如果其中一个触发器作业在等待期间再次触发 D,则先前等待的脚本将被取消并重新启动。如果脚本到达小时的 00,则部署发生。
主要问题是,在最坏的情况下,作业 D 会阻塞一个建筑槽 59 分钟。
默认情况下每小时运行一次作业不是一种选择,因为这样部署就会发生,即使没有任何改变。
在更改后运行作业也很糟糕,因为测试人员习惯于每小时部署。
我知道有一个“安静时间”选项,但这只能让我设置相对于触发时间的等待时间。我需要的是一个“安静的时间”,将工作推迟到某个时间到达。
有人对如何实现这一目标有任何建议吗?
最佳答案
我开发了以下解决方案:
1) 使用 Conditional BuildStep Plugin在作业 A、B 和 C 中设置作业 D 中的 cron 计划 config.xml
:
#!/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) 使用 构建后操作 → Groovy 后构建 在作业 D 中重置其
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()
在此构建后操作中,不会向 Jenkins 的控制台输出打印任何内容。更新:明白了!根据 [JENKINS-18651 – Enable correct writing to the job's log from a post build script]它是
manager.listener.logger.println()
而不仅仅是 println()
.需要注意的是,此解决方案可能会在三种情况下导致问题:
config.xml
并且用户同时通过UI保存D的配置→并发写访问。config.xml
我们可以通过异常处理和几秒钟后在我们的脚本中重试来解决这个问题。 config.xml
我们运气不好 → 在“恶魔” Jenkins 的统治下,用户界面可能会显示错误:.
config.xml
在与 D 相同的时刻,反之亦然 → 我们可以通过异常处理和几秒钟后在我们的脚本中重试来解决这个问题。 config.xml
和 Jenkins 的 TimerTrigger同时读取文件(它每分钟执行一次)→ 由于写入尚未完全完成而读取不正确的数据。 我把它作为读者的一个挑战,以避免在多个上游工作的情况下通过以下方式避免代码重复:
http://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
http://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
不幸的是构建后操作 → Groovy 后构建 不支持 Groovy 脚本文件,因此在这方面仍然存在代码重复(并且日志记录,见上文,无论如何都是不同的)。
关于jenkins - 如何延迟 Jenkins 的工作直到达到某个时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27952216/