CircleCI 2.0 キャッシュ指定で詰まったこと

CircleCIの1.0が8月でサポート終了ということで、先月くらいに、重い腰をあげて作業していました。
いくつかキャッシュの指定関連で詰まって、試行錯誤したのでメモしておきます。

## ディレクトリをjob単位で設定できない
以前書いたこの記事の内容です。
[CircleCI 2.0でbuild_dirを指定する](https://qiita.com/wasnot/items/b2543faa4b8ed6082936)

1.0では`build_dir`という設定があり、checkoutしたリポジトリルートからたどる場所をjob単位で簡単に固定できましたが、
2.0ではtask単位で指定する必要があります。
（2.0でjob単位で指定できるのはリポジトリルートの場所だけ）

名前は`working_directory`という名前に変更されています。
- [Equivalent of CircleCI 1.0 build_dir](https://discuss.circleci.com/t/equivalent-of-circleci-1-0-build-dir/12840)

ただし、`working_directory`は`run`や`deploy`等のタスクにしか適用されず、
キャッシュ関連のタスクである、`save_cache`,`restore_cache`には適用されません :scream: 

### 対策
なのでkey等でファイルのchecksumを参照する場合はrootパスから指定する必要があります。
上記記事の再掲ですが、以下のようにします。

```yaml:ダメな例
      - restore_cache:
          working_directory: api # ここのブロックには有効にならない
          key: venv-{{ checksum "requirements.txt" }}
      # ....
      - save_cache:
          working_directory: api # ここのブロックには有効にならない
          key: venv-{{ checksum "requirements.txt" }}
          paths: venv
```


```yaml:いい例
      - restore_cache:
          key: venv-{{ checksum "api/requirements.txt" }} # checksum関数に渡すときに示す。
      # ....
      - save_cache:
          key: venv-{{ checksum "api/requirements.txt" }}
          paths: api/venv # 保存するパスもきちんと示す
```


## キャッシュキーに環境変数を指定できない
キャッシュのキーは特殊なリテラルでchecksum等を指定できます。

使える関数はこちら: [using-keys-and-templates](https://circleci.com/docs/2.0/caching/#using-keys-and-templates)
いかに少し転記します。

| Template  | Description |
|:--|:--|
| `{{ .Branch }}`  | The VCS branch currently being built. |
| `{{ .BuildNum }}`  | The CircleCI build number for this build. |
| `{{ .Revision }}` | The VCS revision currently being built. |
| `{{ .Environment.variableName }}`  | The environment variable variableName (supports any environment variable exported by CircleCI or added to a specific Context—not any arbitrary environment variable). |
| `{{ checksum "filename" }}` |

ここに書いてあるんですが、`.Environment.variableName`を使えば環境変数もkeyに含めそうだな、と思っていました。
でも自分で設定した変数は使えないと書いてますね、、


- パスの記事: [Can I use variables in cache paths?](https://discuss.circleci.com/t/can-i-use-variables-in-cache-paths/11393)
- keyの記事: [Cannot use circle.yml environment variables in cache keys](https://discuss.circleci.com/t/cannot-use-circle-yml-environment-variables-in-cache-keys/10994)
上の記事でもありますが、他の方法でも関数自体は展開してもらえませんでした。

```yaml:ダメな方法
- save_cache:
    key: v1-{{ epoch }}
    paths:
      - $(my cache dir) # 展開されない
- save_cache:
    key: v1-{{ epoch }}
    paths:
      - $MY_CACHE_DIR # 環境変数も展開されない
- save_cache:
    key: v1-{{ epoch }}
    paths:
      - {{ .Environment.MY_CACHE_DIR }} # これはCircleCIが設定した値しか入らない
- save_cache:
    key: v1-{{ .Environment.MY_CACHE_DIR }} # keyでも同じ。
    paths:
      - /my/cache/dir
```

悲しいですね。

### 対策
pathの対策はhardcodeです :cry:

```yaml:動く例
- save_cache:
    key: v1-{{ epoch }}
    paths:
      - /my/cache/dir # hardcodeしよう
```

keyも基本的にはhardcodeですが、checksumとかで環境変数の値に応じてkeyを変えたいケースもありそうです。
その場合は環境変数を一回ファイルに書き出してchecksumします。


```yaml:動く例1
- save_cache:
    key: v1-{{ 1.0.0 }} # 環境変数もハードコードしちゃう
    paths:
      - /my/cache/dir 
```


環境変数が展開できる場所でファイルに書き出し、そのファイルのchecksumをkeyに入れます。

```yaml:動く例2
version: 2
jobs:
  build:
    # ...
    environment:
      MY_VERSION: 1.0.0
    steps:
      - checkout
      - run: echo ${MY_VERSION} > _my_version  # 展開される場所で書き出す。
      - restore_cache:
          key: v1-{{ checksum "_my_version" }} # ファイルのchecksumをkeyに入れる
      # ...
      - save_cache:
          key: v1-{{ checksum "_my_version" }}
          paths:
            - /my/cache/dir
```

そもそもキャッシュすべきファイルが何か、っていうのも難しいですし、
基本的にバージョン依存はファイルとしてリポジトリに含まれるケースが多いので、
あまり遭遇しないかもしれませんね。

## 削除できない

一度キャッシュされた内容は削除できないようです。
上のように色々試行錯誤して、ちゃんとキャッシュされてるかを確認して行ったらゴミというか以前試した空のキャッシュができてしまって上書きもできなくなり困りました。

- [Does clear cache work for 2.0?](https://discuss.circleci.com/t/does-clear-cache-work-for-2-0/10965)

### 対策
特にないです。
試行錯誤中はkeyに`test`とかprefixを入れるとか、名前や順番を変えて試すしかないです。
そのうち削除機能やフラグが実装されるかもしれませんね。。

## まとめ

大体の問題は既にdiscuss.circleci.comで議論・回答されている内容かなと思うので、
困った時は見て見るといいな、と思います。

でも結構CircleCI担当者の回答が素っ気ないというか、「これで十分」「できないのでハードコードして」という感じでなんか冷たく感じました。
「忙しくて無理 :sweat: 」「いつか検討するからissueに入れておくね」「頑張るから待っててね〜」みたいな姿勢が欲しくなってしまいました。
贅沢ですね。