mcpField
G
gradle-wrapper-upgrade

gradle-wrapper-upgrade

github.com/gradle开发62193 次安装1 周前更新
代码仓库

关于 gradle-wrapper-upgrade

指导 Agent 升级现有 Gradle Wrapper 的技能,涵盖建立基线、验证结果和回滚。它强调连续运行两次 wrapper 任务,使生成文件采用目标 Gradle 版本的模板。

主要功能

  1. 01检查现有 Wrapper 版本和类型
  2. 02指定目标版本及匹配的校验和
  3. 03连续运行两次 wrapper 任务
  4. 04验证生成文件和构建配置
  5. 05提供基于快照的回滚流程

适用场景

  1. 01将项目升级到指定 Gradle 版本
  2. 02未指定版本时选择最新发行版
  3. 03排查未完成的 Wrapper 升级
  4. 04升级后构建失败时恢复 Wrapper 文件

安装

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

$npx skills add https://github.com/gradle/gradle-skills --skill gradle-wrapper-upgrade

安装后在 Claude Code 中输入/gradle-wrapper-upgrade确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.json
references/
rollback.md
snapshot.md

详细介绍

Gradle Wrapper 升级

两种模式——根据用户的动词决定

先做出判断;以下所有内容都取决于这一点。

  • “升级 Wrapper”“将 Gradle 升级到 8.14.4” → 执行:按步骤操作、验证并汇报。
  • “如何升级 Wrapper?” → 给出命令,指出需要运行两次这一注意事项,然后停止。“如何”问题只需回答,不要执行——即使你已经先说了“我来升级 Wrapper”也一样。告诉用户命令;不要运行。

如果确实有歧义,就给出命令。

无论哪种方式,以下步骤都相同:执行模式下运行这些步骤,回答模式下则提供这些步骤——快照除外。回答时省略步骤 3 中的复制操作,以及步骤 5 中的 diff 和删除操作:这些操作是为了让 Agent 能撤销自己的更改,而用户有自己的版本控制。也去掉 cd "<build-root>" && 前缀:用户会在构建根目录中运行命令。

升级意味着运行内置的 wrapper 任务,绝不要手动编辑文件:gradlew、gradlew.bat 和 gradle-wrapper.jar 都是生成文件;下次运行 wrapper 时,任何手动修改都会在没有警告的情况下被还原。**范围:**仅适用于已有 Wrapper 的构建——为没有 Wrapper 的构建添加 Wrapper 是另一项工作(从系统 Gradle 安装运行 gradle :wrapper)。

此文件旁附有两个参考文件:references/snapshot.md(步骤 3——每次升级前保护 Wrapper)和 references/rollback.md(步骤 6——撤销构建无法接受的升级,这种情况大多数升级都不会遇到)。仅在步骤要求时打开。

步骤 1——确认环境

找到构建根目录——即存放 gradle/wrapper/gradle-wrapper.properties 的目录,通常与 gradlew 位于同一目录——并读取该文件中的 distributionUrl。其中包含所需的两项信息:当前版本(gradle-8.14-bin.zip → 8.14)和发行版类型(-bin 或 -all)。

记下构建根目录的绝对路径(在该目录中运行 pwd)。以下每条命令中的 "<build-root>" 都指这个路径——包括每次运行 ./gradlew,以及每次快照、验证、还原和删除操作——这样无论 Shell 当前位于哪个目录,每条命令都会作用于此构建。一个代码库可以包含多个构建,每个构建都有自己的 Wrapper。

执行模式需要 POSIX Shell——macOS、Linux,或 Windows 上的 WSL 或 Git Bash。快照、验证和回滚命令都是 POSIX 命令。在原生 cmd 或 PowerShell 中,不要升级,也不要将这些命令转换成 Copy-Item 或 Remove-Item:请像回答模式一样提供命令,并说明需要使用 WSL 或 Git Bash。

以下命令使用 ./gradlew;在 Windows cmd 中手动运行时,用户应使用 gradlew.bat,在 PowerShell 中则使用 .\gradlew。选项相同。

步骤 2 — 固定目标版本及其校验和

使用用户指定的版本。如果用户没有指定版本,则升级到最新发行版,并解析为一个确定的版本:

curl -s https://services.gradle.org/versions/current     # → version + checksumUrl

目标版本等于当前版本 → 仍然执行这些步骤。 机器人或手动编辑可能在没有执行任务的情况下提升了 distributionUrl,导致脚本和 JAR 过时(参见本页末尾);运行两次即可重新生成。固定版本前先解析版本——--gradle-version=latest 无法查询校验和。每次升级都应像往常一样固定校验和,而不是可选步骤;校验和用于验证下载内容。

校验和地址是发行版 URL 后追加 .sha256,只需查询这一行:

curl -sL https://services.gradle.org/distributions/gradle-9.0.0-bin.zip.sha256

