CircleCiのAPIとjqでビルド情報を特定期間に絞ってサマる


## 本記事のサマリ
CircleCiのブランチ毎、期間毎のbuild失敗数などを取得したかったのですが、画面からは簡単に取得ができなかったので、公開されているAPIとjsonをパースするためのコマンドラインツールであるjqを利用して、情報をサマって取得する情報をまとめました。

### 対象読者
* CircleCiのビルド状況を分析したいけど画面からは取得できないなぁと悩んでいる方向け
* jqって聞いたことあって、なんかjson扱うのに便利そうだと知ってるけど上手く使えなかった方の勉強にも良いかと思います。

### 動作確認バージョン
```
jq --version
-> jq-1.5
```

CircleCiのAPI Versionは、1.1です。

### 経緯
このリポジトリのmasterブランチ、最近失敗が多いようだけど、この期間のGreen率とあの期間のGreen率ってどう変化してるの？？
→調査。。。「ウゲェ、webの画面にはそんな機能はなさそうやん。。。」

## 事前準備
### CircleCiのAPIを叩く準備
まずはAPIを実行するためのトークンを生成しましょう。
CircleCiにログインした上で、以下にアクセスします。
https://circleci.com/account/api

以下のよう画面が表示されると思いますので、CreateNewTokenボタンを押しましょう。
![Kobito.siqL2Z.pn](https://qiita-image-store.s3.amazonaws.com/0/47854/0f9b32b9-e2f4-a7ad-7350-66f742e63d99.png)


トークンの名前を問われるので適当に目的にあったわかりやすい名前をつけましょう。ここでは、説明用なのでsample_tokenとしました。

![Kobito.VukJYb.png](https://qiita-image-store.s3.amazonaws.com/0/47854/96d05b54-4619-34c3-6aeb-30d8c9d06e1a.png)

こんな感じになりました。
![Kobito.u3cOO8.png](https://qiita-image-store.s3.amazonaws.com/0/47854/67628b35-7ec8-0fd3-9ddc-5b652c45d169.png)

このtokenがバレてしまうと、あなたの権限の範囲で自由にAPIを叩かれてしまうので、周りに公開したりしないでください。もしバレたら、一旦削除して再度生成しましょう。


### jqのインストール
もしMACでhomebrewを使っているのであれば以下を実行するとインストールできます。

`brew install jq`

他の環境のインストールについては、こちらをご参照ください。
https://stedolan.github.io/jq/download/

ちなみに、jqのチュートリアルがgithubのapiを使った実例で説明があるので、そちらの方も参考になります。

こんなオンライン実行環境もあってチューニングするのに便利
https://jqplay.org/


## 実行できるか確認してみる。
とりあえず誰のtokenかを確認するAPIがあるので、取得の確認とjqの動作確認を兼ねて実行して見ましょう。

```bash
# XXXXXには、あなたが取得したTOKENの値を設定してください。
TOKEN=XXXXX
curl -s -u ${TOKEN}: https://circleci.com/api/v1.1/me | jq .login
-> "yukimura1227"
```

こんな感じで、user-nameが出てくれば取得できればオーケーです。
失敗する場合は、これまでの設定を見直して見てください。

jqを使わずにcurl〜v1.1/me部分を実行して以下のようになる場合はtokenの設定が怪しいです。
あとは、`${TOKEN]: http` の部分は、コロンの後にスペースがあることに注意してください。

```
{
  "message": "You must log in first."
}
```

`https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/tree/:branch`
試しにこのAPIを実行して見ましょう(:vcs-typeはgithubかbitbucketが指定できるようです。:usernameは対象としているリポジトリのユーザー名、:projectはリポジトリ名です)

# Recipes

## とりあえずビルドの情報を取得してみる。
特定のリポジトリのビルド情報を取得するには以下のAPIを使います。(以降、publicなリポジトリの場合はTOKENが不要なものもありますが、全体的にTOKENをつけていますのでご了承ください。)
https://circleci.com/docs/api/v1-reference/#recent-builds-project

`curl https://circleci.com/api/v1.1/project/:vcs-type/:username/:project?circle-token=:token&limit=20&offset=5&filter=completed`

具体的には、以下のようになります。

```bash
curl -s -u ${TOKEN}: \
'https://circleci.com/api/v1.1/project/github/yukimura1227/codecov_sample?limit=3&offset=5&filter=completed'
# -> 情報量が多すぎるので出力は割愛
```

## 欲しい情報だけピックアップしてtsv化してみる。
これにjqを噛ませて、見やすくしましょう。


```bash
curl -s -u ${TOKEN}: \
'https://circleci.com/api/v1.1/project/github/yukimura1227/codecov_sample?limit=5&offset=0&filter=completed' | jq -r '.[] | [.build_num, .branch, .start_time, .build_time_millis, .outcome, .status] | @tsv'
```

```
20	experiment/not_suitable_path_case	2017-09-26T09:51:03.519Z	60678	success	success
19	experiment/not_suitable_path_case	2017-09-26T09:46:34.319Z	66384	success	fixed
18	experiment/not_suitable_path_case	2017-09-26T09:44:25.728Z	54970	failed	failed
17	experiment/not_suitable_path_case	2017-09-26T09:42:06.639Z	55609	failed	failed
16	experiment/not_suitable_path_case	2017-09-26T09:39:55.172Z	31531	failed	failed
```

apiで叩いて戻ってくる一番外側が、jsonの配列になっているので、 `.[]` を使います。
これは、Array/Object Value Iteratorというもので、これを使うと一番外側の配列に格納されているjsonのオブジェクトが取得できます。
それを `|` (パイプ)を使って、[.build_num,...]に連携しています。
`[.build_num, .branch..., .status]` では、要素を指定して抽出する対象をフィルタリングしています。
最後の `@tsv` で、tsv形式で出力するようにしています。
参考：https://stedolan.github.io/jq/manual/#Basicfilters

### ビルドにかかった時間の単位を整形する
ビルド時間がミリ秒だとわかりづらいので、秒単位にして見ましょう。
jqでは `+` `-` `*` `/` を使って四則演算ができますので、それを使うと以下のようにできます。

```bash
curl -s -u ${TOKEN}: \
'https://circleci.com/api/v1.1/project/github/yukimura1227/codecov_sample?limit=5&offset=0&filter=completed' | jq -r '.[] | [.build_num, .branch, .start_time, .build_time_millis/1000, .outcome, .status] | @tsv'
```

```
20	experiment/not_suitable_path_case	2017-09-26T09:51:03.519Z	60.678	success	success
19	experiment/not_suitable_path_case	2017-09-26T09:46:34.319Z	66.384	success	fixed
18	experiment/not_suitable_path_case	2017-09-26T09:44:25.728Z	54.97	failed	failed
17	experiment/not_suitable_path_case	2017-09-26T09:42:06.639Z	55.609	failed	failed
16	experiment/not_suitable_path_case	2017-09-26T09:39:55.172Z	31.531	failed	failed
```

### 件数の絞り込みをjqでやってみる
CircleCIのapiはパラメータとしてlimit,offsetで絞り込めるようになっていますが、API側が提供していないケースもありますし、jqで絞り込んだ方が柔軟に対処ができるのでjqで件数を絞り込んで見ましょう。

```bash
curl -s -u ${TOKEN}: \
'https://circleci.com/api/v1.1/project/github/yukimura1227/codecov_sample?offset=0&filter=completed' | \
jq -r '.[5:10] | .[] | [.build_num, .branch, .start_time, .build_time_millis, .outcome, .status] | @tsv'
```

```
15	experiment/not_suitable_path_case	2017-09-26T09:31:13.864Z	1049764	failed	failed
14	experiment/not_suitable_path_case	2017-09-26T09:28:15.344Z	46219	failed	failed
13	experiment/not_suitable_path_case	2017-09-26T09:24:21.295Z	46113	failed	failed
12	experiment/not_suitable_path_case	2017-09-26T09:20:16.144Z	169985	failed	failed
11	experiment/not_suitable_path_case	2017-09-26T09:15:40.405Z	619652	failed	failed
```

先ほどのapiのparameterからlimitを除外した上で、`.[5:10]` という処理が追加になっています。この、`.[n:m]` で部分配列を作ることができます。
配列の添字がn〜m-1のものを絞り込むことができます。

参考：https://stedolan.github.io/jq/manual/#Builtinoperatorsandfunctions

## 日付で絞り込んでみる
何かのデータで絞り込みたい場合は、selectを使うと便利です。`and` `or` `not` という論理演算子も使えるので、日付の期間を指定することもできます。

例えば、start_timeの期間を指定して見ましょう。

```bash
curl -s -u ${TOKEN}: \
'https://circleci.com/api/v1.1/project/github/yukimura1227/codecov_sample?offset=0&filter=completed' | \
jq -r '.[] | select(.start_time >= "2017-09-26T09:30" and .start_time <= "2017-09-26T10:00") | [.build_num, .branch, .start_time, .build_time_millis/1000, .outcome, .status] | @tsv'
```

```
20	experiment/not_suitable_path_case	2017-09-26T09:51:03.519Z	60.678	success	success
19	experiment/not_suitable_path_case	2017-09-26T09:46:34.319Z	66.384	success	fixed
18	experiment/not_suitable_path_case	2017-09-26T09:44:25.728Z	54.97	failed	failed
17	experiment/not_suitable_path_case	2017-09-26T09:42:06.639Z	55.609	failed	failed
16	experiment/not_suitable_path_case	2017-09-26T09:39:55.172Z	31.531	failed	failed
15	experiment/not_suitable_path_case	2017-09-26T09:31:13.864Z	1049.764	failed	failed
```

`.[] | select(.start_time >= "2017-09-26T09:30" and .start_time <= "2017-09-27T10:00")`
これで、配列の1件1件に対して、start_timeが"2017-09-26T09:30"と"2017-09-26T10:00"の間にあるデータを絞り込んでいます。

## さらにstatusごとにカウントする
jqのgroup_byとlengthを使えば、取れるのですが、結構わかりづらいので、絞りまでできたら、`uniq -c` でカウントした方が簡単なのでそっちにしました。

```bash
curl -s -u ${TOKEN}: \
'https://circleci.com/api/v1.1/project/github/yukimura1227/codecov_sample?offset=0&filter=completed' | \
jq -r '.[] | select(.start_time >= "2017-09-26T09:30" and .start_time <= "2017-09-26T10:00") | [.status] | @tsv' | sort | uniq -c
```

```
   1 success
   1 fixed
   4 failed
```