簡単なテストケースをいくつか書いただけで5分以上掛かるようになってしまったのでチューニングの機運!
ということで今回はキャッシュについて
バイナリのキャッシュ
Cypressはバイナリのインストールをnode_modules
ではないところにインストールしています
Installing Cypress | Cypress Documentation
CircleCIでCIを回すときに使っているイメージはLinuxなので~/.cache/Cypress
にインストールされるが毎度インストールが走ります
その結果毎度npm install
に1分以上もかかるということになってしまいます
ここはCircleCIのcacheを使えば楽勝じゃん!という感じなのですがそう簡単ではありませんでした
単純に
- save_cache: key: sample-key-{{ .Branch }}-{{ checksum "package.json" }} paths: - "node_modules" - "~/.cache"
としたら下記記事のようにPermissionDiniedで失敗してしまいました
使用するイメージが違ったりすると権限の問題でPermission Dinied
エラーにより失敗するようですね
今回のworkflowではCypress以外にもnodeのイメージを使っていてキャッシュ周りは共通で処理していたのでこの問題に当たりました
Cypress側でキャッシュするディレクトリを指定できれば良さそうです
公式に戻ってもう一度読んでみるとCYPRESS_CACHE_FOLDER
という環境変数があります
これを設定してあげればCypressはそのディレクトリにバイナリを探しに行きます
ということで下記のような設定でキャシュできます
- .circleci/config.yml[一部抜粋]
executors: cypress: docker: - image: cypress/base:10 environment: - CYPRESS_CACHE_FOLDER: /tmp/.cache/Cypress commands: save_cypress_binary: steps: - save_cache: key: sample-key-cypress-binary-{{ .Branch }}-{{ checksum "package.json" }} paths: - "/tmp/.cache/Cypress" jobs: e2e: executor: cypress steps: - checkout - restore_npm - restore_cypress_binary - run: npm install - save_cypress_binary - save_npm
executorで環境変数をセットすることでCypressを実行する際のcache読み込み先と新規インストールする際の保存先を設定します
cache生成時にexecutorで指定したキャッシュディレクトリを指定することで次回実行時にキャッシュから取得することができるようになります
これでPermissionDiniedが出なくなりました
時間も短縮されました!
ここまでだとnode
のイメージとcypress
のイメージで別々の記述が必要になってしまいます
できればまとめたいですね
環境変数をイメージ別(executor別)に用意してそれを利用することができれば実現できそうです
executor側での指定
environment: IMAGE_NAME: 'cypress'
キャッシュキーの指定部分
key: sample-key{{ .Environment.IMAGE_NAME }}-{{ .Branch }}-{{ checksum "package.json" }}
で、なんだ楽ちん!と思っていたら
キーに<no value>
と出てくるんですね・・・
これ使えって書いてあるのに環境変数参照できないのか。。。なんて落胆していたら
Cannot use circle.yml environment variables in cache keys - CircleCI 2.0 / 2.0 Feature Requests - CircleCI Discuss discuss.circleci.com
ということだそうです
{{ .Environment.variableName }}
で参照できるのはCircleCIの組み込み環境変数のみのようです
なので参考ページにもあるようにワークアラウンドではありますがecho
で環境変数の中身をテキストに出力してそのファイルのchecksumをキーに含めることでやりたいことは実現できます
アンカーとエイリアスを使ったりしてゴニョゴニョした結果最終的に下記のような設定になりました
commands: restore_npm: parameters: prefix: &cache_key_prefix_parameter description: cache key prefix type: string default: sample-key steps: - run: &echo_env echo "$IMAGE_NAME" > /tmp/env_image_name.txt - restore_cache: keys: - &cache_key << parameters.prefix >>-{{ checksum "/tmp/env_image_name.txt" }}-{{ .Branch }}-{{ checksum "package.json" }} - << parameters.prefix >>-{{ checksum "/tmp/env_image_name.txt" }}-{{ .Branch }}- - << parameters.prefix >>-{{ checksum "/tmp/env_image_name.txt" }}- save_npm: parameters: prefix: <<: *cache_key_prefix_parameter with_cypress: description: is cypress flag type: boolean default: false steps: - run: *echo_env - when: condition: << parameters.with_cypress >> steps: - save_cache: key: *cache_key paths: - "node_modules" - "/tmp/.cache/Cypress" - unless: condition: << parameters.with_cypress >> steps: - save_cache: key: *cache_key paths: - "node_modules" executors: node: docker: - image: circleci/node:10.15.3-stretch-browsers environment: - CYPRESS_CACHE_FOLDER: /tmp/.cache/Cypress - IMAGE_NAME: node cypress: docker: - image: cypress/base:10 environment: - CYPRESS_CACHE_FOLDER: /tmp/.cache/Cypress - IMAGE_NAME: cypress jobs: # Cypressを使用するjob e2e: executor: cypress steps: - restore_npm ..... ..... - save_npm: with_cypress: true ..... ..... # 普通のjob test: executor: node steps: - restore_npm ..... ..... - save_npm ..... .....
こんな感じですね
結局イメージによって分岐を発生させているのでちょっと微妙感ありますがrestore_cache時にはcypress用のキャッシュはない状態なので余計なリストア時間を短縮することができるようになりました
勉強になりました