⚙️

--frozen-lockfileで防ぐ、yarn installの破壊的変更

に公開

概要

久しく全体的にモジュールのバージョンを見直していた際に、開発用のDocker環境ではビルドが成功するのに、CI/CD環境では失敗するという問題に直面しました。

原因は、CI/CD環境でyarn installが実行された際、間接的な依存モジュールが意図せず更新され、開発環境のキャッシュとバージョンに差異が生まれたことでした。

古いCI/CDパイプラインだったため、依存関係を固定する設定が漏れていたのが直接の原因です。

現象:特定環境でのみビルドが失敗

  • Docker コンテナでビルドは成功
  • GitHub Actions での CI/CD では突然以下の TypeScript ビルドエラーが発生
error TS2769: No overload matches this call.
Argument of type '{ expiresIn: string; algorithm: "RS256"; }' is not assignable to parameter of 'SignOptions'.
Types of property 'expiresIn' are incompatible.
Type 'string' is not assignable to type 'number | StringValue | undefined'.
  • ローカル環境で node_modules を削除しクリーンビルドした際も同じエラーが再現。
    → 環境間の依存関係の差異が原因。

調査

1. 型定義の変更

ビルドが成功する環境と失敗する環境とで、インストールされている@types/jsonwebtokenのバージョンが異なっていました。

  • v9.0.7 (キャッシュ環境): expiresIn?: string | number;
  • v9.0.10 (クリーン環境): expiresIn?: StringValue | number;

v9.0.7v9.0.10PATCH でしたが、string を許容しなくなる変更が含まれていました。

2. 依存関係の解決経緯

yarn why @types/jsonwebtokenで依存関係を追跡すると、firebase-adminから間接的にインストールされていることが分かりました。

firebase-admin ^12.0.0
 └─ jwks-rsa ^3.1.0 → 3.2.0 (MINOR 更新)
    └─ @types/jsonwebtoken ^9.0.2 → ^9.0.4 (依存関係変更)
       └─ 実際の解決: 9.0.7 → 9.0.10

package.json のバージョン指定が ^ だったため、キャッシュの無いクリーン環境で ^9.0.4 の範囲内で最新の 9.0.10 が自動的にインストールされていました。

対策

今回の問題の根本的な対策は、ビルド環境ごとの依存関係を完全に一致させることです。

CI/CD でパッケージをインストールする際は、最低限yarn install --frozen-lockfile コマンド(npmの場合はnpm ci)を使用することが大事だと、再認識しました。

Discussion