CircleCIでマトリックスビルド
CircleCIでマトリックスビルド
現状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: # Node v10でビルドした成果物を永続化 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を見ると、マトリックスビルドの開発が今まさに行われているようなので、正式サポートされるまではファンアウトだけなら上記のパイプラインパラメータ、ファンインが必要ならジョブパラメータが良さそうかな。