Skip to content

UpdateGradleWrapper emits malformed CLASSPATH="\\\"\\\"" for Gradle 8.14.x #7564

@DovOps

Description

@DovOps

UpdateGradleWrapper emits malformed CLASSPATH="\\\"\\\"" for Gradle 8.14.x

What version of OpenRewrite are you using?

  • openrewrite/rewrite - current main branch (confirmed in GradleWrapperScriptDownloader.java, SHA eb4dabd)
  • Affects all consumers of the pre-computed wrapper resources for Gradle 8.14-rc-1 through 9.0-milestone-1

How are you running OpenRewrite?

Via the Moderne platform (which uses the UpdateGradleWrapper recipe internally as part of DevelocityOnboarding and similar recipes). The issue is not in recipe execution - it is in the pre-computed wrapper scripts stored as resources in rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-wrapper/unix/.

The affected stored file is:

rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-wrapper/unix/b67a59d5.txt

This file is used for all Gradle versions from 8.14-rc-1 through 8.14.4 (and equivalently 665958e3.txt covers 9.0.0 through 9.0-milestone-2).

What is the smallest, simplest way to reproduce the problem?

Run UpdateGradleWrapper targeting Gradle 8.14.4 on any project whose current wrapper is pre-8.14. Inspect the resulting gradlew. The CLASSPATH line will be:

CLASSPATH="\\\"\\\""

For comparison, the same recipe targeting Gradle 9.1.0 produces the correct:

CLASSPATH=

What did you expect to see?

CLASSPATH=

This is what the official Gradle 8.14.4 wrapper template renders when invoked via ./gradlew wrapper --gradle-version 8.14.4, and what OpenRewrite itself correctly emits for Gradle 9.1.0-rc-1 and later.

What did you see instead?

CLASSPATH="\\\"\\\""

When the shell evaluates this, CLASSPATH is set to the literal string \"\" (backslash-quote-backslash-quote), which is then passed to the JVM as -classpath "\"\"". This is not a valid classpath.

Root cause

The bug is in GradleWrapperScriptDownloader.java in the unixBindings() method.

For Gradle 9.1.0-rc-1+ (correct):

binding.put("classpath", "");
  • produces CLASSPATH= in the script -

For Gradle 8.14-rc-1 through 9.0-milestone-1 (incorrect):

binding.put("classpath", "\"\\\\\\\\\\\"\\\\\\\\\\\"\"");
  • produces CLASSPATH="\\\"\\\"" in the script -

The over-escaped Java string was further compensated by a fragile post-processing patch in renderTemplate():

.replace("CLASSPATH=\"\\\\\\\\\\\"\\\\\\\\\\\"", "CLASSPATH=\"\\\\\\\"\\\\\\\"");

This bandage trims one layer of extra escaping but still leaves CLASSPATH="\\\"\\\"" rather than a clean empty value.

Why is CLASSPATH empty for 8.14+?

Gradle 8.14 changed the wrapper invocation from class-based to JAR-based. The old pattern:

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# ...
eval set -- ... -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain ...

The new pattern (8.14+):

CLASSPATH=          # intentionally empty
# ...
set -- \
    "-Dorg.gradle.appname=$APP_BASE_NAME" \
    -classpath "$CLASSPATH" \
    -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
    "$@"

Because -jar is used, the JVM specification states that -classpath is ignored. So CLASSPATH="\\\"\\\"" does not break functionality, but it is confusing to code reviewers and linters, and differs from the official Gradle-generated wrapper.

What is the full stack trace of any errors you encountered?

No runtime error - the wrapper functions correctly because -jar overrides -classpath. However, the change raises reviewer concern and fails shellcheck-style audits.

Suggested fix

In unixBindings(), apply the same fix used for 9.1.0-rc-1+ to the 8.14-rc-1 - 9.0-milestone-1 range:

} else if (current.compareTo(GRADLE_8_14_RC_1) >= 0) {
-   binding.put("classpath", "\"\\\\\\\\\\\"\\\\\\\\\\\"\"");
+   binding.put("classpath", "");
    binding.put("entryPointArgs", "-jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\"");
    binding.put("mainClassName", "");

Also apply the same to 9.0-milestone-2:

} else if (current.compareTo(GRADLE_9_0_M_2) >= 0) {
-   binding.put("classpath", "\"\\\\\\\"\\\\\\\"\"");
+   binding.put("classpath", "");

And remove the compensating .replace() in renderTemplate() once the bindings are fixed:

-  .replace("CLASSPATH=\"\\\\\\\\\\\"\\\\\\\\\\\"", "CLASSPATH=\"\\\\\\\"\\\\\\\"");

After the fix, the pre-computed resource files for all affected Gradle versions should be regenerated by re-running GradleWrapperScriptDownloader.main().

Are you interested in contributing a fix to OpenRewrite?

Happy to submit a PR with the above fix if the OpenRewrite team confirms this approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggradle

    Type

    No type

    Projects

    Status

    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions