Yarnを使うとAzure FunctionsのGitHub ActionsからのZipデプロイが檄遅になるのを回避する
npmとYarnでファイルのタイムスタンプの扱いが違うとかとんでもない罠だ。
問題
Azure FunctionsにJavaScript(TypeScript)のコードをGitHub ActionsでAzure/functions-actionアクションを使ってZipデプロイを行なっていました。
プロジェクトは func init --typescript
で生成した構成ほぼそのまま。
パッケージマネージャーをnpmからYarnに変えたところ、数分で済んでいたデプロイが数十分かかるようになってしまいました。
原因
Azure/functions-actionアクションを使ってデプロイする場合、node_modulesディレクトリごとZipで固めてデプロイ先の環境に展開しなおします。
ですが、毎回全ファイルをコピーしているわけではなく、タイムスタンプが更新されていなければスキップするようになっています。
Efficient file copy: Files will only be copied if their timestamps don't match what is already deployed. Generating a zip using a build process that caches outputs can result in faster deployments.
ここで、npmとYarnの動作の違いがネックになってきます。
npm
npmはnode_modulesの下に展開されるファイルのタイムスタンプを全て 1985-10-26T08:15:00.000Z
に固定しています。
これは、タイムスタンプの違いによってファイルのハッシュ値が異なってしまい発生する問題を回避するためだそうです。
Yarn
一方、Yarnではnode_modulesの下のファイルのタイムスタンプは、単純にインストールを行なった日時、またはローカルキャッシュされた日時になります。
つまり、GitHub Actions(またはその他のCI)でYarnのキャッシュを持ち回すようにしていない場合、常にCIが走った日時のタイムスタンプでZipデプロイが行われることになります。
npmでも一番最初のデプロイでは全ファイルのコピーが行われていましたが、当時は依存パッケージも少なく気にならなかったので気付いていなかっただけのようです。
解決策
npmに戻してしまうのも手段の一つです。
しかし、やんごとなき理由でパッケージマネージャーをYarnにせざるを得ないこともあるので、Yarnを使う前提の回避方法が必要です。[1]
キャッシュによって問題を回避する方法と、抜本的にAzure Functionsの使い方を変えてしまう方法の2種類があります。
キャッシュする
Yarnではnode_modulesの下のファイルのタイムスタンプはキャッシュに依存するので、素直にGitHub Actions上でYarnのキャッシュを保持するようにしましょう。
公式のcacheアクションのドキュメントにYarnの場合のサンプルが載っているので参考にします。
GitHub Actionsの設定ファイルは、例えば次のような形になるはずです。
# 関係するstepsだけ抜粋
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile
- run: yarn run build
- run: yarn install --frozen-lockfile --production
- name: Deploy
uses: Azure/functions-action@v1
id: fa-foobar
with:
app-name: func-foobar
publish-profile: ${{ secrets.FOOBAR }}
Run From Package
Zipパッケージを環境上に展開するためファイルコピーに関連する問題が発生してしまっています。
そこで、Zipパッケージをそのまま実行ファイルにする方法が用意されています。
むしろ今Azure Functionsを使うならRun From Packageを使うのが安牌なのですが、手が追いついてませんでした😓
既にZipデプロイをしているなら、よほど特殊な事をしていない限りはRun From Packageへの移行はすんなりいきます。
-
🍏はnpmでいいじゃん派です。 ↩︎
Discussion