Skip to content

Upgrade to Gradle 9.7.0 and GradleUp Shadow, convert build to Kotlin DSL - #238

Open
lhotari wants to merge 2 commits into
apache:mainfrom
lhotari:lh-improve-gradle-9-upgrade
Open

lhotari wants to merge 2 commits into
apache:mainfrom
lhotari:lh-improve-gradle-9-upgrade

Conversation

@lhotari

@lhotari lhotari commented Aug 17, 2026 •

Copy link
Copy Markdown
Member

Motivation

The build is on Gradle 8.9 and uses com.github.johnrengelman.shadow 7.1.2, which has been unmaintained since maintenance moved to the GradleUp organization. Upgrading to Gradle 9.7.0 also requires replacing org.cadixdev.licenser, which fails with a StackOverflowError on Gradle 9 and has had no release since 2021.

While doing the upgrade, the build is converted to Kotlin DSL and brought in line with the Gradle best practices.

Modifications

Gradle and plugin upgrades

Plugin Before After
shadow com.github.johnrengelman.shadow 7.1.2 (hardcoded in the build script) com.gradleup.shadow 9.6.1 (version catalog)
rat 0.8.0 0.8.1
test-logger 3.2.0 4.0.0
versions com.github.ben-manes 0.51.0 io.github.ben-manes 0.61.0 (the com.github id is deprecated)
version-catalog-update 0.8.5 1.1.1
licenser 0.6.1 removed, replaced by Spotless 8.10.0
spring-javaformat 0.0.48 unchanged, already the latest

Gradle itself goes from 8.9 to 9.7.0, with distributionSha256Sum added to the wrapper properties. Library versions are deliberately untouched; that is what versionCatalogUpdate is for.

Shadow 9 migration

  • archiveClassifier.set(null) → archiveClassifier = ""
  • Dropped the manual dependsOn(jar), manifest { inheritFrom … } and build.dependsOn(shadowJar); Shadow 9 wires all three itself
  • components.java.withVariantsFromConfiguration(…) { skip() } → shadow { addShadowVariantIntoJavaComponent = false }
  • The removed dependency(Closure) overload → an explicit Spec<ResolvedDependency>

License header checking

org.cadixdev.licenser is replaced by Spotless, which is actively maintained and is also what apache/pulsar uses. Formatting of Java sources stays with spring-javaformat; Spotless only enforces the ASF header, and Apache RAT remains the repository-wide backstop.

  • CI runs spotlessCheck instead of licenseCheck
  • spotlessApply replaces updateLicenses for adding headers
  • checkstyle/HEADER.txt gains the /* */ markers, because Spotless matches headers verbatim

Published POM <developers>

The two named developer entries are replaced by a single organization entry, matching how apache/pulsar declares developers in its own POM:

<developers>
  <developer>
    <organization>Apache Pulsar developers</organization>
    <organizationUrl>https://pulsar.apache.org/</organizationUrl>
  </developer>
</developers>

Gradle best practices

  • Kotlin DSL, and a build-logic included build instead of buildSrc
  • Repositories declared in the settings files with FAIL_ON_PROJECT_REPOS and exclusiveContent filtering
  • Configuration cache, build cache, parallel execution and UTF-8 encoding enabled in gradle.properties
  • allprojects {} cross-project configuration removed: group moves to gradle.properties, and RAT becomes a single root task (it already scanned every subproject directory)
  • afterEvaluate in the POM replaced by a lazy Property; pluginManager.withPlugin instead of eager plugin checks
  • buildDir → layout.buildDirectory, tasks.register/configureEach throughout, isolated.rootProject instead of rootProject
  • Public task name constants instead of hardcoded strings; group and description on sourceTar
  • BOM constraints listed explicitly instead of iterating rootProject.subprojects

Verifying this change

This change is a build-only change and is covered by the existing CI build.

Verified locally:

  • Cold ./gradlew rat spotlessCheck javadoc check --no-build-cache passes, including the testcontainers integration tests, with no deprecation warnings from the build scripts under --warning-mode all
  • The configuration cache is stored and reused, and build cache hits are confirmed
  • The shaded jar has a byte-identical entry listing (1172 entries) to the jar produced by the previous build
  • The generated POMs are identical to the released 0.7.0 POMs, apart from genuine dependency version bumps since that release and the intentional <developers> change described above
  • The wrapper JAR SHA-256 matches Gradle's official gradle-9.7.0-wrapper.jar.sha256
  • spotlessCheck was confirmed to catch a stripped header in both .java and .gradle.kts files, and spotlessApply restores it byte-identically

