如何在Jenkins运行shell脚本时将构建标记为不稳定

103
在我正在处理的一个项目中,我们使用shell脚本来执行不同的任务。一些是运行rsync的sh/bash脚本,另一些则是PHP脚本。其中一个PHP脚本正在运行一些集成测试,输出JUnit XML、代码覆盖率报告等。

Jenkins能够根据退出状态将作业标记为成功/失败。在PHP中,如果检测到测试在运行期间失败,则该脚本以1的状态退出。其他shell脚本运行命令并使用这些命令的退出码来标记构建失败。

// :: End of PHP script:
// If any tests have failed, fail the build
if ($build_error) exit(1);

Jenkins术语中,不稳定的构建被定义为:

如果构建成功并且一个或多个发布者报告它不稳定,则构建是不稳定的。例如,如果配置了JUnit发布者并且测试失败,则构建将被标记为不稳定。

当运行shell脚本时,如何让Jenkins将构建标记为不稳定而不仅仅是成功/失败?

我通过运行不同的作业步骤和使用Jenkins插件来实现它。 - Fantastory
16个回答

76

现代的Jenkins版本(自2016年10月的2.26版本起)已经解决了这个问题:它只是“执行Shell构建步骤”的高级选项!

退出代码用于构建

您可以选择并设置任意的退出值;如果匹配,构建将不稳定。只需选择一个不太可能被实际进程启动的值来设置即可。


2
由于这是在最新的Jenkins中实现的,因此这应该是一个被接受的答案。 - smoke_lp
3
"Modern Jenkins versions" 指的是 Jenkins 2.26 或更高版本。请参见 https://issues.jenkins-ci.org/browse/JENKINS-23786。 - Blue
6
在使用Jenkinsfile中的sh步骤命令时,是否可以通过代码指定此设置?该设置在GUI界面上的哪里?我找不到它。 - bluenote10
2
我必须点击构建步骤下的“高级...”按钮才能显示它。将一个单一(且不是特别高级)选项隐藏在“单击此处执行操作”的折叠器后面并不是很有用,但事实就是如此。 - tripleee
@Kissaki 是哪个版本?在当前的LTS(2.346.2)中,它是可用且正常工作的。 - Alan Franzoni
显示剩余2条评论

61

不需要打印魔术字符串或使用TextFinder,就可以完成此操作。 此处 提供了相关信息。

基本上,您需要从 http://yourserver.com/cli 获取一个 .jar 文件,并在 shell 脚本中使用以下命令将构建标记为不稳定:

java -jar jenkins-cli.jar set-build-result unstable

要在错误时标记构建为不稳定状态,您可以使用:

failing_cmd cmd_args || java -jar jenkins-cli.jar set-build-result unstable

问题在于jenkins-cli.jar必须从shell脚本中可用。您可以将其放置在易于访问的路径中,或通过作业的shell脚本下载:

解决方法是让jenkins-cli.jar从shell脚本中可用。您可以把它放在易于访问的路径下,或者通过作业的shell脚本下载:

wget ${JENKINS_URL}jnlpJars/jenkins-cli.jar

2
我真的很喜欢这个解决方案,我为此实现了一个 Ruby 类,以便在我的 Rakefile 中轻松重用。 :) - Shire
3
+1 - 这个解决方案比被接受的答案更好,因为文本查找器每次只能搜索一个字符串,所以你只能将构建状态设置为两个值中的一个。 - gareth_bowles
4
有趣的解决方案。但是如果您的 Jenkins 需要身份验证,您需要在其配置中设置公钥验证,否则任何 jenkins-cli 命令都会因 AccessDeniedException 而失败。 - Tom De Leu
2
如果您使用的从机无法访问主服务器,则此方法将无法正常工作。例如,如果Jenkins从机无法与服务器建立HTTP或HTTPS连接。 - Steve HHH
3
我想使用这个解决方案,但是在jenkins-cli中,set-build-result已经被弃用了。 - DrLime2k10
显示剩余2条评论

60

使用Text-finder插件。

不要使用状态1退出(因为这会导致构建失败),而是:

if ($build_error) print("TESTS FAILED!");

在构建后操作中,启用Text Finder,将正则表达式设置为匹配你所打印的消息(TESTS FAILED!),并勾选该条目下的“如果找到则不稳定”复选框。


2
自Jenkins 2.26版本起,可以查看以下选项而无需安装插件: https://dev59.com/T2sz5IYBdhLWcg3wCDl2#49676269 - JSoet

43

您应该使用Jenkinsfile来包装构建脚本,并只需使用currentBuild.result = "UNSTABLE"将当前构建标记为UNSTABLE。

   阶段 {
      状态 = /* 在此处输入您的构建命令 */
      如果(状态 === "MARK-AS-UNSTABLE"){
        currentBuild.result = "UNSTABLE"
      }
   }

