13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GitLabAdvent Calendar 2022

Day 11

GitLab CI/CD の rules を部品化する

Last updated at Posted at 2022-12-11

この記事は GitLab Advent Calendar 2022 の 11 日目の記事です。

3行で

  • ジョブを 処理 と 起動条件 の2要素に分解して考える。
  • 起動条件を実装する rules 部分だけを hidden ジョブとして外部化し、それを継承してジョブを作成する。
  • hidden ジョブを定義した yaml ファイルを持つリポジトリを include する形式にすることで、部品として管理できるようになる。

rules について

.gitlab-ci.yml には、パイプラインにジョブを含めるか否かを制御するための rules キーワード があります。リポジトリへのプッシュ時やタグ作成時などのタイミングで、パイプラインを作成する際に rules で指定した条件が評価され、パイプラインで実行するジョブが決まります。

GitLab 11 系までは onlyexcept キーワードを使ってジョブを制御していましたが、GitLab 12 系(12.3)以降では、rules の利用が推奨されています。

rules 配下には、次のルールの配列を定義できます。

ルール 説明
if 指定した条件式に一致するか?
changes 特定のファイルの変更があるか?
exists 特定のファイルがリポジトリに存在するか?
allow_failure ジョブが追加された場合、パイプラインを止めずにジョブの失敗を許す。
variables 条件に合致した場合、ジョブに変数を追加する。
when ジョブをどのタイミングで実行するか制御する。1

この中でも、使用頻度が高いのは if かと思います。特定のブランチへpushされた場合や、特定の名前のタグが作成された場合、特定の変数が設定された場合等にジョブを起動するように制御することができます。

GitLab の公式ドキュメントにある rules:if の例 は次のとおりです。

rules:if の例
job:
  script: echo "Hello, Rules!"
  rules:
    - if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH
      when: never
    - if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/
      when: manual
      allow_failure: true
    - if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME

この job ジョブの条件は次のようになります。

  • マージリクエストのマージ元ブランチ名が feature から始まり、マージ先ブランチがリポジトリのデフォルトブランチの場合は、ジョブを追加しない。
  • マージリクエストのマージ元ブランチ名が feature から始まる場合は、手動起動のジョブを追加し、ジョブの失敗を許す。
  • マージリクエストのマージ元ブランチ名がある場合(=マージリクエストにより作成されたパイプラインの場合)は、ジョブを追加する。

このように、複数の条件式を組み合わせて、ジョブの起動条件を細かく指定することができます。

ジョブの処理と起動条件を分離する

ジョブの起動条件は、開発プロセスの中でのブランチやマージリクエスト、タグの使われ方に密接に関連しています。したがって、システム全体の開発プロセスを定め、それに合わせて rules を実装していくことが多いと思います。つまり、一つのプロジェクト(≠ GitLab の Project)の中で複数のリポジトリを持つ場合にも、同じようなルールを使って CI/CD を構成していくことになります。

通常、GitLab ではリポジトリ単位の .gitlab-ci.yml でパイプラインを構成するため、複数のリポジトリを持つ場合はそれぞれ別の .gitlab-ci.yml が作られることになりますが、先に述べたとおり、ルールの部分については実装が重複しがちです。

そこで、ジョブの構成要素を 処理起動条件 の2つに分離して考えてみます。例えば、feature ブランチへのpush時に、テスト環境へデプロイするジョブ は次の2つの要素に分離します。

  • 処理:テスト環境へデプロイする
  • 条件:feature ブランチへのpush時にジョブを起動する

処理の部分は、開発する言語やリポジトリの用途によって変わるものであり、カスタマイズが必要な部分です。一方で、条件の部分は他の処理の場合にも使い回しが利くものです。例えば、feature ブランチへのpush時にビルドするジョブ であれば、条件については全く同じものを利用できます。このように、処理と条件を分離することで、条件部分を部品的に使い回すことができ、メンテナンス性を高めることができます。

