はじめに
CircleCIのworkflowを使ってjobを実行する場合に、ブランチでフィルターをかけて実行するjobを決めていました。例えばリリース用のブランチへマージされた場合のみデプロイに関するjobを実行するといった使い方です。
開発をしている中でブランチよりももう少し細かい条件で分岐でjobを分けたいという要望があり、その時行った方法について紹介します。
やりたいこと
remoteのブランチへのcommitをトリガーにブランチによって以下のようなworkflowが別れている場合に
ブランチ | job |
---|---|
master以外 | test-job |
master | test-job,deploy-job |
masterブランチへのcommitをトリガーに実行されるworkflowを二つに分けて特定の条件の場合にテストをskipするようにしたい。
ブランチ | job |
---|---|
master以外 | test-job |
master | test-job,deploy-job |
master(特定の条件を満たす) | deploy-job |
前提
前提としてはworkflowを利用する
簡単のため以下のようなシンプルなconfig.yml
があった場合で説明していきます。
version: 2.1 # executorsのみ2.1の機能を使っていますが、それ以外は2.0の機能です
executors:
default:
working_directory: ~/sample
docker:
- image: hoge/hoge:latest
jobs:
test-job:
executor:
name: default
steps:
- run:
name: test_step
command: echo 'execute test job'
deploy-job:
executor:
name: default
steps:
- checkout
- run:
name: deploy_step
command: echo 'execute deploy job'
workflows:
test-and-deploy:
jobs:
- test-job
- deploy-job:
requires:
- test-job
filters:
branches:
only:
- master
採用した方法
単純な方法ですが、commit logに特定の文字列が入っている場合にテストの実行をskipするようにしました。
例えばskip_test
のような文字列が入っている場合に実行をする。場合はconfig.yml
を以下のように修正します。
version: 2.1
executors:
default:
working_directory: ~/sample
docker:
- image: hoge/hoge:latest
jobs:
test-job:
executor:
name: default
steps:
- checkout
- run:
name: test_step
command: | # commandの内容を修正
if [ -z "`git log -1 --oneline | grep skip_test`" ]; then
echo 'execute test job'
else
echo "skip test job"
fi
deploy-job:
executor:
name: default
steps:
- run:
name: deploy_step
command: echo 'execute deploy job'
workflows:
test-and-deploy:
jobs:
- test-job
- deploy-job:
requires:
- test-job
filters:
branches:
only:
- master
シンプルな方法はですが微妙な点もあります。jobの中でテストを実行するかどうかを判断しているため、テストの実行自体はスキップできますが、その前処理(例でいうとexecutorの中の処理や、checkoutの処理)は実行されるためそのそのオーバーヘッドは必ずかかってしまいます。
採用しなかった方法
gitのtagを使って実行するjobにfilterをかけることもできるため、tagでの制御も検討しましたが以下のような理由で採用しませんでした。
- 今の開発チームではtagを使った運用をしていない
- テストをスキップするためにタグを使い分けるのもタグの使い方として微妙
- branchでfilterをかける場合と比較してfilterが複雑になってくる
以下はtagを使った制御の例になります。
発火条件 | job |
---|---|
master以外のブランチへのコミット | test-job |
skiptestの文字列を含まないtagの作成 | test-job,deploy-job |
skiptestの文字列を含むtagの作成 | deploy-job |
# workflowsのみ抜粋
workflows:
test:
jobs:
- test-job:
filters:
branches:
ignore: master # masterブランチでどのjobを実行するかはtagで制御するためignore
test-and-deploy:
jobs:
- test-job:
filters:
tags:
only: /^(?!.*skiptest).*$/ # skiptestを含まないタグの場合のみ発火
branches:
ignore: /.*/ # branchへのcommitで発火しないようにignore
- deploy-job:
requires:
- test-job
filters:
tags:
only: /^(?!.*skiptest).*$/ # skiptestを含まないタグの場合のみ発火
branches:
ignore: /.*/ # branchへのcommitで発火しないようにignore
only-deploy:
jobs:
- deploy-job:
filters:
tags:
only: /^.*skiptest.*$/ # skiptestを含むタグの場合のみ発火
branches:
ignore: /.*/ # branchへのcommitで発火しないようにignore
この例ではworkflow自体がシンプルなのでそこまで複雑ではないですが、workflow自体が複雑になってくるとfilterまわりが複雑になってくるなという印象です。
まとめ
CircleCIのworkflowを使って特定ブランチで実行されるjobをさらに分けて実行する方法を紹介しました。採用した方法では不要なオーバーヘッドがかかっているので、jobレベルで分けれてもっとシンプルにできる方法がないかなあと思うところです。