依存関係の更新
目標は、依存関係が最新で既知の脆弱性がなく、ユーザーが信頼できる変更として提供されることです。すべてのステップはプロジェクト自身のチェックで検証され、リスクのあるステップは個別にレビューまたは元に戻せるように分離されています。
依存関係の作業は、典型的に2つの方法で失敗します。1つは「ビッグバン」アップグレードで、すべてを一度にバンプし、何かが壊れてもどのバンプが原因か特定できません。もう1つは、問題を隠す「修正」、例えば --force、--legacy-peer-deps、バージョン範囲の緩和、アドバイザリーの抑制です。以下のワークフローは、これら両方を避けるように構築されています。
0. 依頼内容を判断する
- 確認のみ(「脆弱性はある?」「何が古い?」): ステップ1〜3を実行し、報告して停止します。ファイルは変更しません。
- 更新(「depsを更新して」「監査警告を修正して」「パッケージを最新に保って」のデフォルト): ワークフロー全体を実行します。
- 1つのパッケージ(「viteをv8にアップグレードして」): ステップ1〜2を実行し、そのパッケージ(およびそれに伴って移動する必要があるパッケージ)に対してステップ6を実行し、その後ステップ7〜8を実行します。
1. 事前準備
-
リポジトリのコントリビューター向けドキュメント(
AGENTS.md、CLAUDE.md、CONTRIBUTING.md)とCI設定(.github/workflows/*)を読み、検証コマンド(lint、typecheck、test、build)とコミットメッセージの規約を把握します。リポジトリがCIで実際に実行しているものを使用します。何もドキュメント化されていない場合は、マニフェストで定義されているスクリプトを使用します。 -
ワーキングツリーがクリーンであることを確認します。そうでない場合は停止して確認します。ユーザーの未コミットの作業を依存関係のコミットに混在させると、実際に害を及ぼします。
-
ロックファイルとマニフェストからエコシステムを検出します(1つのリポジトリに複数のエコシステムが存在する可能性があります):
ファイル 参照先 package-lock.json、pnpm-lock.yaml、yarn.lock、bun.lockreferences/node.mdrequirements*.txt、pyproject.toml、uv.lock、poetry.lock、Pipfile.lockreferences/python.mdCargo.toml/Cargo.lockreferences/rust.mdgo.mod/go.sumreferences/go.mdその他(Gemfile.lock、composer.lockなど) エコシステム独自のツールと、監査用の scripts/osv_check.pyを使用対応する参照ファイルを読みます。そこには正確なコマンドとそのエコシステムの落とし穴が記載されています。
-
プロジェクトが対象とするランタイムバージョンを見つけます:
.nvmrc、engines、.python-version、requires-python、rust-version、goディレクティブ、またはCI内のバージョン。一部のパッケージはランタイムに追従する必要があり、最新リリースではありません。例えば、@types/nodeはプロジェクトが実行しているNodeのメジャーバージョンと一致する必要があるため、より新しいNodeメジャーにバンプすると、コードが本番環境でクラッシュするAPIを使用できるようになります。「最新」は候補として扱い、ターゲットとしては扱いません。 -
何も変更する前に検証コマンドを実行し、結果を記録します。すでに何かが失敗している場合、それを今知っておく必要があります。そうしないと、もともとあった障害をアップグレードのせいにしたり、赤いブランチを出荷したりすることになります。ベースラインが赤の場合は、ユーザーに伝え、続行するかどうかを尋ねます。
2. 棚卸し
エコシステムのツールを使用して2つのリストを収集します(参照を参照):
- 脆弱性: ネイティブの監査ツール。インストールされておらず、一時的に実行できない場合は、バンドルされたスキャナーを使用します:
python3 <skill-dir>/scripts/osv_check.py <lockfile>...(osv.devに問い合わせます;標準ライブラリのみ)。 OSVスキャンをネイティブツールに加えて実行するのはコストが低く、しばしば価値があります。なぜなら、ネイティブツールはパッケージのネストまたは重複したコピーのアドバイザリーを見逃すことがあるからです。 - 古いパッケージ: 現在 → 望ましい(宣言された範囲内) → 最新。
各脆弱性について、どの直接依存関係が脆弱なパッケージを引き込み、その直接依存関係のどのバージョンが修正するかを特定します。ユーザーは直接依存関係に対してアクションを起こします。推移的なパッケージ名だけではアクション可能ではありません。
3. 計画と報告
すべてを3つのバケットに分類し、変更を行う前に計画を短い表で示します:
- セキュリティ修正: アドバイザリーを除去するもの。最も緊急。修正にメジャーバンプが必要な場合は、それはメジャー(バケット3)ですが、そこに最初にリストし、セキュリティ駆動であることを明記します。
- セーフバンプ: semver内のパッチ/マイナー(0.xパッケージの場合、マイナーは破壊的変更としてカウントされます)。
- メジャー: その他すべて、各行1つずつ。一緒に移動する必要があるパッケージ(フレームワークとそのプラグイン、ツールとその型定義、ピア依存関係を共有するパッケージ)をグループ化し、各グループを1つのユニットとして扱います。
重要度は順序付けに影響し、到達可能性は説明に影響します。開発専用のテストランナーにおける重要アドバイザリーであっても修正する価値はありますが、ユーザーを驚かせないために、それが開発専用であることを明確に述べます。確認のみのリクエストの場合、このレポートが成果物です。重要度、アドバイザリーID、依存関係パス、修正バージョンを含めます。
4. ブランチ
ユーザーがこの作業用にすでに作成したブランチにいない限り、最新のデフォルトブランチから chore/deps-YYYY-MM-DD(今日の日付)を作成します。
5. セキュリティ修正とセーフバンプ
- 範囲内のセキュリティ修正を適用し、検証コマンドを再実行し、コミットします。
- セーフなパッチ/マイナーバンプをまとめて適用し、検証コマンドを再実行し、コミットします。 検証が失敗した場合は、二分探索します: バッチの半分を元に戻して再試行し、原因を見つけ、それを取り出してメジャーと同様に処理します。
パッケージマネージャーにロックファイルを書き込ませます。ロックファイルを手動で編集しないでください。
6. メジャー、1ユニットずつ
各メジャーユニットについて、順番に(セキュリティ駆動を最初に、次にそれに依存するものより先に基盤ツール、例えばコンパイラの前にそれにピア依存するリンタープラグイン):
- 変更内容を読む。 通過するすべてのメジャーの移行ガイドまたはリリースノートを見つけます(v2→v5はv3、v4、そして v5を読むことを意味します)。良い情報源は、プロジェクトの
CHANGELOG.md、GitHubリリース、ドキュメントサイトの「移行」ページです。このコードベースに適用される破壊的変更を抽出します: 削除または名前変更されたAPIと設定キーをgrepします。 - ピアを確認する。 新しいバージョンが、その周囲のすべてのピアおよびエンジン要件を満たしていることを確認します。プラグインが追いついていないために、最新のメジャーをまだ採用できない場合があります。その場合は、動作する最新バージョンを採用し、残りをブロックしているものを記録します。
- アップグレードして修正する。 ユニットをバンプし、移行を適用し(設定の名前変更、APIの変更、プロジェクトが提供する場合はコードモッド)、検証コマンドが通るまで再実行します。コードを適切に修正します。テストを弱めたり、
skipを追加したり、anyや@ts-ignoreスタイルのエスケープで型エラーを黙らせてグリーンにしたりしないでください。 - ユニットを単独でコミットし、移行ノートを本文に含めます。
- いつ止めるべきかを知る。 ユニットに大規模なリファクタリングやプロダクト上の判断(機能の削除、検証できないランタイム動作の変更)が必要な場合、または合理的な努力でグリーンにできない場合は、きれいに元に戻し(
git checkout -- .で最後のコミットまで戻す)、特定のブロッカーとともに延期として記録します。明確な理由のある延期されたメジャーは良い結果です。中途半端なものはそうではありません。
7. 再検証と再監査
最終状態で完全な検証コマンドと監査を再度実行します。すべてのアドバイザリーが修正されているか、説明されている必要があります。修正できなかったアドバイザリーについて:
- 推移的で親が修正をリリースしていない場合、パッチが適用された推移的バージョンをピン留めするオーバーライド/リゾリューションは、そのバージョンが親の期待するものとsemver互換である場合にのみ許容されます。それを検証し、後で誰かが削除できるようにPRに記録します。
- パッチが適用されたバージョンがどこにも存在しない場合は、その旨を述べ、脆弱なコードパスがこのプロジェクトで到達可能かどうかを評価します。
- 監査を黙って通過させるために、無視/許可リストのメカニズムを使用しないでください。
8. 納品
コミットはリポジトリの規約に従います。リポジトリがConventional Commitsを使用しており、それ以上具体的でない場合は、次のようにします:
fix(deps): ...: ランタイム依存関係のセキュリティ修正build(deps): ...: アップグレード、および開発専用ツールのセキュリティ修正- サマリーはパッケージとターゲットを指定します。例:
build(deps): bump vitest to v5。本文には、修正されたアドバイザリーと行われた移行変更をリストします。
ユーザーが別途要求した場合やリモートがない場合を除き、プッシュしてPRを開きます(gh pr create)。次の本文を使用します:
## 概要
<1〜2文: 何が動いたか、いくつのアドバイザリーが解決されたか>
## セキュリティ
| アドバイザリー | 重要度 | パッケージ(パス) | 修正バージョン |
|---|---|---|---|
## 更新
| パッケージ | 変更前 | 変更後 | タイプ | 備考 |
|---|---|---|---|---|
<タイプ = security / patch / minor / major; 備考 = 行われた移行変更>
## 延期
| パッケージ | 最新 | 今やらない理由 |
|---|---|---|
<ブロックされたピア、リファクタリングが必要、ランタイム固定など; 空の場合は「なし」>
## 残っているアドバイザリー
<なし、またはそれぞれ理由と到達可能性評価>
## 検証
<最終コミットで実行された正確なコマンドとその結果>
短いチャットサマリーで締めくくります: PRリンク、修正されたアドバイザリー数と残っている数、延期されたメジャーとその理由(1行)。