CircleCI の設定ファイルを分割して CUE で合成してみたら割と簡単で便利そう

ぼーっと CUE のドキュメントを読みながら

CUE という設定用の言語・・・と呼んで良いのかな?のドキュメントを読みながら

「これ、いろんな機能があるけど、それは置いといて、YAML の合成が簡単にできるのでは?・・・とすると、CircleCI の設定を簡単に分割できて面白いかもなぁ」

と思ったので試してみた。わりとアリかもしれない

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

github.com

どういう感じ?

こんな感じに適当に分割した設定を

❯ tree .circleci/configs 
.circleci/configs
├── header.yml
├── job1.yml
├── sample
│   ├── job2.yml
│   └── mixed\ sample.yml
├── workflow1.yml
└── workflow2.yml

1 directory, 6 files

CircleCI の実行時に合成して、ワークフローを動かした。結果はこんな感じ:

ディレクトリ構造とかには特に制限はない

内容は適当にこんな感じで、バージョン部分だけ分けてみたり

header.yml

version: 2.1

ジョブを複数のファイルに分けて定義してみたり

job1.yml

jobs:
  say-hello:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Say hello"
          command: "echo Hello, World!"

job2.yml

jobs:
  say-hello2:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Say hello2"
          command: "echo Hello, World2!"

ワークフローを複数のファイルに分けてみたり

workflow1.yml

workflows:
  say-hello-workflow:
    jobs:
      - say-hello

workflow2.yml

workflows:
  say-hello-workflow2:
    jobs:
      - say-hello
      - say-hello2:
          requires:
            - say-hello

ジョブとワークフローを混ぜて定義したファイルを作ってみたりして

mixed sample.yml

jobs:
  say-hello3:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: "Say hello3"
          command: "echo Hello, World3!"

workflows:
  say-hello-workflow3:
    jobs:
      - say-hello
      - say-hello2:
          requires:
            - say-hello
      - say-hello3:
          requires:
            - say-hello

ファイル名にスペース入れてみたり、サブフォルダの中に入れてみたりした

それを CUE で合成

この分割したファイルを CUE で合成するんだけど、なかなか強力。たとえば job1.ymljob2.yml を合成するとこんな感じになる

❯ cue export job1.yml sample/job2.yml --out yaml
jobs:
  say-hello:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Say hello
          command: echo Hello, World!
  say-hello2:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Say hello2
          command: echo Hello, World2!

内部的には、YAML から CUE に変換して、合成して、それを YAML で出力してるのかな。CUE の合成のルールが面白いからぜひドキュメント読んでみてー

CUE には型があって、一度決定した値は上書きできないので、同じ名前のジョブやワークフローを定義してたらエラーになるのも便利そう

実際の合成部分

config ディレクトリの中の YAML ファイルを cue export に渡して生成

cd .circleci/configs
find . -type f -name "*.yml" | awk '{printf "\"%s\" ", $0}' \
| xargs -0 -I {} sh -c 'cue export {} --out yaml' | tee ../generated_config.yml

こういうの得意じゃないから、ここにいちばん時間がかかった気がするw

もっといい書き方あったら教えて欲しいです

CircleCI のダイナミックコンフィグ

さて、その合成をするのに、CircleCI のダイナミックコンフィグ機能を使う

ダイナミックコンフィグは、設定ファイルを動的に生成する機能

config.yml はこう

version: 2.1

# ダイナミックコンフィグのセットアップ用の設定だよーってしるし
setup: true

# 「生成した設定ファイルを使って起動してー!」てのに、この Orb を使う
orbs:
  continuation: circleci/continuation@0.3.1

jobs:
  build:
    docker:
      # CUE を入れたイメージを作っといた
      - image: bufferings/cimg-cue
    steps:
      - checkout
      # さっき書いたやつ
      - run:
          name: Generate config
          command: |
            cd .circleci/configs
            find . -type f -name "*.yml" | awk '{printf "\"%s\" ", $0}' \
            | xargs -0 -I {} sh -c 'cue export {} --out yaml' | tee ../generated_config.yml
      # 生成した設定ファイルを使ってパイプラインを実行
      - continuation/continue:
          configuration_path: .circleci/generated_config.yml

これが実行されると、分割された YAML を CUE で合成して generated_config.yml が生成されて、その生成された設定ファイルで CircleCI のパイプラインが実行される

ちなみに generated_config.yml の内容はこう

version: 2.1
jobs:
  say-hello:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Say hello
          command: echo Hello, World!
  say-hello2:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Say hello2
          command: echo Hello, World2!
  say-hello3:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Say hello3
          command: echo Hello, World3!
workflows:
  say-hello-workflow:
    jobs:
      - say-hello
  say-hello-workflow2:
    jobs:
      - say-hello
      - say-hello2:
          requires:
            - say-hello
  say-hello-workflow3:
    jobs:
      - say-hello
      - say-hello2:
          requires:
            - say-hello
      - say-hello3:
          requires:
            - say-hello

CUE かしこいー

ということで

CircleCI の設定ファイルを分割して CUE で合成してみたら割と簡単で便利そうでしたー

設定ファイルが大きくなってメンテしづらいなぁってときに便利かもしれない!

試してみたい人は、config.yml をそのままコピーして、configs ディレクトリの中に、分割した設定ファイルを入れたら遊べると思うー!

参考にさせてもらいました!

なるほどー!と思いながら読ませてもらいました!ありがとうございましたー!

bufferings/cimg-cue

cimg/gocue を足しただけの Docker Image:

https://github.com/bufferings/cimg-cue/blob/main/Dockerfile

今日も宣伝

ぜひ遊びにきてくださいー!

bufferings.hatenablog.com