必须使用 .sha256,而不是 .sha256sum——后者对每个版本都会返回 404,让人误以为发行版不存在。必须使用 -L:该 URL 会 301 重定向到 downloads.gradle.org,而 -f 不会捕获这种情况,因为它只会在出现 4xx 和 5xx 响应时失败,所以如果返回的是 HTML,就说明你没有跟随重定向。响应正文是纯十六进制校验和。若确实返回 404,请使用上方 JSON 中的 checksumUrl,或访问 https://gradle.org/release-checksums/——不要再猜其他 URL。

校验和必须与 distributionUrl 中指定的 ZIP 文件匹配。 每个发行版都会分别为 -bin ZIP、-all ZIP 和 Wrapper JAR 发布校验和。使用错误的校验和会导致 Verification of Gradle distribution failed!,看起来像是下载被篡改,但通常只是复制粘贴出错。

设置 distributionSha256Sum 后,之后任何会更改 --gradle-version 的 wrapper 运行都必须传入新的匹配校验和——否则构建会失败,而不会沿用过时的校验和。如果版本没有变化,校验和会自动保留。

步骤 3 — 建立构建基线,然后保护 Wrapper

首先,确认在修改构建之前,构建能够正常工作:

cd "<build-root>" && ./gradlew --console=plain tasks

记下退出代码。这是金丝雀检查,步骤 5 和 6 会重新运行它:升级后金丝雀检查失败并不说明什么,除非升级前它是通过的。

使用 tasks,不要使用 build。 tasks 会应用所有插件并注册所有任务——这正是新主版本可能破坏的范围——同时不会编译或运行任何内容,因此速度快,且只会因升级导致的问题而失败。build 可能会因不稳定的测试或无法访问的仓库而失败;若因此回滚,就会撤销原本没有问题的升级。

如果构建设置了 org.gradle.configuration-on-demand=true,则改用 tasks --all——普通的 tasks 只会配置构建的根项目,几乎不会配置其他部分,因此即使构建的其他部分已经损坏,它也可能通过。无论选择哪种命令,之后每次都必须使用完全相同的命令;升级前运行 tasks、升级后运行 tasks --all,不能作为对比。

如果此处金丝雀检查失败,请停止,不要升级。 :wrapper 也会配置构建,因此步骤 4 也会因相同原因失败——没有受支持的方法可以升级无法完成配置的构建。报告故障情况;首先应让当前版本的构建通过。

然后阅读 references/snapshot.md,并将 Wrapper 文件从构建中复制出来——每次升级都必须执行,无论是否使用 git。不要跳过此步骤。没有快照就无法回滚,而步骤 6 假定快照已经存在。

步骤 4 — 运行两次 wrapper 任务

cd "<build-root>" && ./gradlew --console=plain :wrapper --gradle-version=8.14.4 --gradle-distribution-sha256-sum=<sha256>
cd "<build-root>" && ./gradlew --console=plain :wrapper --gradle-version=8.14.4 --gradle-distribution-sha256-sum=<sha256>

相同的命令,运行两次:第一次使用旧版 Gradle 的模板重新生成脚本和 jar,第二次使用新版本的模板重新生成。两次都必须运行——见下文。前导的 : 会在构建根目录运行此任务,这是它唯一存在的位置。

升级绝不会改变发行版类型。 如果步骤 1 中是 -all.zip,则两次运行都添加 --distribution-type=all;该任务默认使用 bin,否则会悄无声息地切换构建的发行版类型。更改类型应由用户决定,而不是升级的副作用。

作为 Agent 运行时:

  • 相信退出代码;绝不要通过 grep 搜索 BUILD SUCCESSFUL。 校验和失败不能被误判为升级成功。任一次运行返回非零退出码,都应转到步骤 6,而不是再尝试第三次。
  • 预期下载时间较长。 第二次运行会获取完整发行版(100 MB+),可能需要几分钟。请设置至少 10 分钟的超时时间,或在后台运行并等待其退出。长时间没有输出是正常的,不代表挂起——中途放弃会留下下文所述的半完成状态,且没有可引用的错误消息。
  • --console=plain 可让输出便于解析:没有进度条,也没有颜色。

步骤 5 — 验证文件,然后运行金丝雀测试

使用字面量快照路径和构建根目录路径,并用双引号括起来——绝不要使用 $SNAP:

diff -q "<snapshot>/gradle/wrapper/gradle-wrapper.properties" "<build-root>/gradle/wrapper/gradle-wrapper.properties"

必须输出 differ。只有当目标版本已经是当前版本时,才预期没有输出——此时两次运行已重新生成脚本和 jar,这正是修复半完成升级所需的操作;继续运行金丝雀测试。其他情况下没有输出,表示 wrapper 运行修改了其他构建——它们是在构建根目录之外运行的。停止并告知用户它们在哪个目录中运行;不要回滚,因为此构建并未更改。No such file 表示路径错误,而不是文件内容不同。然后,cd "<build-root>" && ./gradlew --version 应报告目标版本。

