こんにちは、テクマトリックスの監物です。

MonorepoのようなリポジトリでCIを構築する場合に、コード変更の度に変更に関連のないモジュールまでビルド・テストされてしまうのは、フィードバックが遅れるため避けたいものです。そこで、Jenkinsでコード変更に関連するモジュールのみビルド・テストする方法をご紹介します。

Monorepoとは

単一のリポジトリで複数のプロジェクトを管理することを指します。例えば、2つの関連するモジュールがあった場合、それらを単一のリポジトリでディレクトリを切って管理することをいいます。反対に単一のリポジトリで1つのモジュールを管理することをPolyrepo(Multirepo)といいます。

. (Monorepo)              . (Polyrepo)
├── module_A             └── module_A
└── module_B
                          . (Polyrepo)
                          └── module_B

関連するモジュールを単一のリポジトリで管理することにより、モジュール間での変更や共通するもの(ライブラリやツールなど)の管理が容易になるなどのメリットがあります。詳細については、Monorepoを採用しているBabelのリポジトリのドキュメントが参考になります。
https://github.com/babel/babel/blob/main/doc/design/monorepo.md

一方で、MonorepoでCIを構築しようとすると工夫が必要です。なぜなら、基本的にJenkinsではリポジトリ毎に実行するワークフローを定義します。リポジトリ内のモジュール毎にワークフローを定義するわけではないため、Monorepoのような複数モジュールを管理しているリポジトリの場合、特定のモジュールに変更を加えた時に、変更とは関連しない他のモジュールの余計なビルド・テストが実行されてしまいます。

Monorepo管理ツール(Nx, Lernaなど)を利用すれば、コード変更に影響がある部分のみをビルド・テストでき、かつキャッシュによる高速化も見込めるようなので良さそうですが、今回はJenkinsのみで簡単に実行時間を短縮する方法を試してみます。

MonorepoでJenkinsを使ってみた

単一のリポジトリで2つのモジュール(module_A, module_B)が管理されているMonorepoに対してCIを構築します。module_Aにコード変更があった場合は、module_Aのみをビルドする環境です。

. (Monorepo)
├── module_A
└── module_B

Jenkinsfileの作成

変更のあったモジュールのみをビルドするために、パイプラインのwhenディレクティブを利用します。whenはstageを実行するか否かを制御するもので条件と一緒に定義します。下記の例だと、条件としてchangeset “module/**” が指定されているため、リポジトリのmoduleディレクトリ以下のファイルに変更があった時のみstageを実行します。

stage('Stage') {
  when {
    changeset "module/**"
  }
  //...
}

これらを利用してmodule_Aディレクトリに変更があった場合はmodule_Aのみをビルド、module_Bディレクトリに変更があった場合はmodule_Bのみをビルドするパイプラインを作成します。

pipeline {
  agent none
  stages {
    stage('Build Module A') {
      agent any
      when {
        changeset "module_A/**"
      }
      steps {
        dir('module_A') {
          bat 'build.bat'
        }
      }
    }
    stage('Build Module B') {
      agent any
      when {
        changeset "module_B/**"
      }
      steps {
        dir('module_B') {
          bat 'build.bat'
        }
      }
    }
  }
}

Jenkinsfileの作成が完了したら、JenkinsfileをMonorepoのルートディレクトリに配置します。

Jenkinsジョブの作成と実行

Jennkinsのダッシュボードにアクセスして、パイプラインジョブを作成します。[新規ジョブ作成]からMultibranch Pipelineを選択して作成します。

Multibranch Pipelineジョブを作成すると、対象のリポジトリがスキャンされ自動でジョブが作成されます。初回の実行では、変更ログが空のため何も実行されませんが、module_Aディレクトリ以下のファイルに変更を加えて再度ジョブを実行するとmodule_Aのビルドだけが実行されます。

これで、コード変更に関連するモジュールのみビルドする環境の完成です。

Module Aのビルドのみ実行
module_A/src/main/java/example/Hoge.javaのみ変更

Tips

日本語のファイル名のファイルに変更を加えてもビルドが実行されない場合は、Jenkinsから変更履歴を確認してください。以下は、module_A/日本語.txtを変更した時の変更履歴ですが、このようにファイル名が文字化けしている場合は、changesetが機能しませんでした。

\346\227\245\346\234\254\350\252\236.txt に文字化け

Jenkins環境のGitの設定を下記のコマンドにより変更することで文字化けが解消され、changesetが機能するようになります。

git config --global core.quotepath false

まとめ

Jenkinsでコード変更に関連するモジュールのみワークフローを実行する方法をご紹介しました。これにより、コード変更に関連しない余計な実行時間を削減できますので、試してみてはいかがでしょうか。