現状CircleCIではマトリックスビルドはサポートされていませんが、実現するための方法がいくつかあります。
matrix
キーがサポートされました 👏
Configuring CircleCI - CircleCI
node v8、v10、v12、それぞれの実行環境でライブラリのインストール、テスト、ビルドを行い、すべてに成功した場合のみ、ビルドした成果物をNode v10でデプロイしたいとします。また、テストとビルドは互いに依存しないが、インストールに依存するとします。
それぞれの実行環境ごとに、ジョブを定義する
次のように、実行環境(Nodeバージョン)ごとにジョブを定義することができます。

version: 2.1
jobs:
install_deps:
docker:
- image: circleci/node:10
steps:
- checkout
- restore_cache:
keys:
- v1-npm-{{ .Branch }}-{{ checksum "package-lock.json" }}
- v1-npm-{{ .Branch }}-
- v1-npm-
- run: npm install
- save_cache:
paths:
- node_modules
key: v1-npm-{{ .Branch }}-{{ checksum "package-lock.json" }}
- persist_to_workspace:
root: ./
paths:
- node_modules
"node-8":
docker:
- image: circleci/node:8
steps:
- checkout
- attach_workspace:
at: ./
- run: npm test
- run: npm run build
"node-10":
docker:
- image: circleci/node:10
steps:
- checkout
- attach_workspace:
at: ./
- run: npm test
- run: npm run build
- persist_to_workspace:
root: ./
paths:
- lib/
- node_modules
"node-12":
docker:
- image: circleci/node:12
steps:
- checkout
- attach_workspace:
at: ./
- run: npm test
- run: npm run build
deploy:
docker:
- image: circleci/node:10
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run deploy
workflows:
version: 2
matrix:
jobs:
- install_deps
- "node-8":
requires:
- install_deps
- "node-10":
requires:
- install_deps
- "node-12":
requires:
- install_deps
- deploy:
requires:
- "node-8"
- "node-10"
- "node-12"
test
とbuild
が直列に定義されていることが気になるところですが、テストとビルドが依存していないからと言って別ジョブに分けて定義したいという欲がでてくると、node-8-test
、node-8-build
、node-10-test
…をそれぞれ定義しなければならなくなるため、ちょっと厳しくなります。

