背景
CircleCIを使っているのですが
- 多数のリポジトリを管理している
- config.ymlが肥大化している。けれど殆どは似たような記述
といった背景がある上で、新しいjobやworkflowを各リポジトリに適用していく際に
- コピペ漏れが起きやすい
- レビューがつらい
といった課題を持っていました。
そんな時にCircleCIがDynamic Configという新機能を出していたので、これを使って
を分割して管理することで、
- 前者はファイル自体をコピペすれば良い
- レビューは後者だけ注視すれば良い
という対応をします。
環境
- CircleCI 2.1
- circleci/continuation 0.2.0
- yq 4.24.2
Dynamic Configとは
Dynamic Configを使うと動的にconfigファイルを用意できるので、以下のようなことができます。
- Workflow毎にyamlを分割して管理できる
- testやbuild、deployなどを分割して利用できる
yq
コマンドなどで分割したyamlを結合して1つのconfig.yamlとして扱える- ビルド対象をpathベースで指定できる
- 変更のあるサービスだけビルドする。モノリポでの差分ビルドを実現できる
Github Actionsなら元々できてましたが、CircleCIでも同様なことができるようになりました。
今回はyq
を使ったやり方を紹介します。
フロー
Dynamic Configでは以下の図のようなフローを取ります。
ref: api-preview-docs/setup-workflows.md at master · CircleCI-Public/api-preview-docs · GitHub
つまり最初に動的にconfigを設定するSetup処理があり、その後で設定したconfigのワークフローを実行する流れになります。
導入手順
CircleCI WebUI
CircleCIのWebコンソールで
Project Settings
→Advanced Settings
にてDynamic Configを有効化します。
Setup workflowの用意
.circleci/config.yml
をSetup処理に書き換えます。
version: 2.1 # Dynamic Configuration setup: true orbs: continuation: circleci/continuation@0.2.0 jobs: some_job: docker: - image: cimg/base:stable steps: - checkout - run: name: "Generate yaml" command: | # 動的にconfigファイルを用意する処理 - continuation/continue: configuration_path: # 用意したconfigファイル workflows: setup-workflow: jobs: - some_job
ポイント
基本的にworkflowの書き方自体は変わりませんが、以下の点が違います。
setup: true
を付ける- circleci/continuation Orbを使う
configuration_path
に動的にconfigファイルを指定
動的に用意するconfig
例えばビルド処理は同じだけれど、リポジトリによってデプロイ先はGCPやAWSと異なるみたいな場合に、ビルド処理とデプロイ処理を分割して管理します。
build.yml
version: 2.1 jobs: build: docker: - image: cimg/go:1.18 auth: username: $DOCKERHUB_USER password: $DOCKERHUB_ACCESS_TOKEN steps: - checkout - setup_remote_docker: version: 20.10.11 docker_layer_caching: true - run: name: Login command: docker login -u $DOCKERHUB_USER -p $DOCKERHUB_ACCESS_TOKEN - run: name: Push command: make push
deploy.yml
version: 2.1 jobs: deploy: docker: - image: cimg/go:1.18 auth: username: $DOCKERHUB_USER password: $DOCKERHUB_ACCESS_TOKEN steps: - checkout - run: name: Deploy command: make deploy workflows: build-and-deploy: jobs: - build: context: - DockerHub - deploy: context: - DockerHub requires: - build filters: branches: only: - /.*/
yqで結合
yq
というコマンドで結合することができます。cimg/base
にはデフォルトでインストールされています。
$ yq eval-all '. as $item ireduce ({}; . * $item )' common.yml unique.yml
ref: Combine multiple sources (i.e. reduce) · Issue #674 · mikefarah/yq · GitHub
先程の2つのファイルを結合すると以下のようになります。
version: 2.1 jobs: build: docker: - image: cimg/go:1.18 auth: username: $DOCKERHUB_USER password: $DOCKERHUB_ACCESS_TOKEN steps: - checkout - setup_remote_docker: version: 20.10.11 docker_layer_caching: true - run: name: Login command: docker login -u $DOCKERHUB_USER -p $DOCKERHUB_ACCESS_TOKEN - run: name: Push command: make push deploy: docker: - image: cimg/go:1.18 auth: username: $DOCKERHUB_USER password: $DOCKERHUB_ACCESS_TOKEN steps: - checkout - run: name: Deploy command: make deploy workflows: build-and-deploy: jobs: - build: context: - DockerHub - deploy: context: - DockerHub requires: - build filters: branches: only: - /.*/
config.yml
は以下になります。
version: 2.1 # Dynamic Configuration setup: true orbs: continuation: circleci/continuation@0.2.0 jobs: yq: docker: - image: cimg/base:stable steps: - checkout - run: name: "Generate yaml" command: | yq eval-all '. as $item ireduce ({}; . * $item )' .circleci/build.yml .circleci/deploy.yml > .circleci/merged.yml - continuation/continue: configuration_path: .circleci/merged.yml workflows: setup-workflow: jobs: - yq
動作確認
CircleCIで実行するとSetup処理の後で動的に用意したconfigが実行されます。
https://app.circleci.com/pipelines/github/jun06t/circleci-dynamic-config?branch=main&filter=all
その他
tag filterはSetup処理でも必要
CircleCI 2.0 でworkflowを使ったtagからのデプロイ - Carpe Diemでも説明したようにtagフィルターは依存するjobにも同じtagフィルタを付ける必要があります。
なんとDynamic Configのjobにも付けないと発火しないことが分かりましたので付けて下さい。
workflows: setup-workflow: jobs: - yq: context: - DOCKER filters: tags: only: - /.*/
サンプルコード
今回のサンプルコードはこちら
まとめ
Dynamic Configを使ってCIのconfigファイルを分割して管理できるようになりました。