Gitlab CIのrulesの挙動を確認する
GitLab CIのruleを使うと、いろいろな条件でパイプラインの実行を制御できます。その際にrulesキーワードを使用するのですが、理解が怪しかったので、実際に触りながら検証しいきます。
検証環境はgitlab-runnerのDockerイメージを使用して、ローカル環境でGitLab CIを実行する、で構築した環境を使っています。
ドキュメントを読む
Section titled “ドキュメントを読む”rulesを確認してみます。
- rulesはパイプラインの作成時に評価され、最初に一致するまで順番に評価される。
 - 一致するものが見つかると、設定に応じて、そのジョブがパイプラインに含まれたり、除外される。
 
ジョブがパイプラインに追加される条件
if、changes、existsのruleがマッチし、かつwhen: on_success(デフォルト)、when: delayed、またはwhen: alwaysのいずれかを持つ場合when: on_success、when: delayed、when: alwaysのいずれかのみを持つruleに到達した場合
ジョブがパイプラインに追加されない条件
- マッチするrulesがない場合
 - マッチしたruleが
when: neverになっている場合 
whenを確認してみます。
whenのデフォルトはon_success
| whenの値 | 説明 | 
|---|---|
| on_success | それ以前のステージのジョブがすべて成功するか、allow_failure: trueとなったときのみ、ジョブを実行する。 | 
| manual | 手動でのみ実行できる Create a job that must be run manually | 
| always | 前に実行されたjobのstatusに関係なくジョブを実行する | 
| on_failure | 以前のステージのジョブが少なくとも1つ失敗した場合にのみ、そのジョブを実行する。allow_failure: trueを指定したジョブは、常に成功したとみなされる | 
| delayed | 指定した期間、ジョブの実行を遅らせる。 | 
| never | ジョブを実行しない。rulesセクションまたはworkflow: rulesでのみ使用可能 | 
リポジトリのルートにマークダウンファイルをコミットしたときのみジョブを実行したくないというケースを想定して検証してみます。
検証1: mainブランチにファイルをコミットする
Section titled “検証1: mainブランチにファイルをコミットする”設定 1-1
Section titled “設定 1-1”.gitlab-ci.ymlを以下の設定にします。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "*.md"      when: never結果 1-1
Section titled “結果 1-1”ジョブの実行を o、実行しなかったものを x とします。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.mdをコミットする | x | x | 
| exapmle/test.mdをコミットする | o | x | 
| リポジトリのルートにtest.jsをコミットする | o | x | 
この設定では、理想と結果に違いがあります。先ほど記述した “マッチするrulesがない場合、ジョブがパイプラインに追加されない” が関係していそうです。マークダウンファイル以外はジョブを実行してほしいので、追加でwhen: alwaysをつけてみます。
設定 1-2
Section titled “設定 1-2”stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "*.md"      when: never    - when: always結果 1-2
Section titled “結果 1-2”想定した結果になりました。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.mdをコミットする | x | x | 
| exapmle/test.mdをコミットする | o | o | 
| リポジトリのルートにtest.jsをコミットする | o | o | 
検証2: ブランチを作成し、検証1と同じ.gitlab-ci.ymlを使用する
Section titled “検証2: ブランチを作成し、検証1と同じ.gitlab-ci.ymlを使用する”検証1ではmainブランチに直接コミットしました。しかし、普段の開発ではそんなことをしないはずです。
次はmainから作成したブランチにコミットします。
設定 2-1
Section titled “設定 2-1”設定は設定1-2と同じです。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "*.md"      when: never    - when: always結果 2-1
Section titled “結果 2-1”ファイルへのコミットは検証1と相違はありませんでした。
しかし、test.md, example/test.md, test.jsの変更を含んだマージリクエストをmainブランチにマージするとジョブは実行されませんでした。これはマージリクエストのtest.mdがchangesの条件に引っ掛かり、パイプラインにジョブが追加されなかったからです。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.mdをコミットする | x | x | 
| exapmle/test.mdをコミットする | o | o | 
| リポジトリのルートにtest.jsをコミットする | o | o | 
| mainブランチにマージする | o | x | 
検証3: 検証2のchangesをneverからon_successにする
Section titled “検証3: 検証2のchangesをneverからon_successにする”検証2ではブランチを作成して、changesの条件 (“*.md”) がtrueの場合、when: neverでジョブの実行を止めていました。
次は条件を逆にして、実行したい条件をchangesに書きました。
設定 3-1
Section titled “設定 3-1”changesの条件を設定2-1から変更します。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "example/**/*.md"      - "*.js"結果 3-1
Section titled “結果 3-1”意図した通りになりました。意図した挙動なので設定はこれで合っていそうです。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.mdをコミットする | x | x | 
| exapmle/test.mdをコミットする | o | o | 
| リポジトリのルートにtest.jsをコミットする | o | o | 
| mainブランチにマージする | o | o | 
設定 3-2
Section titled “設定 3-2”今度はwhen: neverを追加して、動作確認してみます。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "example/**/*.md"      - "*.js"    - changes:      - "*.md"      when: never結果 3-2
Section titled “結果 3-2”こちらも意図した通りになりました。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.mdをコミットする | x | x | 
| exapmle/test.mdをコミットする | o | o | 
| リポジトリのルートにtest.jsをコミットする | o | o | 
| mainブランチにマージする | o | o | 
設定 3-3
Section titled “設定 3-3”今度はchangesの条件の順番を入れ替えます。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "*.md"      when: never    - changes:      - "example/**/*.md"      - "*.js"結果 3-3
Section titled “結果 3-3”設定2-1と同じように、mainブランチへのマージしたときにジョブが実行されませんでした。
rulesは最初に一致するまで順番に評価されるので、test.md, example/test.md, test.js をmaibブランチにマージした場合、最初の条件であるwhen: neverが評価されたことになります。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.mdをコミットする | x | x | 
| exapmle/test.mdをコミットする | o | o | 
| リポジトリのルートにtest.jsをコミットする | o | o | 
| mainブランチにマージする | o | x | 
検証4: おまけ rulesにマッチしないファイルをコミットする
Section titled “検証4: おまけ rulesにマッチしないファイルをコミットする”設定 4-1
Section titled “設定 4-1”設定 3-2の.gitlab-ci.ymlを使います。
changesの条件に評価されないファイルをコミットした場合を確認してみます。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "example/**/*.md"      - "*.js"    - changes:      - "*.md"      when: never結果 4-1
Section titled “結果 4-1”test.tsをコミットして確認してみます。
rulesの条件に一致しない場合は、ジョブが実行されないことがわかりました。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.ts | - | x | 
| mainブランチにマージする | - | x | 
設定 4-2
Section titled “設定 4-2”設定 3-3の.gitlab-ci.ymlを使います。
changesの条件に評価されないファイルをコミットした場合を確認してみます。
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes:      - "*.md"      when: never    - changes:      - "example/**/*.md"      - "*.js"結果 4-2
Section titled “結果 4-2”test.tsをコミットして確認してみます。
rulesの条件に一致しない場合は、ジョブが実行されないことがわかりました。
手を動かしていて思ったのは、やっていることは設定1-1のときと同じでしたね。
| 内容 | 理想 | 結果 | 
|---|---|---|
| リポジトリのルートにtest.ts | - | x | 
| mainブランチにマージする | - | x | 
エイリアスとアンカーを使用する
Section titled “エイリアスとアンカーを使用する”ジョブを実行したいファイルパターンをリストで書くと、行数が多くなると思います。そこからさらに、複数ジョブにもchangesを書いていくと重複し、メンテナンスが難しくなるはずです。
しかし、yamlのエイリアスとアンカーを利用すると記述を再利用できます。設定3-1で使用した.gitlab-ci.ymlは以下のように書くことができます。
.test-pattern: &test-pattern  - "example/**/*.md"  - "*.js"
stages:  - test
test:  stage: test  script:    - ls  rules:    - changes: *test-patternさらにgitlabではrulesの条件を再利用したい場合、!reference tagを利用することで再利用できます。
Reuse rules in different jobs - GitLab Docs
以下のことがわかりました。
- GitLab CIのrulesは順番に評価するので、記述の順番が大切
 - コミットがすべてのrulesに引っかからない場合、ジョブは実行されない
 
.gitlab-ci.ymlではいろいろな設定、書き方ができるので、引き続き検証を進めていきたいと思います。
gitlab 本体のCIの設定は書き方の参考になったので、備忘録として残しておきます。