正确姿势是用 providers.environmentVariable("FOO").forUseAtConfigurationTime() 或 project.findProperty("FOO"),避免 System.getenv() 和 System.getProperty() 破坏配置缓存。

Gradle插件里读取环境变量的正确姿势
直接用 System.getenv("FOO") 在插件里取值,看似简单,但会破坏 Gradle 的配置缓存(Configuration Cache)。因为配置缓存要求所有构建逻辑必须是确定性的、与外部状态无关的,而环境变量属于不可序列化的外部输入——Gradle 无法保证下次构建时它的值不变,所以一旦检测到这种调用,就会静默禁用缓存或直接报错 Configuration cache is not supported because task ... uses environment variable 'FOO'。
真正安全的做法是把环境变量“注入”为构建参数或项目属性,再由插件读取:
- 启动时传入:
gradle build -Penv=prod -Dorg.gradle.configuration-cache=true,插件里用project.findProperty("env")获取 - 在
gradle.properties里写env=staging,同样用findProperty读,且该文件默认被配置缓存信任 - 如果必须依赖环境变量(比如 CI 系统注入的密钥),改用
providers.environmentVariable("API_KEY").forUseAtConfigurationTime()—— 这个 API 是 Gradle 7.3+ 提供的显式声明方式,告诉 Gradle:“我清楚这是外部值,只用于配置阶段,不参与任务输入”,从而绕过缓存拦截
为什么 System.getProperty() 也不行?
System.getProperty("foo") 看起来比 getenv “干净”,但它依然不被配置缓存接受。原因在于:JVM 属性是全局、可变、不可序列化的运行时状态。Gradle 配置缓存要求所有参与配置决策的数据必须能完整序列化并复现,而 System.getProperty 的返回值无法保证跨 JVM 实例一致(尤其在 daemon 模式下)。
正确替代方案只有两个:
- 用
project.findProperty("xxx"),它从 Gradle 自身的属性源(gradle.properties、-P参数、系统属性前缀org.gradle.)中安全读取,且这些源已被缓存机制白名单覆盖 - 用
providers.systemProperty("xxx").forUseAtConfigurationTime(),和环境变量那个 API 一样,属于显式授权模式,需配合@Option或@Input注解使用才有效
插件中暴露可配置参数的最佳实践
如果你开发的是一个可复用插件,别让使用者去碰环境变量或 JVM 属性。应该提供类型安全的扩展 DSL:
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create("myConfig", MyExtension::class.java)
project.afterEvaluate {
// 从 extension 取值,而非 System.*
val apiKey = extension.apiKey.get()
// ...
}
}
}
<p>open class MyExtension @Inject constructor(private val providers: ProviderFactory) {
val apiKey: Provider<String> = providers.environmentVariable("MY_API_KEY")
.forUseAtConfigurationTime()
}这样既满足配置缓存要求,又把环境依赖显式收敛到 extension 定义里,避免散落在各处的 getenv 调用。
注意:所有 Provider 类型的属性必须用 .get() 或 .getOrElse(...) 显式解包,不能直接赋值给普通字段——否则 Gradle 无法追踪其来源,缓存仍会失效。
容易被忽略的缓存陷阱:wrapper 和本地发行版路径
即使插件本身完全合规,构建仍可能因 GRADLE_USER_HOME 或 GRADLE_HOME 指向了非标准路径而触发缓存降级。Gradle 会检查这些路径是否在可信目录列表内(如 $HOME/.gradle 或 /opt/gradle),否则拒绝启用配置缓存。
验证方法很简单:
- 运行
gradle help --scan,打开生成的 Build Scan,看 “Configuration Cache” 标签页是否显示 “Enabled” - 若显示 “Disabled”,点开详情,通常会看到类似
reason: Gradle home directory is not trusted的提示 - 解决办法:要么把
GRADLE_HOME改到默认位置,要么在gradle.properties中加一行org.gradle.configuration-cache.trusted-gradle-homes=/your/custom/path
这个配置项在 Gradle 8.4+ 才支持,旧版本只能迁路径——很多人卡在这一步,却以为是插件代码有问题。


















