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

发布于 2024-12-16 00:58:46 字数 708 浏览 1 评论 0原文

在我正在进行的一个项目中,我们使用 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 将构建标记为不稳定而不是仅成功/失败?

In a project I'm working on, we are using shell scripts to execute different tasks. Some are sh/bash scripts that run rsync, and some are PHP scripts. One of the PHP scripts is running some integration tests that output to JUnit XML, code coverage reports, and similar.

Jenkins is able to mark the jobs as successful / failed based on exit status. In PHP, the script exits with 1 if it has detected that the tests failed during the run. The other shell scripts run commands and use the exit codes from those to mark a build as failed.

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

In Jenkins Terminology, an unstable build is defined as:

A build is unstable if it was built successfully and one or more publishers report it unstable. For example if the JUnit publisher is configured and a test fails then the build will be marked unstable.

How can I get Jenkins to mark a build as unstable instead of only success / failed when running shell scripts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

森末i 2024-12-23 00:58:46

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

退出代码build

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

Modern Jenkins versions (since 2.26, October 2016) solved this: it's just an advanced option for the Execute shell build step!

exit code for build

You can just choose and set an arbitrary exit value; if it matches, the build will be unstable. Just pick a value which is unlikely to be launched by a real process in your build.

夜访吸血鬼 2024-12-23 00:58:46

使用 Text-finder 插件。

不要以状态 1 退出(这会导致构建失败),而是执行以下操作:

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

在构建后操作中启用文本查找器,设置正则表达式以匹配您打印的消息 (TESTS FAILED!)并选中该条目下的“如果找到则不稳定”复选框。

Use the Text-finder plugin.

Instead of exiting with status 1 (which would fail the build), do:

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

Than in the post-build actions enable the Text Finder, set the regular expression to match the message you printed (TESTS FAILED!) and check the "Unstable if found" checkbox under that entry.

虐人心 2024-12-23 00:58:46

无需打印魔术字符串和使用 TextFinder 即可完成此操作。 这里有关它的一些信息。

基本上,您需要在 shell 脚本中使用来自 http://yourserver.com/cli 的 .jar 文件,然后您可以使用以下命令将构建标记为不稳定:

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 脚本下载:

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

It can be done without printing magic strings and using TextFinder. Here's some info on it.

Basically you need a .jar file from http://yourserver.com/cli available in shell scripts, then you can use the following command to mark a build unstable:

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

To mark build unstable on error, you can use:

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

The problem is that jenkins-cli.jar has to be available from shell script. You can either put it in easy-to-access path, or download in via job's shell script:

wget ${JENKINS_URL}jnlpJars/jenkins-cli.jar
漆黑的白昼 2024-12-23 00:58:46

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

   stage {
      status = /* your build command goes here */
      if (status === "MARK-AS-UNSTABLE") {
        currentBuild.result = "UNSTABLE"
      }
   }

You should use Jenkinsfile to wrap your build script and simply mark the current build as UNSTABLE by using currentBuild.result = "UNSTABLE".

   stage {
      status = /* your build command goes here */
      if (status === "MARK-AS-UNSTABLE") {
        currentBuild.result = "UNSTABLE"
      }
   }
反目相谮 2024-12-23 00:58:46

您还应该能够使用 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 插件

you should also be able to use groovy and do what textfinder did

marking a build as un-stable with groovy post-build plugin

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()
}

Also see Groovy Postbuild Plugin

一笑百媚生 2024-12-23 00:58:46

在我的作业脚本中,我有以下语句(此作业仅在 Jenkins master 上运行):

# 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 wiki 上看到此内容以及有关设置构建状态的更多信息: jenkins-ci.org/display/JENKINS/Jenkins+CLI" rel="noreferrer">https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI

In my job script, I have the following statements (this job only runs on the Jenkins master):

# 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

You can see this and a lot more information about setting build statuses on the Jenkins wiki: https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI

踏雪无痕 2024-12-23 00:58:46
  1. 配置 PHP 构建以生成 xml junit 报告

    
       <记录>
           <日志类型=“junit”目标=“build/junit.xml” 
               logIncompleteSkipped="false" title="测试结果"/>
       
    
       ....
    
     
    
  2. 完成构建脚本,状态为 0

    <前><代码>...
    退出 0;

  3. 为测试报告 XML 添加构建后操作发布 JUnit 测试结果报告。当测试失败时,该插件会将稳定版本更改为不稳定版本。

    <前><代码>**/build/junit.xml

  4. 添加带有控制台输出扫描和未选中选项的Jenkins Text Finder插件。该插件因致命错误而导致整个构建失败。

    PHP 致命错误:
    
  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:
    
