Carpe Diem

備忘録

CircleCIのDynamic Configで差分ビルド

概要

CircleCIのDynamic Configでconfig.ymlを分割管理する - Carpe Diem

↑ではファイルを分割管理する方法を説明しました。

今回はpath filteringを使って差分ビルド(変更のあるディレクトリのみビルド)する方法を説明します。

環境

  • CircleCI 2.1
  • circleci/path-filtering 0.1.2

以下のようなモノリポ環境だったとします。

$ tree
.
├── LICENSE
├── README.md
├── go
│   ├── pkg
│   │   └── uuid
│   │       └── uuid.go
│   └── services
│       ├── service1
│       │   └── cmd
│       │       └── main.go
│       └── service2
│           └── cmd
│               └── main.go
├── go.mod
└── go.sum

差分ビルド要件

今回は

  • service1に変更があればservice1のみビルド
  • service2に変更があればservice2のみビルド
  • service1, service2が依存するpkgに変更があれば両方ビルド

という差分ビルドを実現したいとします。

導入手順

Setup workflowの用意

version: 2.1

setup: true

orbs:
  path-filtering: circleci/path-filtering@0.1.2

workflows:
  always-run:
    jobs:
      - path-filtering/filter:
          name: check-updated-files
          # <regex path-to-test> <parameter-to-set> <value-of-pipeline-parameter>
          mapping: |
            go/services/service1/.* run-build-service1-job true
            go/services/service2/.* run-build-service2-job true
            go/pkg/.* run-build-pkg-job true
          base-revision: main
          config-path: .circleci/continue_config.yml

ポイント

ポイントは以下です。

  • mappingで「pathの正規表現」「次のconfigに渡すパラメータ名」「セットするパラメータの値」を書く
  • base-revisionに比較するベースとなるブランチを指定する
  • config-pathに次に実行するconfigファイル

continue_config.yml

pipeline parameters

「次のconfigに渡すパラメータ名」は予め以下のように定義しておく必要があります。

parameters:
  run-build-service1-job:
    type: boolean
    default: false
  run-build-service2-job:
    type: boolean
    default: false
  run-build-pkg-job:
    type: boolean
    default: false

workflows

そしてworkflowsではwhenで上記のパイプラインパラメータを使用します。

workflows:
  version: 2
  noop:
    when:
      and:
        - not: << pipeline.parameters.run-build-service1-job >>
        - not: << pipeline.parameters.run-build-service2-job >>
        - not: << pipeline.parameters.run-build-pkg-job >>
    jobs:
      - noop
  service1:
    when:
      or:
        - << pipeline.parameters.run-build-service1-job >>
        - << pipeline.parameters.run-build-pkg-job >>
    jobs:
      - build:
          target: service1
  service2:
    when:
      or:
        - << pipeline.parameters.run-build-service2-job >>
        - << pipeline.parameters.run-build-pkg-job >>
    jobs:
      - build:
          target: service2

ポイント

  • whenでパイプラインパラメータを使って発火させる
  • or条件、and条件をうまく組み合わせる
  • 1つのjobも発火しないとエラーになるので何もしないjobを用意しておく

jobs

jobs:
  noop:
    executor: default
    steps:
      - run:
          name: "No operation"
          command: echo "No operation"

  build:
    executor: default
    parameters:
      target:
        type: string
    steps:
      - checkout
      - setup_remote_docker:
          version: 20.10.11
          docker_layer_caching: true
      - run:
          name: Build
          command: |
            cd go/services/<< parameters.target >>
            go build cmd/main.go

jobsの方はこんな感じです。job内のparametersを使ってうまく共通化すると良いです。

動作確認

それでは動作確認してみます。

service1だけ変更した場合

以下のようにservice1だけビルドされます。

f:id:quoll00:20220415001702j:plain

https://app.circleci.com/pipelines/github/jun06t/circleci-dynamic-config?branch=test-diff&filter=all

共通なpkgが変更された場合

service1、service2の両方がビルドされます。

f:id:quoll00:20220415001808j:plain

https://app.circleci.com/pipelines/github/jun06t/circleci-dynamic-config?branch=update-pkg&filter=all

サンプルコード

今回のサンプルコードはこちら

github.com

まとめ

CircleCIで手軽に差分ビルドを実現することができました。
Bazelのようなツールを使わないモノリポ環境ではぜひ導入したいですね。

参考