Notes for reviewers

  • gradle.properties is no longer license-checked. licenser covered it, but Spotless would need a second hash-comment header file, and RAT already excludes the file because the release plugin strips its header when bumping the version. Happy to add it back if preferred.
  • keepUnusedLibraries was dropped from the versionCatalogUpdate config: version-catalog-update 1.x no longer removes unused entries, so the option no longer exists.
  • checkstyle stays pinned at 9.3, which is what spring-javaformat-checkstyle 0.0.48 depends on.
  • versionCatalogUpdate reads Task.project at execution time and therefore discards the configuration cache entry. It is a maintenance task outside the CI flows, and notCompatibleWithConfigurationCache had no observable effect, so this is documented in a comment rather than worked around.
  • The rat plugin emits one Gradle 10 deprecation (ReportingExtension.file) from its own code. 0.8.1 is the latest release, so this needs an upstream fix.
  • Pre-existing quirk left alone: sourceTar produces pulsar-client-reactive-<version>-<version>-src.tar.gz because archiveBaseName already embeds the version and archiveVersion appends it again. Fixing it would change a release artifact name, so it is left for a separate decision.

Does this pull request potentially affect one of the following parts:

  • Dependencies (add or upgrade a dependency): yes (build plugins only; no changes to the libraries the published artifacts depend on)
  • The public API: no
  • The schema: no
  • The default values of configurations: no
  • The threading model: no
  • The binary protocol: no
  • The rest endpoints: no
  • The admin cli options: no
  • Anything that affects deployment: no

Documentation

  • doc-not-needed

This is a build-internal change. The only developer-facing difference is that the license header tasks are now spotlessCheck / spotlessApply.

Upgrades the build to Gradle 9.7.0 (from 8.9) and migrates the Shadow
plugin from the unmaintained `com.github.johnrengelman.shadow` 7.1.2 to
`com.gradleup.shadow` 9.6.1, with the version moved into the version
catalog. The build scripts are converted to Kotlin DSL, `buildSrc` is
replaced by a `build-logic` included build, and the Gradle best practices
are applied throughout.

Plugin upgrades:
- shadow: johnrengelman 7.1.2 -> com.gradleup.shadow 9.6.1
- rat: 0.8.0 -> 0.8.1
- test-logger: 3.2.0 -> 4.0.0
- versions: com.github.ben-manes 0.51.0 -> io.github.ben-manes 0.61.0
  (the `com.github` plugin id is deprecated)
- version-catalog-update: 0.8.5 -> 1.1.1
- licenser 0.6.1 -> replaced by Spotless 8.10.0 (see below)

`org.cadixdev.licenser` had to be replaced: it fails with a
StackOverflowError on Gradle 9 and has had no release since 2021. Spotless
takes over the ASF license header check and apply, so CI now runs
`spotlessCheck` instead of `licenseCheck` and `spotlessApply` replaces
`updateLicenses`. `checkstyle/HEADER.txt` gains the comment markers because
Spotless matches headers verbatim. Formatting of Java sources stays with
spring-javaformat and Apache RAT remains the repository-wide backstop.

Shadow 9 migration details:
- `archiveClassifier.set(null)` -> `archiveClassifier = ""`
- dropped the manual `dependsOn(jar)`, `manifest { inheritFrom ... }` and
  `build.dependsOn(shadowJar)`; Shadow 9 wires all three itself
- `components.java.withVariantsFromConfiguration(...) { skip() }` ->
  `shadow { addShadowVariantIntoJavaComponent = false }`
- the removed `dependency(Closure)` overload -> explicit
  `Spec<ResolvedDependency>`

Best practices applied (docs.gradle.org/current/userguide/best_practices_index.html):
- Kotlin DSL and a `build-logic` composite build instead of `buildSrc`
- repositories declared in the settings files with
  `FAIL_ON_PROJECT_REPOS` and `exclusiveContent` filtering
- configuration cache, build cache, parallel execution and UTF-8 encoding
  enabled in `gradle.properties`
- `allprojects {}` cross-project configuration removed: `group` moves to
  `gradle.properties` and RAT becomes a single root task, which already
  covered every subproject directory
- `afterEvaluate` in the POM replaced by a lazy `Property`, and
  `pluginManager.withPlugin` used instead of eager plugin checks
- `buildDir` -> `layout.buildDirectory`, `tasks.register`/`configureEach`
  throughout, `isolated.rootProject` instead of `rootProject`
- public task name constants instead of hardcoded strings, group and
  description on `sourceTar`
- BOM constraints listed explicitly instead of iterating
  `rootProject.subprojects`
- `distributionSha256Sum` added to the wrapper properties

Verified that the shaded jar entries are identical to those produced by the
previous build, and that the generated POMs match the released 0.7.0 POMs
apart from genuine dependency version bumps.
@lhotari
lhotari requested a review from onobc August 17, 2026 10:13
Replaces the two named developer entries with a single organization entry,
matching how apache/pulsar declares developers in its own POM.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants