Environment Variables Not Tracked by Gradle

Environment variables are NOT automatically tracked by Gradle. This means that changes to environment variables won’t trigger tasks to be re-executed, and they won’t be considered in Gradle’s up-to-date checking or incremental build system.

Why Environment Variables Aren’t Tracked:

  • Gradle’s incremental build support is based on its ability to track task inputs and outputs, such as files or task properties explicitly defined within the build script.
  • Environment variables exist outside of Gradle’s scope, so it doesn’t automatically know about changes to them. As a result, tasks depending on environment variables can still be considered up-to-date even if the environment variables change.

Example: Environment Variable is not Tracked

Glass thought Sandbox Snapshot & Explanation

In this example we will run the following shell, which will vary the environment variable MY_ENV_VAR between two values and run the task saveEnvVarToFile which writes the environment variable to a file.

export MY_ENV_VAR=HI4 \
&& ./gradlew saveEnvVarToFile --console=plain \
&& cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt \
&& export MY_ENV_VAR=HI5 \
&& ./gradlew saveEnvVarToFile --console=plain \
&& cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt

This is the gradle task that writes the environment variable to a file:

tasks.register("saveEnvVarToFile") {
    val envVar = System.getenv("MY_ENV_VAR")

    // Without declaring the property as input and relying on env variable.
    // inputs.property("MY_ENV_VAR", envVar)
    outputs.file("build/output.txt")    // Output file

    doLast {
        val outputFile = file("build/output.txt")
        outputFile.writeText("Environment property: $envVar \n")

        println("Environment variable: $envVar")
    }
}

Notice that we have explicitly commented out the inputs.property("MY_ENV_VAR", envVar) line, which would have declared the environment variable as a task input. Now when we run the 2nd run even though the environment variable has changed, the task is still considered up-to-date.

Command to reproduce:

gt.sandbox.checkout.commit 5b726c5 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "export MY_ENV_VAR=HI4 && ./gradlew saveEnvVarToFile --console=plain && cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt && export MY_ENV_VAR=HI5 && ./gradlew saveEnvVarToFile --console=plain && cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt"

Recorded output of command:

> Task :lib:saveEnvVarToFile UP-TO-DATE

BUILD SUCCESSFUL in 424ms
1 actionable task: 1 up-to-date
Environment property: HI4 
> Task :lib:saveEnvVarToFile UP-TO-DATE

BUILD SUCCESSFUL in 337ms
1 actionable task: 1 up-to-date
Environment property: HI4 

Workaround: Manually Declare Environment Variables as Inputs

If you want Gradle to consider environment variables when determining whether a task is up-to-date, you can declare them as task inputs. This way, Gradle will re-execute the task when the environment variable changes.

Example: Tracking an Environment Variable as a Task Input

tasks.register("saveEnvVarToFile") {
    val envVar = System.getenv("MY_ENV_VAR")

    // Declare the environment variable as input:
    inputs.property("MY_ENV_VAR", envVar)
    outputs.file("build/output.txt")    // Output file

    doLast {
        val outputFile = file("build/output.txt")
        outputFile.writeText("Environment variable saved in file: $envVar \n")

        println("Environment variable: $envVar")
    }
}
Glass thought Sandbox Snapshot & Explanation

Now when we run

export MY_ENV_VAR=HI4 \
&& ./gradlew saveEnvVarToFile --console=plain \
&& cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt \
&& export MY_ENV_VAR=HI5 \
&& ./gradlew saveEnvVarToFile --console=plain \
&& cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt

The task will re-execute when the environment variable changes.

Command to reproduce:

gt.sandbox.checkout.commit 18b8810 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "export MY_ENV_VAR=HI4 && ./gradlew saveEnvVarToFile --console=plain && cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt && export MY_ENV_VAR=HI5 && ./gradlew saveEnvVarToFile --console=plain && cat ${GLASSTHOUGHT_SANDBOX:?}/lib/build/output.txt"

Recorded output of command:


> Task :lib:saveEnvVarToFile
Environment variable: HI4

BUILD SUCCESSFUL in 350ms
1 actionable task: 1 executed
Environment variable saved in file: HI4 

> Task :lib:saveEnvVarToFile
Environment variable: HI5

BUILD SUCCESSFUL in 342ms
1 actionable task: 1 executed
Environment variable saved in file: HI5 

Gotcha

The gotcha with this approach is that we need to know which tasks depend on which environment variables and declare them as inputs manually.

Key Points:

  • Environment variables are not tracked by default, so changes to them won’t trigger task re-execution.
  • To ensure a task re-executes when an environment variable changes, declare the environment variable as a task input using inputs.property().
  • By declaring it as an input, Gradle will treat the environment variable as part of the task’s state and include it in the up-to-date checks.

Conclusion:

While Gradle doesn’t automatically track environment variables, you can make Gradle aware of them by manually declaring them as task inputs. This ensures that tasks depending on environment variables will re-run when those variables change.


Backlinks