无悔心 2024-12-23 00:58:46

此处复制我的答案,因为我花了一些时间寻找这个:

这现在可以在较新版本的 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}")
    }
  }
}

管道语法生成器在高级选项卡中向您显示此内容:

管道语法示例

Duplicating my answer from here because I spent some time looking for this:

This is now possible in newer versions of Jenkins, you can do something like this:

#!/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}")
    }
  }
}

The Pipeline Syntax generator shows you this in the advanced tab:

Pipeline Syntax Example

零時差 2024-12-23 00:58:46

除了所有其他答案之外,jenkins 还允许使用 unstable() 方法(我认为这更清楚)。
此方法可以与消息参数一起使用,该消息参数描述了构建不稳定的原因。

除此之外,您还可以使用 shell 脚本(bat 或 sh)的 returnStatus 来启用此功能。

例如:

def status = bat(script: "<your command here>", returnStatus: true)
if (status != 0) {
    unstable("unstable build because script failed")
}

当然,您可以根据您的需求和退货状态制作更细粒度的东西。


此外,为了引发错误,您还可以使用 warnError() 代替 unstable()。它会指示您的构建失败而不是不稳定,但语法是相同的。

In addition to all others answers, jenkins also allows the use of the unstable() method (which is in my opinion clearer).
This method can be used with a message parameter which describe why the build is unstable.

In addition of this, you can use the returnStatus of your shell script (bat or sh) to enable this.

For example:

def status = bat(script: "<your command here>", returnStatus: true)
if (status != 0) {
    unstable("unstable build because script failed")
}

Of course, you can make something with more granularity depending on your needs and the return status.


Furthermore, for raising error, you can also use warnError() in place of unstable(). It will indicate your build as failed instead of unstable, but the syntax is same.

夏の忆 2024-12-23 00:58:46

我发现最灵活的方法是读取 groovy 后期构建插件中的文件。
输入图片此处描述

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”,则构建将设置为不稳定。这将适用于本地主服务器和运行作业的任何从服务器,以及可以写入磁盘的任何类型的脚本。

I find the most flexible way to do this is by reading a file in the groovy post build plugin.
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()
}

If the file contents are 'true' the build will be set to unstable. This will work on the local master and on any slaves you run the job on, and for any kind of scripts that can write to disk.

疯了 2024-12-23 00:58:46

我想我会为可能正在寻找类似内容的人发布另一个答案。

在我们的构建工作中,我们会遇到这样的情况:我们希望构建继续,但被标记为不稳定。对于我们来说,它与版本号有关。

因此,我想在构建上设置一个条件,如果满足该条件,则将构建设置为不稳定。

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

然后,我使用执行系统 ​​Groovy 脚本作为构建步骤,该步骤将在满足该条件时运行。

我使用 Groovy Command 并将脚本设置为以下,

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

I thought I would post another answer for people that might be looking for something similar.

In our build job we have cases where we would want the build to continue, but be marked as unstable. For ours it's relating to version numbers.

So, I wanted to set a condition on the build and set the build to unstable if that condition is met.

I used the Conditional step (single) option as a build step.

Then I used Execute system Groovy script as the build step that would run when that condition is met.

I used Groovy Command and set the script the following

import hudson.model.*

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

return

That seems to work quite well.

I stumbled upon the solution here

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

樱桃奶球 2024-12-23 00:58:46

仅当作业状态未从成功更改为失败或中止时,TextFinder 才有效。
对于这种情况,请在 PostBuild 步骤中使用 groovy 脚本:

errpattern = ~/TEXT-TO-LOOK-FOR-IN-JENKINS-BUILD-OUTPUT.*/;
manager.build.logFile.eachLine{ line ->
    errmatcher=errpattern.matcher(line)
    if (errmatcher.find()) {
        manager.build.@result = hudson.model.Result.NEW-STATUS-TO-SET
    }
 }

请参阅我写的有关它的帖子中的更多详细信息:
http://www.tikalk.com/devops/JenkinsJobStatusChange/

The TextFinder is good only if the job status hasn't been changed from SUCCESS to FAILED or ABORTED.
For such cases, use a groovy script in the PostBuild step:

errpattern = ~/TEXT-TO-LOOK-FOR-IN-JENKINS-BUILD-OUTPUT.*/;
manager.build.logFile.eachLine{ line ->
    errmatcher=errpattern.matcher(line)
    if (errmatcher.find()) {
        manager.build.@result = hudson.model.Result.NEW-STATUS-TO-SET
    }
 }

