CI ではプロジェクトの依存パッケージのインストールを走らせることが多いです。 1 からインストールを行うと、規模によっては数分掛かりることもあります。 数回であれば気になりませんが、push
の度複数ワークフローを実行とかになると、準備ステップに時間をあまり掛けたくありません。
(プライベートリポジトリの場合、 ワークフローの合計実行時間の制限もあるので余計に)
cache アクション
キャッシュには公式が管理している actions/cache という action を使います。これを使うと、以前と同じ状態、または近いものがあれば今に復元できます。
実行時間の掛かる処理によって作られたものを復元することで、 1 から再度作るよりも時間を短縮できるはずです。
2020-05-07 だとバージョン 1 が主流です。使うには以下のように記述します。
steps:
- # ...略
- uses: actions/cache@v1
id: something-cache
with:
path: ...
key: ...
restore-keys: ...
入力値
3 つの値を渡して実行できます。path
とkey
そしてrestore-keys
です。この中でpath
とkey
の 2 つの値は必須です。
まず最初にpath
にはキャッシュしたいディレクトリやファイルへの絶対パスを渡します。次にkey
にはキャッシュする際のキー( 512 文字以下)を指定します。
ワークフローの最後にそのキーによるキャッシュが行われ、次に実行した際にそのキーによるキャッシュが存在すればそれが復元されます。
restore-keys
は指定したキーによるキャッシュが存在しなかった場合に使われるフォールバック的なキーです。キーによって前方一致した最新のキャッシュを復元できるようになります。不完全でも前の結果がある方が処理を早く終えられるようなものに設定するのが良いです。
出力値
cache-hit
という値があります。これはキャッシュを復元できた場合のみ、steps.something-cache.outputs.cache-hit
にtrue
という文字列が格納されます。
ジョブの例
このジョブはパッケージマネージャ Yarn を用いた JavaScript プロジェクトを想定してます。プロジェクトルートにはpackage.json
があり、適用にパッケージが追加されてます。また依存はローカルでインストール済でその解決方法が記載されたyarn.lock
もプロジェクトルートに置かれれる状態です。
キャッシュに関するステップは- id: yarn-cache-dir
の次の次の行にあります。
jobs:
build:
# ubuntu 18.04 環境上で実行
runs-on: ubuntu-18.04
# それぞれ以下のNode.jsが使える環境にして実行
# - node@^10
# - node@^12
# - node@^13
strategy:
matrix:
node-version: [10.x, 12.x, 13.x]
# 実行処理
steps:
# Node.js の指定バージョンで使えるようにする
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
# コードを持ってくる
- uses: actions/checkout@v2
# キャッシュがあれば復元
- id: yarn-cache-dir
run: echo "::set-output name=value::$(yarn cache dir)"
- id: restore-keys
run: echo "::set-output name=value::node-dependencies-${{ matrix.node-version }}-v1"
- uses: actions/cache@v1
id: node-dependencies
with:
path: ${{ steps.yarn-cache-dir.outputs.value }}
key: ${{ steps.restore-keys.outputs.value }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ steps.restore-keys.outputs.value }}
# インストール
- run: yarn
キャッシュアクション部分を見ていきます。まずpath
ですが${{ steps.yarn-cache-dir.outputs.value }}
を指定してます、この中身は id がyarn-cache-dir
のステップのアウトプット値value
、つまりはyarn cache dir
コマンドを実行時の結果となっています。yarn
はインストール時に各パッケージ結果をキャッシュディレクトリに保存していて、次回のインストール時には可能ならそこからパッケージを解決してくれる為高速化が望めます。
echo "::set-output name=NAME::VALUE"
部分ですが、ここは特殊なecho
コマンドで、 GitHub Actions ではこのように書くと、steps.step-id.outputs.NAME
にVALUE
を格納して、後のステップで変数のように使えるようになります。
次にkey
です。これには${{ steps.restore-keys.outputs.value }}-${{ hashFiles('**/yarn.lock') }}-
を指定してます。steps.restore-keys.outputs.value
はid
がrestore-keys
なステップのアウトプット値value
とhashFiles
によるハッシュ値、それぞれの文字列を組み合わせたものを指定してます。
hashFiles
は GitHub Actions で使える関数の 1 つで指定したファイルパターンからハッシュを計算します。例のように**/yarn.lock
のように書くとプロジェクトに含まれるすべてのyarn.lock
ファイルを対象に計算してくれます。
最後のrestore-keys
では、key
からhashFiles
部分を除いたものを指定してます。こうしておくことで、依存に変更が入りyarn.lock
の中身が更新され、完全マッチなキャッシュが無くなったとしても、この最新のキャッシュを復元できるようになります。これは上記でも述べた前方一致すればいいキーだからです。
実行してみる
yarn.lock
が同じ状態で 2 回ワークフローを走らせてみます。キャッシュがうまく効いていれば 2 回目はより早く完了するはずです。
1 回目
actions/cache1 回目を実行しました。これにより 2 つの事が分かりました。
yarn
の実行時間は 17 秒終わる前に実行されるキャッシュアクションによるステップのログ
Cache saved successfully
からキャッシュがnode-dependencies-12.x-v1-a135e48fc66de6bb6175d4726583f1b07aefbaac334cc580f8f80e8b70532c3a
キーで作られた
2 回目
上記が完了した後 actions/cache1 回目2 回目を実行しました。
yarn
の実行時間は 5 秒Cache hit occurred on the primary key... not saving cache
(プリマリキーでキャッシュヒットが発生しました、キャッシュを保存してません)から今度はキャッシュが作られなかった
上記の結果からキャッシュが正常に作られたことで、ワークフローに掛かる時間が短縮できることが分かりました。
actions/cache1 回目2 回目1 回目とactions/cache1 回目2 回目1 回目2 回目はリンク先から結果を見ることができます。
キャッシュを復元できた時、作成コマンドの実行がいらない場合
トップの例でキャッシュが復元できた時steps.something-cache.outputs.cache-hit
がtrue
になると書きました。もし作成コマンドの実行がいらないなら、この値を使ってステップをスキップできます。
それにはstep.if
を設定します。以下ではsteps.something-cache.outputs.cache-hit != 'true'
の時のみrun: ...
部分のコマンドが実行されます。
steps:
- # ...略
- uses: actions/cache@v1
id: something-cache
with:
path: ...
key: ...
restore-keys: ...
- if: steps.something-cache.outputs.cache-hit != 'true'
run: ...