4
为什么这个回答没有更多的赞?除了使用“magic”字符串UNSTABLE之外,它有什么问题吗?它似乎比其他答案更直截了当。 - Kevin
6
这个问题问的是自由风格工作,而这个回答是关于流水线工作的。这个流水线的回答不适用于自由风格工作。 - Mark Waite
这到底是怎么回事?当我试图在一个阶段内直接设置 currentBuild.result 时,会出现错误:Expected one of "steps", "stages", or "parallel" for stage - dokaspar
@dokaspar 在您可以使用 if 块放置此任意代码之前,很可能需要在 stage {} 内部添加 steps {} 和可能的 script {} 块。 - ErikE
我将它添加到我的管道脚本中,但没有帮助(目标-如果测试失败,则构建状态将为SUCCESS,如果测试通过,则状态将为FAILED,如果是混合的pass+failed,则状态将为SUCCESS:阶段('Tests') { 步骤 { 脚本{ //运行测试 sh "${mvnHome}/bin/mvn clean test -e -Dgroups=categories.knownBug" currentBuild.currentResult = "SUCCESS" } } - Noy M

12

你还可以使用Groovy来完成TextFinder所做的事情。

使用Groovy后置构建插件将构建标记为不稳定

if(manager.logContains("Could not login to FTP server")) {
    manager.addWarningBadge("FTP Login Failure")
    manager.createSummary("warning.gif").appendText("<h1>Failed to login to remote FTP Server!</h1>", false, false, false, "red")
    manager.buildUnstable()
}

还可以参考Groovy Postbuild Plugin


7
在我的工作脚本中,我有以下语句(该作业仅在Jenkins主服务器上运行):
# This is the condition test I use to set the build status as UNSTABLE
if [ ${PERCENTAGE} -gt 80 -a ${PERCENTAGE} -lt 90 ]; then
  echo WARNING: disc usage percentage above 80%

  # Download the Jenkins CLI JAR:
  curl -o jenkins-cli.jar ${JENKINS_URL}/jnlpJars/jenkins-cli.jar

  # Set build status to unstable
  java -jar jenkins-cli.jar -s ${JENKINS_URL}/ set-build-result unstable

fi

你可以在Jenkins维基上查看有关设置构建状态的更多信息,链接如下:https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI

4

我将我的答案从这里复制过来,因为我花了一些时间寻找:

在较新版本的Jenkins中,现在可以像这样做:

#!/usr/bin/env groovy

properties([
  parameters([string(name: 'foo', defaultValue: 'bar', description: 'Fails job if not bar (unstable if bar)')]),
])


stage('Stage 1') {
  node('parent'){
    def ret = sh(
      returnStatus: true, // This is the key bit!
      script: '''if [ "$foo" = bar ]; then exit 2; else exit 1; fi'''
    )
    // ret can be any number/range, does not have to be 2.
    if (ret == 2) {
      currentBuild.result = 'UNSTABLE'
    } else if (ret != 0) {
      currentBuild.result = 'FAILURE'
      // If you do not manually error the status will be set to "failed", but the
      // pipeline will still run the next stage.
      error("Stage 1 failed with exit code ${ret}")
    }
  }
}

管道语法生成器会在高级选项卡中展示给您:

Pipeline Syntax Example


4
  1. Configure PHP build to produce xml junit report

    <phpunit bootstrap="tests/bootstrap.php" colors="true" >
       <logging>
           <log type="junit" target="build/junit.xml" 
               logIncompleteSkipped="false" title="Test Results"/>
       </logging>
    
       ....
    
     </phpunit>
    
  2. Finish build script with status 0

    ...
    exit 0;
    
  3. Add post-build action Publish JUnit test result report for Test report XMLs. This plugin will change Stable build to Unstable when test are failing.

    **/build/junit.xml
    
  4. Add Jenkins Text Finder plugin with console output scanning and unchecked options. This plugin fail whole build on fatal error.

    PHP Fatal error:
    

3

我想为那些可能在寻找类似解决方案的人发布另一个答案。

在我们的构建作业中,有些情况下我们希望构建继续进行,但被标记为不稳定状态。对我们而言,这与版本号有关。

因此,我想在构建上设置条件,并在满足该条件时将构建设置为不稳定状态。

我使用了“条件步骤(单个)”选项作为构建步骤。

然后,在满足条件时运行的构建步骤中,我使用了“执行系统Groovy脚本”的选项。

我使用了“Groovy命令”,并设置了以下脚本:

import hudson.model.*

def build = Thread.currentThread().executable
build.@result = hudson.model.Result.UNSTABLE

return

看起来这个方法相当有效。

我在这里偶然发现了解决方案。

http://tech.akom.net/archives/112-Marking-Jenkins-build-UNSTABLE-from-environment-inject-groovy-script.html


3
我发现最灵活的方法是通过在Groovy后构建插件中读取文件来实现。 enter image description here
import hudson.FilePath
import java.io.InputStream

def build = Thread.currentThread().executable

String unstable = null
if(build.workspace.isRemote()) {
    channel = build.workspace.channel;
    fp = new FilePath(channel, build.workspace.toString() + "/build.properties")
    InputStream is = fp.read()
    unstable = is.text.trim()
} else {
    fp = new FilePath(new File(build.workspace.toString() + "/build.properties"))
    InputStream is = fp.read()
    unstable = is.text.trim()
}

manager.listener.logger.println("Build status file: " + unstable)
if (unstable.equalsIgnoreCase('true')) {
    manager.listener.logger.println('setting build to unstable')
    manager.buildUnstable()
}

如果文件内容为“true”,则构建将被设置为不稳定状态。这适用于本地主机和您在其中运行作业的任何从属节点,以及可以写入磁盘的任何类型的脚本。

我假设这句话的意思是“如果工作区中有一个名为build.properties的文件”,则标记为不稳定。是这样吗?我对Groovy很陌生,您能否再详细解释一下? - uchuugaka
@uchuugaka 是的,如果有一个文件,它有那些内容。文件名和内容都是任意的。使用适合您情况的任何东西。 - jeremyjjbrown
谢谢!非常有帮助。Groovy Postbuild相当间接,而且Groovy从Java中吸收了大量的东西并添加了更多...这对我来说是一个新技巧。 - uchuugaka
@uchuugaka 我认为这不是 Groovy 的问题 :) - jeremyjjbrown
没有任何问题。只是一个需要努力克服的挑战! - uchuugaka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接