依存関係の管理

サイトが npm 依存関係をどのようにインストール、検証、更新するか

このページの内容は古くなっている可能性があり、一部のリンクが無効になっている場合があります。 このページのより新しいバージョン英語版にあります。

詳細情報 ...

このページが最後に更新されてからの英語ページの変更を確認するには、 GitHub compare bdfe4631..08cb6781 にアクセスし、content/en/site/build/dependencies.md を検索してください。

npm 依存関係はコミット済みの package-lock.json によって固定され、インストール時にはレビュー済みのライフサイクルスクリプトのみが実行されます。 これらの制御の背景にある脅威モデルと根拠については、Supply-chain security を参照してください。

インストール時の動作

CI、devcontainer、Netlify は、ロック固定かつスクリプト無効でインストールし、その後、レビュー済みのフック1つだけを明示的に再有効化します。 それは固定された Hugo バイナリを取得する hugo-extended のリビルドです。 リビルドは scripts/rebuild-hugo-extended.mjs を通じて実行され、制限付きバックオフでフェッチをリトライし、HUGO_* インストーラーオーバーライドが設定されている間は実行を拒否します。 環境ごとの詳細は以下のとおりです。

  • CI: npm run ci:min を実行します。 サイトをビルドするジョブは続けて npm run ci:prepare を実行します。
  • Devcontainer: npm run install:safe を実行します。 同じ動作を保証しますが、オプショナルな依存関係を保持します。
  • Netlify: npm run install:safe を実行します。 Netlify のビルドコマンドによって、不活性な自動インストールの後、クリーンなワーキングツリーのチェックの間に実行されます。
    • ロックのドリフトやその他の Git で検出可能な変更があればビルドが失敗します。
    • インストールが変更していないパスでの失敗については、後述の古い Netlify ビルドキャッシュを参照してください。
  • ローカル: npm run install:safe、または標準の npm install を実行します。 標準の npm install は、package.json と一致する間はロックに従い、ライフサイクルスクリプトを無効化するかわりに許可リストでゲートします。 詳細はローカルセットアップを参照してください。

ネストされた Docsy テーマのセットアップも同じ動作に従います。 prepare ステップが Docsy 自身のロック固定かつスクリプト無効のテーマ依存関係インストールを呼び出します。

古い Netlify ビルドキャッシュ

Netlify はデプロイコンテキストごとにビルドキャッシュを保持します。

  • プロダクション用に1つ
  • Deploy Preview 用にヘッドブランチ名ごとに1つ。 そのブランチ名の最初のビルド時にプロダクションキャッシュからシードされます。 キャッシュの系統は明示的なクリアでのみ消滅します。 ブランチの削除は Netlify からは見えないため、同じ名前で再作成されたブランチ(リサイクルされたボットブランチ名を含む)は古いキャッシュに再接続されます。

各キャッシュにはリポジトリのクローンが含まれており、git サブモジュールを削除するコミットをチェックアウトすると、サブモジュールのワーキングツリーがそのまま残ります。 そのため、削除されたサブモジュールがキャッシュに乗って後のビルドに未追跡の残留物として混入し、クリーンなワーキングツリーのチェックに失敗する可能性があります。 デプロイログには ?? 接頭辞のステータス行としてそのパスが表示されます。

パスを .gitignore に追加するのではなく、該当するビルドキャッシュをクリアしてください。

  • プロダクション:
    • Deploys > Trigger deploy からキャッシュをクリアしてサイトをデプロイします。
  • Deploy Preview: 既にビルドされた各ブランチは独自のキャッシュコピーを保持しており、プロダクションのクリア後も影響を受けません。
    • PR の最新デプロイページから Retry > Clear cache and retry with latest branch commit でクリアします。 ブランチ全体を一括でクリアする方法はありません。

依存関係の更新

通常の更新

npm run update:packagespackage.json のみを更新します。 提供されるバージョンにはリリースクールダウンが適用されます。 その後、ロックファイルを再生成し、両方のファイルをまとめてコミットします。

npm install --package-lock-only --ignore-scripts

スクリプトを持つパッケージ

allowScripts エントリを持つ(または必要とする)パッケージを追加または更新する場合、変更を行うコントリビューターは以下を行います。

  1. 新しいバージョンのライフサイクルスクリプトをレビューする。
  2. 結果を、依存関係の変更と一緒にコミットし、PR レビューで検証する。 必要なスクリプトは正確なバージョン承認として、不要なスクリプトは名前レベルの拒否(false、以降のバージョンアップ時に更新不要)として記録する。
  3. 新しい承認の場合、.github/renovate.json5 の Renovate 自動マージ除外リストにもそのパッケージを追加する。 承認済みパッケージのすべてのバージョンアップには上記の手順が必要なため、その更新 PR はコントリビューターを待つ必要がある。

ロックファイルのメンテナンス

  • 依存関係を変更した場合: 通常の更新と同様にロックファイルを再生成し、package.json と一緒にコミットする。
  • ロックファイルのマージコンフリクト: main のバージョンを採用し、再生成コマンドを再実行する。
  • 依存関係を変更していないのにロックファイルが変更された場合postinstall チェックがインストール時にこれを検出すると警告します): ドリフトを示しています。 ロックファイルを復元し、リライトをコミットするのではなく調査してください。

サプライチェーン制御

サプライチェーン監査

サプライチェーン監査テスト scripts/supply-chain-audit.test.mjs は、test:local-tools の実行ごとにコミット済みファイルのみから以下の制御を検証します。 これにより、制御の退行はインシデントを待たずにテスト失敗として検出されます。 監査自体の検証原則については、その設計ページを参照してください。

