CircleCI 2.1 の新機能を使って冗長な config.yml をすっきりさせよう!
福井祐人
こんにちは。スタディサプリ English の開発を担当しているwebフロントエンドエンジニアの福井です。
CircleCI で 2.1 configuration がプレビューとして使えるようになりました。試しに使ってみたところ冗長なconfig.yml
が ものすごくすっきりした ので簡単な例を交えて紹介します。
参考
2.0 の冗長な config.yml
今回用意したconfig.yml
はwebフロントエンドの典型的な例で、下記のような流れで順次ジョブを実行します。
setup
- npmパッケージのインストールとキャッシュtest
- テストを実行- デプロイ
3.adeploy_dev
-develop
、master
ブランチ以外は開発環境(dev
)にデプロイ
3.bdeploy_stg
-develop
ブランチはステージ環境(stg
)にデプロイ
3.cdeploy_prod
-master
ブランチは本番環境(prod
)にデプロイ
setup
以降の各ジョブではsetup
でキャッシュしたものをレストアしているのですが、それがジョブごとに記載されており、さらに working_directory
、docker
などの実行環境もジョブごとに指定されています。やっていることは非常に単純なのですが何も考えずに書くとすごく冗長です…
2.0のconfig.yml(同じ記述だらけ)
version: 2
jobs:
setup:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Install dependencies
command: npm install
- save_cache:
name: Cache npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
paths:
- ~/workspace/node_modules
test:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Run test
command: npm run test
deploy_dev:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Deploy to dev environment
command: npm run deploy:dev
deploy_stg:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Deploy to stg environment
command: npm run deploy:dev
deploy_prod:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Deploy to prod environment
command: npm run deploy:prod
workflows:
version: 2
setup_and_deploy:
jobs:
- setup
- test:
requires:
- setup
- deploy_dev:
requires:
- test
filters:
branches:
ignore:
- develop
- master
- deploy_stg:
requires:
- test
filters:
branches:
only: develop
- deploy_prod:
requires:
- test
filters:
branches:
only: master
ただし、この例は実はあまりに無工夫です。通常はYAMLのAnchor/Alias、CIRCLE_JOB
などの環境変数を使って冗長な記述をまとめることが多いと思います。しかし2.1の機能を使った方がより自然かつ柔軟な形でconfig.yml
を書けるので、実際に適用するとどうなるか見ていきましょう。
2.1の新機能を使って config.yml を書く
👆のconfig.yml
を2.1の新機能であるexecutor
、command
、parameter
を使ってすっきりさせていきます。
準備
有効化
2018年10月現在、2.1 configuration はプレビューのため、デフォルトでは オフ となっています。そのためconfig.yml
を編集する前に各プロジェクトの Settings -> Advanced Settings のEnable build processing (preview)
をOn
にする必要があります。CircleCI 側で必要な設定はこれだけです。
CLI
CLIを使うとローカルでconfig.yml
のチェックができるので入れておきましょう。
インストール
$ bash -c "$(curl -fSl https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/master/install.sh)"
# Mac だと Homebrew も使える
$ brew install circleci
更新(最新にすると2.1にも対応)
config.yml
のチェック
$ circleci config validate -c .circleci/config.yml
Config file at .circleci/config.yml is valid
executor
executor
は実行環境の情報を定義して再利用する機能です。以下のキーを指定できます。
docker
ormachine
ormacos
environment
working_directory
shell
resource_class
今回の例ではnode.jsの実行環境をdefault
としてexecutors
の中で定義してジョブの中で利用してみます。
version: 2.1
executors:
# ここに好きな名前で executor を定義できる
default:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
jobs:
setup:
executor: # name で使いたい executor を指定する
name: default
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Install dependencies
command: npm install
- save_cache:
name: Cache npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
paths:
- ~/workspace/node_modules
test:
executor:
name: default
steps:
- checkout
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
- run:
name: Run test
command: npm run test
command
ジョブの中で再利用したいstep
をcommand
として定義できるようになりました。この機能のおかげでこれまでYAMLのAnchor/Aliasを使って何とかしていた部分を共通化できるようになります。
今回の例ではnpmパッケージのリストアとキャッシュをrestore_npm
、save_npm
としてコマンド化してみます1)save_npm
は1箇所でしか利用していませんがセットで定義したほうが綺麗な感じがするのでついでにまとめます。
version: 2.1
executors:
default:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
commands:
# ここにコマンドを定義する
restore_npm: # コマンド の名前(任意)
steps: # 再利用したステップをここに記載
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
save_npm:
steps:
- save_cache:
name: Cache npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
paths:
- ~/workspace/node_modules
jobs:
setup:
executor:
name: default
steps:
- checkout
- restore_npm # 使いたいコマンドを指定するとそのコマンドで定義したステップが展開される
- run:
name: Install dependencies
command: npm install
- save_npm
test:
executor:
name: default
steps:
- checkout
- restore_npm
- run:
name: Run test
command: npm run test
parameter
ワークフローの中でジョブを指定するときに合わせてパラメータも指定することができるようになりました。この機能を使うとほとんど同じ処理だけど一部分だけ変えたい場合にそれらを1つのジョブとして定義できます。 パラメータ定義はジョブ定義時に parameters
キーを使って行います。 以下のキーが指定できます。
Key | 必須 or 任意 | 説明 |
---|---|---|
description |
任意 | パラメータの説明 |
type |
必須 | パラメータの型。現時点ではstring 、boolean 、enum 、steps が指定可能 |
default |
任意 | デフォルト値。指定しないとワークフローの中でジョブを指定する際にパラメータの指定が必須になる |
enum |
enum のとき必須 |
type が enum のときに列挙するパラメータを書く |
使うときはコマンド等の中で<< parameters.パラメータ名 >>
と書いてやると渡ってきたパラメータが展開されます。また <<# parameters.パラメータ名 >>hogehoge<</ parameters.パラメータ名 >>
と書くとparameters.パラメータ名
が真
の場合にブロックの中身(hoghoge
)が展開されます。真偽の判定については後述するConditional Stepsを参考にしてください。
今回の例では deploy_dev
、deploy_stg
、deploy_prod
の3つのジョブはnpm run deploy:
の後に指定するパラメータ以外はすべて同じなのでdeploy
ジョブとしてまとめてみます。
version: 2.1
executors:
default:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
commands:
restore_npm:
steps:
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
jobs:
deploy:
parameters:
# ここにパラメータを定義する
env: # パラメータの名前(任意)
type: enum # enum にするとタイポしたときに検知してくれる
enum: ["dev", "stg", "prod"]
executor:
name: default
steps:
- checkout
- restore_npm
- run:
name: Deploy to << parameters.env >> environment
command: npm run deploy:<< parameters.env >>
workflows:
setup_and_deploy:
deploy: # ジョブはすべて deploy になる。パラメータを変えることでstep中のコマンドを変える
name: deploy_dev # name を指定すると UI 上でもこの名前で表示されるので見分けやすくなります
env: dev # << parameters.env >> が dev として展開されます。 enum なので hoge とか書くとエラー
deploy:
name: deploy_stg
env: stg
deploy:
name: deploy_prod
env: prod
おまけとして、 パラメータはexecutor
やcommand
でも使うことができます 。似たような実行環境や処理をパラメータを使って1つにまとめることもできるので使えそうな場面では積極的に使っていくと良いでしょう。 パラメータはジョブ定義時に指定することもできますし、ワークフローのジョブの中で指定することもできます。後者の場合はジョブ定義時にジョブのパラメータを渡すように明示的に書く必要があります。
ジョブ定義時に指定する場合
version: 2.1
executors:
default:
parameters:
node_version:
type: enum
enum: ['8.12.0', '8.11.4']
working_directory: ~/workspace
docker:
- image: circleci/node:<< parameters.node_version >>
commands:
echo:
parameters:
val:
type: string
steps:
- run: echo << parameters.val >>
jobs:
test:
executor:
name: default
node_version: 8.12.0
steps:
- echo:
val: test value
workflows:
normal:
jobs:
- test
ワークフローのジョブの中で指定する場合
version: 2.1
executors:
default:
parameters:
node_version:
type: enum
enum: ['8.12.0', '8.11.4']
working_directory: ~/workspace
docker:
- image: circleci/node:<< parameters.node_version >>
commands:
echo:
parameters:
val:
type: string
steps:
- run: echo << parameters.val >>
jobs:
test:
# ジョブ側でも executor, command に渡すパラメータを定義する必要がある
parameters:
node_version: # command 側と同じ定義になるので Anchor/Alias を使うこともできる
type: enum
enum: ['8.12.0', '8.11.4']
val:
type: string
executor:
name: default
node_version: << parameters.node_version >> # executor にパラメータを渡す
steps:
- echo:
val: << parameters.val >> # command にパラメータを渡す
workflows:
normal:
jobs:
- test:
node_version: 8.11.4
val: test value 2
最終的な config.yml
executor
、command
、parameter
を使って config.yml
を書き直すとこのようになります!
如何でしょう?再利用可能な部分がうまくまとめられてすっきりしたのではないでしょうか。
version: 2.1
executors:
default:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
commands:
restore_npm:
steps:
- restore_cache:
name: Restore npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
save_npm:
steps:
- save_cache:
name: Cache npm dependencies
key: npm-{{ checksum "package-lock.json" }}-{{ .Environment.CACHE_VERSION_NPM }}
paths:
- ~/workspace/node_modules
jobs:
setup:
executor:
name: default
steps:
- checkout
- restore_npm
- run:
name: Install dependencies
command: npm install
- save_npm
test:
executor:
name: default
steps:
- checkout
- restore_npm
- run:
name: Run test
command: npm run test
deploy:
parameters:
env:
type: enum
enum: ["dev", "stg", "prod"]
executor:
name: default
steps:
- checkout
- restore_npm
- run:
name: Deploy to << parameters.env >> environment
command: npm run deploy:<< parameters.env >>
# 2.1では workflows への version 指定は必要ありません
workflows:
setup_and_deploy:
jobs:
- setup
- test:
requires:
- setup
- deploy:
name: deploy_dev
env: dev
requires:
- test
filters:
branches:
ignore:
- develop
- master
- deploy:
name: deploy_stg
env: stg
requires:
- test
filters:
branches:
only: develop
- deploy:
name: deploy_prod
env: prod
requires:
- test
filters:
branches:
only: master
その他の新機能
今回の例で使った機能以外にも新機能があるので簡単に使い方を紹介します。
Conditional Step
ジョブの中でパラメータの値によってステップを分岐できるようになりました。when
とunless
キーをジョブの中に記載します。when
の場合は指定したパラメータが真
の場合にwhen
以下で定義したステップを実行し、unless
の場合は逆に指定したパラメータが偽
の場合にunless
以下で定義したステップを実行します。
どのパラメータを使うかはcondition
キーを使って定義します。パラメータのタイプがboolean
の場合はtrue
が真
、false
が偽
、string
やenum
の場合は空文字列が偽
、それ以外は真
と判定されます。steps
の場合は試してみたところ何かしらのステップが指定されていても空([]
)でも真
と判定される結果になりました。
以下の例ではrun_test
がtrue
のときにnpm run test
が含まれる一連のステップを実行し、false
のときはskip test
と表示されるだけです。
version: 2.1
executors:
default:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
jobs:
check:
parameters:
run_test:
type: boolean
executor:
name: default
steps:
- run: echo begin steps
- when:
condition: << parameters.run_test >>
steps:
- run: echo begin test
- run: npm run test
- run: echo end test
- unless:
condition: << parameters.run_test >>
steps:
- run: echo skip test
workflows:
normal:
jobs:
- check:
run_test: true # 'npm run test' を実行する
- check:
run_test: false # 'npm run test' を実行しない
ネスト
ネストを表現することもできます。深くなるほど分かりにくくなるので使いすぎには注意したほうがよいでしょう。
以下の例ではrun_test
とrun_lint
がtrue
のときのみnpm run lint
を実行します。
version: 2.1
executors:
default:
working_directory: ~/workspace
docker:
- image: circleci/node:8.11.4
jobs:
check:
parameters:
run_test:
type: boolean
run_lint:
type: boolean
default: false
executor:
name: default
steps:
- run: echo begin steps
- when:
condition: << parameters.run_test >>
steps:
- run: echo begin test
- run: npm run test
- run: echo end test
- when:
condition: << parameters.run_lint >>
steps:
- run: echo begin lint
- run: npm run lint
- run: end lint
- unless:
condition: << parameters.run_test >>
steps:
- run: echo skip test
workflows:
normal:
jobs:
- check:
run_test: true
run_lint: false
- check:
run_test: false
Orb
Orb はcommand
、executor
、job
をプロジェクトをまたいで共有するための仕組みで、共有したい部分を各プロジェクトのconfig.yml
とは別に管理できる仕組みです。Orbから別のOrbをインポートしたり、各プロジェクトで直接インポートして利用します。Orbをインポートして利用する際にジョブ側でpre-steps
、post-steps
というキーを指定してOrbを利用する環境に合わせた前後の処理を Orbを変更することなく追加する ことができます。Orbはバージョン管理もできるのでバージョンを指定してインポートすることもできます。注意事項として、PublishしたOrbはorganizationのメンバーだけでなく 誰からでもアクセスできる状態になる のでOrbの中に認証情報など機密性の高い情報を含めないようにしなければいけません。詳細な利用方法については以下を参照ください。
- Orb structure
- Using orbs in CircleCI configuration
- Authoring and publishing orbs
- Testing orbs
- Writing inline orbs
- Pre and post steps
- Explaining the design approach to CircleCI configuration
- Design Considerations
まとめ
executor
やcommand
だけを見ると、単なる定義の抜き出しの様でYAMLのAnchor/Aliasと比べてそこまで恩恵を感じることはできません。しかしながらparameter
やConditional Stepを使うことで一気に定義の柔軟性が高まり使える機能になったと思います。
みなさんも2.1の便利な新機能を使ってconfig.yml
をガンガンすっきりさせましょう!
脚注
↑1 | save_npm は1箇所でしか利用していませんがセットで定義したほうが綺麗な感じがするのでついでにまとめます |
---|