git Jenkins - 如果启动新的构建,则中止运行构建
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40760716/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Jenkins - abort running build if new one is started
提问by kakty3
I use Jenkins and Multibranch Pipeline. I have a job for each active git branch. New build is triggered by push in git repository. What I want is to abort running builds in current branch if new one appears in same branch.
我使用 Jenkins 和 Multibranch Pipeline。我为每个活跃的 git 分支都有一份工作。新构建是通过推送 git 存储库触发的。我想要的是如果新分支出现在同一分支中,则中止在当前分支中运行的构建。
For example: I commit and push to branch feature1
. Then BUILD_1
started in Jenkins. I make another commit and push to branch feature1
while BUILD_1
is still running. I want BUILD_1
to be aborted and to start BUILD_2
.
例如:我提交并推送到 branch feature1
。然后BUILD_1
在詹金斯开始。我再次提交并feature1
在BUILD_1
仍在运行时推送到分支。我想BUILD_1
被流产并开始BUILD_2
。
I tried to use stage concurrency=x
option, and stage-lock-milestonefeature, but didn't manage to solve my problem.
我尝试使用stage concurrency=x
选项和阶段锁定里程碑功能,但未能解决我的问题。
Also I've read this thread Stopping Jenkins job in case newer one is started, but there is no solution for my problem.
我也读过这个线程停止詹金斯的工作,以防新的工作开始,但我的问题没有解决方案。
Do you know any solution to this?
你知道有什么解决办法吗?
回答by Brandon Squizzato
With Jenkins script security many of the solutions here become difficult since they are using non-whitelisted methods.
使用 Jenkins 脚本安全性,这里的许多解决方案变得困难,因为它们使用非白名单方法。
With these milestone steps at the start of the Jenkinsfile, this is working for me:
通过 Jenkinsfile 开头的这些里程碑步骤,这对我有用:
def buildNumber = env.BUILD_NUMBER as int
if (buildNumber > 1) milestone(buildNumber - 1)
milestone(buildNumber)
The result here would be:
这里的结果是:
- Build 1 runs and creates milestone 1
- While build 1 is running, build 2 fires. It has milestone 1 and milestone 2. It passes milestone 1, which causes build #1 to abort.
- Build 1 运行并创建里程碑 1
- 在构建 1 运行时,构建 2 触发。它有里程碑 1 和里程碑 2。它通过了里程碑 1,这会导致构建 #1 中止。
回答by daggett
enable job parallel run for your project with Execute concurrent builds if necessary
为您的项目启用作业并行运行 Execute concurrent builds if necessary
use execute system groovy script
as a first build step:
使用execute system groovy script
作为第一生成步骤:
import hudson.model.Result
import jenkins.model.CauseOfInterruption
//iterate through current project runs
build.getProject()._getRuns().iterator().each{ run ->
def exec = run.getExecutor()
//if the run is not a current build and it has executor (running) then stop it
if( run!=build && exec!=null ){
//prepare the cause of interruption
def cause = { "interrupted by build #${build.getId()}" as String } as CauseOfInterruption
exec.interrupt(Result.ABORTED, cause)
}
}
and in the interrupted job(s) there will be a log:
并且在中断的作业中会有一个日志:
Build was aborted
interrupted by build #12
Finished: ABORTED
回答by midN
If anybody needs it in Jenkins Pipeline Multibranch, it can be done in Jenkinsfile like this:
如果有人在 Jenkins Pipeline Multibranch 中需要它,可以像这样在 Jenkinsfile 中完成:
def abortPreviousRunningBuilds() {
def hi = Hudson.instance
def pname = env.JOB_NAME.split('/')[0]
hi.getItem(pname).getItem(env.JOB_BASE_NAME).getBuilds().each{ build ->
def exec = build.getExecutor()
if (build.number != currentBuild.number && exec != null) {
exec.interrupt(
Result.ABORTED,
new CauseOfInterruption.UserInterruption(
"Aborted by #${currentBuild.number}"
)
)
println("Aborted previous running build #${build.number}")
} else {
println("Build is not running or is current build, not aborting - #${build.number}")
}
}
}
回答by Stefanos Kalantzis
Based on the idea by @C4stor I have made this improved version... I find it more readable from @daggett 's version
基于@C4stor 的想法,我制作了这个改进的版本......我发现它从 @daggett 的版本中更具可读性
import hudson.model.Result
import hudson.model.Run
import jenkins.model.CauseOfInterruption.UserInterruption
def abortPreviousBuilds() {
Run previousBuild = currentBuild.rawBuild.getPreviousBuildInProgress()
while (previousBuild != null) {
if (previousBuild.isInProgress()) {
def executor = previousBuild.getExecutor()
if (executor != null) {
echo ">> Aborting older build #${previousBuild.number}"
executor.interrupt(Result.ABORTED, new UserInterruption(
"Aborted by newer build #${currentBuild.number}"
))
}
}
previousBuild = previousBuild.getPreviousBuildInProgress()
}
}
回答by C4stor
Got it to work by having the following script in the Global Shared Library :
通过在全局共享库中使用以下脚本来使其工作:
import hudson.model.Result
import jenkins.model.CauseOfInterruption.UserInterruption
def killOldBuilds() {
while(currentBuild.rawBuild.getPreviousBuildInProgress() != null) {
currentBuild.rawBuild.getPreviousBuildInProgress().doKill()
}
}
And calling it in my pipeline :
并在我的管道中调用它:
@Library('librayName')
def pipeline = new killOldBuilds()
[...]
stage 'purge'
pipeline.killOldBuilds()
Edit : Depending on how strongly you want to kill the oldBuild, you can use doStop(), doTerm() or doKill() !
编辑:根据您想要杀死 oldBuild 的强度,您可以使用 doStop()、doTerm() 或 doKill()!
回答by CodeMonkey
Adding to Brandon Squizzato's answer. If builds are sometimes skipped the milestone mechanism as mentioned will fail. Setting older milestones in a for-loop solves this.
添加到 Brandon Squizzato 的答案。如果有时跳过构建,上述里程碑机制将失败。在 for 循环中设置较旧的里程碑可以解决这个问题。
Also make sure you don't have disableConcurrentBuildsin your options. Otherwise the pipeline doesn't get to the milestone step and this won't work.
还要确保您的选项中没有disableConcurrentBuilds。否则管道不会到达里程碑步骤,这将不起作用。
def buildNumber = env.BUILD_NUMBER as int
for (int i = 1; i < buildNumber; i++)
{
milestone(i)
}
milestone(buildNumber)
回答by mmm20
Is there a way to abort the build just from one given branch and allow other builds from a different branch to run at the same job. For example, let's say I have Branch1 and Branch2 running in a job. I want to be able to abort all currently executing build from Branch1 except the latest one, at the same time Branch2 starts executing a build and I want Branch2 to be able to execute their build since it's a different branch. Is it possible?
有没有办法只从一个给定的分支中止构建,并允许来自不同分支的其他构建在同一作业中运行。例如,假设我在作业中运行 Branch1 和 Branch2。我希望能够中止当前从 Branch1 执行的所有构建,除了最新的构建,同时 Branch2 开始执行构建并且我希望 Branch2 能够执行它们的构建,因为它是一个不同的分支。是否可以?
To clarify; We have many branches so we cannot just prevent concurrent builds, cancel the last as soon as the next starts, etc. Whatever method is used must specifically check if the branch already has a job running for it, not if the job, in general, is already running. Running the code is execute system groovy in Jenkins.
澄清; 我们有很多分支,所以我们不能只是阻止并发构建,在下一个开始时立即取消最后一个,等等。无论使用什么方法,都必须专门检查分支是否已经有一个作业在运行,而不是作业,一般来说,已经开始。运行代码是在 Jenkins 中执行系统 groovy。
import hudson.model.Result
import jenkins.model.CauseOfInterruption
import groovy.transform.Field
import jenkins.model.*
build.getProject()._getRuns().iterator().each{ run ->
def exec = run.getExecutor()
//from each run get the branch_name and put it into the list, add it to the list
def branchName = build.environment.get("GIT_BRANCH")
def list = []
list.add(branchName)
for (i = 0; i <list.size(); i++) {
if( run!=build && exec!=null && branchName == list[i]){
exec.interrupt(Result.ABORTED)
}
}
}
回答by Ian Kwok
Based on @daggett method. If you want to abort running build when the new push is coming and before fetch updates.
1. Enable Execute concurrent builds if necessary
2. Enable Prepare an environment for the run
3. Running bellow code in Groovy Script
or Evaluated Groovy script
基于@daggett 方法。如果您想在新推送到来时和获取更新之前中止运行构建。
1. 启用Execute concurrent builds if necessary
2. 启用Prepare an environment for the run
3. 运行以下代码Groovy Script
或Evaluated Groovy script
import hudson.model.Result
import hudson.model.Run
import jenkins.model.CauseOfInterruption
//def abortPreviousBuilds() {
Run previousBuild = currentBuild.getPreviousBuildInProgress()
while (previousBuild != null) {
if (previousBuild.isInProgress()) {
def executor = previousBuild.getExecutor()
if (executor != null) {
println ">> Aborting older build #${previousBuild.number}"
def cause = { "interrupted by build #${currentBuild.getId()}" as String } as CauseOfInterruption
executor.interrupt(Result.ABORTED, cause)
}
}
previousBuild = previousBuild.getPreviousBuildInProgress()
}
//}
回答by Janis Peisenieks
I also compiled a version from the previously given ones with a few minor tweaks:
我还从之前给出的版本中编译了一个版本,并进行了一些小调整:
- the
while()
loop generated multiple outputs for each build - the UserInterruption currently expects a userId instead of a reasoning string, and will not show a reasoning string anywhere. Therefore this just provides the userId
- 所述
while()
环路产生的多个输出用于每个生成 - UserInterruption 当前需要一个 userId 而不是推理字符串,并且不会在任何地方显示推理字符串。因此,这只是提供了 userId
def killOldBuilds(userAborting) {
def killedBuilds = []
while(currentBuild.rawBuild.getPreviousBuildInProgress() != null) {
def build = currentBuild.rawBuild.getPreviousBuildInProgress()
def exec = build.getExecutor()
if (build.number != currentBuild.number && exec != null && !killedBuilds.contains(build.number)) {
exec.interrupt(
Result.ABORTED,
// The line below actually requires a userId, and doesn't output this text anywhere
new CauseOfInterruption.UserInterruption(
"${userAborting}"
)
)
println("Aborted previous running build #${build.number}")
killedBuilds.add(build.number)
}
}
}