From d30dfc89bf1b673b2fdc0638766b930adaec228c Mon Sep 17 00:00:00 2001
From: nsfisis
hello-world:
- stage: test
- image: alpine:latest
- script:
- - 'echo "Hello, World!"'
- rules:
- - if: '$CI_MERGE_REQUEST_IID'
- when: always
+ hello-world:
+ stage: test
+ image: alpine:latest
+ script:
+ - 'echo "Hello, World!"'
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+ when: always
+
ここで、script に指定したコマンドが失敗する (exit status が 0 以外になる) と、即座に実行が停止され、ジョブは失敗する。
@@ -106,14 +107,16 @@
では、次のようなケースだとどうなるか。
hello-world:
- stage: test
- image: alpine:latest
- script:
- - 'exit 1 | exit 0'
- rules:
- - if: '$CI_MERGE_REQUEST_IID'
- when: always
+ hello-world:
+ stage: test
+ image: alpine:latest
+ script:
+ - 'exit 1 | exit 0'
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+ when: always
+
失敗するコマンドをパイプに接続した。通常 Bash では、パイプの最後のコマンドの exit code が全体の exit code になる。
@@ -126,10 +129,12 @@
前述したようなケースにおいて、途中で失敗したときに全体を失敗させるには、pipefail オプションを有効にする。
# On にする
-set -o pipefail
-# Off にする
-set +o pipefail
+ # On にする
+set -o pipefail
+# Off にする
+set +o pipefail
+ こうすると、パイプ全体が失敗するようになる。この設定は、デフォルトだと off になっている。 @@ -143,14 +148,16 @@ 次のような GitLab CI/CD ジョブが失敗してしまった。
-hoge:
- stage: test
- image: alpine:latest
- script:
- - 'cat hoge.txt | grep piyo | sed -e "s/foo/bar/g"'
- rules:
- - if: '$CI_MERGE_REQUEST_IID'
- when: always
+ hoge:
+ stage: test
+ image: alpine:latest
+ script:
+ - 'cat hoge.txt | grep piyo | sed -e "s/foo/bar/g"'
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+ when: always
+
grep コマンドは、パターンにマッチする行が一行もなかったとき、exit code 1 を返す。よって、pipefail が on になっていると、このジョブは失敗する。現在の pipefail がどうなっているか確かめるため set +o で全オプションを出力させたところ、pipefail が on になっていた。
@@ -160,20 +167,22 @@
しかし、先述したように Bash における pipefail のデフォルト値は off のはずだ。実際に、ローカルで alpine:latest を動かしてみたところ、
$ docker run --rm alpine:latest sh -c "set +o"
-set +o errexit
-set +o noglob
-set +o ignoreeof
-set +o monitor
-set +o noexec
-set +o xtrace
-set +o verbose
-set +o noclobber
-set +o allexport
-set +o notify
-set +o nounset
-set +o vi
-set +o pipefail
+ $ docker run --rm alpine:latest sh -c "set +o"
+set +o errexit
+set +o noglob
+set +o ignoreeof
+set +o monitor
+set +o noexec
+set +o xtrace
+set +o verbose
+set +o noclobber
+set +o allexport
+set +o notify
+set +o nounset
+set +o vi
+set +o pipefail
+
確かに pipefail は無効になっている。
@@ -190,9 +199,11 @@ set +o pipefail
.gitlab-ci.yml で明示的には書いていないので、GitLab Runner (GitLab CI/CD のスクリプトを実行するプログラム) が勝手に追加しているに違いない。そう仮説を立てて GitLab Runner のリポジトリ を調査したところ、ソースコード中の以下の箇所 で set -o pipefail していることが判明した (コメントは筆者による)。
// pipefail オプションが存在しない環境にも対応するため、
-// 先に set -o でオプション一覧を表示させたあと、set -o pipefail している
-buf.WriteString("if set -o | grep pipefail > /dev/null; then set -o pipefail; fi; set -o errexit\n")
+ // pipefail オプションが存在しない環境にも対応するため、
+// 先に set -o でオプション一覧を表示させたあと、set -o pipefail している
+buf.WriteString("if set -o | grep pipefail > /dev/null; then set -o pipefail; fi; set -o errexit\n")
+ pipefail が on になっていては困る場所だけ off にしてやればよい。
- hoge:
- stage: test
- image: alpine:latest
- script:
-+ - 'set +o pipefail'
- - 'cat hoge.txt | grep piyo | sed -e "s/foo/bar/g"'
-+ - 'set -o pipefail' # この例の場合、ここで終わりなので戻さなくてもよい
- rules:
- - if: '$CI_MERGE_REQUEST_IID'
- when: always
+ hoge:
+ stage: test
+ image: alpine:latest
+ script:
++ - 'set +o pipefail'
+ - 'cat hoge.txt | grep piyo | sed -e "s/foo/bar/g"'
++ - 'set -o pipefail' # この例の場合、ここで終わりなので戻さなくてもよい
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+ when: always
+