Jenkins中的Execute Shell何时会将构建标记为失败?

122

在寻找答案的过程中,我发现了一些可怕的故事...

好的,我有一个 .sh 脚本,它基本上做了 Jenkins 应该做的一切:

  • 从 SVN 检出源代码
  • 构建项目
  • 部署项目
  • 清理干净

所以在 Jenkins 中,我只需要通过运行一个 Execute Shell 命令来“构建”项目。脚本也会运行(源代码被下载,项目被构建/部署),但是它标记构建过程为失败: Build step 'Execute shell' marked build as failure 即使脚本成功运行!我尝试使用以下命令来关闭脚本:

  • exit 0(仍将其标记为失败)
  • exit 1(按预期标记为失败)
  • 根本不使用 exit 命令(仍将其标记为失败)

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

8个回答

142

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

如果你拥有一个能够自行执行“检出、构建、部署”的shell脚本,那你为什么还要使用Jenkins呢?这样做就放弃了Jenkins的所有特性。你不如直接让cron或者SVN的后提交挂钩直接调用该脚本。Jenkins执行SVN检出非常重要。它可以在发生变更时才触发构建(或按计时器、手动方式),也会跟踪构建之间的变化,并显示这些变化,因此您可以看到哪次构建是为了哪个变更集而构建的。它会向提交者发送电子邮件,告诉他们他们的变更导致了成功或失败的构建(根据您的配置)。当提交者修复了失败的构建时,它也会发送电子邮件通知。除此之外,Jenkins还有更多功能。Jenkins归档工件使它们可以直接从Jenkins中获取,每个构建都有一个相应的工件。虽然不像SVN检出那么重要,但这同样是Jenkins的一个组成部分。部署也是一样的。除非您只有一个环境,否则部署通常会发生在多个环境中。Jenkins可以通过Promotions来跟踪特定构建(具有特定的SVN变更集)已经部署到哪个环境。你放弃了所有这些。听起来像是“你必须使用Jenkins”,但你并不想使用它,只是为了让你的老板放心,只是为了打一个勾“是的,我使用了Jenkins”。
简单来说,Jenkins的“执行Shell”构建步骤中的“last”命令的退出代码决定了“Build Step”的成功/失败。0表示成功,任何其他数字表示失败。请注意,这是确定“build step”成功/失败,而不是整个作业运行的成功/失败。整个作业运行的成功/失败还可能受到多个构建步骤、后构建操作和插件的影响。
您提到了“Build step 'Execute shell' marked build as failure”,因此我们将仅关注单个构建步骤。如果您的“执行Shell”构建步骤只有一行调用您的shell脚本,则您的shell脚本的退出代码将确定构建步骤的成功/失败。如果您有更多行在shell脚本执行之后,请仔细审查它们,因为它们可能会导致失败。

最后,阅读此处Jenkins构建脚本在Google测试执行后退出。虽然与你的问题不直接相关,但请注意关于Jenkins启动Execute Shell构建步骤的部分,作为带有/bin/sh -xe的shell脚本。

-e意味着即使只有一个命令失败,shell脚本也会失败退出,即使您对该命令进行错误检查(因为脚本在到达您的错误检查之前退出)。这与通常执行shell脚本的方式相反,通常会打印失败命令的错误消息(或将其重定向为空并通过其他方式处理),然后继续执行。

为了规避这种情况,请在shell脚本顶部添加set +e

由于您说您的脚本已完成所有应该完成的工作,所以失败的命令可能在脚本的末尾某个位置。也许是最后的echo?还是复制某个地方的artifact?如果没有看到完整的控制台输出,我们只是在猜测。

请发布作业运行的控制台输出,最好也附上shell脚本本身,这样我们就可以告诉您哪一行出错了。

