Shell执行程序如何运行脚本?

时间:2017-10-19 10:40:37

标签: linux bash gitlab-ci-runner

我们最近搬到Gitlab并开始使用管道。我们已经设置了一个构建服务器(一个Ubuntu 16.04实例)并安装了一个使用Shell执行器的运行器,但我不确定它是如何实际执行.gitlab-ci.yml文件中定义的脚本的。请考虑以下代码片段:

script:
    - sh authenticate.sh $DEPLOY_KEY
    - cd MAIN && sh deploy.sh && cd .. 
    - sh deploy_service.sh MATCHMAKING
    - sh deauthenticate.sh

我的印象是它只是将这些命令传递给Bash,因此我期待默认的Bash行为。然而,会发生deploy.shssh错误而失败的情况; Bash然后继续执行deploy_service.sh(这是预期的行为)但是这失败并出现can't open deploy_service.sh错误,并且作业终止而Bash没有执行最后一个语句。

根据我的理解,如果先执行set -e,Bash只会因错误而中止,因此我期待所有语句都被执行。我已经尝试添加set -e作为第一个语句,但这没有任何区别 - 它不会在第一个ssh错误上终止。

我在下面的Gitlab中添加了确切的输出:

没有set -e

$ cd MAIN && sh deploy.sh && cd ..
deploy.sh: 72: deploy.sh: Bad substitution
Building JS bundles locally...

> better-npm-run build

running better-npm-run in x
Executing script: build

to be executed: node ./bin/build 
-> building js bundle...
-> minifying js bundle...
Uploading JS bundles to server temp folder...
COMMENCING RESTART. 5,4,3,2,1...
ssh: Could not resolve hostname $: Name or service not known
$ sh deploy_service.sh MATCHMAKING
sh: 0: Can't open deploy_service.sh
ERROR: Job failed: exit status 1

使用set -e

$ set -e
$ cd MAIN && sh deploy.sh && cd ..
deploy.sh: 72: deploy.sh: Bad substitution
Building JS bundles locally...

> better-npm-run build

running better-npm-run in x
Executing script: build

to be executed: node ./bin/build 
-> building js bundle...
-> minifying js bundle...
Uploading JS bundles to server temp folder...
COMMENCING RESTART. 5,4,3,2,1...
ssh: Could not resolve hostname $: Name or service not known
$ sh deploy_service.sh MATCHMAKING
sh: 0: Can't open deploy_service.sh
ERROR: Job failed: exit status 1

为什么在没有set -e的情况下终止错误(同样,为什么它仅在第二个错误上终止而不是ssh错误)?任何见解将不胜感激。

2 个答案:

答案 0 :(得分:0)

Gitlab脚本块实际上是一个shell脚本数组。 https://docs.gitlab.com/ee/ci/yaml/#script 数组的每个元素都失败将导致整个数组失败。 要解决此问题,请将脚本块放在某个script.sh文件中

rect

答案 1 :(得分:0)

我不认为您的sh deploy.sh正在生成非零退出代码。

如果命令以非零返回码退出,您正在使用set -e告诉当前进程退出,但是您正在创建一个子进程来运行shell脚本。

这是一个我称之为deploy.sh的简单示例脚本:

#!/bin/bash
echo "First."
echox "Error"
echo "Second"

如果我运行脚本,您可以看到错误的处理方式:

$ sh deploy.sh 
First.
deploy.sh: line 5: echox: command not found
Second

如果我先运行set -e,您会发现它没有效果。

$ set -e
$ sh deploy.sh 
First.
deploy.sh: line 5: echox: command not found
Second

现在,我将-e添加到/bin/bash shebang:

#!/bin/bash -e
echo "First."
echox "Error"
echo "Second"

当我使用sh运行脚本时,-e 仍然不起作用。

$ sh ./deploy.sh 
First.
./deploy.sh: line 3: echox: command not found
Second

当使用bash直接运行此脚本时,-e生效。

$ ./deploy.sh 
First.
./deploy.sh: line 3: echox: command not found

要解决您的问题,我相信您需要:

  • -e添加到脚本shebang行(#!/bin/bash -e
  • 使用./deploy.sh直接从bash调用脚本,而不是通过sh运行脚本。

请注意,如果deploy.sh 失败,则cd ..运行(&&表示运行下一个命令如果前一个成功),这意味着你在错误的目录中运行deploy_service.sh。使用cd MAIN; sh deploy.sh; cd ..会更好,但我建议您使用更简单的替代方案替换deploy.sh的来电:

script:
  - sh authenticate.sh $DEPLOY_KEY
  - (cd MAIN && sh deploy.sh) 
  - sh deploy_service.sh MATCHMAKING
  - sh deauthenticate.sh

这并没有太大的不同,但会导致cd MAIN && sh deploy.sh在子进程中运行(这是括号所做的),这意味着整个脚本的当前目录是不受影响。可以把它想象成"生成一个子流程,并在子流程更改目录中运行该脚本",当子流程结束时,你最终会在你开始的地方。

正如其他用户评论的那样,您实际上是在sh中运行脚本,而不是bash,所以这可能会更好:

script:
  - ./authenticate.sh $DEPLOY_KEY
  - (cd MAIN && ./deploy.sh) 
  - ./deploy_service.sh MATCHMAKING
  - ./deauthenticate.sh