mcpField
E
eliminate-redundant-restores

eliminate-redundant-restores

github.com/dotnet开发5.8k552 次安装1 天前更新
代码仓库

关于 eliminate-redundant-restores

Eliminate Redundant Restores 是用于查找构建管线和脚本中重复 .NET 与 MSBuild 包还原的技能。它会检查项目覆盖范围、还原输入和可用资产,再判断哪些后续步骤可以安全跳过还原。

主要功能

  1. 01盘点各调用中的还原行为
  2. 02检查项目与解决方案覆盖范围
  3. 03比较运行时和包还原输入
  4. 04核查响应文件中的还原标志
  5. 05在干净副本中验证变更

适用场景

  1. 01减少 CI 管线中的重复还原
  2. 02诊断多个构建步骤的还原输出
  3. 03检查 --no-restore 的安全用法

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/skills --skill eliminate-redundant-restores

安装后在 Claude Code 中输入/eliminate-redundant-restores确认已加载。

详细介绍

消除冗余还原

即使还原操作不执行任何实际更改,仍会评估每个项目并检查还原状态,因此在多步构建中,每次额外还原都会让大型代码库耗费数秒到数十秒。解决方法很容易出错:如果某一步添加了 --no-restore,但此前的还原并未涵盖该步骤,就会把缓慢的构建变成失败的构建。此技能会找出哪些调用执行了还原,确认哪些调用已由之前的还原覆盖,并且只修改这些调用。

适用场景

  • 流水线或脚本运行多个 dotnet/msbuild 步骤,日志显示其中不止一个步骤输出了还原信息
  • 存在还原步骤,但后续步骤仍输出 Determining projects to restore...
  • 仓库中的 Directory.Build.rsp 或当前 MSBuild 安装目录下的 MSBuild.rsp 包含 -restore
  • 有人询问可以在哪些位置安全地添加 --no-restore 或 -restore:false
  • 添加 --no-restore 后出现了 NETSDK1004 或 NETSDK1047

不适用场景

  • 首次还原速度慢(源延迟、包缓存缺失):这是缓存问题,而非冗余还原
  • 所有调用中只有一次还原:无需去重
  • 还原错误涉及版本、锁文件或审核:应修复还原本身
  • 未经分析就问“为什么构建很慢”:先从 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 不会自动加载
  • 如有,还原证据:步骤日志或二进制日志

工作流

步骤 1:确认还原重复发生,且值得移除
  • 日志证据:每次调用都出现一次 Determining projects to restore... 或 All projects are up-to-date for restore.
  • 二进制日志证据:还原评估包含全局属性 MSBuildRestoreSessionId 和 ExcludeRestorePackageImports=true;目标性能摘要列出 Restore,并显示 N 次调用。
  • 量化每次无操作还原的耗时。如果只有一次还原,或重复还原的总耗时约为一秒,则报告“没有值得移除的内容”并停止,不做任何修改。
步骤 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 中使用与版本无关的 --no-restore -restore:false,即使同时指定了 --no-build 也是如此。dotnet msbuild 和 msbuild.exe 接受的是 -restore:false,而不是 --no-restore。

dotnet run 不支持 MSBuild 的 -restore:false 覆盖选项。在较旧的 SDK 上使用 rsp 时,应先使用 dotnet build --no-restore -restore:false 构建,然后使用 dotnet run --no-build 运行,并传入相同的项目、配置、框架和运行时参数(或直接执行生成的 DLL)。向 dotnet run 传递 -restore:false 并不能阻止还原。

步骤 3:逐次调用判断

保留首次还原。只有以下四项检查全部通过时,才跳过后续还原:

检查项未通过时
项目集合相同。 还原会覆盖入口项目或解决方案,以及该项目的 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:仅指定 RID 的还原足以满足普通 SDK 项目的需求。只有当仓库条件会改变包输入时,才匹配 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:实施最小改动
  • 执行一次包含所有还原输入的显式还原,然后在后续步骤使用 --no-restore、-restore:false 或 --no-build。
  • 对同一解决方案执行多个 dotnet test 步骤:先构建一次,然后对每个步骤执行 dotnet test --no-build。如果 rsp 文件启用了还原,则添加 --no-restore -restore:false,包括在 .NET 8 及更早版本中。单独使用 --no-restore 仍会重新构建。
  • 保留自包含发布设置。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:验证并如实报告
  1. 在干净的临时副本中端到端运行已更改的管道或脚本(不含 obj/、不含 project.assets.json)。从未还原过的项目只会在此处失败。YAML 语法或标志存在性检查无法验证还原覆盖范围或测试执行情况。如果无法运行,请说明“未验证”,并列出需要确认的步骤和项目。
  2. 成功证据:后续每个步骤均已通过;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、体系结构/操作系统选项或 --use-current-runtime 发布使用相同的有效运行时标识符还原,包括别名使用的 SDK 主机默认值
生成器步骤在还原后修改了 PackageReferences在生成器运行后还原

评论

支持 Markdown