CircleCI artifactsのurlをjobのスクリプト内で取得する

#導入

Python製のドキュメントツール[Sphinx](https://www.sphinx-doc.org/ja/master/)で社内ドキュメントを制作するタスクがあった。
各自の手元では`docker-compose up -d --build`を叩けば`sphinx-autobuild`で随時更新されるので、よかろうと思っていた。
しかし、Pull Requestのレビューにあたり元のreSTファイルの差分をwebで見たり、手元にpullして見るのは億劫だという意見が出た。
もっともな話である。
幸い、CIツールとして利用していたCircleCIにはartifactsという仕組みがあり、CIのjob内でbuildしたhtmlをホストできた。
では、artifactsのurlをbitbucketのPRにコメント、あるいはslackに通知するためにurlをどう取得するか、転がっている情報が半端だったので記しておきたい。

#artifactsへの保存

[ビルド アーティファクトの保存 - CircleCI](https://circleci.com/docs/ja/2.0/artifacts/)にある通り、artifactsへの保存は非常に簡単だ。circleCIの設定ファイルのstep内で`run`と同じく`store_artifacts`を記述すれば良い。保存したいファイルまたはディレクトリを設定するだけで設定は完了だ。

```yml:.circleci/config.yml
- store_artifacts:
    path: /tmp/artifacts
```

実際にジョブを回すとartifactsにファイルの一覧がずらっと表示される。
レビュアーにbitbucketのビルドウィジェットなりからCircleCIのjobの結果ページにアクセスしてもらい、artifactsのタブをクリックし、index.htmlへのリンクをクリックしてもらえば、ドキュメントが見られる。
ただ、これはちょっと手間である。

githubであれば、[moxci](https://github.com/moxci/moxci)というライブラリがあり、PRに対してメッセージを送ったりslackに投げたりできる。
CircleCIのAPIを叩いてartifactsの一覧を取得し、あらかじめ定めたpathと部分一致するartifactのurlを返すという仕組みのようだ。
artifactsのurlを取得するにあたってはCircleCIのドキュメントにAPIの記述があり、stackoverflowを見てもそうしろと書いてある。
ただ、CircleCIのTokenを取得して環境変数に定義し、arifactsのリストを取得して、そこからurlを抽出する手間が本当に必要だろうか？

#artifactのurl

[PRごとにCIでStorybookをビルドしてデザイナーとインタラクションまで作っていく話](https://qiita.com/resessh/items/38ef6492d0cf21facec8)には、
>// Artifactsのurlのhostnameは
>// "https://4321-12345678-gh.circle-artifacts.com"
>// のフォーマットですが、これの "12345678" を確認し、環境変数に入れておきます。
>CIRCLE_REPO_ID,

と書かれている。一度artifactsに保存してurlを見、そこから抽出する、これならAPIを叩く必要はない。
但し、初回についてはこの環境変数を設定することができない。
この謎のCIRCLE_REPO_IDとは何なのか。
[[メモ] CircleCI_環境変数一覧（1.0&2.0） - Qiita](https://qiita.com/jkkitakita/items/4554a965e86d376e3ecd)に存在するどの変数とも違う。類似したフォーマットの変数がいくつかあるので、入れて試してみるという愚行を犯してしまった。他に環境変数がないか出力したが、目的の値は存在しなかった。
[Circle Ci 2.0 artifacts url - stackoverflow](https://stackoverflow.com/questions/46880788/circle-ci-2-0-artifacts-url)には
>The {UNKNOWN_NUMBER} represents the Github id of the repository.

との回答があった。なるほど。もしかしたらbitbucketでも同様なのではないか。PRへのコメント用にbitbucketのPersonal Settingからアプリパスワードを発行していたのでこのパスワードを認証に使ってみる。
user_name, app_pass, workspace, repositoryは各自置き換えて欲しい。

```bash
curl -v -X GET --user <user_name>:<app_pass> --header 'Content-type: application/json' "https://api.bitbucket.org/2.0/repositories/<workspace>/<repository>"
```

すると、

```json:response.json
{"scm": "git", "website": null, "has_wiki": false, "uuid": "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",...
```

というレスポンスが得られる。
このレスポンスのuuidの中括弧を外したものがまさにartifactsのurlに含まれるものに一致した。

結論として、artifactsのurlは

```python:create_url.py
build_number = os.environ.get('CIRCLE_BUILD_NUM')  # CircleCI内で定義されている環境変数
node_index = os.environ.get('CIRCLE_NODE_INDEX')  # CircleCI内で定義されている環境変数

REPOSITORY_UUID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'  # bitbucket APIで取得した値
VCS_TYPE = 'bb'  # リポジトリのホストサービスコード。GitHubなら'gh'
TARGET_PATH = '/tmp/artifacts/index.html'  # 例。store_artifactsのpath・destinationを参考に決める

url_base = f'https://{build_number}-{REPOSITORY_UUID}-{VCS_TYPE}.circle-artifacts.com/{node_index}' # 基本のurl
url = f'{url_base}{target_path}' # 該当artifactのurl
```

と表せる。

##ブラウザから取得

本来リポジトリのuuidはもっと簡単に手に入りそうな気がする。APIを叩いて取得するのは面倒なので。
試しにbitbucketの該当リポジトリのページ

```
https://bitbucket.org/<workspace>/<repository>/src/master/
```

のソースを開いて先ほどのuuidで検索してみると、

```javascript
window.__initial_state__ = {"section": {"repository": {"connectActions": [], "cloneProtocol": "https", "currentRepository": {"scm": "git", "website": null, "uuid": "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",...
```

と引っかかる。やったぜ。
CircleCIで初回設定する際は`__initial_state__`でソースを検索し、`section.repository.currentRepository.uuid`の値を利用すると良さそう。

CircleCIの変数でどこか持ってそうな気はするが…。

ちなみに、GitHubだと`octolytics-dimension-repository_id`がレポジトリのidらしい。

#注意

forkした場合は試していない。artifactsのurlのuuid部分、つまりリポジトリのuuidが変わるなら、moxciのようにCircleCIのAPIを叩く必要がありそう。

#参考文献
[Sphinx](https://www.sphinx-doc.org/ja/master/)
[ビルド アーティファクトの保存 - CircleCI](https://circleci.com/docs/ja/2.0/artifacts/)
[CircleCIのartifactsを使い倒す](https://talks.leko.jp/circleci-artifacts/)
[moxci](https://github.com/moxci/moxci)
[Get a job's artifacts - CircleCI API (v2)](https://circleci.com/docs/api/v2/#operation/getJobArtifacts)
[PRごとにCIでStorybookをビルドしてデザイナーとインタラクションまで作っていく話](https://qiita.com/resessh/items/38ef6492d0cf21facec8)
[CircleCI as a Resource](https://qiita.com/tkngue/items/968253067944481cff8e)
[[メモ] CircleCI_環境変数一覧（1.0&2.0） - Qiita](https://qiita.com/jkkitakita/items/4554a965e86d376e3ecd)
[Circle Ci 2.0 artifacts url - stackoverflow](https://stackoverflow.com/questions/46880788/circle-ci-2-0-artifacts-url)
[Returns the object describing this repository. - Bitbucket API](https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D#get)