PR で監査が失敗した場合、アサーションメッセージに期待される条件が記載されています。 よくあるケースは以下のとおりです。

  • allowScripts エントリを持つ依存関係をバージョンアップした場合: スクリプトを持つパッケージに従ってください。 失敗メッセージにエントリが移行すべきバージョンが示されます。
  • インストールパスのスクリプト、.npmrc、または netlify.toml を変更した場合: その失敗こそが目的です。 監査はインストール面を固定し、変更のたびに意図的なレビューが行われるようにしています。 変更に合わせて対応するアサーションを更新し、PR にその理由を記載してください。

グリーンにするためだけにアサーションを緩和しないでください。 各アサーションはこのページの制御を強制しているため、まず変更によってどの制御が緩和されるかを把握してください。

監査のスコープ外:

  • GitHub ワークフローファイル
  • Docsy テーマ自身の依存関係インストール(上流で監査済み)
  • インストール境界を越えたビルド側の npm スクリプト

リリースクールダウン

バージョン解決では、設定された最小経過期間より新しいリリースは無視されます。

  • 適用: .npmrcmin-release-age
  • スコープ:
    • 影響を受けるのはバージョン解決操作のみです。 ロック固定インストール(npm ci)はバージョンを解決しません。
    • npm はプロジェクト設定をユーザー設定より優先するため、ユーザーの .npmrc でより厳しいクールダウンを設定していても、ここのプロジェクト値に緩和されます。 特定の呼び出しで自分の設定を維持するには、npm_config_min_release_age 環境変数を設定してください。 この変数は両方の設定より優先されます。
  • Renovate: 開く更新 PR に独自のクールダウンを適用します。 .github/renovate.json5minimumReleaseAge で設定されます。 人間のレビューなしでマージされる更新にはより長い期間が設定されます。

ライフサイクルスクリプト許可リスト

インストール時にパッケージのライフサイクルスクリプトが実行されるのは、その正確な名前とバージョンが allowScripts 許可リストに記載されている場合のみです。

  • 適用: package.jsonallowScripts マップ。 .npmrcstrict-allow-scripts によりフェイルクローズドとなります。
  • 拒否:
    • false に設定されたエントリは、レビュー済みの拒否を記録します。 パッケージはインストールされますが、そのスクリプトはスキップされます。
    • 拒否は何も許可しないため、バージョンをまたいで名前単位でパッケージをカバーします。
  • --ignore-scripts との相互作用:
    • 許可リストはフィルタリングのみを行います。 ignore-scripts が無効にしたスクリプトを再有効化することは決してないため、スクリプト無効のインストールでは許可リストの有無にかかわらず何も実行されません。
    • レビュー済みの例外は、呼び出し箇所で明示的に --ignore-scripts=false を指定する必要があります。

npm バージョンフロア

アクティブな npm が engines フロアより古い場合、インストールは失敗します。 engines フロアとは、上記の制御をサポートする最も古いバージョンです。

  • 適用:
    • package.jsonengines がフロアを設定します。
    • .npmrcengine-strict によりフェイルクローズドとなります。
  • フロアポリシー:
    • npm が制御の適用ギャップを修正するたびにフロアは引き上げられます。
    • Node LTS リリースにバンドルされる npm バージョンに準拠するため、デフォルトのツールチェーンでチェックを通過できます。
  • Netlify:
    • Netlify の Node バンドルのデフォルト npm はフロアより古い場合があります。 netlify.tomlNPM_VERSION でフロアを満たすバージョンを固定しています。
    • 少なくともフロアが引き上げられた際にはこの固定値も更新してください。

不活性な Netlify 自動インストール

ビルド開始時の Netlify の自動インストールは、netlify.tomlNPM_FLAGS によって無効化されます。

  • --dry-run: npm はインストールが何を変更するかを解決してログに記録しますが、何も書き込みません。
  • --ignore-scripts: ライフサイクルスクリプトは、ドライランの副作用としてではなく、明示的な指示により無効化されたままになります。

スコープ: NPM_FLAGS は Netlify のビルド設定であり npm の設定ではありません。 自動インストールにのみ適用され、ビルドコマンドの npm 実行には適用されません。

多層防御: 実際のインストールnpm ci であり、node_modules を丸ごと置換します。 そのため、自動インストールやビルドキャッシュの node_modules 内の残留物は、クリーンなワーキングツリーのチェックからは見えない(Git で検出可能な変更のみを見る)にもかかわらず、ビルドには持ち越されません。

素の npx の禁止

リポジトリの設定(パッケージスクリプト、CI、ヘルパースクリプト、コントリビュータードキュメント)は、npx BIN としてバイナリを呼び出しません。 node_modules が古いか存在しない場合、npx は公開レジストリにフォールバックし、その名前を持つパッケージが何であれ実行します。 インストールプロンプトは防御になりません。 非インタラクティブなコンテキストではスキップされ、その他の場合は反射的に「はい」を誘います。 ローカライズされたコントリビュータードキュメントのコピーは、ドリフトトラッキングを通じてこのルールに追従します。

  • 代替手段:
    • パッケージスクリプトは、依存関係が提供するバイナリを直接呼び出します。 npm が node_modules/.binPATH に追加するため、バイナリが存在しなければレジストリへの通信なしに大きなエラーで失敗します。
    • その PATH エントリがないコンテキスト(ドキュメント、スタンドアロンスクリプト)では、npm exec --no -- BIN を使用します。 これはインストールを行いません。
  • 適用: レビューの規律による。 自動化されたチェックはありません。