7
我仍在使用Jenkins的原因对我来说不太清楚...似乎高层中的某个人没有完全理解我们已经有了这个脚本,坚持让我们使用Jenkins。我感觉自己像是身处Dilbert漫画中。谢谢你提供的-e提示。它解决了问题。 - tester
48
仍有很多好理由使用Jenkins:审计追踪,构建状态可见性等。如果您已经有一个构建脚本,将其移至Jenkins是在重构以利用Jenkins功能之前迈出的良好第一步。 - aehlke
3
可以在脚本中的任何位置指定set +e和set -e。如果返回值不为0,则在其之间的任何代码都不会导致构建失败。 - Alex Skrypnyk
2
非常好的一句话,关于Shell脚本与Jenkins集合。 - pushya
我是新手。当我尝试构建一个Angular应用程序时,我遇到了相同的错误。我尝试了一切...然后我想可能是因为我的服务器可用内存不足的原因。所以我升级并增加了更多的RAM。之后它就正常工作了。 :) - Celso Soares
显示剩余2条评论

102

回答您问题的简短答案是:

请将以下行添加到您的“执行shell”构建步骤中。

#!/bin/sh

现在让我向您解释一下为什么我们需要在“执行 Shell”构建作业中加入此行。

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

因此,添加#!/bin/sh将允许您执行没有任何选项的命令。


4
点赞了。不知道-xe默认值。当我的grep命令找不到一个字符串时,整个脚本都失败了,因为grep返回了非0的返回值 :) - Somaiah Kumbera
非常好用!我在我的一些非关键步骤中使用它,比如一个清理步骤,只需执行类似于 find . -name 'bower_components' -exec rm {} \; 的操作,但在某些情况下,它会失败。谢谢! - yorch
这清除了一切 - “另一个选项-e,会导致shell在任何命令以非零(当任何命令失败时)退出代码退出时立即停止运行脚本。” - Paramvir Singh Karwal

3

我尝试了所有提到的选项(甚至将sh更改为没有-xe参数的bash),唯一有效的选项是:

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

3
在我看来,关闭 shell 的 -e 选项是一个非常糟糕的想法。最终,你的脚本中的某个命令将由于诸如磁盘空间不足或网络错误等暂时性条件而失败。没有 -e,Jenkins 将不会注意到并会继续运行。如果你已经设置了 Jenkins 进行部署,那么可能会导致不良代码被推送,并使你的网站崩溃。
如果你的脚本中有一行预期会失败的命令,比如 grep 或 find,那么只需在该行末尾添加 || true 即可。这可以确保该行始终返回成功。
如果你需要使用该退出码,你可以将该命令提升到 if 语句中:
grep foo bar; if [ $? == 0 ]; then ...    -->   if grep foo bar; then ...

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

grep foo bar || ret=$?

1
谢谢Bryan。你救了我的一天。另外,我认为打开-x和-e是个好主意,这样你就可以在Jenkins日志中看到了。 - Bikal Basnet

2

简单明了:

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

为什么会发生这种情况取决于您的构建脚本。

我从另一个角度写了一些类似的东西,但也许阅读它可能有所帮助:为什么Jenkins认为我的构建成功了?


2

因此,添加#!/bin/sh将允许您无需选项执行。

它还帮助我修复了一个问题,即我正在从Jenkins主服务器上的Linux从机执行bash脚本。只需在“Execute Shell”块中在我的实际脚本上方添加#!/bin/bash,它便修复了我的问题,否则它将执行Windows Git提供的bash shell版本,导致错误。


0

尝试并始终通过将以下行添加到“执行 shell”构建步骤中,找到确切的失败位置。

#!/bin/sh -xe

通过添加-x选项,您将打印每个运行的命令(包括嵌入式脚本中的行),这有助于发现根本原因。

删除-e选项即运行#!/bin/sh将允许您以没有选项的方式执行,这实际上是一种糟糕的想法,正如Bryan在其中一个答案中解释得很好。

问题在于没有选项Jenkins将忽略错误并继续执行后续步骤(如果有的话),这将使您的进程处于一种不一致的状态。 如果这是用于生产版本或部署,则可能会产生不良影响。

一旦找到问题区域,请手动以jenkins-user目录下同样失败的命令运行,以获取精确的错误/根本原因。


-1
在Jenkins ver. 1.635中,无法像这样显示本地环境变量:
$BUILD_NUMBER or ${BUILD_NUMBER}

在这种情况下,您必须将它设置在另一个变量中。
set BUILDNO = $BUILD_NUMBER
$BUILDNO

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