eliminate-redundant-restores について
Eliminate Redundant Restores は、ビルドパイプラインやスクリプトで繰り返される .NET および MSBuild のパッケージ復元を見つけるスキルです。プロジェクトの対象範囲、復元入力、利用可能なアセットを確認し、後続の復元を安全に省けるか判断します。
主な機能
- 01各呼び出しの復元動作を一覧化
- 02プロジェクトとソリューションの対象確認
- 03ランタイムと復元入力の比較
- 04レスポンスファイルの復元フラグ確認
- 05クリーンな複製で変更を検証
活用シーン
- 01CI パイプラインの重複復元を削減
- 02複数のビルド手順での復元出力を調査
- 03--no-restore を安全に使えるか確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill eliminate-redundant-restoresインストール後、Claude Code で/eliminate-redundant-restoresを入力して読み込まれたか確認します。
詳細
余分な復元をなくす
何も変更しない復元でもすべてのプロジェクトを評価して復元状態を確認するため、複数の手順からなる大規模リポジトリのビルドでは、復元が1回増えるごとに数秒から数十秒かかります。簡単に間違えやすい対処法として、先行する復元で実際に対象になっていない手順に --no-restore を指定すると、遅いビルドが失敗するようになります。このスキルでは、どの呼び出しで復元が行われるかを特定し、すでに対象になっていることを確認したうえで、その呼び出しだけを変更します。
使用するタイミング
- パイプラインやスクリプトで複数の
dotnet/msbuild手順が実行され、複数の手順で復元の出力が表示される - 復元手順があるにもかかわらず、後続の手順で
Determining projects to restore...が表示される - リポジトリの
Directory.Build.rspまたは使用中の MSBuild インストールのMSBuild.rspに-restoreが含まれている --no-restoreまたは-restore:falseを安全に追加できる場所を尋ねられた--no-restoreの追加後にNETSDK1004またはNETSDK1047が発生した
使用しないタイミング
- 最初の復元が遅い(フィードの応答遅延、パッケージキャッシュの不足):これは重複ではなく、キャッシュの問題です
- すべての呼び出しを通じて復元が1回だけ行われる:重複排除する対象はありません
- バージョン、ロックファイル、監査に関する復元エラーがある:復元自体を修正してください
- 根拠のない「ビルドが遅い理由」についての問い合わせ:まず
build-perf-baselineを実行し、次にbuild-perf-diagnosticsを実行してください
入力
作業ディレクトリでリポジトリの入力ファイルを見つけてください。ユーザーにパスを尋ねてはいけません。
- CI 定義(
*.yml、.github/workflows、azure-pipelines*.yml、Jenkinsfile)とビルドスクリプト(build.cmd、build.sh、build.ps1、eng/) Directory.Build.rsp、Directory.Build.props/.targets、NuGet.config- 使用中の MSBuild 実行ファイルと同じ場所にあり、自動的に読み込まれる
MSBuild.rsp、または@fileで明示的に渡される応答ファイル。リポジトリ内のMSBuild.rspは自動的に読み込まれません - 復元に関する証拠がある場合:手順のログまたは binlog
ワークフロー
ステップ 1:復元が繰り返されており、削除する価値があることを確認する
- ログの証拠:呼び出しごとに
Determining projects to restore...またはAll projects are up-to-date for restore.が1回表示される。 - Binlog の証拠:復元の評価にグローバルプロパティ
MSBuildRestoreSessionIdとExcludeRestorePackageImports=trueが含まれる。ターゲットのパフォーマンス概要には、Restoreの呼び出し回数が表示される。 - 何も変更しない各復元にかかる時間を算出する。復元が1回だけの場合、または繰り返される復元の合計が約1秒の場合は、「削除する価値のあるものはありません」と報告し、編集せずに終了する。
ステップ 2:実行順にすべての呼び出しを確認する
| 呼び出し | 復元を実行しますか? |
|---|---|
dotnet build、test、publish、pack | --no-restore を指定しない限り暗黙的に実行されます。継承された rsp フラグも確認してください |
dotnet run | ビルドの前に暗黙的に実行されます。古い SDK では、--no-restore を指定しても rsp から復元設定が継承されることがあります。--no-build を指定すると、ビルドも復元も実行されません |
--no-build を指定した dotnet test、publish、pack | 暗黙的な復元は実行されませんが、rsp の -restore によって復元が有効になる場合があります。--no-restore -restore:false を追加してください |
dotnet msbuild、msbuild.exe、CI の MSBuild タスク | -restore または -t:Restore が指定されている場合に実行されます。リポジトリの Directory.Build.rsp またはインストールレベルの MSBuild.rsp から継承されたフラグも対象です。ビルドのみを行うように見えるステップでも同様です |
dotnet restore、nuget restore、msbuild -t:Restore | 明示的に復元が実行されます |
| 「パッケージを復元」オプションがある CI ビルドタスク | 既定値を確認してください |
.NET SDK 9.0.1xx 以降では、--no-restore により -restore:false が渡され、rsp フラグが上書きされます。古い SDK では、CLI の暗黙的な復元をスキップしても -restore が継承される場合があります。rsp によって復元が有効になる場合は、dotnet build、test、publish、pack に、SDK のバージョンに依存しない --no-restore -restore:false を指定してください。--no-build を指定する場合も同様です。dotnet msbuild と msbuild.exe では、--no-restore ではなく -restore:false を指定します。
dotnet run には、MSBuild の -restore:false に相当するオプションがありません。古い SDK で rsp がある場合は、まず dotnet build --no-restore -restore:false でビルドしてから、同じプロジェクト、構成、フレームワーク、ランタイムの入力を指定して dotnet run --no-build を実行するか、ビルド済みの DLL を直接実行してください。dotnet run に -restore:false を渡しても、復元を抑止する方法にはなりません。
ステップ 3:呼び出しごとに判断する
最初の復元は残します。後続の復元を省略するのは、4 つのチェックすべてに合格した場合だけです。
| チェック | 不合格の場合 |
|---|---|
同じプロジェクトの範囲。 復元の対象は、エントリープロジェクトまたはソリューションと、そのプロジェクトの ProjectReference の依存関係全体です。別のソリューションや .slnf に含まれるプロジェクト、別のパスからビルドされるツール、またはオーケストレーション用の .proj が MSBuild タスク経由でビルドするプロジェクトは対象外です(オーケストレーション用プロジェクトの -restore では、それらは復元されず、NETSDK1004 で失敗します) | そのステップで復元を残すか、明示的な復元ステップにプロジェクトを追加します |
同じ復元入力。 -r/--runtime、RID を生成する -a/--arch、--os、--ucr/--use-current-runtime、TargetFramework(s)、RuntimeIdentifier(s)、PublishAot、パッケージソース(--source、--configfile、RestoreSources)、RestorePackagesPath、および PackageReference の条件で参照されるすべての -p: 値です。アーキテクチャ/OS オプションは SDK の現在の RID と組み合わせられ、--use-current-runtime はその RID を選択します。RID のターゲットがない場合、NETSDK1047 で失敗します(Assets file ... doesn't have a target for 'net10.0/linux-x64') | 明示的な復元で有効な RID を一致させるか(たとえば publish --os linux --arch x64 には restore -r linux-x64 が必要です)、後続のステップで復元を残します。-r がないという理由だけで、汎用の復元がこれらのエイリアスを対象とすると判断してはいけません。publish が自己完結型でも、それだけを理由に復元で SelfContained=true を必須にしないでください。通常の SDK プロジェクトであれば、RID のみを指定した復元で対応できます。リポジトリ内の条件によってパッケージ入力が変わる場合に限り、Configuration または SelfContained を一致させます |
同じ復元状態を利用できること。 先行する project.assets.json ファイルが、後続の呼び出しで参照される場所に存在している必要があります。--artifacts-path、UseArtifactsOutput/ArtifactsPath、MSBuildProjectExtensionsPath/BaseIntermediateOutputPath を一致させます。別のジョブやワークスペースで、これらのファイルが自動的に共有されることはありません | 明示的な復元の出力レイアウトを build/test に合わせるか、後続のワークスペースで復元を残します。build --artifacts-path artifacts --no-restore を使う場合は、先に restore --artifacts-path artifacts を実行します。通常の obj/ 内のアセットでは、このレイアウトに対応できません |
途中でパッケージが変更されないこと。 プロジェクトを生成または編集するステップ、dotnet add package の実行、Directory.Packages.props または NuGet.config の書き換えを行うステップがあると、先行する復元は無効になります | 明示的な復元をそのステップの後に移動します |
ステップ 4:変更を最小限にする
- 復元に必要なすべての入力を指定した明示的な復元を 1 回行い、後続のステップでは
--no-restore、-restore:false、または--no-buildを指定します。 - 1 つのソリューションに対して複数の
dotnet testステップを実行する場合:ビルドは 1 回だけ行い、その後、各ステップでdotnet test --no-buildを実行します。rsp で復元が有効になっている場合は、.NET 8 以前も含め、--no-restore -restore:falseを追加します。--no-restoreだけでは再ビルドが行われます。 - 自己完結型 publish の設定はそのままにします。
publish --no-buildでも、構成、フレームワーク、RID、自己完結型出力が一致する事前のビルドが必要です。復元の互換性があるからといって、ビルド出力が存在するとは限りません。 - 変更範囲は CI とスクリプトに限定します。開発者が利用しているため、
Directory.Build.rspの-restoreは残し、呼び出しごとに-restore:falseを渡します。 - 最初の復元は決して削除しません。
--locked-modeのゲートである場合は、そのまま残します。 - この変更に静的グラフ復元やパッケージキャッシュを含めないでください。
# One restore carries every input later steps use
dotnet restore MyApp.sln -r linux-x64
# Explicit overrides also work when an rsp enables restore on older SDKs
dotnet build MyApp.sln -c Release --no-restore -restore:false
dotnet test MyApp.sln -c Release --no-build --no-restore -restore:false
dotnet publish src/Api -c Release -r linux-x64 --no-restore -restore:false
# msbuild invocations need the explicit switch if an rsp file adds -restore
dotnet msbuild Packaging.proj -t:Pack -restore:false
ステップ 5:検証し、事実に基づいて報告する
- 変更したパイプラインまたはスクリプトを、クリーンな一時コピー(
obj/とproject.assets.jsonがない状態)で最初から最後まで実行します。復元が一度も行われていないプロジェクトは、この段階でのみ失敗します。YAML 構文やフラグの有無を確認しても、復元の網羅性やテストの実行を検証したことにはなりません。実行できない場合は「未検証」と記載し、確認が必要な手順とプロジェクトを列挙します。 - 成功の根拠:後続のすべての手順が成功したこと、
Restoreの呼び出し(binlog)またはDetermining projects to restoreの行数が N から想定される件数に減ったこと、時間に関する主張をする場合は少なくとも 3 回実行した前後の中央値を示します。 - 復元の失敗(NETSDK1004、NETSDK1047)とビルドの失敗は分けて報告し、どちらかが発生した後に成功したと主張してはいけません。
出力
どの手順で復元が行われ、どの先行する復元がそれらをカバーしているか、何を変更したか、何が未検証のままかを説明します。必須の最初の復元は維持したうえで、冗長な復元を削除したか、変更が不要だったかを明記します。必要に応じて、文章または調査結果の表を使用してください。表の見出しや判定の書式に決まりはありません。
よくある落とし穴
| 落とし穴 | 解決策 |
|---|---|
すべてのコマンドに --no-restore を追加する | ステップ 3 の確認を各手順に適用します |
rsp が「ビルドのみ」の msbuild 手順に -restore を追加していることを見落とす | 手順で復元が行われないと結論づける前に、*.rsp を検索します |
Directory.Build.rsp から -restore を削除する | CI の呼び出しごとに上書きします。rsp は開発者向けです |
オーケストレーターの .proj は復元したが、子プロジェクトは復元していない | 各子プロジェクトを明示的に復元するか、ビルドする手順で復元を有効にしたままにします |
汎用的な復元の後、-r、アーキテクチャ/OS オプション、または --use-current-runtime を指定して発行する | エイリアスで使用される SDK ホストの既定値も含め、同じ実効ランタイム識別子で復元します |
| ジェネレーターの手順が復元後に PackageReferences を編集する | ジェネレーターの実行後に復元します |