処理と条件を分離した場合の、ジョブの構成のイメージを図示するとこうなります。

.gitlab-ci.ymlextends キーワード2を使ってジョブを継承できるため、rules だけを定義した hidden ジョブ3 を用意し、処理を記載するジョブ側でそれを継承することで、上記のような構成を実現できます。

継承を使った処理と条件の分離の例を次に示します。

.gitlab-ci.yml
# . から始まるジョブは hidden ジョブであり、ジョブとして評価されない
.rules-push-feature:
  rules:
    - if: "$CI_COMMIT_BRANCH =~ /^feature/"

stages:
  - build

job:
  stage: build
  # hidden ジョブを継承して rules 部分を取り込む
  extends:
    - .rules-push-feature
  script:
    - echo "job-feature"

rules の部品化

さて、上のイメージ図を見ても分かるとおり、条件部分を複数のリポジトリ間で共有する必要があります。これは include キーワード4を使って実現できます。

まず、rules 用の hidden ジョブを実装した yaml ファイルを別のリポジトリに切り出します。そして、パイプラインを動かす側のリポジトリでは、その yaml ファイルを include で取り込みます。すると、extends で hidden ジョブを継承できるようになるので、rules の部分は 継承元をそのまま利用し、処理など必要な箇所のみを実装するようにします。
このように、rules を部品のように扱うことで、ジョブの起動条件だけをプロジェクト全体で一律に管理できるようになります。ブランチ名やタグ名のルールに修正が入る場合には部品を修正するだけで済むので、修正コストも小さくすることができます。

先ほどのイメージ図を少し修正し、わかりやすくしましょう。

.gitlab-ci.yml の構成例は以下のとおりです。

部品用リポジトリの rules.yml
.rules-push-feature:
  rules:
    - if: "$CI_COMMIT_BRANCH =~ /^feature/"

同じ GitLab インスタンス 内のプロジェクトから参照する場合は include:project を使うことで読み込むことができます。

パイプラインを動かすリポジトリの .gitlab-ci.yml
include:
  project: <some-group>/<project-name>
  file: rules.yml
  
stages:
  - build

job-feature:
  stage: build
  extends:
    - .rules-push-feature
  script:
    - echo "job-feature"

上記のような構成で、rules 以外の部分も部品化することができ、共通的な処理やvariableを一括で管理することができたりします。小規模なプロジェクトで小回りが効くケースでは、このような部品化のメリットは少ないかもしれませんが、中〜大規模なプロジェクトでは一度採用を検討してみてもいいかもしれません。

まとめ

ジョブを 処理と起動条件に分離して考え、rules 部分を部品化する方法について説明しました。

rules は少し癖のある書き方が必要なため、有識者がまとめて実装し、その部品を使うだけにしたほうが安心かと思います。5
本記事で紹介したやり方を参考に、皆さんのプロジェクトでも includeextends を活用してみてください。

  1. デフォルトは when: on_success であり、前のステージのすべてのジョブについて、成功するか あるいは allow_failure: true の場合に ジョブが実行されます。

  2. https://docs.gitlab.com/ee/ci/yaml/#extends

  3. https://docs.gitlab.com/ee/ci/jobs/index.html#hide-jobs

  4. https://docs.gitlab.com/ee/ci/yaml/#includeproject

  5. 例えば、$CI_PIPELINE_SOURCE == "push" という条件はブランチまたはタグがpushされたときにジョブが稼働します。ブランチだけを対象とする場合は $CI_PIPELINE_SOURCE == "branch" ではなく、$CI_COMMIT_BRANCH を使う必要があります。しかし、$CI_COMMIT_BRANCH は スケジュールビルドでも可動をするため、いわゆる「コミット」を契機として起動するわけではありません。厳密に「ブランチへのpush時だけ」にするには、複合条件を書く必要があります。

13
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?