Bitbucket Pipelines の cache の使い方

ようやく理解したのでメモ

# TL;DR
- cache がやっていることは、「保存しておいたディレクトリをビルドの最初にまとめて展開している」だけ。
- 自分で cache を活用しようとしたら、当然、「キャッシュがあれば使う。なければダウンロード」というスクリプトを書く必要がある。

# はじめに
Bitbucket Pipelines には cache という機能があります。
もともと私がやろうとしていたことは、「`make && make install` するようなライブラリのビルドをキャッシュで速くしたい」ということだったのですが、
[公式ドキュメント](https://confluence.atlassian.com/bitbucket/build-test-and-deploy-with-pipelines-792496469.html "Build, test, and deploy with Pipelines - Atlassian Documentation")の、特に [custom cache に関する部分](https://confluence.atlassian.com/bitbucket/caching-dependencies-895552876.html "Caching dependencies - Atlassian Documentation")がわかりにくくてハマってしまいました。

# ところで
Bitbucket Pipelines では、任意の Docker image を元にすることができます。
よって、

- 開発環境の docker image を public にすることができる (例えば Dockerhub)
- public ではないものの、private な docker registry から pull することができる (例えば自分で建てた docker registry)

以上のどちらかに該当する人は、そもそも Pipelines のキャッシュではなく、docker build 側のキャッシュ機構に任せたほうが何かと楽だと思います。
以下は、何らかの理由で docker での開発ができなかったり、公開が不可能だったり、あるいは docker ではどうしても不可能なワークフローをキャッシュ化したい人向けとなります。

# 本題：Pipelines の cache を使う
ここでは、MeCab+IPAdic のビルドをキャッシュ化してみます。

最終的な構成は次のようになりました：

```
your_repo/
  bitbucket-pipelines.yml
  bitbucket-pipelines-mecab-download.sh
  bitbucket-pipelines-ipadic-download.sh
```

それぞれ説明していきます。

## bitbucket-pipelines.yml

```yml
image: python:3.6.4

pipelines:
  default:
    - step:
        caches:
          - pip
          - mecab
          - mecab-ipadic
        script:
          - bash bitbucket-pipelines-mecab-download.sh
          - (cd ~/mecab/mecab/mecab-0.996 && make install && ldconfig)
          - bash bitbucket-pipelines-ipadic-download.sh
          - (cd ~/mecab/mecab-ipadic/mecab-ipadic-2.7.0-20070801 && make install)
          - pip install -r requirements.txt
          - python setup.py test  # your test here

definitions:
  caches:
    mecab: ~/mecab/mecab
    mecab-ipadic: ~/mecab/mecab-ipadic
```

`script` 内の `bash bitbucket-pipelines-mecab-download.sh` が、
「mecab のビルド済みキャッシュをチェック。なければソースをダウンロードして `make`」
をしています。
その直後の行で `make install` をして、実際に mecab をインストールしています。

`bash bitbucket-pipelines-ipadic-download.sh` も同様です。

それらが終わったら環境構築が終了なので、`python setup.py test` で本当にやりたかったテストを走らせています。

## bitbucket-pipelines-mecab-download.sh

```bash
#!/bin/bash
CACHE_DIR=${HOME}/mecab/mecab
mkdir -p ${CACHE_DIR}
if [ -d "${CACHE_DIR}/mecab-0.996" ]; then
  echo "found mecab cache"
else
  wget "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE" -O mecab-0.996.tar.gz
  tar zxfv mecab-0.996.tar.gz -C ${CACHE_DIR}/
  cd ${CACHE_DIR}/mecab-0.996
  ./configure --with-charset=utf8
  make
fi

```

このスクリプトは、先述のように
「最初の `if` で mecab のビルド済みキャッシュをチェック。なければソースをダウンロードして `make`」
をしています。
Pipelines 上ではどうせ同じ設定でしか動かさないので、`./configure` と `make` まで済ませてしまいます。(それらが終われば `make install` は基本的にコピーしか動かないのですぐ終わります)[^1]

ここでチェックしている「キャッシュ」とは、Pipelines が以前に一度でも成功状態で終了していれば、Pipelines 上で保存されて、新しいビルドの開始時に Pipelines 側で配置してくれているものです。
`bitbucket-pipelines.yml` の `step: caches:` 以下と `definitions: caches:` 以下で指定しているディレクトリがそれです。
(`pip` の `definitions` がないのは、よく使われるパッケージマネージャのキャッシュの定義は Pipelines が事前に設定されているからです。)

## bitbucket-pipelines-ipadic-download.sh

```bash
#!/bin/bash
CACHE_DIR=${HOME}/mecab/mecab-ipadic
mkdir -p ${CACHE_DIR}
if [ -d "${CACHE_DIR}/mecab-ipadic-2.7.0-20070801" ]; then
  echo "found ipadic cache"
else
  wget "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM" -O mecab-ipadic-2.7.0-20070801.tar.gz
  tar zxfv mecab-ipadic-2.7.0-20070801.tar.gz -C ${CACHE_DIR}/
  cd ${CACHE_DIR}/mecab-ipadic-2.7.0-20070801
  ./configure --with-charset=utf8
  make
fi

```

IPAdic についても、先ほどと同じことをしています。

## どれだけ早くなるの？

![image.png](https://qiita-image-store.s3.amazonaws.com/0/62450/c52d2adc-de29-8b6a-4b28-3d841e66f00a.png)

私の環境では、最終的に1/3くらいに短縮されました。
(ちなみに、短縮後の 51 sec のうち、`pip install` にかかっている時間が26秒でした。いくらダウンロードにキャッシュが効いているとは言っても、インストールが重い処理だとやはり時間がかかってしまいます。
冒頭で言った通り、できるならば素直に docker image として運用したいところです)

# 参考資料
- [Caching dependencies - Atlassian Documentation](https://confluence.atlassian.com/bitbucket/caching-dependencies-895552876.html "Caching dependencies - Atlassian Documentation")


[^1]: と思ったけど、ディレクトリまるごと復元されるので、前回の `make` の結果が残っているから Makefile 側の冗長性排除機構が動くので `make` は毎回やってしまっても実行時間に影響はない気がしてきた