Gradle 工程手册:从任务模型到可验证构建缓存
一个 Gradle 项目在开发者机器上第二次构建只用几秒,CI 却每次从头执行;把 --build-cache 打开后速度变快,偶尔又复用了错误产物;升级插件后,help 都变慢,业务任务还没开始。只盯着“Gradle 快不快”,很难解释这些现象。Gradle 的性能与正确性来自同一套模型:初始化阶段发现 build,配置阶段创建并连接 Project 与 Task,执行阶段才运行选中任务;增量构建、Build Cache 和 Configuration Cache 分别复用不同层次的状态。
所以 Gradle 工程化的第一步不是搜一组加速参数,而是让构建事实可见:哪个 Wrapper 和 JVM 在运行,哪些 settings 与插件形成项目模型,任务声明了什么输入输出,依赖从哪里解析,缓存为什么命中,失败后怎样回到无缓存基线。
先判断是否适用
当项目需要可编程的构建模型、细粒度任务图、多项目组合、Toolchain 选择和分层缓存时,Gradle 能把这些能力组织到 Settings、Project 与 Task 中。使用它的代价是团队必须治理 DSL、插件、输入输出、Daemon 与缓存,不能把 build.gradle(.kts) 当作随手执行的脚本。
Android、Spring、Kotlin Multiplatform 等平台插件会扩展任务模型,但业务开发规则仍属于各自技术栈。远端缓存服务端、跨语言 affected 计算和 Nx、Turborepo、Bazel 等任务平台应由 Monorepo 与构建加速体系承接;Gradle 项目保留本地任务模型、客户端缓存接入和构建证据,不把一个 root build 膨胀成组织级调度平台。
先分清 Gradle JVM、Toolchain 与 IDE 运行时
准备非生产开发机、可删除练习目录和 JDK。先查看命令来源:
Get-Command java,gradle -ErrorAction SilentlyContinue | Format-Table Name,Source
java -version
gradle --versionPOSIX 环境使用:
command -v java
command -v gradle || true
java -version
gradle --version已有项目不要求全局 Gradle;后续统一执行 gradlew / gradlew.bat。还要区分四个 JVM:启动 Wrapper 的 JVM、运行 Gradle Daemon 的 JVM、Java Toolchain 选择的编译 JVM、IDE 自身运行时。它们可能版本相同但路径、供应商和用途不同。
先确认版本与兼容边界
先读取 gradle/wrapper/gradle-wrapper.properties,再执行 ./gradlew --version,确认 Wrapper 分发版本、Gradle home、Launcher JVM 与 Daemon JVM。若项目选择 9.6.1,还要在 Gradle 兼容矩阵 核对运行 JVM 17–26 的边界;更老 JDK 可以通过 Toolchain 用于特定编译或测试任务,但“能编译到 Java 8”不代表 Gradle 9 可以运行在 Java 8。
版本和兼容矩阵会继续变化。升级前必须重新打开兼容矩阵与目标版本升级指南,并以 Wrapper properties、插件兼容记录和 CI 对照实验确定项目基线。已有 Wrapper 的项目按 Wrapper 使用说明 执行,无需为了运行项目再安装另一套全局 Gradle。
Build Cache 与 Configuration Cache 都不能仅凭版本存在就默认打开。Build Cache 说明其启用和远端读写边界;Configuration Cache 需要检查默认状态、兼容报告和 incubating 能力。团队应先建立无缓存结果,再逐层启用并比较产物。
已有仓库直接使用 Wrapper
先检查四类文件:
gradlew
gradlew.bat
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.properties然后运行:
Get-Content gradle\wrapper\gradle-wrapper.properties
.\gradlew.bat --versioncat gradle/wrapper/gradle-wrapper.properties
./gradlew --version官方明确期望 Wrapper JAR 随仓库提交。Wrapper 会按 distributionUrl 下载 Gradle 分发包;应配置 distributionSha256Sum,并审查下载主机、分发类型和超时。大多数 CI 使用更小的 -bin 分发即可,-all 包含源码与文档,不应无理由增加下载和缓存成本。
新仓库生成 Wrapper
新项目需要一次受控的全局 Gradle 来生成 Wrapper。以下命令演示如何固定 9.6.1;执行前先用发布页和兼容矩阵确认目标版本:
gradle wrapper --gradle-version 9.6.1 --distribution-type bin
./gradlew --version随后把 Wrapper 脚本、JAR 与 properties 一起提交,并根据 Gradle 官方发布页填写分发包 SHA-256。生成动作不是日常 CI 步骤;CI 只消费已评审 Wrapper。
全局安装只做引导
Gradle 官方支持手工安装,也提到 SDKMAN!、Homebrew 等包管理入口。发行版仓库可能提供过旧或定制包,因此安装后必须验证 gradle --version。一旦 Wrapper 生成,项目文档、IDE 和 CI 都应改用 Wrapper,避免全局版本悄悄覆盖仓库契约。
升级或卸载全局 Gradle 按原入口操作。删除前确认 Get-Command gradle / command -v gradle,避免 PATH 中还有另一套安装。不要为了“修复 Gradle”先删除整个用户目录 .gradle;其中包含依赖缓存、Wrapper 分发、Daemon 状态和诊断证据。
最小验证:从空目录看到任务与产物
创建目录:
gradle-verify-lab/
├─ settings.gradle.kts
├─ build.gradle.kts
└─ src/
└─ main/
└─ java/
└─ lab/
└─ App.javasettings.gradle.kts:
rootProject.name = "gradle-verify-lab"build.gradle.kts:
plugins {
application
}
group = "example.lab"
version = "1.0.0-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
application {
mainClass = "lab.App"
}
tasks.register("verifyArtifact") {
dependsOn(tasks.jar)
val jarFile = tasks.jar.flatMap { it.archiveFile }
inputs.file(jarFile)
doLast {
val file = jarFile.get().asFile
check(file.isFile) { "missing artifact: $file" }
println("artifact=${file.name}, bytes=${file.length()}")
}
}src/main/java/lab/App.java:
package lab;
public final class App {
private App() {}
public static void main(String[] args) {
System.out.println("gradle-model-ok");
}
}先生成并提交 Wrapper,再执行:
./gradlew clean build verifyArtifact
./gradlew runWindows 使用 .\gradlew.bat。预期看到 BUILD SUCCESSFUL,verifyArtifact 输出 JAR 文件名与非零字节数,run 输出 gradle-model-ok。如果配置的 Java 17 Toolchain 不存在,Gradle 不保证自动下载;只有配置了 Toolchain Resolver 插件和批准来源后才可能自动提供。否则应安装批准 JDK 或把实验基线调整为团队已有版本。
再次执行:
./gradlew build verifyArtifact --info预期部分任务显示 UP-TO-DATE。这证明当前工作区的任务输入输出没有变化,不等于远端缓存已经启用。清理只执行:
./gradlew cleanclean 删除项目 build/,不会清除用户依赖缓存与 Wrapper 分发。实验结束后删除整个练习目录即可。
Settings、Project 与 Task:先看对象再看脚本
Gradle 脚本不是普通顺序执行脚本。settings.gradle(.kts) 配置 Settings,决定 root project、subprojects、included builds、plugin management 和依赖解析治理;每个 build.gradle(.kts) 配置对应 Project;插件向 Project 增加 extension、configuration 与 task。
Task 是工作单元。它的依赖关系形成 task graph,输入输出决定能否增量跳过或复用缓存。一个任务“在文件里写得靠前”不代表先运行,真正顺序来自 dependsOn、输入输出推导、mustRunAfter 等关系。
用这些命令观察模型:
./gradlew projects
./gradlew tasks --all
./gradlew help --task build
./gradlew properties
./gradlew build --dry-run--dry-run 展示计划任务,但不证明任务动作、依赖下载或运行时副作用会成功。properties 和 debug 日志可能暴露内部路径、代理或变量,分享前脱敏。
三阶段生命周期:慢在配置还是执行
Gradle 官方把构建分为三个阶段:
初始化:发现参与的 projects 与 included builds。配置:评估构建脚本、创建和配置 task,形成可执行图。执行:运行选中 task 的动作。
如果执行 ./gradlew help 仍很慢,问题多半发生在初始化或配置,例如脚本进行网络调用、遍历大目录、急切创建全部任务,或插件在配置阶段做昂贵工作。把这些操作挪到 doLast 也不一定正确;更好的做法是使用 Provider API、配置规避和声明式输入,让工作只在任务真正被选择时发生。
任务注册优先使用 tasks.register,避免不必要的 eager creation。自定义任务若在执行阶段读取环境变量、当前时间或用户目录,却没有声明为输入,会破坏增量与缓存正确性。性能问题最终会回到模型完整性,而不只是 JVM 参数。
Groovy DSL 与 Kotlin DSL
Gradle 同时支持 build.gradle 的 Groovy DSL 和 build.gradle.kts 的 Kotlin DSL。它们配置的是同一 Gradle API,不是两套构建引擎。
Kotlin DSL 提供静态类型、IDE 补全与更明确的重构反馈,但编译脚本和插件 API 兼容仍受 Gradle 内嵌 Kotlin 版本约束。Groovy DSL 更动态、历史样例多,错误可能较晚暴露。选型应看现有团队能力、插件文档、仓库规模和迁移成本,不以“新旧”一刀切。
不要在一次业务变更中同时迁移 DSL。官方迁移指南建议渐进转换;每转换一个脚本,都要比较 projects、tasks、dependencies、测试与产物。约定插件或 build-logic 可以集中复用构建逻辑,但它们是代码,应有测试和版本边界,不能变成无人理解的全局魔法。
依赖、Configuration 与 Repository
Gradle 依赖不是一个平面列表。插件创建 implementation、runtimeOnly、testImplementation 等声明 configuration,并派生可解析的 compile/runtime classpath。把依赖放错 configuration,可能导致 API 泄露、运行时缺类或缓存输入扩大。
最小 Java 项目常见声明:
repositories {
mavenCentral()
}
dependencies {
implementation("org.example:example-api:1.2.3")
runtimeOnly("org.example:example-runtime:1.2.3")
testImplementation("org.example:example-test:1.2.3")
}示例坐标只表达形态,不应直接复制。真实依赖要来自批准源和版本策略。查看解析结果:
./gradlew dependencies
./gradlew dependencyInsight --dependency example-api --configuration runtimeClasspathrepository 的声明顺序会影响元数据查找,仓库内容过滤能减少错误来源和 dependency confusion。团队应在 settings.gradle(.kts) 集中治理 dependency resolution,限制项目私自增加仓库。插件解析由 pluginManagement.repositories 控制,与普通依赖仓库不是同一个入口,二者都要审查。
动态版本、changing module 与 snapshot 会让坐标不变但内容或解析结果变化。Gradle 默认会缓存这类元数据一段时间,--refresh-dependencies 只应在证据指向缓存/元数据陈旧时使用,不是所有错误的固定修复。
Java Toolchain:运行 Gradle与编译项目分离
Gradle Daemon 运行 JVM 决定 Gradle 和插件能否启动,Java Toolchain 决定支持 Toolchain 的编译、测试或文档任务使用哪个 JDK。示例:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.ADOPTIUM
}
}指定 vendor 会提高一致性,也会降低可用 JDK 范围。是否需要锁 vendor,要看字节码、证书库、加密实现、性能和支持策略,而不是默认越严越好。
诊断:
./gradlew --version
./gradlew javaToolchains如果 IDE 可以编译而 CLI 找不到 Toolchain,检查 IDE 是否偷偷提供 JDK、Gradle JVM 是否不同、Toolchain 自动探测路径和 Resolver 插件是否一致。CI 应显式安装批准 JDK,或配置受控 Toolchain 下载源;不能把公网自动下载当成无审计便利功能。
多项目与 Composite Build 边界
多项目 build 由一个 Settings 管理 root 与 subprojects:
rootProject.name = "platform"
include("api", "service")项目依赖写成:
dependencies {
implementation(project(":api"))
}这会把项目关系纳入 task graph。可以执行 ./gradlew :service:build,Gradle 会按依赖选择上游任务。共同约定适合放到 convention plugin,而不是在 root 使用大段 allprojects / subprojects 动态改写所有模块。
Composite Build 通过 includeBuild 组合多个完整 build,参与依赖替换和插件开发。它和 subproject 不同:每个 included build 有自己的 Settings 与生命周期边界。官方 Build Cache 文档还说明 included build 会继承顶层 build cache 配置;依赖校验元数据也有顶层作用域规则。引入 composite 前要评估缓存、校验、插件解析和 CI checkout 边界。
普通多项目继续由 Gradle Settings 与 Project 关系管理;出现跨语言任务图、affected 计算和组织级远端缓存平台需求时,转向专门的 Monorepo 与构建加速工具。
Daemon:复用进程,不是正确性来源
Gradle Daemon 复用 JVM,减少启动与类加载开销。查看和停止当前版本可见的 Daemon:
./gradlew --status
./gradlew --stopDaemon 可能因 Gradle 版本、Java home、JVM 参数等不同而分裂成多个进程。机器内存持续上涨时先看 --status、进程命令行和 gradle.properties,不要把 --no-daemon 当永久性能方案。CI 是否使用 Daemon取决于 runner 生命周期;单次容器中复用价值有限,长生命周期 agent 则要控制进程和内存回收。
常用 JVM 参数放在用户或项目 gradle.properties:
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8内存不是越大越快。堆过大可能挤压并发 worker、测试 JVM 和容器限制;判断应结合 GC、Daemon 数量、任务并行度和宿主机内存,而不是复制统一 -Xmx。
三类复用机制不能混称“缓存”
增量与 UP-TO-DATE
任务在同一工作区有可比较的输入输出,且没有变化时,Gradle 可以标记 UP-TO-DATE。任务必须准确声明文件、属性、环境和输出;漏声明输入会错误跳过,声明过宽会频繁失效。
Build Cache
Build Cache 复用可缓存任务的输出,可以跨工作区,远端缓存还可跨机器。默认不启用:
./gradlew clean build --build-cache --info输出中 FROM-CACHE 才表示从 Build Cache 恢复。远端缓存是制品分发边界:写权限、来源信任、隔离维度、TLS、保留与污染处置必须治理。开发机和不受信分支通常只读,受控主线负责写入。
Configuration Cache
Configuration Cache 保存配置阶段结果,避免下次重新配置整个任务图:
./gradlew help --configuration-cache
./gradlew help --configuration-cache第二次应显示复用配置缓存。若报告不兼容,打开 .gradle/configuration-cache/.../configuration-cache-report.html 定位未声明输入、Project 在执行阶段被引用等问题。problems=warn 只适合迁移观察,不应长期掩盖问题。
三者的回滚开关不同:
./gradlew build --rerun-tasks --no-build-cache --no-configuration-cache这条命令建立“尽量不复用”的对照,但依赖缓存仍可能存在。若对照正确、缓存构建错误,才继续调查任务输入输出、路径敏感性、环境变量和远端缓存来源。
依赖锁定与校验
版本锁定固定解析结果,适合动态选择器治理,但不能让 changing module 的同坐标内容变得稳定。启用所有可解析 configuration 的锁定:
dependencyLocking {
lockAllConfigurations()
}生成并提交锁状态:
./gradlew dependencies --write-locks升级单个依赖应使用受控更新并评审 lockfile diff。没有对应 lock state、解析出额外依赖或版本不一致时,严格锁定应失败。插件 classpath 也需单独考虑,不能只锁业务依赖。
Dependency Verification 用 checksum 或签名验证依赖、插件和元数据。官方说明它覆盖 Gradle 依赖引擎下载的多类产物,但不验证 changing dependencies 与本地生成产物。引导生成基线后必须人工审查来源,不能把“自动采集当前 checksum”当作信任判断:
./gradlew --write-verification-metadata sha256 help生成 gradle/verification-metadata.xml 后,在干净环境执行构建。新增或升级依赖导致校验失败,应核对官方来源、仓库审计和变更 PR;不能直接重新生成覆盖所有 checksum。
Build Scan 与本地证据
./gradlew build --scan 会创建 Build Scan,并可能把构建元数据发布到 Gradle 托管服务或组织 Develocity。官方文档明确可能要求接受服务条款。仓库路径、用户名、环境变量、依赖坐标、任务参数和日志都可能属于数据边界,团队必须先审查采集内容、部署位置、保留和访问权限。
不能出网或未批准上传时,使用本地证据:
./gradlew build --profile
./gradlew build --info
./gradlew help --warning-mode=all
./gradlew dependencies
./gradlew dependencyInsight --dependency <name> --configuration <configuration>--profile 生成本地 HTML 报告,--info 展示任务跳过和缓存原因,warning mode 暴露升级风险。--debug 信息量更大且更容易泄露凭证,只在隔离环境短时使用并脱敏保存。
项目接入与常用操作
一个团队级 Gradle 仓库至少保留:
gradlew / gradlew.bat # 权威入口
gradle/wrapper/* # 固定分发与 bootstrap
settings.gradle(.kts) # 项目发现、插件与仓库治理
build.gradle(.kts) # 根项目构建模型
gradle.properties # 可共享非敏感行为
gradle/libs.versions.toml # 可选,版本目录
gradle/verification-metadata.xml # 可选,依赖校验
gradle.lockfile # 启用锁定后提交
docs/build.md # JDK、缓存、CI、升级说明诊断命令按层次选择:
./gradlew --version
./gradlew projects
./gradlew tasks --all
./gradlew build --dry-run
./gradlew build --info
./gradlew dependencies
./gradlew dependencyInsight --dependency <name> --configuration runtimeClasspath
./gradlew javaToolchains
./gradlew --status不要把 clean build 设为每次开发的唯一入口;那会主动放弃增量优势。日常执行最小目标任务,CI 定期做干净基线。正确性依赖 clean 才能保证,通常说明输入输出声明或生成目录隔离有问题。
代理、CA、权限与凭证
Gradle 网络访问至少分 Wrapper 分发、插件仓库、依赖仓库、Toolchain 下载和远端缓存。它们可能使用不同主机与凭证,排障时不能只验证 Maven Central。
JVM 代理可放在用户级 ~/.gradle/gradle.properties:
systemProp.http.proxyHost=proxy.example.invalid
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=proxy.example.invalid
systemProp.https.proxyPort=8080
systemProp.http.nonProxyHosts=localhost|127.*|*.example.invalid不要把带真实账号密码的文件提交。企业 TLS 拦截应把批准 CA 导入运行 Gradle 的 JVM truststore,或显式提供受控 truststore;关闭证书校验会同时破坏 Wrapper、依赖和缓存的信任边界。
仓库凭证使用 Provider 延迟读取环境变量或 Gradle property,不在脚本中打印:
repositories {
maven {
name = "approvedPrivate"
url = uri("https://packages.example.invalid/maven/")
credentials {
username = providers.gradleProperty("repoUser").orNull
password = providers.gradleProperty("repoToken").orNull
}
}
}CI 只注入只读消费凭证;发布、签名和远端缓存写权限放在隔离 job。Gradle Wrapper 官方还警告把下载凭证放进系统属性可能在 Wrapper 被指向恶意主机时泄露,因此分发 URL 与凭证作用域要一起评审。
插件供应链与治理入口
插件在配置和执行阶段运行代码,能读取源码、环境与凭证。plugins {} 中的版本、pluginManagement.repositories、settings plugin、init script、buildSrc 和 included build 都能改变构建逻辑。
团队应在 Settings 集中插件源:
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}真实企业配置应按批准源收敛,而不是无条件复制两个公网入口。插件版本必须显式或由受控 version catalog/convention plugin 管理。用户级 init script 可以在本机偷偷改仓库和任务,CI 应使用可重建的干净 Gradle User Home,避免继承个人全局脚本。
Dependency Verification 能覆盖插件产物,但首次基线仍需信任审查。新增插件 PR 至少说明维护状态、许可证、权限面、网络行为、配置缓存兼容、升级路径和替代方案。
常见失败:沿生命周期分层定位
Unsupported class file major version 或 JVM 不兼容
Wrapper 启动失败、插件加载失败,或编译能过但测试 JVM 失败。
比较 ./gradlew --version 中 Daemon JVM、javaToolchains 和任务实际 toolchain;不要只看 shell 的 java -version。
运行 Gradle 的 JVM 不在兼容范围,插件用更高字节码编译,或 Toolchain 只配置了 compile 未覆盖 test。
按兼容矩阵调整 Wrapper/运行 JVM,统一 Toolchain 任务,升级或回滚插件。
CLI、IDE 和 CI 的版本证据一致,clean build 与测试通过。
Could not resolve、401 或仓库内容不一致
某些依赖可下载,插件或私有依赖失败。
区分 pluginManagement、dependencyResolutionManagement、普通 project repository 和 Wrapper URL;用 --info 查看实际请求主机,检查凭证 property 是否存在但不打印真值。
修正源作用域、content filter、凭证与 CA;撤销可能发往错误主机的凭证。
隔离 Gradle User Home 从批准源恢复,校验元数据通过。
任务错误地 UP-TO-DATE 或错误命中缓存
输入已变但任务被跳过,产物含旧内容。
用 --info 查看跳过原因,再用 --rerun-tasks --no-build-cache 建立对照;审查任务输入、输出、环境变量、路径敏感性和共享目录。
补全输入输出声明,移除不可控外部状态;污染远端缓存时先停止写入并隔离命名空间。
干净、增量、本地缓存和受控远端缓存四种结果等价。
Configuration Cache 报告不兼容
启用后构建失败,报告指出执行阶段引用 Project 或读取未声明环境。
打开 configuration cache report,定位具体 task/plugin;不要把所有问题改成 warn 后宣布完成。
改用 Provider/ValueSource、声明输入、升级插件,或对明确不兼容任务标记边界。
连续两次相同命令,第二次复用配置缓存;关键测试和产物与关闭缓存一致。
Daemon 数量增多、内存被耗尽
开发机或长生命周期 runner 存在多个 idle Daemon,构建被 OOM kill。
./gradlew --status、进程命令行、Gradle/JDK/JVM args 组合和容器内存限制。
统一 Wrapper 与 Gradle JVM,合理设置堆和 worker,并在升级窗口停止旧 Daemon。
代表并发下宿主机无交换抖动/OOM,Daemon 组合数量可解释。
多项目只构建一个模块却漏掉验证
:service:build 通过,完整 build 的聚合检查失败。
查看 task graph,确认质量插件绑定在 root、subproject 还是独立 task;检查项目依赖是否准确。
为局部开发和合并门禁定义不同但明确的权威入口,聚合规则不能靠开发者记忆。
局部入口快速反馈,完整入口覆盖聚合检查,两者产物依赖关系一致。
CI 等价入口
CI 与本机调用同一 Wrapper 和同一权威 task:
steps:
- name: versions
run: |
java -version
./gradlew --version
- name: verify
run: ./gradlew clean build --stacktrace是否每次 clean 由团队策略决定;保留定期干净基线,同时允许受控增量 job。缓存键至少包含 Wrapper、运行 JDK、操作系统/架构、settings、build logic、lockfile 与 verification metadata。只缓存 Gradle 官方可迁移目录,不能把未知用户级 init script 或明文 credentials 一并打包。
远端 Build Cache 的开发分支默认只读,主线受控 job 才能写。缓存命中率不是唯一目标;错误复用率必须为零,出现可疑产物时有一键 --no-build-cache --rerun-tasks 的回退入口和缓存隔离流程。
Build Scan 在 CI 中只有完成数据边界审批后才能发布。未批准时归档 --profile 报告、测试报告、版本、task 日志和依赖证据。
升级与回滚
Gradle 官方建议通过 Wrapper 升级。升级前先在旧版本运行:
./gradlew help --warning-mode=all
./gradlew clean build
./gradlew dependencies随后在独立分支更新 Wrapper,校验分发 SHA-256,并比较:
Gradle/Daemon JVM/内嵌 Kotlin 与插件兼容。projects、tasks、依赖图和锁文件。deprecation、configuration cache 与 dependency verification 报告。
干净、增量和缓存构建的产物与测试。IDE 导入、CI runner、代理、CA 和 Toolchain。
大版本不要跨多级一次跳完。先升级到旧主线最后一个小版本,消除警告,再进入新主线。插件使用内部 API 时可能在小版本也破坏,官方 9.6 升级说明就列出内部 Problems API 移除风险;升级结论必须来自代表仓库验证。
回滚恢复 Wrapper 脚本、JAR、properties、build logic、插件版本、lockfile、verification metadata 和 CI 缓存命名空间的同一提交。停止新版本 Daemon,隔离新缓存,重新跑旧基线。只改 distributionUrl 而保留新插件和锁文件,不是完整回滚。
团队应把 Gradle 当成受维护的软件系统:
Wrapper 是唯一入口,JAR 与分发 checksum 进入评审。Settings 统一项目发现、插件源和依赖源,项目不得随意增加公网仓库。Java Toolchain、Gradle JVM 和 IDE 导入规则分别记录。
自定义任务必须声明输入输出,build logic 有测试和 owner。锁文件与 verification metadata 随依赖变更评审,不能自动覆盖。缓存有分层权限、命名空间、保留、污染隔离和删除演练。
Build Scan/Develocity 有数据分类、访问、留存和退出策略;未批准则使用本地证据。升级 PR 单独进行,包含警告、插件兼容、性能、产物和回滚结果。
构建性能基线至少记录配置时间、任务执行时间、缓存命中、下载量和峰值内存。不要用一台热缓存开发机的总耗时承诺全团队性能。
三类缓存会掩盖三类不同模型缺陷
UP-TO-DATE 错误通常是任务输入输出不完整,Build Cache 错误还涉及跨工作区可迁移性,Configuration Cache 错误则暴露配置阶段读取外部状态。遇到错误产物,先用 --rerun-tasks --no-build-cache --no-configuration-cache 建对照,再逐层恢复。一次性删除 .gradle 只能抹掉证据,不能证明根因。
远端缓存写权限等价于产物注入能力
能向共享 cache 写入错误或恶意输出的 job,可能影响其他开发者和主线。必须按仓库、分支、工具链和信任级别隔离,主线写、开发只读;缓存服务端启用 TLS、认证和审计。污染后先禁写、切新 namespace,再从可信提交重建,不能边查边继续扩散。
用户级 init script 会绕过仓库评审
~/.gradle/init.d 可以改仓库、任务和插件行为。本机“神秘可用”、CI 失败时,要检查 Gradle User Home。受控 CI 使用干净或模板化 user home;组织策略若必须用 init script,也应版本化、签名/校验、有 owner,并在构建证据中记录版本。
Toolchain 自动下载扩大供应链与网络边界
自动下载 JDK 提高一致性,也引入 Resolver 插件、下载源、许可证、校验、磁盘与代理问题。团队要明确允许的供应商和版本、缓存目录、离线预热、漏洞响应与退出路径。没有 Resolver 时找不到 JDK 是预期失败,不应临时改用未知本机 JDK。
Configuration Cache 的“能存”不等于行为正确
某些构建逻辑即使通过序列化,也可能遗漏外部输入,导致复用旧配置。验证时需主动改变受声明配置、环境和文件,确认缓存按预期失效;同时比较关闭缓存结果。warning 模式只服务迁移,不能作为永久成功标准。
Build Scan 提效与数据出境必须一起决策
Build Scan 能显著降低远程排障成本,但可能包含源码路径、依赖坐标、主机信息、任务参数和日志。团队必须先回答数据发到哪里、谁能访问、保留多久、能否删除、敏感字段如何过滤。未完成审批时,用本地 profile 和结构化日志替代,不以排障便利绕过数据治理。
DSL 和 build logic 迁移会改变配置时序
Groovy 转 Kotlin、buildSrc 转 included build、脚本插件转 convention plugin,不只是语法替换。插件应用时机、类型安全访问器、classloader 与缓存边界都可能变化。迁移应分步比较 task graph、依赖、配置时间、产物和 IDE 导入,保留旧入口直到等价门禁通过。
仓库提交完整 Wrapper 文件,并校验分发 URL、版本与 SHA-256。./gradlew --version、Daemon JVM、Toolchain 与 IDE Gradle JVM 都可解释。Settings、Project、Task 和 initialization/configuration/execution 三阶段边界清楚。
Groovy/Kotlin DSL 选型有团队与迁移依据,不在业务变更中顺手重写。插件源、依赖源和 Toolchain 下载源分别受控,无项目私自绕过。依赖锁定与 verification metadata 已评审,changing dependency 有明确例外。
自定义任务声明完整输入输出,干净、增量和缓存结果等价。Build Cache 与 Configuration Cache 独立启用、诊断和回滚。代理使用受信 CA,仓库、日志、Build Scan 中没有真实凭证。
远端缓存写权限、污染隔离、保留与清理演练明确。本机与 CI 调用同一 Wrapper 和权威 task,并保留无缓存对照入口。升级记录包含警告、插件、DSL、依赖、缓存、产物和完整回滚。
Build Scan/Develocity 已完成数据边界审批,或使用本地证据替代。构建 owner、升级窗口、例外到期和工具退出路径明确。
升级前检查入口
安装入口和 Wrapper 是否正确:用 安装说明 判断何时需要全局 Gradle,再用 Wrapper 文档 核对分发 URL、SHA-256、JAR 与升级步骤。
目标 Gradle 能否运行在当前 JVM:查看 兼容矩阵,分别确认运行 Gradle、编译、测试、Kotlin/Groovy DSL 和平台支持,不能只看 Java language level。
模型或任务顺序为什么变化:用 核心概念 与 构建生命周期 复核 Settings、Project、Task 和三阶段行为。
DSL 或 JDK 选择是否改变:查看 Kotlin DSL 与 Java Toolchains,确认内嵌 Kotlin、插件 API、Gradle JVM 与编译 JDK 的兼容关系。
依赖为什么没有更新或离线失败:查看 依赖缓存,区分动态版本、changing module、离线模式、刷新与 Gradle User Home 污染。
缓存为什么命中、失效或复用错误:分别检查 Build Cache 和 Configuration Cache,确认任务输入输出、可迁移性、配置输入和回滚开关。
解析结果与下载内容能否受控:用 依赖锁定 固定版本,用 依赖校验 审查 checksum、签名、插件和元数据。
性能或弃用从哪里取证:先看 本地性能与 Build Scan 入口,完成数据边界判断后再决定本地 profile 或发布 Build Scan。
是否可以进入 Gradle 9 的目标小版本:逐项执行 Gradle 9 升级指南 中的警告、插件和 Wrapper 检查,再比较任务图、依赖、测试、产物与缓存结果。
