こんにちは、クラウド事業部 CI/CDサービスメニューチームの山路です。
今回はGitLab CI/CDを扱う上で重要なキーワードである rules
について、その使い方を整理しました。
背景
GitLabは .gitlab-ci.yml
にCI/CDの実行内容を定義します。 .gitlab-ci.yml
は多くのキーワードを組み合わせて利用しますが、その中でも重要なものの一つが rules
です。
rules
はGitLab CI/CDのWorkflowやJobに対して設定し、いつ、どのような条件でWorkflow/Jobを実行するかを定義します。CI/CDのワークフローで実行する処理は多岐にわたり、それらを実行するタイミングや対象のブランチ・ファイルは、条件に応じて異なる処理を実行することが殆どです。そのためGitLab CI/CDにおいて rules
を理解することは重要と言えます。
一方、GitLab CI/CDには数多くのキーワードが存在し、 rules
も多くのキーワードを含んでいます。そのため、 rules
を理解するには、ある程度調査や検証をする時間が必要です。私も以前 rulesを簡単に試してみたことがありますが、ドキュメントを眺めると思った以上に多くのキーワードや使い方が紹介されており、なかなか全容をつかむのが難しいと感じました。
今回はそんな rules
を一度整理し、GitLab CI/CDを使いこなすための一歩を踏みたい!という気持ちで書き始めました。何かの参考になれば幸いです。
rulesの紹介
GitLabのrulesはWorkflow/Jobで設定できますが、今回はJobに設定することを前提に紹介します。
WorkflowはGitLabのPipeline全体の動きを定義するために使います。workflow
で定義したrules
は、GitLabのPipeline作成後、およびJobの実行前に評価されるため、ワークフロー自体を実行するか否か制御します。一方、実際の現場ではワークフロー全体より、個々のJobをいつ実行するか制御する場面のほうが多いだろうと考え、今回はworkflow
の紹介はしません。
workflow
の内容を知りたい方は、公式ドキュメントをご覧ください。
前提
rules
の各キーワードの紹介の前に、 rules
を扱う上での前提を紹介します。
.gitlab-ci.yml
に定義したrules
は、GitLabのPipelineが作成されたときに評価され、最初に条件にマッチするまで順番に評価されます。条件にマッチすると、該当したJobをPipelineに追加するか除外するか、rules
の設定によって決定されます。- 上述の通り
rules
はPipelineが作成されたときに評価されるので、Pipeline作成後に生成される変数等をrules
の条件に使うことはできません。例えばGitLabはartifacts:reports:dotenv
というキーワードでJob間の変数の受け渡しが可能ですが、あるJobが作成したdotenv変数をrules
に渡して評価する、という使い方はできません。 rules
は only/except という別のキーワードと併用することはできません。only
/except
はrules
以前に使われ、似たような機能を提供するキーワードでしたが、現在はdeprecated扱いとなり将来的には削除される予定です。
rules
は以下のキーワードを使用します。各キーワードは組み合わせて使うことも可能です。
if
changes
exists
when
allow_failure
needs
variables
interruptible
以降は、各キーワードの紹介、及びその他rules
に関する情報の紹介になります。
rules:if
if
キーワードは、主にGitLabのCI/CD変数を対象に、特定のステータスやイベントに合致した場合にのみJobを追加します。よく使われるCI/CD変数には以下のようなものがあります。
CI_PIPELINE_SOURCE
: Pipelineが何によって開始されたか (push イベント、スケジュール実行、ChatOpsなど) 、そのイベント名が挿入されます。イベントに応じてJobの起動条件を分ける場合に使います。CI_COMMIT_BRANCH
: あるブランチからPushされた場合に、コミット先のブランチ名が挿入されます。例えばdevelop
/main
ブランチのような寿命の長いブランチでテスト・デプロイ用のJobを起動するときに使います。CI_COMMIT_TAG
: あるタグからPushされた場合に、コミット先のタグ名が挿入されます。例えばGit Flowを採用するリポジトリでv1.x
のようなタグを対象に、リリース用のドキュメントを作成するようなJobを起動することができます。- ユーザーの定義したカスタム変数 : 例えば
when:manual
キーワードと組み合わせ、手動でJobを実行するときに入力する変数の値に応じて、Jobの実行を制御できます。
rules:if を使ったいくつかの例を載せておきます。
# Merge request作成時に起動する at-merge-request: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" # featureブランチからMerge requestを作成したときに起動する at-merge-request-from-feature-branch: script: echo "Hello, Rules!" rules: - if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ # mainブランチにコミットしたときに起動する at-main-branch-commit: script: echo "Hello, Rules!" rules: - if: $CI_COMMIT_BRANCH == "main" # 何かしらタグがPushされたときに起動する at-tag-push: script: echo "Hello, Rules!" rules: - if: $CI_COMMIT_TAG # 手動実行時、CUSTOM_VARという変数が“sample value”を含むときに起動する at-manual: script: echo "Hello, Rules!" rules: - if: $CUSTOM_VAR == "sample value" when: manual
rules:if
キーワードの追加情報は以下の通りです。
- 後ほど紹介する
when
というキーワードは、Jobの中だけでなくrules
の中に定義することもできます。Jobとrules
の両方にwhen
を設定した場合、rules
に定義されたwhen
の設定が優先されます。 - CI/CD変数との等号に
=~
!~
を使うと、右辺は正規表現として評価されます。前述の例の中では、feature
ブランチからMerge requestを作成した時が該当します。
rules:changes
changes
キーワードは、リポジトリ中の特定のファイルを指定し、そのファイルに変更があるか否かを検出して、Jobの起動を制御します。対象のファイルは rules:changes
配下に記載することもできますが、 rules:changes:paths
配下に記載することもできます。
rules:changes
rules:changes:paths
は、ファイル名を直接指定するほか、ファイル名・拡張子に対するワイルドカードも指定できます。
rules:changes:paths
は rules:changes:compare_to
と組み合わせて使える点でrules:changes
と異なります。rules:changes:compare_to
に参照先のブランチやタグ・コミットを指定し、どの時点のものと比較して変化があったかを指定することもできます。
なお、 rules:changes
は新しいブランチの作成やMerge requestの作成など、ブランチに変更があったときにJobを制御するのが主な用途です。例えばタグのPushやスケジュール・手動実行されたPipelineの場合、ブランチの作成などがなければ思ったようにJobが起動しない場合もあります。そのため該当のPipelineの場合は rules:changes:compare_to
を使って特定のブランチと比較することが推奨されます。
また rules:changes
のみを設定すると、例えば新しいブランチを作成したときも rules:changes
を設定したJobは起動します。これを抑制するには rules:changes
を利用する時に rules:if
など別のキーワードと組み合わせることも良いでしょう。
以下に rules:changes
の例を載せておきます。
# Dockerfile変更時に起動する at-dockerfile-changes: script: echo "Hello, Rules!" rules: - changes: - Dockerfile # Merge request作成時、かつDockerfileに変更があったときに起動する at-merge-request-with-dockerfile-change: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - Dockerfile # dir/ ディレクトリ配下のいずれかのファイルが更新されたときに起動する at-changes-any-files-under-directory: script: echo "Hello, Rules!" rules: - changes: - dir/* # mainブランチと比較してDockerfileに変更があったときに起動する at-changes-compare-to-main-branch: script: echo "Hello, Rules!" rules: - changes: paths: - Dockerfile compare_to: 'main'
rules:changes
キーワードの追加情報は以下の通りです。
- 1つの
rules:changes
につき、最大50のパターン・ファイルパスを定義できます。 rules:changes
はonly:changes
/except:changes
キーワードと同じように働きます。changes
は、設定したパターンやファイルパスと一致するいずれかのファイルが変更されている場合にTrue
となります。
rules:exists
※GitLab 15.6で導入
exists
キーワードは、特定のファイルが存在するか否かを判定し、存在する場合のみJobを起動します。 exists
配下は、ファイル名を直接記載するか、GlobパターンやCI/CD変数を利用できます。
また exists
は changes
と似たような rules:exists:paths
というキーワードが存在します (Self-managed版でのみ利用可能) 。 rules:exists:paths
は rules:exists:project
と合わせて使うことで、どのProjectにファイルが存在するかを指定できます。また rules:exists:ref
と組み合わせることで、検索するタグやブランチ名を指定できます。
以下に rules:exists
の例を載せます。
# Dockerfileが存在するときに起動する at-dockerfile-exists: script: echo "Hello, Rules!" rules: - exists: - Dockerfile # testというProject上にDockerfileが存在するときに起動する at-dockerfile-exists-in-test-project: script: echo "Hello, Rules!" rules: - exists: paths: - Dockerfile project: username/test-project
rules:exists
キーワードの追加情報は以下の通りです。
exists
は、一致するいずれかのファイルが存在する場合にtrue
となります。rules:exists
には、最大50のファイルパス・パターンを指定できます。- パフォーマンス上の理由から、GitLabは
exists
のファイルパスやパターンに対して最大10000回のチェックを行います。また10000回目以降のチェックについては注意が必要で、Projectに10000個以上のファイルがある場合、または10000回以上のチェックを行う場合、exists
は常に条件に一致すると解釈します。
rules:when
when
キーワードは Job / rules
のほか workflow:rules
でも定義可能で、Pipeline全体の中でいつJobを起動するか、実行する条件や遅延時間などを制御します。when
の条件をなにも設定しない場合、デフォルトでは on_success
という値が設定されます。
when
には以下の6つの値のどれかが設定されます。
on_success
: 設定したJobより前の時点で失敗したJobが存在しない、もしくはallow_failure: true
が設定されている場合に起動します。on_failure
: 設定したJobより前の時点で、いずれかのJobが失敗した場合に起動します。never
: 設定より前のJobの状態に依らず、Jobを実行しません。always
: 設定より前のJobの状態に依らず、Jobを実行します。manual
: 手動実行でのみJobを実行します。delayed
: 指定した時間だけJobの実行を遅らせます。start_in
と組み合わせる
rules
の中で when:never
を使う場合は rules:if
など別の条件と組み合わせ、「ある特定の条件下ではJobを実行しない (それ以外の条件では起動する)」よう制御するために使われることが多いようです。
when:manual
はすでに何度か登場していますが、Pipelineの中で手動実行したいJob、例えば本番環境向けのJobだけは人の手で実行したい場合などに利用できます。
以下に when
の例を載せます。
# これ以前のstageのJobが失敗したら起動する at-job-failed: script: echo "Hello, Rules!" rules: - when: on_failure # Merge request時はJobは実行されない job-is-not-started-at-merge-request: script: echo "Hello, Rules!" rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: never # このJobは必ず実行される always: script: echo "Hello, Rules!" rules: - when: always # 30秒後にJobを開始する delayed-30seconds: script: echo "Hello, Rules!" rules: - when: delayed start_in: 30 seconds
rules:allow_failure
allow_failure
キーワードは、allow_failure: true
とすることで、そのJobが失敗してもPipelineを停止しないよう制御します。
allow_failure
は when:manual
と組み合わせることも可能です。 when:manual
は手動実行でJobを起動するよう制御するキーワードですが、 allow_failure: false
と組み合わせることで、手動実行したJobが失敗した場合は後続のJobを実行しないよう設定できます。
なお、allow_failure
はJobレベルでも定義可能ですが、rules
と Jobの両方を定義した場合は rules
のほうが優先されます。
以下に allow_failure
の例を載せます。
# Jobが失敗してもPipelineを続行する pipeline-continues-even-job-failed: script: echo "Hello, Rules!" rules: - allow_failure: true # 手動実行したJobが失敗したらPipelineを停止する pipeline-stops-when-manual-job-failed: script: echo "Hello, Rules!" rules: - when: manual allow_failure: false
rules:needs
※GitLab 16.1で導入
needs
は Jobレベルでも設定可能なキーワードであり、Job間の依存関係を宣言することでJobの実行順を制御できます (Job/rules
両方とも設定した場合はrules
のほうを優先します)。 rules:needs
も同様の機能を持ちますが、 needs
を rules
とJobの両方で定義した場合は rules
のほうが優先されます。GitLabのドキュメントではこれを利用し、開発環境向けと本番環境向けのJobを切り替える方法が紹介されています。
build-dev: stage: build rules: - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH script: echo "Feature branch, so building dev version..." build-prod: stage: build rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH script: echo "Default branch, so building prod version..." tests: stage: test needs: ['build-dev'] rules: - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH needs: ['build-prod'] - when: on_success # Run the job in other cases script: echo "Running tests for dev by default, or prod when default branch..."
※公式ドキュメントより
needs
はJob名のほか []
と設定することも可能です。 []
の場合、依存するJobがないと判定され、即座にJobを開始します。
また needs
は artifacts
optional
というキーワードを利用できます。 artifacts
は needs
を使うJobがArtifactsをダウンロードするか否かを制御します。optional
は optional:true
と設定することで、依存先のJobが存在する場合はその終了を待機し、存在しない場合は無視する動きになります。
rules:variables
rules:variables
は特定条件下での変数を設定するのに利用します。 rules:variables
にはKey:Value形式で変数を定義し、すでに定義されている場合も値を上書きできます。
rules:interruptible
※GitLab 16.10で導入
interruptible
キーワードは、新しいコミットに対して同じref上のPipelineが開始しているときに、Jobの完了前にJobをキャンセルするよう制御します。 rules:interruptible
はJobの interruptible
の値を更新するのに利用します。
なお本機能を有効にするには、GitLab CI/CDの設定から Auto-cancel redundant pipelines (冗長なパイプラインを自動キャンセル)
を有効にするか、 workflow:auto_cancel:on_new_commit
キーワードを使う必要があります。
複数のキーワードを組み合わせる例
rules
は複数のキーワードを組み合わせることで、より複雑な条件を設定できます。複数のキーワードを組み合わせた場合、全ての条件を満たさなければ対象のJobは起動しません。
複数のキーワードを使った例はすでにいくつか紹介済みですが、GitLabの公式ドキュメントにはさらに複雑な条件の例も紹介されています。
docker build: script: docker build -t my-image:$CI_COMMIT_REF_SLUG . rules: - if: $VAR == "string value" changes: # Include the job and set to when:manual if any of the follow paths match a modified file. - Dockerfile - docker/scripts/**/* when: manual allow_failure: true
※公式ドキュメントより
ここでは if
changes
when
allow_failure
を組み合わせており、以下の条件を満たした場合にのみJobが起動します。
- Project中の
Dockerfile
、もしくはdocker/scripts
配下のいずれかのファイルが変更されている - ユーザーの設定した変数
VAR
にstring value
という値が格納されている
上記条件を満たしたとき、このJobは手動実行でのみ起動が可能です。そしてこのJobが失敗してもPipelineは停止せず、後続にJobがある場合はそれらを準に実行します。
また、 rules
で複数条件を組み合わせる場合、 ()
&&
||
などを使うことで1行に収めることも可能です。ただし .gitlab-ci.yml
の可読性は低くなることもあるので、使う場面は選ぶべきかと思います。
job1: script: - echo This rule uses parentheses. rules: - if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE
rulesを再利用する
rules
は !reference
タグや extends
キーワードと合わせることで、他のJobに定義された rules
を再利用できます。 !reference
extends
は rules
だけでなく script
といったJobの設定に対しても利用できます。また !reference
extends
は include
で呼び出した他のテンプレートの内容も参照できます。
さらにGitLab CI/CDのJobは、Job名の先頭に .
をつけることでJobを実行不能にしつつ (hidden job) 、テンプレートとして利用できます。 !reference
extends
はhidden jobと組み合わせることで、テンプレートの設定を再利用することも可能です。
なお rules
を使う場合、使用者は !reference
extends
で呼び出した rules
と通常の rules
を組み合わせて利用できます。 !reference
を使うことで同じrulesを繰り返し定義することなく、 .gitlab-ci.yml
の管理と見通しの向上を期待できます。
ここでは !reference
extends
を使った例をいくつか載せます。
# hidden jobで設定されたrulesを再利用する .default_rules: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual Job1-reference: script: - echo "This is from Job1." rules: - !reference [.default_rules, rules] Job1-extends: script: - echo "This is from Job1." extends: [.default_rules] # 他のJobのrulesを再利用する Job-with-rules: script: - echo "This is from Job-with-rules." rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" Job2-reference: script: - echo "This is from Job2." rules: - !reference [Job-with-rules, rules] Job2-extends: script: - echo "This is from Job2." extends: [Job-with-rules] # 別のテンプレートからrulesを再利用する include: - local: test.yml Job3-reference: script: - echo "This is from Job3." rules: - !reference [.test, rules] Job3-extends: script: - echo "This is from Job3." extends: [.test]
test.yml
.test: script: - echo “This is from .test” rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"
includeとの組み合わせ
include
は .gitlab-ci.yml
からテンプレートファイルを呼び出すのに使うキーワードです。 include
は rules
と組み合わせることで、 rules
によるJobの実行条件を上書きできます。これにより、他テンプレートの実行条件を上書きし、より柔軟にJobの再利用と実行を制御できます。
なお include
と組み合わせ可能なのは rules:if
rules:exists
rules:changes
の3つです。
いくつかの例を載せます。
include: # mainブランチにコミットしたときにmain.ymlを使用する - local: main.yml rules: - if: $CI_COMMIT_BRANCH == "main" # exception-file.mdが存在するときはexcept.ymlを使用しない - local: except.yml rules: - exists: - exception-file.md when: never # Dockerfileに変更があったときはdocker.ymlを使用する - local: docker.yml rules: - changes: - Dockerfile
include
と組み合わせたときの補足情報を記載します。
include
とrules:exists
を組み合わせたとき、異なるProjectからinclude
で参照した場合は注意が必要です。include
で参照したテンプレートファイル上でrules:exists
を使用した場合、GitLabはPipelineを実行したProjectやrefではなく、include
で指定した別のProject情を検索するよう動きます。これを避けるにはrules:exists
でなくrules:exists:paths
rules:exists:project
を使用する必要があります。
さいごに
今回はGitLabの rules
について紹介しました。 本記事で rules
の使い方については一通りさらったんじゃないかと思いますので、rules
でどんなことができるか、自分たちのやりたいことを rules
で実現できるか等知りたい方は参考にしていただければと思います。
最後に、弊社はGitLabオープンパートナー認定を受けております。 また以下のようにCI/CDの導入を支援するサービスも行っているので、何かご相談したいことがあればお気軽にご連絡ください。