まだプレビューですが CircleCI の新機能が 2.1 として使えるようになっているという話と、2.1 の新機能で yaml の設定を DRY にする方法を紹介します。
長いので目次。
2.0 までの DRY な yaml を書く方法
2.0 までは CircleCI の yaml の設定を DRY にしようとすると yaml のエイリアスを使う必要がありました。
例えば、node.js のプロジェクトで node の複数バージョンでビルドする設定をエイリアスで DRY にしようとすると次のようになります。
node-base: &node-base steps: - checkout - restore_cache: keys: - v1-npm-deps-{{ checksum "package-lock.json" }} - v1-npm-deps- - run: npm install - run: npm test - save_cache: key: v1-npm-deps-{{ checksum "package-lock.json" }} paths: - node_modules version: 2 jobs: test-v8: docker: - image: circleci/node:8 <<: *node-base test-v10: docker: - image: circleci/node:10 <<: *node-base workflows: version: 2 build: jobs: - test-v8 - test-v10
ジョブの内容について詳細な説明は省きますが、&node-base
の部分でアンカーを作成し、<<: *node-base
の部分でオブジェクト(正確にはマッピング)のキーとしてマージすることで、共通部分の steps
を DRY にしています。
エイリアスでも結構 DRY に書けるのですが、yaml の仕様上、配列(正確にはシーケンス)のマージができない、共通部分でも部分的に変数化してパラメータとして受け取るようなことができないなどがあり、共通化したい内容によっては困ることがありました。(正直 yaml の仕様にそこまで詳しくないので、全然違ってたら教えてください)
このあたりの問題が 2.1 では解消されています。
2.1 を有効にする方法
2.1 の新機能は現時点でオプトインのプレビュー版となっています。有効にするには、プロジェクトごとに設定を変更する必要があります。
- Project Settings → Advanced Settings を開く
- Enable build processing (preview) を On にする
この記事では触れませんが、この設定は 2.1 の新機能だけでなく、新しいビルドトリガー API や冗長なビルドの自動キャンセル機能のワークフロー対応も有効化されます。
有効にする前に知っておくべき注意点としては、次があります。
- プレビュー版なのでまだ安定していないかもしれない
- ほぼ後方互換性を保っているが、一部変更点がある
<<
が特殊なキーワードになるので、これまでのシェルなどの記述で '<<' を書いてた部分は\
でエスケープする必要があるっぽい- CLI でローカルビルドがサポートされてない
- 回避策はある(後述)
- すでにドキュメントに記載されていない古いシンタックスが使えなくなる
shell
やsetup_docker_engine
など
2.1 の新機能
ここからは 2.1 の新機能を紹介していきます。全体に共通することとして、.circleci/config.yml
の先頭に書いていた version: 2
を、version: 2.1
にしないと新機能が使えないので注意してください。あと、workflows
下の version
はいらなくなったみたいです。
Commands
commands
を使うと独自のステップを定義できます。例えば、先程の例の restore_cache
と run: npm install
をまとめて 1 つのステップにしてみます。
version: 2.1 commands: npm_install_with_cache: steps: - restore_cache: keys: - v1-npm-deps-{{ checksum "package-lock.json" }} - v1-npm-deps- - run: npm install jobs: test-v8: docker: - image: circleci/node:8 steps: - checkout - npm_install_with_cache - run: npm test - save_cache: key: v1-npm-deps-{{ checksum "package-lock.json" }} paths: - node_modules (省略)
npm_install_with_cache
という独自のコマンドを定義して、ジョブの steps
内で呼び出しています。コマンドの必須キーは steps
だけですが、オプショナルなキーとして description
でコマンドの説明を、parameters
でパラメータを定義できます。パラメータについては後述します。
Executors
executors
を使うとビルドの実行環境を定義できます。今回の例では node
という executor を定義して、ジョブから呼び出してみます。
version: 2.1 executors: node: parameters: tag: type: string docker: - image: circleci/node:<< parameters.tag >> jobs: test-v8: executor: name: node tag: "8" steps: (省略) test-v10: executor: name: node tag: "10" steps: (省略)
例だと docker
だけ定義しているので元の例と比べてあまり共通化してる感じがしないかもですが、executor には次のキーを組み合わせて使えます。
docker
ormachine
ormacos
environment
working_directory
shell
resource_class
また、コマンドと同じように parameters
キーも定義できるため、今回の例でイメージのタグを tag
というパラメータで切り替えられるようにしています。
parameters
では type
のみが必須キーで、string
,boolean
,enum
,steps
が値としてサポートされています。オプショナルキーとしては description
でパラメータの説明を、default
でパラメータのデフォルト値を指定できます。
ジョブのパラメータ化&ワークフロー内での複数回実行
ジョブの定義にも parameters
が設定できるようになり、さらに 1 つのワークフローの中で同じジョブを複数回実行できるようになっています。今回の例だと、ジョブの定義を node のバージョンごとに分けて用意する必要がなくなります。
version: 2.1 executors: node: parameters: tag: type: string docker: - image: circleci/node:<< parameters.tag >> jobs: test: parameters: node-version: type: string executor: name: node tag: << parameters.node-version >> steps: (省略) workflows: build: jobs: - test: name: "test-v8" node-version: "8" - test: name: "test-v10" node-version: "10"
ジョブの定義は test
だけになり、workflows
内で test
ジョブをパラメータを変えて複数回実行しています。workflows
で test
ジョブを呼び出すときに name
キーでジョブ名を指定することによって、ブラウザ上の表示で見やすくなりますし、今回の例では使ってないですが requires
で依存関係を指定するときにも便利です。
Conditional Steps
同じジョブでも boolean
のパラメータによってステップを実行するかどうか分岐させることができるようになっています。今回の例だとちょっと説明が難しいですが、例えば node のバージョンが 10 のときだけカバレッジを取得してテスト実行するようにしてみます。
version: 2.1 jobs: test: parameters: node-version: type: string measure-coverage: type: boolean default: false executor: name: node tag: << parameters.node-version >> steps: - checkout - npm_install_with_cache - when: condition: << parameters.measure-coverage >> steps: - run: npm run test:coverage - unless: condition: << parameters.measure-coverage >> steps: - run: npm test - save_cache: key: v1-npm-deps-{{ checksum "package-lock.json" }} paths: - node_modules workflows: build: jobs: - test: name: "test-v8" node-version: "8" - test: name: "test-v10" node-version: "10" measure-coverage: true
ジョブの steps
内で when
キーと unless
キーを使って measure-coverage
というパラメータが true のときは npm run test:coverage
、false のときは npm test
が実行されるようにしています。
Orb
Orb は、これまで紹介したコマンドや実行環境やジョブをパッケージングして共通化するための仕組みです。ここまでの説明は 1 リポジトリの設定内での共通化を例にしましたが、実際には複数プロジェクトで設定を共通化したい場合もあり、そういうときに Orb という形でパッケージングして共有するようです。
ただし、現時点では個人で Orb の公開をすることなどがまだできないようなので、この記事ではまだ詳細について触れるのをやめておきます。
CLI の変更
2.1 に合わせて CircleCI の CLI ツールが新しくなっています。
これまでの CLI を使っている人は、以下のコマンドを実行する必要があります。
$ circleci update $ circleci switch
circleci config validate
はこれまで通り実行できます。
ローカルでビルドを実行する方法が少し変わっていて、circleci build
だったのが circleci local execute
に変わっています。(実はヘルプに表示されないだけで circleci build
も今は実行できるようですが)
さらに、現状だと 2.1 の設定ファイルだと circleci local execute
がエラーになります。
$ circleci local execute Error: You attempted to run a local build with version '2.1' of configuration. Local builds do not support that version at this time. You can use 'circleci config process' to pre-process your config into a version that local builds can run (see 'circleci help config process' for more information)
エラーメッセージにある通り、circleci config process
で前処理して 2.0 形式に変換するとローカルビルドが実行できます。
$ circleci config process .circleci/config.yml > .circleci/config-2.0.yml $ circleci local execute -c .circleci/config-2.0.yml --job test-v8
まとめ
新機能が多いので全部は拾いきれてないですが、CircleCI 2.1 について簡単にまとめました。
例だとちょっとありがたみが実感しづらいかもしれませんが、これまでよりとても柔軟にジョブ設定の共通化ができるようになっています。複雑で大規模なビルドパイプラインを構築しようとすると yaml が肥大化しがちだったので、これはとてもありがたい新機能追加だと感じています。実際、会社のあるチームは 2.1 の新機能のおかげで config.yml を 500 行ぐらい削減してました。
読みやすさという面でも、これまでのエイリアス方式と比較して意味単位がわかりやすいと感じています。CI 沼にハマると無限に時間が過ぎていってしまうので、設定が理解しやすいというのはとても大事です。
2.1 はまだプレビューなので不安定な部分もあるかもしれませんが、とても便利かつ大きな変更なので、今のうちに試して気づいたことがあれば中の人にフィードバックするといいと思います。(CircleCI Japan の twitter アカウントはレスポンスがとてもよくていつも助かっています、ありがとうございます!)