version: 2.1
jobs:
(略)
"node-8-test":
docker:
- image: circleci/node:8
steps:
- checkout
- attach_workspace:
at: ./
- run: npm test
"node-8-build":
docker:
- image: circleci/node:8
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run build
"node-10-test":
docker:
- image: circleci/node:10
steps:
- checkout
- attach_workspace:
at: ./
- run: npm test
"node-10-build":
docker:
- image: circleci/node:10
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run build
- persist_to_workspace:
root: ./
paths:
- lib
- node_modules
"node-12-test":
docker:
- image: circleci/node:12
steps:
- checkout
- attach_workspace:
at: ./
- run: npm test
"node-12-build":
docker:
- image: circleci/node:12
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run build
deploy:
docker:
- image: circleci/node:10
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run deploy
workflows:
version: 2
matrix:
jobs:
- install_deps
- "node-8-test":
requires:
- install_deps
- "node-8-build":
requires:
- install_deps
- "node-10-test":
requires:
- install_deps
- "node-10-build":
requires:
- install_deps
- "node-12-test":
requires:
- install_deps
- "node-12-build":
requires:
- install_deps
- deploy:
requires:
- "node-8-test"
- "node-8-build"
- "node-10-test"
- "node-10-build"
- "node-12-test"
- "node-12-build"
この場合、パラメータを使ってジョブコンフィグをまとめることができます。
ジョブパラメータで管理する
パラメータを使えば、テスト・ビルドのジョブは使い回すことができるため、設定ファイルがすっきりします。ただし、これでも手動で値を入れる必要があるため、2軸のマトリックス(言語バージョン×OSなど)はうまく管理できる気がしません。
version: 2.1
executor-template: &executor-template
parameters:
tag:
default: "10"
type: string
persist_built_files:
type: boolean
default: false
docker:
- image: circleci/node:<< parameters.tag >>
jobs:
install_deps:
<<: *executor-template
steps:
- checkout
- restore_cache:
keys:
- v1-npm-{{ .Branch }}-{{ checksum "package-lock.json" }}
- v1-npm-{{ .Branch }}-
- v1-npm-
- run: npm install
- save_cache:
paths:
- node_modules
key: v1-npm-{{ .Branch }}-{{ checksum "package-lock.json" }}
- persist_to_workspace:
root: ./
paths:
- node_modules
test:
<<: *executor-template
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run test
build:
<<: *executor-template
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run build
- when:
condition: << parameters.persist_built_files >>
steps:
- persist_to_workspace:
root: ./
paths:
- node_modules
- lib
deploy:
<<: *executor-template
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run deploy
workflows:
version: 2
matrix:
jobs:
- install_deps
- test:
name: node-8-test
tag: "8"
requires:
- install_deps
- test:
name: node-10-test
tag: "10"
requires:
- install_deps
- test:
name: node-12-test
tag: "12"
requires:
- install_deps
- build:
name: node-8-build
tag: "8"
requires:
- install_deps
- build:
name: node-10-build
tag: "10"
persist_built_files: true
requires:
- install_deps
- build:
name: node-12-build
tag: "12"
requires:
- install_deps
- deploy:
tag: "10"
requires:
- node-8-test
- node-8-build
- node-10-test
- node-10-build
- node-12-test
- node-12-build
パイプラインパラメータで管理する
少しトリッキーですが、パイプラインパラメータを使えば、ワークフロー全体にパラメータを渡すことができるため、より少ない記述量で実現できます。この設定ファイルを使用するには、APIトークン(CIRCLE_TOKEN
)が必要です。
(パラメータを使った動的なジョブ名は前からできたっけ?)
parameters:
tag:
default: '10'
type: string
run-main-workflow:
default: false
type: boolean
run-deploy-job:
default: false
type: boolean
executors:
node:
docker:
- image: circleci/node:<< pipeline.parameters.tag >>
version: 2.1
jobs:
install_deps:
executor: node
steps:
- checkout
- restore_cache:
keys:
- v1-npm-{{ .Branch }}-{{ checksum "package-lock.json" }}
- v1-npm-{{ .Branch }}-
- v1-npm-
- run: npm install
- save_cache:
paths:
- node_modules
key: v1-npm-{{ .Branch }}-{{ checksum "package-lock.json" }}
- persist_to_workspace:
root: ./
paths:
- node_modules
test:
executor: node
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run test
build:
executor: node
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run build
- when:
condition: << pipeline.parameters.run-deploy-job >>
steps:
- persist_to_workspace:
root: ./
paths:
- lib
- node_modules
deploy:
docker:
- image: circleci/node:<< pipeline.parameters.tag >>
steps:
- when:
condition: << pipeline.parameters.run-deploy-job >>
steps:
- checkout
- attach_workspace:
at: ./
- run: npm run deploy
- unless:
condition: << pipeline.parameters.run-deploy-job >>
steps:
- run: echo "No deployment on Node v<< pipeline.parameters.tag >>"
trigger-main-workflows:
machine:
image: ubuntu-1604:201903-01
parameters:
deploy-node-version:
default: '10'
type: string
steps:
- run:
name: Trigger main worflow
command: |
VCS_TYPE=$(echo ${CIRCLE_BUILD_URL} | cut -d '/' -f 4)
for NODE_VERSION in 8 10 12
do
PIIPELINE_PARAM_MAP="{\"run-main-workflow\": true, \"tag\":\"$NODE_VERSION\"}"
if [ "$NODE_VERSION" = << parameters.deploy-node-version >> ]
then
PIIPELINE_PARAM_MAP="{\"run-main-workflow\": true, \"tag\":\"$NODE_VERSION\", \"run-deploy-job\": true}"
fi
curl -u ${CIRCLE_TOKEN}: -X POST --header "Content-Type: application/json" -d "{
\"branch\": \"${CIRCLE_BRANCH}\",
\"parameters\": ${PIIPELINE_PARAM_MAP}
}" "https://circleci.com/api/v2/project/${VCS_TYPE}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pipeline"
done
workflows:
version: 2
matrix:
unless: << pipeline.parameters.run-main-workflow >>
jobs:
- trigger-main-workflows:
deploy-node-version: "10"
main:
when: << pipeline.parameters.run-main-workflow >>
jobs:
- build:
name: build-<< pipeline.parameters.tag >>
- test:
name: test-<< pipeline.parameters.tag >>
- deploy:
requires:
- build-<< pipeline.parameters.tag >>
- test-<< pipeline.parameters.tag >>
trigger-jobs
ワークフローが、Node v8, v10, v12の実行環境において、それぞれmain
ワークフローをトリガーします。
Nodeバージョン×DBといったマトリックスも可能です。
全文
(略)
trigger-main-workflows:
machine:
image: ubuntu-1604:201903-01
parameters:
steps:
- run:
name: Trigger main worflow
command: |
VCS_TYPE=$(echo ${CIRCLE_BUILD_URL} | cut -d '/' -f 4)
for NODE_VERSION in 8 10 12
do
for DB in mongo mysql
do
PIIPELINE_PARAM_MAP="{\"run-main-workflow\": true, \"tag\":\"$NODE_VERSION\", \"db\":\"$DB\"}"
if [ "$NODE_VERSION" = "10" ] && [ "$DB" = "mongo" ]
then
PIIPELINE_PARAM_MAP="{\"run-main-workflow\": true, \"tag\":\"$NODE_VERSION\", \"db\":\"mongo\", \"run-deploy-job\": true}"
fi
curl -u ${CIRCLE_TOKEN}: -X POST --header "Content-Type: application/json" -d "{
\"branch\": \"${CIRCLE_BRANCH}\",
\"parameters\": ${PIIPELINE_PARAM_MAP}
}" "https://circleci.com/api/v2/project/${VCS_TYPE}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pipeline"
done
done
ただし、APIを叩く必要があるため、これでもまだ他のCIでできるようなマトリックスビルドと比べれば、やぼったさは否めません。また、デプロイジョブをすべてのワークフローが成功した場合のみ実行するしくみ(ファンイン)にすることが非常に難しく、APIを使ってビルド結果をポーリングするか、承認ジョブを使って目視で確認してから手動で起動にするしかなさそうです。
(ファンアウト・ファンインについて)
RFC: Matrix Jobs syntaxを見ると、マトリックスビルドの開発が今まさに行われているようなので、正式サポートされるまではファンアウトだけなら上記のパイプラインパラメータ、ファンインが必要ならジョブパラメータが良さそうかな。