CircleCIで特定のディレクトリ配下にコミットされた時にjobを実行したい

最近Terraformの勉強をはじめてドキュメントを一通り読んだのでさてコードを書くかというのが今

個人でinfraというリポジトリを作っていてpacker用のmanifestファイルとかが入っている
特にリポジトリをわける理由もなかったので、このリポジトリterraform というディレクトリをほった

こんな感じ↓

.
├── README.md
├── packer
└── terraform 

問題

CI環境にはCircleCIを使っているのですが、毎回CIを回す時にpacker buildしたりterraform planをするのかということ

出来れば変更したファイルによってきりかえたい
つまりpackerディレクトリ内のファイルが変更された時のみpacker build
terraformディレクトリ内のファイルが変更された時のみterraform plan
をしたい

準備

まずは今回やりたいことに 特定のディレクトリ内が変更された 時に処理を行う
とあるので、このトリガーを作る必要がある
そこで今回は次のようなスクリプトを用意した

#!/bin/bash

if [ "${CIRCLE_BRANCH}" = "master" ]; then
  # masterはマージコミットなどの場合なので一つ前と比較する
  DIFF_TARGET="HEAD^ HEAD"
else
  DIFF_TARGET="origin/master"
fi

DIFF_FILES=(`git diff ${DIFF_TARGET} --name-only --relative=${1}`)

if [ ${#DIFF_FILES[@]} -eq 0 ]; then
  exit 1
else
  exit 0
fi

is_changed という名前で保存したとする
使い方はシンプルでterraformディレクトリ配下のコミットがあったかを知りたい時は is_changed "terraform" とすれば良い
実際は↓のように使う

if is_changed "terraform"; then
  terraform plan
fi

今回はこのファイルをリポジトリ配下の bin/is_changed に保存する

CircleCIの設定

まずは今回の完成品から

version: 2

common_stepup_commaand: &common_stepup_commaand
  command: |
    echo 'export PATH=$PATH:${CIRCLE_WORKING_DIRECTORY}/bin' >> $BASH_ENV
jobs:
  build:
    docker:
      - image: busybox
    steps:
      - run: echo "これはビルドタスクです"
  test_terraform:
    docker:
      - image: circleci/golang:1.11.1
    steps:
      - checkout
      - run:
          <<: *common_stepup_commaand
      - run:
          name: Run
          command: |
            if is_changed "terraform"; then
              echo "terraformに変更があったよ"
            else
              echo "terraformに変更はなかったよ"
            fi
  test_packer:
    docker:
      - image: circleci/golang:1.11.1
    steps:
      - checkout
      - run:
          <<: *common_stepup_commaand
      - run:
          name: Run
          command: |
            if is_changed "packer"; then
              echo "packerに変更があったよ"
            else
              echo "packerに変更はなかったよ"
            fi
workflows:
  version: 2
  test:
    jobs:
      - build
      - test_terraform:
          requires:
            - build
      - test_packer:
          requires:
            - build

処理としては次のようにbuild jobが成功した後にtest_packerとtest_terraformのjobが実行される

f:id:hatappi1225:20181008231212p:plain

ただtest_packer, test_terraformのどちらのjobも処理の中でさきほど作成した is_changed を呼出している
これにより特定のファイルが実行された時に処理を行うことが実現できた

実現できた??

本当は指定のディレクトリ配下にコミットされた時のみjobを実行したかったが、 今回はjob自体は実行されて、そのstep内のcommandで実行するかどうかを分岐させている
なのでちょっとこれじゃない感が出てしまっている

CircleCIは任意のjobを実行することの出来るAPIが提供されているのでこれをbuild job内で分岐させてリクエストすることも考えた
ただこれを使うとworkflowにはのらなくなり、それぞれが単独のjobとして実行されてしまう
そのためbuild jobが成功すればAPIでリクエストしたjobが失敗したとしてもworkflow自体は成功という判定になってしまう

ということでこんな形になった

もっと良い方法思いついたらupdateしていきたい