只有属性文件肯定会更改。只有当 Gradle 更改了相应模板时,脚本和 jar 才会更改——许多版本之间的 jar 内容完全相同——因此有文件未更改不代表漏运行。上述任一检查都不能证明文件是最新的,也无需证明:两次 wrapper 运行都以 0 退出,才是证明。

这些检查只能确认升级已生效。它们无法说明构建是否仍然正常工作,因此要重新运行金丝雀测试——使用与步骤 3 相同的命令:

cd "<build-root>" && ./gradlew --console=plain tasks

非零退出码 → 步骤 6。 如果此处成功,且步骤 3 的基线也成功,则升级已验证:删除快照并报告结果。

rm -f "<snapshot>/gradlew" "<snapshot>/gradlew.bat" "<snapshot>/gradle/wrapper/gradle-wrapper.properties" "<snapshot>/gradle/wrapper/gradle-wrapper.jar" \
  && rmdir "<snapshot>/gradle/wrapper" "<snapshot>/gradle" "<snapshot>"

先按名称删除文件,再删除空目录——绝不要使用 rm -rf。如果 rmdir 因目录非空而拒绝删除,说明该路径不是你的快照:停止并告知用户。

第 6 步 — 如果构建无法再完成配置,请回滚并停止

有三种情况会导致这种结果,其中第二种最重要:

  • wrapper 任务本身失败。 Run 2 就是新版本,因此无法完成配置的构建也会导致第二次调用失败。情况很明显,而且会让属性指向构建无法运行的版本。
  • wrapper 任务成功,但金丝雀检查失败。 升级已顺利完成,wrapper 文件也正确,但构建现在无法使用。这种情况最危险,因为第 5 步中的所有文件级检查都会通过,而在此处停止的 Agent 会报告升级成功,尽管构建已经损坏。
  • Run 2 未完成——下载中途放弃。没有错误信息可引用,也无法得知构建能否运行目标版本。

前两种情况的原因相同:新主版本移除了 API、提高了最低 JVM 版本要求,并附带了新版 Groovy;如果构建脚本、插件或 buildSrc 依赖已移除的内容,就无法再完成配置。这是被阻止的升级,不是部分升级。

现在阅读 references/rollback.md 并按其中步骤操作——从快照恢复 wrapper 文件、验证恢复结果,并确认结尾报告必须说明的四件事。不要自行参照本页尝试处理。

进入这种情况后,本页其他内容都不适用。不要重试升级,也不要编辑构建脚本来强行通过。

为什么要运行该任务两次——现实中最常见的问题

该任务始终会写入全部四个文件,但脚本和 jar 来自当前运行的 Gradle 模板,而不是目标版本的模板。Run 1(仍在运行旧版 Gradle)会将属性指向新版本,并根据旧版模板重新生成文件——通常会重现已有内容,而手动编辑过的 gradlew 也会悄然消失。Run 2 会因为属性已更改而自动下载新版本,并根据新版模板重新生成这些文件。

升级只完成了一半的迹象在支持问题中很常见:wrapper 或弃用警告一直无法消除、gradlew 的差异看起来很奇怪,或者机器人(例如 Dependabot)修改了 distributionUrl,却从未运行该任务。解决办法始终相同——运行两次。

版本格式

--gradle-version 还接受 latest、release-candidate、release-nightly、nightly 和 release-milestone,以及 Gradle 9+ 中单独使用的 9 或 9.1;需要固定校验和时,优先使用具体版本。自 Gradle 9.0 起,gradle-wrapper.properties 必须填写完整的 X.Y.Z——在文件中填写单独的主版本号或主版本号.次版本号会被拒绝,但 CLI 仍接受这些格式。请通过 ./gradlew help --task=:wrapper 确认版本支持的选项。

不在处理范围内

修复构建不在处理范围内,无论是哪种情况——既不能为强行完成被阻止的升级而修复,也不能修复新版本造成的问题。机械步骤正确但导致构建无法使用的升级仍然算任务失败;应执行第 6 步,而不是打补丁。

分界线在构建阶段,而不在命令本身。 无法再完成配置——wrapper 任务失败或金丝雀检查失败——说明升级受阻:请回滚。能够完成配置,但在执行期间失败——例如编译错误或测试失败——则说明升级已完成,构建另有问题:保留新 wrapper 并报告问题。测试未通过就回滚,与留下无法完成配置的构建一样不妥。分界线设在这里,是因为配置失败明确且检查成本低,而执行失败则两者皆非;tasks 通过并不意味着构建能够编译。

权威文档:https://docs.gradle.org/current/userguide/gradle_wrapper.html。

评论

支持 Markdown