See more details in a post I've wrote about it:
http://www.tikalk.com/devops/JenkinsJobStatusChange/

落叶缤纷 2024-12-23 00:58:46

作为现有答案的更轻量级替代方案,您可以使用简单的 HTTP POST 来访问 Groovy 脚本控制台 REST API 来设置构建结果:

    curl -X POST \
     --silent \
     --user "$YOUR_CREDENTIALS" \
     --data-urlencode "script=Jenkins.instance.getItemByFullName( '$JOB_NAME' ).getBuildByNumber( $BUILD_NUMBER ).setResult( hudson.model.Result.UNSTABLE )" $JENKINS_URL/scriptText

优点:

  • 无需下载并运行巨大的 jar 文件,
  • 无需组装用于设置和读取某些全局状态(控制台文本、工作区中的文件),
  • 无需插件(Groovy 除外),
  • 无需配置在“通过”或“失败”情况下多余的额外构建步骤。

对于此解决方案,您的环境必须满足以下条件:

  • 可以从从属设备访问 Jenkins REST API 从属
  • 设备必须有权访问允许访问 Jenkins Groovy 脚本 REST API 的凭据。

As a lighter alternative to the existing answers, you can set the build result with a simple HTTP POST to access the Groovy script console REST API:

    curl -X POST \
     --silent \
     --user "$YOUR_CREDENTIALS" \
     --data-urlencode "script=Jenkins.instance.getItemByFullName( '$JOB_NAME' ).getBuildByNumber( $BUILD_NUMBER ).setResult( hudson.model.Result.UNSTABLE )" $JENKINS_URL/scriptText

Advantages:

  • no need to download and run a huge jar file
  • no kludges for setting and reading some global state (console text, files in workspace)
  • no plugins required (besides Groovy)
  • no need to configure an extra build step that is superfluous in the PASSED or FAILURE cases.

For this solution, your environment must meet these conditions:

  • Jenkins REST API can be accessed from slave
  • Slave must have access to credentials that allows to access the Jenkins Groovy script REST API.
过去的过去 2024-12-23 00:58:46

如果您想使用声明性方法,我建议您使用这样的代码。

pipeline {
    stages {
        // create separate stage only for problematic command
        stage("build") {
            steps {
                sh "command"
            }
            post {
                failure {
                    // set status
                    unstable 'rsync was unsuccessful'
                }
                always {
                    echo "Do something at the end of stage"
                }
            }
        }
    }

    post {
        always {
            echo "Do something at the end of pipeline"
        }
    }
}

如果您想将所有内容保留在一个阶段中,请使用 catchError

pipeline {
    stages {
        // create separate stage only for problematic command
        stage("build") {
            steps {
                catchError(stageResult: 'UNSTABLE') {
                    sh "command"
                }
                sh "other command"
            }
        }
    }
}

If you want to use a declarative approach I suggest you to use code like this.

pipeline {
    stages {
        // create separate stage only for problematic command
        stage("build") {
            steps {
                sh "command"
            }
            post {
                failure {
                    // set status
                    unstable 'rsync was unsuccessful'
                }
                always {
                    echo "Do something at the end of stage"
                }
            }
        }
    }

    post {
        always {
            echo "Do something at the end of pipeline"
        }
    }
}

In case you want to keep everything in one stage use catchError

pipeline {
    stages {
        // create separate stage only for problematic command
        stage("build") {
            steps {
                catchError(stageResult: 'UNSTABLE') {
                    sh "command"
                }
                sh "other command"
            }
        }
    }
}
痴梦一场 2024-12-23 00:58:46

将构建设置为不稳定的一种简单方法是在“execute shell”块中运行 exit 13

One easy way to set a build as unstable, is in your "execute shell" block, run exit 13

‖放下 2024-12-23 00:58:46

您可以只调用“exit 1”,构建将在此时失败并且不会继续。我最终创建了一个直通 make 函数来为我处理它,并调用 safemake 而不是 make 进行构建:

function safemake {
  make "$@"
  if [ "$?" -ne 0 ]; then
    echo "ERROR: BUILD FAILED"
    exit 1
  else
    echo "BUILD SUCCEEDED"
  fi
}

You can just call "exit 1", and the build will fail at that point and not continue. I wound up making a passthrough make function to handle it for me, and call safemake instead of make for building:

function safemake {
  make "$@"
  if [ "$?" -ne 0 ]; then
    echo "ERROR: BUILD FAILED"
    exit 1
  else
    echo "BUILD SUCCEEDED"
  fi
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文