在我们当前的项目设置中,我们有工作A,B,C.
成功构建A,B或C时,我们希望将生成的工件部署到开发服务器.因此我们使用工作D.
由于部署会重新创建开发数据库,因此我们只希望每隔一小时运行一次该作业.我们的测试人员熟悉这个计划并以这种方式工作.
实际上当D由A,B或C触发时,会启动一个脚本,等待服务器分钟为00.如果其中一个触发器作业在等待时再次触发D,则以前等待的脚本将被取消并重新启动.如果脚本达到小时的00,则发生部署.
主要问题是,在最坏的情况下,作业D会阻塞建筑物槽59分钟.
默认情况下,每小时运行一次作业不是一种选择,因为即使没有任何更改,也会发生部署.
在更改后运行作业也很糟糕,因为测试人员习惯于每小时部署.
我知道有一个’安静时间’选项,但这只能让我设置一个相对于触发时间的等待时间.我需要的是一个“安静的时间”,将工作推迟到一定时间.
有人就如何实现这一目标提出建议吗?
最佳答案 我开发了以下解决方案:
1)在作业A,B和C中使用Conditional BuildStep Plugin在作业D的config.xml中设置cron计划:
>构建
>条件步骤(单个)
>运行?:执行shell [仅用于测试,在此使用您的构建步骤]
>命令
#!/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
> Builder:执行系统Groovy脚本
>◉Groovy命令
// From: How to delay Jenkins job until a certain time was reached?
// https://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计划:
>构建
>执行shell [仅用于测试,在此处使用您的部署步骤]
>命令
#!/bin/bash
echo " This script is a placeholder to represent a build step"
>后期制作行动
> Groovy Postbuild
> Groovy脚本
// From: How to delay Jenkins job until a certain time was reached?
// https://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()在此Post-build Action中不会向Jenkin的控制台输出打印任何内容.
更新:知道了!根据[JENKINS-18651 – Enable correct writing to the job’s log from a post build script],它是manager.listener.logger.println()而不仅仅是println().
应该注意的是,这种解决方案有三种情况会导致问题:
>作业A,B,C或D写入config.xml,用户同时通过UI保存D的配置→并发写访问.
>如果UI首先打开config.xml,我们可以通过异常处理和在脚本中几秒钟后重试来覆盖它.
>如果我们的脚本是第一个打开config.xml我们运气不好→UI可能会在“恶魔”Jenkins的制度下显示错误:
.
>作业A或B或C在与D同时写入config.xml,反之亦然→我们可以通过异常处理和在我们的脚本中几秒钟后重试来覆盖它.
>作业A,B,C或D写入config.xml,Jenkins’TimerTrigger同时读取文件(它每分钟执行一次)→由于写入尚未完全读取而读取错误数据.
我将其作为读者的挑战,以避免在多个上游作业的情况下通过以下方式重复代码:
>将上游作业的Groovy代码移动到:
HTTP://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
>在任何上游作业中使用以下内容:
>构建
>条件步骤(单个)
>生成器
>◉Groovy脚本文件
HTTP://jenkins/userContent/scripts/JobCronScheduleConfigurator.groovy
不幸的是,构建后的操作→Groovy Postbuild不支持Groovy脚本文件,所以在这方面仍然存在代码重复(并且记录,参见上文,无论如何都是不同的).