关于 gradle-wrapper-upgrade
指导 Agent 升级现有 Gradle Wrapper 的技能,涵盖建立基线、验证结果和回滚。它强调连续运行两次 wrapper 任务,使生成文件采用目标 Gradle 版本的模板。
主要功能
- 01检查现有 Wrapper 版本和类型
- 02指定目标版本及匹配的校验和
- 03连续运行两次 wrapper 任务
- 04验证生成文件和构建配置
- 05提供基于快照的回滚流程
适用场景
- 01将项目升级到指定 Gradle 版本
- 02未指定版本时选择最新发行版
- 03排查未完成的 Wrapper 升级
- 04升级后构建失败时恢复 Wrapper 文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gradle/ gradle-skills --skill gradle-wrapper-upgrade安装后在 Claude Code 中输入/gradle-wrapper-upgrade确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.jsonreferences/rollback.mdsnapshot.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。