Workflowsに設定されたCircleCIのジョブをAPI経由で叩く方法

# はじめに
CIツールのスタンダードとなりつつある [CircleCI](https://circleci.com/)は、Github等のpushをトリガーに設定したCIジョブを実行してくれる便利なツールです。

CircleCIは[API](https://circleci.com/docs/api/)を公開しています。このAPIによってリポジトリにpushしたりtagの発行がなくてもCIを実行することができます。例えば、複数リポジトリ間のCIトリガーを一括で制御したい！といった場合や、リポジトリ内のコードに変更はないけど、内部でダウンロードしてくるファイルやDockerイメージの中身に変更が生じたためpushせずにテストを回したい！というときに非常に有用です。

しかし、API経由ではCircleCI 2.0で実装された`workflows`機能が働かないということがわかりました。　
というより、`workflows`ブロックが`config.yml`に入ったCIをAPI経由で実行すると必ずCIが失敗します。


公式でもそんなことが書いてあります。
> Note: It is not yet possible to run Workflows with the API.
https://circleci.com/docs/2.0/api-job-trigger/


この記事では`workflows`が設定されたCIをAPI経由で実行する方法を共有します。

# 解決策

CircleCIのAPIではCIの設定単位ではなく、ymlの中で設定されたjob単位でも実行することができます。
この方法だと上記のworkflows問題を回避することができます。

やり方は以下の通り

- 設定単位

```bash
curl https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/tree/:branch?circle-token=${CIRCLE_TOKEN}
```
- job単位

```bash
curl -d build_parameters[CIRCLE_JOB]=${JOB_NAME} https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/tree/:branch?circle-token=${CIRCLE_TOKEN}
```

# 実行例

例として以下のようなconfig.ymlを用意します。
このCIでは必ずjob1が実行され、その後developブランチであればjob2、masterブランチであればjob3が実行されます。


```yml:config.yml

version: 2
jobs:
  job1:
    docker:
      - image: circleci/python:3.6.1

    steps:
      - run:
          name: echo1
          command: echo fuwafuwa

  job2:
    docker:
      - image: circleci/python:3.6.1

    steps:
      - run:
          name: echo2
          command: echo mochimochi


  job3:
    docker:
      - image: circleci/python:3.6.1

    steps:
      - run:
          name: echo3
          command: echo pikapika

workflows:
  version: 2
  test-jobs:
    jobs:
      - job1
      - job2:
          requires:
            - job1
          filters:
            branches:
              only: develop
      - job3:
          requires:
            - job1
          filters:
            branches:
              only: master

```


それではまず、gitのpushをトリガーとしてCIを走らせてみましょう。
masterブランチにpushした結果が以下の通り。

![Screenshot from 2018-04-14 14-40-01.png](https://qiita-image-store.s3.amazonaws.com/0/240970/353ec1e8-3bef-500e-da5b-6dc4cca2ee6a.png)

共通jobのjob1と、masterブランチ限定のjob3が走っていることがわかります。job2はdevelopブランチにpushされたときだけ実行されるので、今回は実行されていません。

API経由で全く同じCIを叩いてみましょう。

```bash:
curl https://circleci.com/api/v1.1/project/github/smn-ailab/hiranuma_qiita/tree/master?circle-token=${CIRCLE_TOKEN}
```

下の画像のような結果が出ました。
![Screenshot from 2018-04-14 15-04-34.png](https://qiita-image-store.s3.amazonaws.com/0/240970/6e7da774-d910-a2f8-4ed6-eadedc472fe4.png)

ymlのsyntaxエラーのような記述ですね。なにはともあれ、API経由では`workflows`が設定されたCIは実行できないことが確認できました。

それでは今回のテーマである、job単位での実行をやってみましょう。

```bash
curl -d build_parameters[CIRCLE_JOB]=job1 https://circleci.com/api/v1.1/project/github/smn-ailab/hiranuma_qiita/tree/master?circle-token=${CIRCLE_TOKEN}
```

画面を確認してみると、、
![Screenshot from 2018-04-14 15-15-59.png](https://qiita-image-store.s3.amazonaws.com/0/240970/9cd44e81-df8d-f02b-df7f-66f9df8c49e7.png)



動作できています！
これで`workflows`を無視してAPIからjobを実行できました！

# おまけ: APIでworkflowsの挙動を再現してみた

`workflows` の機能が使えないため、jobの依存関係を呼び出し側で制御する必要があります。
buildのstatusを参照するAPIを使って依存関係を書いてみます。

```bash:依存関係を再現.sh
BUILD_NUM=`curl \
          -d build_parameters[CIRCLE_JOB]=job1 \
          https://circleci.com/api/v1.1/project/github/smn-ailab/hiranuma_qiita/tree/master?circle-token=${CIRCLE_TOKEN} | jq -r '.build_num'`


while true
do
  STATUS=`curl -s -S https://circleci.com/api/v1.1/project/github/smn-ailab/minerva-analysis/${BUILD_NUM}?circle-token=${API_TOKEN} | jq -r '.status'`
  if [ ${STATUS} = "success" ]; then
    echo ${STATUS}
    break 
  elif [ ${STATUS} = "failed" ]; then
    echo ${STATUS}
    exit 1
  else
    echo ${STATUS}
    sleep 60
  fi  
done

curl \
-d build_parameters[CIRCLE_JOB]=job3 \
https://circleci.com/api/v1.1/project/github/smn-ailab/hiranuma_qiita/tree/master?circle-token=${CIRCLE_TOKEN}
```

CIを実行するAPIでは、返り値としてBuildの情報がjson形式で返ってきます。
ここではCIを実行すると同時にBuild番号を受け取り、番号を元にAPIを叩いて該当BuildのStatusを確認しています。$STATUSが"success"であれば、次のBuildを走らせるcurlコマンドの部分へ移行し、failedであればその時点でシェルは終了します。それ以外の場合は1分待ってから再度STATUS確認に移ります。このようにすればjobの依存関係を再現できます。

# おわりに
今回ご紹介した方法では`workflows`の設定を一切無視するので、`branches: only master` としているjobをdevelopブランチで実行することも出来てしまいます。
使用する際はご注意下さい。