CircleCI の config.yml を分割できる(というかマージできる)Orb を作ったので
今日は、その Orb の裏側の「CUE で YAML をマージするときの動き」を確認してみるー!
CUE で YAML をマージする
こんなファイルがあるときに
foo.yml
sample: foo: "foo value"
bar.yml
sample: bar: "bar value"
こう書くと CUE で YAML をマージすることができる
❯ cue export foo.yml bar.yml --out yaml sample: foo: foo value bar: bar value
---
を使って、↓こう書いても同じなので、今日の記事の中ではこの形式で書こうと思う
foobar.yml
sample: foo: "foo value" --- sample: bar: "bar value"
❯ cue export foobar.yml --out yaml sample: foo: foo value bar: bar value
どうマージされるのか?
とてもシンプルで、これだけだった↓
注意:自分の頭の中の理解を自分の言葉で説明しているので、YAMLのLeafまでのパス: 値
とか Leaf
は、正式に定義されている言葉じゃないです
YAMLのLeafまでのパス?
こんな YAML があったときに
a: 3 b: c: "foo"
こんな風に考えることができる
a: 3 b: c: "foo"
この、a:
や b: c:
のことを YAMLのLeafまでのパス
と、この記事の中では呼ぶことにする。3
や "foo"
がそのLeafの値。
パスが違う場合はマージされる
パスが違う場合はマージされる
a: 1 --- b: 2
❯ cue export foobar.yml --out yaml a: 1 b: 2
階層構造を持っている場合でもパスがそれぞれ違うなら問題なくマージされる
a: b: "bbb" c: "ccc" --- a: d: "ddd"
❯ cue export foobar.yml --out yaml a: b: bbb d: ddd c: ccc
値がひとつに決まらなければエラーになる
複数の YAML に同じパスの Leaf が定義してあって、それらの値が異なる場合はエラーになる
a: 1 --- a: 2
❯ cue export foobar.yml --out yaml a: conflicting values 2 and 1: ./foobar.yml:1:5 ./foobar.yml:3:5
a: 1
と a: 2
で値がひとつに決まらないからエラーになる
階層構造を持っている場合も同じ
a: b: "bbb" --- a: b: "ddd"
❯ cue export foobar.yml --out yaml a.b: conflicting values "ddd" and "bbb": ./foobar.yml:2:7 ./foobar.yml:5:7
a: b:
の値がひとつに決まらなくてエラーになる
値がひとつに決まる場合はエラーにはならない
じゃあ、値がひとつに決まる場合はどうなるの?というと、ひとつの Leaf として出力される
a: 1 --- a: 1
❯ cue export foobar.yml --out yaml a: 1
階層構造の場合も同じ
a: b: "bbb" --- a: b: "bbb"
❯ cue export foobar.yml --out yaml a: b: bbb
値がシーケンスの場合
これまではスカラー値だけを見てきたけど、シーケンス値の場合でも同じで、値が異なる場合にはエラーになるし
a: - 1 - 2 --- a: - 1
❯ cue export foobar.yml --out yaml a: incompatible list lengths (1 and 2)
同じ値ならエラーにはならない
a: - 1 - 2 --- a: - 1 - 2
❯ cue export foobar.yml --out yaml a: - 1 - 2
シーケンス値を縦に書くと少し分かりにくいので、こう書くと、値ということが分かりやすいかもしれない
a: [1, 2] --- a: [1]
❯ cue export foobar.yml --out yaml a: incompatible list lengths (1 and 2)
もちろん、シーケンスが別の Leaf の値であればマージされる
a: - 1 - 2 --- b: - 1
❯ cue export foobar.yml --out yaml a: - 1 - 2 b: - 1
つまり「複数のファイルで、同じパスのLeafにそれぞれ異なるシーケンス値を定義していても、それらのシーケンスが結合されることはなく、エラーになる」
マージのルール(再掲)
ということで、とてもシンプルだった:
- YAML を
YAMLのLeafまでのパス: 値
と捉えたときに、それらのすべてのLeafが任意の順番でマージされる - ただし、そのLeafに対するスカラー値またはシーケンス値がひとつに決まらなければエラーになる
CircleCI の設定ファイル的にはどうなの?
CircleCI の設定ファイルを、最初に書いた Orb でマージするときにどうなるかというと、例えば↓こういうファイルがあるときに
version: 2.1 jobs: service1-say-hello: docker: - image: cimg/base:stable steps: - checkout - run: name: "Say hello" command: "echo Hello, World!1" workflows: service1-say-hello-workflow: when: << pipeline.parameters.build-service1 >> jobs: - service1-say-hello
version: 2.1 jobs: service2-say-hello: docker: - image: cimg/base:stable steps: - checkout - run: name: "Say hello" command: "echo Hello, World!2" workflows: service2-say-hello-workflow: when: << pipeline.parameters.build-service2 >> jobs: - service2-say-hello
version:
は値がひとつに決まるのでその値が使用されるjobs: service1-say-hello:
とjobs: service2-say-hello:
は別のノードになるので、それぞれがjobs
の下にぶらさがるworkflows
も同様
だからマージすると、こうなる
version: 2.1 jobs: service1-say-hello: docker: - image: cimg/base:stable steps: - checkout - run: name: Say hello command: echo Hello, World!1 service2-say-hello: docker: - image: cimg/base:stable steps: - checkout - run: name: Say hello command: echo Hello, World!2 workflows: service1-say-hello-workflow: when: << pipeline.parameters.build-service1 >> jobs: - service1-say-hello service2-say-hello-workflow: when: << pipeline.parameters.build-service2 >> jobs: - service2-say-hello
同じ名前のジョブやワークフローを書いてしまった場合は?
例えば、間違って複数のファイルに同じ名前のワークフロー定義を書いてしまった場合は、大体の場合は jobs
などの値が異なってエラーになると思う
service-say-hello-workflow: jobs: - service-say-hello --- service-say-hello-workflow: jobs: - service-say-hello # jobs の内容が違う - notify-something
jobs のシーケンス値が異なるので、エラーになる
❯ cue export foobar.yml --out yaml
"service-say-hello-workflow".jobs: incompatible list lengths (1 and 2)
ただ、Leafの内容が全く同じで、別の要素が追加されている場合は、マージされるので注意が必要。↓こういう風に書いてしまった場合
service-say-hello-workflow: jobs: - service-say-hello --- service-say-hello-workflow: # 要素を追加 when: << pipeline.parameters.build-service1 >> jobs: - service-say-hello
マージされてしまう
❯ cue export foobar.yml --out yaml service-say-hello-workflow: when: << pipeline.parameters.build-service1 >> jobs: - service-say-hello
CircleCI の設定ファイルを分割するときの基本的な方針
基本的な方針としては、各ファイルごとにネームスペース的なプレフィックスを付けるようにして、ジョブやワークフローの名前が重複しないようにするのが良さそう
どういう仕様で、こんな動きをするの?
CUE の仕様について触れる必要があるので、また次回にでもー!
2022-07-25 後編書いた