如何/何时在 Jenkins 中执行 Shell 将构建标记为失败?

我在寻找这个问题的答案时发现的恐怖故事..。

好的,我有一个.sh 脚本,它几乎完成了 Jenkins 应该做的所有事情:

  • 查看 SVN 的消息来源
  • build the project
  • 部署项目
  • 自己清理干净

所以在 Jenkins 中,我只需要在 Execute Shell 命令中运行脚本来“构建”项目。 脚本被运行(源代码被下载,项目被构建/部署) ,然后它将构建标记为失败: 构建步骤“执行 shell”将构建标记为失败 即使脚本运行成功! 我试着用以下方式结束脚本:

  • 退出0(仍将其标记为失败)
  • 退出1(按预期将其标记为失败)
  • 根本没有退出命令(标记为失败)

执行 Shell 何时、如何以及为什么将我的构建标记为失败?

219514 次浏览

首先,将鼠标悬停在下面的灰色区域上。这不是答案的一部分,但我必须说:

如果您有一个完全自己执行“签出、构建、部署”的 shell 脚本,那么为什么要使用 Jenkins 呢?你放弃了詹金斯的所有特征这才是真正的詹金斯。您还可以直接使用 cron 或 SVN 提交后钩子调用该脚本.Jenkins 执行 SVN 检查本身是至关重要的。它只允许在发生更改时触发构建(如果愿意,也可以按计时器或手动方式触发)。它跟踪构建之间的更改。它显示了这些更改,因此您可以看到哪个构建用于哪组更改。当提交者的更改导致构建成功或失败时,它会向提交者发送电子邮件(同样,根据您的喜好进行配置)。它会在提交者修复错误的构建时给他们发电子邮件。越来越多。詹金斯存档的艺术品也使他们可用,每建立,直接关闭詹金斯。虽然不像 SVN 结帐那么重要,但是这又是 Jenkins 不可或缺的一部分。部署也一样。除非您只有一个环境,否则部署通常会发生在多个环境中。Jenkins 可以跟踪特定构建(具有特定的 SVN 更改集)部署它的环境,通过使用 Protions。你要放弃这一切。这听起来像是你被告知“你必须利用詹金斯”,但是你并不真的想这么做,你这么做只是为了让你的老板不再烦你,只是为了给“是的,我利用过詹金斯”打个勾

简短的回答是: Jenkin 的 执行命令构建步骤的 最后命令的退出代码决定了 构建步骤的成败。成功,失败。 Note, this is determining the success/failure of the 构建步骤, not the whole 出任务了. The success/failure of the whole job run can further be affected by multiple build steps, and post-build actions and plugins.

您已经提到了 Build step 'Execute shell' marked build as failure,因此我们将只关注一个构建步骤。如果 Execute shell构建步骤只有一行调用 shell 脚本,那么 shell 脚本的退出代码将决定构建步骤的成败。如果您有更多的代码行,之后您的 shell 脚本执行,然后仔细检查它们,因为它们可能会导致失败。

Finally, have a read here 詹金斯构建脚本在谷歌测试执行后退出. It is not directly related to your question, but note that part about Jenkins launching the Execute Shell build step, as a shell script with /bin/sh -xe

-e意味着 shell 脚本将 出口失败,即使只有1个命令失败,如果对该命令执行错误检查,则为 甚至(因为脚本在进行错误检查之前退出)。这与 shell 脚本的正常执行相反,后者通常为失败的命令打印错误消息(或者将其重定向为 null 并通过其他方式处理) ,然后继续。

为了避免这种情况,可以将 set +e添加到 shell 脚本的顶部。

因为您说您的脚本完成了它应该完成的所有工作,所以失败的命令很可能出现在脚本的末尾。也许是最后的回声?或者藏在什么地方的艺术品复制品?没有看到完整的控制台输出,我们只是猜测。

请发布作业运行的控制台输出,最好还有 shell 脚本本身,然后我们可以准确地告诉您哪一行失败了。

简单明了:

如果 Jenkins 看到构建步骤(也是一个脚本)以非零代码退出,那么构建将被标记为红球(= 失败)。

具体发生的原因取决于您的构建脚本。

我从另一个角度写了一些类似的东西,但也许读一读会有帮助: 为什么詹金斯认为我的构建成功了?

你问题的简单回答是

请在“执行 shell”构建步骤中添加以下代码行。

#!/bin/sh

现在让我解释一下为什么我们需要“ Execute Shell”构建作业使用这一行。

默认情况下,詹金斯采用 /bin/sh -xe,这意味着 -x将打印每个命令。另一个选项 -e,它导致 shell 在任何命令退出时立即停止运行脚本,并且退出代码为非零(当任何命令失败时)。

因此,通过添加 #!/bin/sh将允许您在没有选项的情况下执行。

在我看来,关闭对 shell 的 -e选项是一个非常糟糕的主意。最终,由于磁盘空间不足或网络错误等暂时性情况,脚本中的一个命令将失败。没有 -e詹金斯将不会注意到,并将继续愉快地前进。如果您已经将 Jenkins 设置为执行部署,那么可能会导致错误代码被推送并导致站点崩溃。

如果您的脚本中有一行预期会出现故障,比如 grep 或 find,那么只需将 || true添加到该行的末尾。这就确保了这条线路总是会返回成功。

如果需要使用退出代码,可以将命令提升到 If 语句中:

grep foo bar; if [ $? == 0 ]; then ...    -->   if grep foo bar; then ...

或者您可以在 ||子句中捕获返回代码:

grep foo bar || ret=$?

因此,通过添加 #!/bin/sh将允许您在没有选项的情况下执行。

它还帮助我解决了一个问题,即在 Linux 奴隶上从 Jenkins master 执行 bash 脚本。通过在“ Execute Shell”块中的实际脚本上添加 #!/bin/bash,它修复了我的问题,否则它将执行 windows git 提供的 bash Shell 版本,该版本将出现错误。

在詹金斯1.635版本中,不可能展示这样一个本地环境变量:

$BUILD_NUMBER or ${BUILD_NUMBER}

In this case, you have to set it in an other variable.

set BUILDNO = $BUILD_NUMBER
$BUILDNO

我已经尝试了所有提到的选项(甚至将 sh 改为 bash 而没有 -xe params) ,唯一对我有效的选项是:

<command-which-returns-not-zero> || exit 0

Try and always find where exactly its failing by adding the following line into your "Execute shell" Build step.

# !/bin/sh-xe

By adding the -x you will print each and every command that ran (including the lines from embedded scripts) and that will help in spotting the root cause.

移除-e 选项,即运行 # !/bin/sh 将允许您在没有选项的情况下执行,正如 Bryan 在其中一个答案中所解释的那样,这确实是一个糟糕的主意。

问题是没有选择 Jenkins 将忽略错误并继续执行后续步骤(如果有的话) ,这将使您的流程处于一致的状态。如果这是用于生产构建或部署,那么可能会产生不良影响。

Once you find the problem area, run the same failing command from the directory as jenkins-user manually, to get to the exact error/rootcause.