Skip to content

build: Gradle 9.6.1 on JDK 25, and clear the Gradle 10 deprecations - #3291

Merged
juherr merged 26 commits into
testng-team:masterfrom
juherr:build/gradle-9-jdk-25
Jul 28, 2026
Merged

build: Gradle 9.6.1 on JDK 25, and clear the Gradle 10 deprecations#3291
juherr merged 26 commits into
testng-team:masterfrom
juherr:build/gradle-9-jdk-25

Conversation

@juherr

@juherr juherr commented Jul 26, 2026

Copy link
Copy Markdown
Member

What

Moves the build to Gradle 9.6.1 on JDK 25, and clears every Gradle 10 deprecation that originates in this repository.

The bytecode target does not change. targetJavaVersion stays at 11, so TestNG still runs on Java 11+ and this is not a breaking change for users. Only contributors are affected: building now requires JDK 25.

This is the build half of #3288, rebased on current master and kept deliberately free of the source refactors in that PR so it can be reviewed on its own.

Build

  • jdkBuildVersion and targetJavaVersion are declared once, in the root gradle.properties. Included builds do not inherit it, so build-logic-commons reads the file from disk. Their fallback stays at 17 (the Kotlin DSL floor) rather than following targetJavaVersion, since the JVM target of the build scripts is unrelated to TestNG's bytecode target.
  • Gradle 8.11.1 → 9.6.1: dirMode/fileModedirPermissions/filePermissions, BuildIdentifier.isCurrentBuild()buildPath, non-null manifest attributes, build-logic-commons included via pluginManagement, vlsi gradle-extensions 1.90 → 3.0.2, Shadow com.github.johnrengelman:8.1.1com.gradleup.shadow:9.6.1.
  • Build JDK 21 → 25. Error Prone 2.20 crashes on JDK 25, hence error_prone_core 2.20 → 2.50 and gradle-errorprone-plugin 3.1.0 → 4.1.0. The new SelfAssertion check is disabled: it only flags the intentionally trivial assertions in TestNG's own sample/fixture classes.

Plugin updates

  • net.ltgt.errorprone 4.1.0 → 5.1.0. Not just hygiene: Error Prone 2.46.0 onwards requires -XDaddTypeAnnotationsToSymbol=true, which the plugin only started passing in 4.4.0, so running error_prone_core 2.50.0 on plugin 4.1.0 was below what the checker expects. None of the 5.0.0 breaking changes apply (min Gradle 7.1, min JDK 11, and the isEnabled/isCompilingTestOnlyCode rename touches no property this build sets).
  • org.sonarqube 4.4.1.3373 (2023) → 7.3.1.8318
  • Kotlin plugin and kotlin-bom 2.3.0 → 2.4.10
  • assertj-core 3.23.1 → 3.27.7
  • foojay-resolver-convention 0.8.0 → 1.0.0
  • org.gradlex.build-parameters 1.4.4 → 1.4.5

Autostyle stays at 4.0. 4.0.1 was tried and fails every module on JDK 25:

class com.google.googlejavaformat.java.RemoveUnusedImports cannot access
class com.sun.tools.javac.file.JavacFileManager (in module jdk.compiler)
because module jdk.compiler does not export com.sun.tools.javac.file to unnamed module

Gradle 10 deprecations

Every remaining deprecation that comes from this repository is fixed:

  • Develocity plugin 3.19.1 → 4.5.0 (Gradle 10 rejects < 4.0)
  • DependencyHandler.create(group, name, version) → single-string notation
  • The Kotlin DSL property delegates (by creating, by registering(T::class), by project)
  • nmcp's publishAllProjectsProbablyBreakingProjectIsolation(), which feeds Project objects to the dependency handler (see below)

CI

  • Every action pinned to a commit SHA, with the version kept as a trailing comment so Dependabot can still bump them. All 10 SHAs were verified against the tags they claim.
  • The test workflow provisions the matrix test JDK plus JDK 25 (last, so it becomes JAVA_HOME); the publish workflows move from zulu 21 to temurin 25.
  • Java 26 is treated as released; 27 and 28 are the new early-access entries. Java 11 stays in the matrix, since the artifacts still target it.
  • MATRIX_JOBS 7 → 8: the java_version axis now has 7 values, so 7 jobs left the matrix exactly at capacity with no slack for the other axes to pair against.

Three points worth reviewer attention

1. The Autostyle → Spotless swap was attempted and dropped

An earlier revision of this branch replaced Autostyle with Spotless, on the grounds that Autostyle 4.0 has been unmaintained since 2021. It was removed after Spotless proved flaky in this build, and Autostyle is kept as-is.

Under org.gradle.parallel=true, spotlessJavaCheck fails intermittently — roughly 1 run in 6 of clean build — with the formatter classloader collapsing mid-run:

Execution failed for task ':testng:spotlessJavaCheck'
> There were 1 lint error(s), they must be fixed or suppressed.
  .../VerifyShadedJarWorksTest.java:LINE_UNDEFINED removeUnusedImports(java.lang.NoClassDefFoundError)
      com/google/googlejavaformat/java/RemoveUnusedImports (...)

The class reported missing varies between runs (com/google/common/collect/Iterators$MergingIterator, ImmutableList$SubList, common/base/Predicate, and google-java-format's own RemoveUnusedImports), which points at a classloader being closed while still in use rather than at any one bad step.

Measured across configurations, 12 runs of clean build -x test each:

Configuration Failures
Spotless, --no-parallel 0 / 6
Spotless, removeUnusedImports("cleanthat-javaparser-unnecessaryimport") ~2 / 12
Spotless, default removeUnusedImports() ~2 / 12
Spotless, without the root apply false declaration 12 / 12
Spotless, tasks serialized through a maxParallelUsages = 1 build service 6 / 12
Autostyle (this PR) 0 / 10

Spotless 8.8.0 is the latest release, so there is no upstream fix to pick up. Disabling parallel execution or serializing every formatter task is too high a price for a formatter swap, and shipping an intermittently red check would be worse than keeping an unmaintained plugin.

Tracked in #3292, which records the measurements above and the leads worth trying (applying Spotless from the root project only, so there is a single set of formatter tasks and no cross-project concurrency; reporting the classloader race upstream). Keeping Autostyle also keeps this PR's diff to build files: the swap dragged 86 reformatted Java files with it, because the google-java-format version bundled with Spotless reflows javadoc differently.

Note this does not hold back the configuration cache: Gradle 9 still keeps it off by default, and Spotless' removeUnusedImports is not configuration-cache compatible either.

2. The nmcp aggregation was rewritten — please check this one

publishAllProjectsProbablyBreakingProjectIsolation() discovers publishing projects automatically, but it does so by handing Project objects to the dependency handler, a notation Gradle 10 removes. nmcp 1.6.1 (bumped here from 1.4.1) still has not fixed it.

The projects to aggregate are therefore listed explicitly, which required applying com.gradleup.nmcp in the testng.maven-publish convention. The obvious risk is that a future publishing module gets forgotten and silently disappears from a release, so a projectsEvaluated guard fails the build when a project applies maven-publish without being aggregated.

Verified: the aggregation zip still holds the same 24 entries, the :testng POM and module.json are identical to the pre-change baseline (8 variants, testng-guice / testng-yaml capabilities, same files), and the guard does fire when the aggregation entry is removed.

3. Optional features on the main source set — deliberately NOT fixed

Two deprecations are left in place:

The 'guice' feature was created using the main source set. This behavior has been deprecated.
The 'yaml' feature was created using the main source set. This behavior has been deprecated.

They come from registerFeature(name) { usingSourceSet(sourceSets["main"]) } in testng, testng-core and testng-core-api, and from OptionalFeaturesExtension.

I started this and then backed out, because Gradle's prescribed replacement does not fit what the code is doing:

  • Today, testng-core-api's guiceApiElements variant serves the main jar. That is the entire point of the arrangement: same artifact, plus optional dependencies, surfacing as <optional>true</optional> in the POM.
  • With a dedicated source set, that variant would serve an empty testng-core-api-guice.jar, and the guice dependencies would leave main's compile classpath — GuiceHelper would stop compiling unless the dependencies are also added as compileOnly.
  • That changes the artifacts attached to the published variants and what merge-feature-jars resolves into the shaded jar, which cannot be validated honestly without a real Central publication.

This is a redesign of the optional-feature model rather than an API replacement, so it belongs in its own PR. Happy to take direction on the preferred shape.

Note: one deprecation is not ours to fix

The AbstractTestTask.afterTest(Closure) method has been deprecated.
The AbstractTestTask.afterSuite(Closure) method has been deprecated.

These are emitted by com.github.vlsi.gradle-extensions:3.0.2 (PrintTestResults.kt:184), already the latest release. Nothing to do on our side until upstream migrates to addTestListener(TestListener); the alternative would be dropping the plugin, which would also cost the improved test logging.

Verification

  • ./gradlew clean build green on JDK 25 — 12 528 tests, 0 failures.
  • clean build -x test repeated 10 times with no failure, after the Spotless flakiness described above.
  • Bytecode target confirmed unchanged: all 464 classes in testng-<version>-all.jar are major version 55 (Java 11).
  • A TestNG suite was compiled and executed against JDK 11 using the produced jar, and passes.
  • :testng POM and Gradle Module Metadata compared against the pre-refactor baseline: identical.
  • OSGi test module passes from a clean build.
  • Every plugin bump above was applied and built individually before being committed.
  • All 7 workflow YAML files parse; matrix.mjs generates a valid matrix over 15 seeded runs with no unsatisfied requirements; both early-access JDK pages are reachable.

Notes

  • Building now requires JDK 25. .github/CONTRIBUTING.md and docs/ are updated accordingly, along with several pre-existing inaccuracies found on the way (a .sdkmanrc file that does not exist, a test-runtime list mentioning Java 24, an nmcp error quoted as "Java 21+" instead of its actual "Java 17+", and matrix.js renamed to matrix.mjs).
  • No test case was added: this PR changes no product behaviour. The existing suite running green on the new toolchain is the test.

Summary by CodeRabbit

  • Build & Compatibility

    • Builds now require JDK 25, while published artifacts continue targeting Java 11.
    • Updated Gradle to 9.6.1 and refreshed build tooling/plugin versions.
    • Expanded CI to test Java 26 plus early-access Java 27/28.
  • Documentation

    • Updated build, local testing, CI/testing, release process, and quick-reference docs to use Java 25.
  • CI & Release Reliability

    • Pinned GitHub Actions to commit hashes for more consistent CI/publishing runs.
    • Improved reproducible build settings and added verification for published POM dependencies during checks.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The build is upgraded to Gradle 9.6.1 and JDK 25 while published artifacts continue targeting Java 11. Publishing logic adopts newer Gradle APIs, optional feature variants gain shared helpers, CI actions are pinned, Java matrix coverage expands, and related documentation is updated throughout.

Changes

Build and CI modernization

Layer / File(s) Summary
Java toolchain definition and Gradle wrapper upgrade
gradle.properties, build-logic-commons/..., gradle/wrapper/*, gradlew, gradlew.bat, settings.gradle.kts
Repository defines centralized Java version properties (jdkBuildVersion=25, targetJavaVersion=11); included build-logic loads gradle.properties to determine JVM targets; Gradle wrapper updates to 9.6.1; gradlew scripts refactor jar launching and Cygwin handling.
Build-logic plugin and dependency refresh
build-logic/{build-parameters,code-quality,jvm,basics}/...
Plugin versions updated in build-parameters, code-quality, and jvm logic; jdkBuildVersion default changes to 25; SonarQube, Error Prone, Kotlin, and AssertJ dependencies refreshed; reproducible-builds permissions use new Gradle API forms.
Optional feature variants implementation and wiring
build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt, testng-core/..., testng-core-api/...
New OptionalFeatureVariants helpers create feature-scoped consumable configurations and wire Maven optional dependencies; testng-core and testng-core-api replace Gradle's java.registerFeature with the new shared registerOptionalFeatureVariants pattern.
Publishing aggregation and configuration validation
build.gradle.kts, build-logic/settings.gradle.kts, build-logic/publishing/build.gradle.kts, build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt
Root build.gradle.kts switches nmcpAggregation to explicit project dependencies with build-time validation; publishing logic refactors to use new Gradle DSL; settings.gradle.kts restructures plugin/dependency resolution via pluginManagement blocks.
Publishing and configuration DSL migration
build-logic/publishing/src/main/kotlin/..., testng/..., testng-test-osgi/...
Publishing configurations and tasks migrate to explicit configurations.create() / tasks.register<T>() forms; testng build adds verifyPublishedPomDependencies task; signing uses findProperty; manifest attributes use rootProject.property() forms.
CI matrix expansion and action pinning
.github/workflows/*, .github/workflows/matrix.mjs
GitHub Actions pinned to commit SHAs across all workflows; build/publish jobs switch to JDK 25; test matrix expands to Java 26 and early-access 27/28 with updated distribution implication rules; matrix job count increases from 7 to 8.
Documentation and release notes refresh
docs/*, .github/CONTRIBUTING.md, CHANGES.txt
Build, testing, release, contributor, and Java-version documentation updated for JDK 25 requirement, matrix.mjs generator, Java 26 and EA 27/28 coverage; changelog documents toolchain and plugin refresh.

Estimated code review effort: 4 (Complex) | ~50 minutes

Sequence Diagram(s)

sequenceDiagram
  participant matrix_prep as matrix_prep job
  participant matrix_mjs as matrix.mjs
  participant test_job as test job (8x parallel)
  participant build_gradle as Gradle + JDK 25
  matrix_prep->>matrix_mjs: generate Java/distro matrix
  matrix_mjs->>matrix_prep: return 8 job configs (Java 20–26, EA 27–28)
  matrix_prep->>test_job: dispatch matrix
  test_job->>build_gradle: setup Java 25 for Gradle
  build_gradle->>build_gradle: run tests on matrix Java version
Loading

Possibly related PRs

Suggested reviewers: krmahadevan

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main changes: upgrading to Gradle 9.6.1 on JDK 25 and removing Gradle 10 deprecations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@juherr

juherr commented Jul 26, 2026

Copy link
Copy Markdown
Member Author

@vlsi could you review this? It moves the build to Gradle 9.6.1 on JDK 25 and clears the Gradle 10 deprecations. Bytecode target stays at Java 11.

Two questions for you:

1. gradle-extensions — Gradle 9 deprecates AbstractTestTask.afterTest/afterSuite(Closure) (removed in Gradle 10), and the trace points at PrintTestResultsKt.printTestResults(PrintTestResults.kt:184). We are on 3.0.2 and I found no issue for it. Is the move to addTestListener(TestListener) on your radar?

2. Optional featuresregisterFeature(name) { usingSourceSet(sourceSets["main"]) } is deprecated, and the prescribed fix does not seem to fit: today guiceApiElements serves the main jar, whereas a dedicated source set would serve an empty one and push the guice dependencies off main's compile classpath. For :testng the feature has no artifact at all anyway, since OptionalFeaturesExtension calls artifacts.clear().

What would you do here? I left it untouched in this PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts (1)

12-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Scope SelfAssertion suppression to test/fixture compilation.

tasks.withType<JavaCompile>().configureEach applies this disable to production sources as well as fixtures, so future SelfAssertion findings in main code will be silently accepted. Restrict the suppression to test/fixture compile tasks or suppress only the intentional fixture assertions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts` around
lines 12 - 17, Restrict the SelfAssertion suppression in the JavaCompile
configuration to test/fixture compilation tasks only, rather than applying
options.errorprone.disable("SelfAssertion") to every JavaCompile task. Keep
disableWarningsInGeneratedCode unchanged and ensure production compilation still
reports SelfAssertion findings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/gradle-wrapper-validation.yml:
- Line 9: Update the actions/checkout steps in
.github/workflows/gradle-wrapper-validation.yml:9-9,
.github/workflows/label-commenter.yml:16-16, and
.github/workflows/publish-maven-central.yml:27-27 to configure
persist-credentials as false under each checkout step’s with block.

In @.github/workflows/publish-snapshot.yml:
- Line 13: Disable checkout credential persistence for the checkout steps in
.github/workflows/publish-snapshot.yml:13-13, .github/workflows/test.yml:31-33,
and .github/workflows/test.yml:49-51 by setting persist-credentials to false. In
.github/workflows/update-gradle-wrapper.yml:12-12, also disable ambient checkout
credentials while retaining the Gradle wrapper action’s explicitly scoped
repo-token configuration.

In `@build-logic-commons/gradle-plugin/build.gradle.kts`:
- Around line 18-29: Normalize jdkBuildVersion before JVM target selection in
build-logic-commons/gradle-plugin/build.gradle.kts lines 18-29 and
build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts
lines 13-24: when the configured value is 0, replace it with
JavaVersion.current().majorVersion, then use the normalized version for
JavaVersion.toVersion(...) and subsequent target selection; preserve existing
behavior for nonzero values.

In `@docs/BUILD_SYSTEM.md`:
- Around line 277-279: Update the matrix-size documentation consistently with
MATRIX_JOBS: 8: in docs/BUILD_SYSTEM.md, revise the later seven-job descriptions
and examples to eight; in docs/CI_TEST_WORKFLOW.md, update the “7 random
configurations” walkthrough to eight or clearly mark it as illustrative; and
update the local command default from MATRIX_JOBS=7 to 8 or document it as an
example value.
- Around line 290-296: Update the actions/setup-java references in the
BUILD_SYSTEM documentation examples, including the snippets near the setup and
publish sections, to use the same SHA-pinned v5 commit as the workflows; keep
the documented configuration unchanged.

---

Nitpick comments:
In `@build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts`:
- Around line 12-17: Restrict the SelfAssertion suppression in the JavaCompile
configuration to test/fixture compilation tasks only, rather than applying
options.errorprone.disable("SelfAssertion") to every JavaCompile task. Keep
disableWarningsInGeneratedCode unchanged and ensure production compilation still
reports SelfAssertion findings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bbe3b163-7cad-4593-a218-b3928d3000aa

📥 Commits

Reviewing files that changed from the base of the PR and between b29e4f5 and 5d8183b.

⛔ Files ignored due to path filters (1)
  • gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (43)
  • .github/CONTRIBUTING.md
  • .github/workflows/combine-prs.yml
  • .github/workflows/gradle-wrapper-validation.yml
  • .github/workflows/label-commenter.yml
  • .github/workflows/matrix.mjs
  • .github/workflows/publish-maven-central.yml
  • .github/workflows/publish-snapshot.yml
  • .github/workflows/test.yml
  • .github/workflows/update-gradle-wrapper.yml
  • CHANGES.txt
  • build-logic-commons/gradle-plugin/build.gradle.kts
  • build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts
  • build-logic/basics/src/main/kotlin/testng.reproducible-builds.gradle.kts
  • build-logic/build-parameters/build.gradle.kts
  • build-logic/code-quality/build.gradle.kts
  • build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts
  • build-logic/code-quality/src/main/kotlin/testng.testing.gradle.kts
  • build-logic/jvm/build.gradle.kts
  • build-logic/jvm/src/main/kotlin/testng.java-library.gradle.kts
  • build-logic/jvm/src/main/kotlin/testng.kotlin-library.gradle.kts
  • build-logic/publishing/build.gradle.kts
  • build-logic/publishing/src/main/kotlin/buildlogic/DependencyHandlerExtensions.kt
  • build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt
  • build-logic/publishing/src/main/kotlin/testng.local-maven-repo.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.maven-publish.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.signing.gradle.kts
  • build-logic/settings.gradle.kts
  • build.gradle.kts
  • docs/BUILD_SYSTEM.md
  • docs/CI_TEST_WORKFLOW.md
  • docs/JAVA_VERSIONS_QUICK_REFERENCE.md
  • docs/LOCAL_TESTING_GUIDE.md
  • docs/README.md
  • docs/RELEASE_PROCESS.md
  • gradle.properties
  • gradle/wrapper/gradle-wrapper.properties
  • gradlew
  • gradlew.bat
  • settings.gradle.kts
  • testng-core/testng-core-build.gradle.kts
  • testng-test-osgi/testng-test-osgi-build.gradle.kts
  • testng/testng-build.gradle.kts
💤 Files with no reviewable changes (1)
  • testng-core/testng-core-build.gradle.kts

Comment thread .github/workflows/gradle-wrapper-validation.yml
Comment thread .github/workflows/publish-snapshot.yml
Comment thread build-logic-commons/gradle-plugin/build.gradle.kts
Comment thread docs/BUILD_SYSTEM.md
Comment thread docs/BUILD_SYSTEM.md
Comment on lines +290 to 296
- name: Set up Java ${{ matrix.non_ea_java_version }} (test) and 25 (Gradle + build)
uses: actions/setup-java@v5
with:
java-version: |
${{ matrix.non_ea_java_version }} # e.g., 11
21 # Always install Java 21
25 # Always install Java 25
distribution: ${{ matrix.java_distribution }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs BUILD_SYSTEM relevant sections =="
if [ -f docs/BUILD_SYSTEM.md ]; then
  nl -ba docs/BUILD_SYSTEM.md | sed -n '260,340p'
else
  echo "docs/BUILD_SYSTEM.md not found"
fi

echo
echo "== workflow action setup-java usages =="
git ls-files .github/workflows | while read -r f; do
  echo "--- $f"
  rg -n "actions/setup-java@" "$f" || true
done

echo
echo "== docs references to SHA-pinned/mutable actions =="
rg -n "actions/setup-java|setup-java@" docs .github/workflows || true

Repository: testng-team/testng

Length of output: 238


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs BUILD_SYSTEM relevant sections =="
if [ -f docs/BUILD_SYSTEM.md ]; then
  awk 'NR>=260 && NR<=340 { printf "%6d\t%s\n", NR, $0 }' docs/BUILD_SYSTEM.md
else
  echo "docs/BUILD_SYSTEM.md not found"
fi

echo
echo "== workflow setup-java usages =="
git ls-files .github/workflows 2>/dev/null | sort | while IFS= read -r f; do
  echo "--- $f"
  grep -n "actions/setup-java@" "$f" || true
done

echo
echo "== docs and workflow references =="
find . -path ./.git -prune -o \( -path '*.md' -o -path '.github/workflows/*' \) -print 2>/dev/null | sort | while IFS= read -r f; do
  grep -n "actions/setup-java@" "$f" 2>/dev/null || true
done

Repository: testng-team/testng

Length of output: 4208


Mirror the SHA-pinned workflow action in the docs.

.github/workflows/test.yml and .github/workflows/publish-maven-central.yml pin actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5, but docs/BUILD_SYSTEM.md still shows actions/setup-java@v5 at lines 291 and 322. Update those examples to the same SHA-pinned reference or clearly mark them as mutable illustrative snippets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/BUILD_SYSTEM.md` around lines 290 - 296, Update the actions/setup-java
references in the BUILD_SYSTEM documentation examples, including the snippets
near the setup and publish sections, to use the same SHA-pinned v5 commit as the
workflows; keep the documented configuration unchanged.

@krmahadevan

Copy link
Copy Markdown
Member

@juherr LGTM. We can merge once the merge conflicts are resolved.

@vlsi

vlsi commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Is the move to addTestListener(TestListener) on your radar?

I've fixed that one and released in 4.0.0.

What would you do here? I left it untouched in this PR

  1. I would suggest adding verification of the generated pom (if you don't have one), see pgjdbc/pgjdbc@103678d#diff-50b7afa4bed361ddce66779ebb739b3916621afae1df821ab39af9ff71575664R472-R473
  2. I haven't analyzed the reasons for Gradle change regarding optional features, however, Codex came up with the following change which worked far for pgjdbc: pgjdbc/pgjdbc@49d4598#diff-50b7afa4bed361ddce66779ebb739b3916621afae1df821ab39af9ff71575664R83

juherr added 17 commits July 27, 2026 11:15
The build JDK and the bytecode target were hardcoded in three places:
build-parameters, and the two build-logic-commons scripts that pick a JVM
target for the build scripts themselves.

Declare jdkBuildVersion and targetJavaVersion once in the repository-root
gradle.properties. Included builds do not inherit it, so build-logic-commons
reads the file from disk. Their fallback stays at 17 (the Kotlin DSL floor)
rather than following targetJavaVersion, since the build scripts' JVM target
is unrelated to TestNG's bytecode target.
Gradle 9 removes several APIs the build relied on:
- AbstractArchiveTask.dirMode/fileMode -> dirPermissions/filePermissions
- BuildIdentifier.isCurrentBuild() -> buildPath (the root build's path is ":")
- manifest attributes must be non-null
- plugins from an included build are only resolved when the build is included
  from pluginManagement

Also bump the plugins that were not Gradle 9 compatible: vlsi gradle-extensions
1.90 -> 3.0.2 and the Shadow plugin from the unmaintained
com.github.johnrengelman:8.1.1 to com.gradleup.shadow:9.6.1.
Gradle 9 runs on JDK 25, so the build toolchain moves from 21 to 25. Error
Prone 2.20 crashes on JDK 25, hence error_prone_core 2.20 -> 2.50 and
gradle-errorprone-plugin 3.1.0 -> 4.1.0.

The new SelfAssertion check is disabled: it only flags the intentionally
trivial assertions in TestNG's own sample/fixture test classes.

The bytecode target stays at Java 11 (targetJavaVersion), so this is not a
breaking change for TestNG users.
The comment described code that no longer exists.
Mutable tags let a compromised action publish arbitrary code into our
workflows. Every action is now pinned to the commit SHA the tag currently
points at, with the version kept as a trailing comment so Dependabot can still
bump them.
Gradle 9 runs on JDK 25, and the build toolchain is now jdkBuildVersion=25.
The test workflow provisions the matrix test JDK plus JDK 25 (last, so it
becomes JAVA_HOME) instead of the previous JDK 21; the publish workflows move
from zulu 21 to temurin 25.

The test matrix still covers Java 11 and the artifacts still target Java 11.
Java 26 is released, so it moves out of the early-access slot; eaJava becomes
a list holding 27 and 28, which are only published on jdk.java.net.

Both EA pages were checked as reachable. Note the java_version axis now has 7
values for MATRIX_JOBS=7, so it is exactly at capacity: adding another version
without raising MATRIX_JOBS would leave some versions untested.
The build docs still described the JDK 21 / Gradle 8 setup: the Gradle runtime
and build toolchain are now JDK 25, the CI matrix covers 11-26 plus EA 27/28,
and the publish workflows use temurin 25.

Also corrected pre-existing inaccuracies found while updating: the claimed
.sdkmanrc file does not exist, the test-runtime list mentioned Java 24 which is
not in the matrix, the nmcp error message quoted "Java 21+" instead of its
actual "Java 17+", and the matrix builder was renamed to matrix.mjs upstream.
mise is added alongside SDKMAN/jenv in the install snippets.
The note restated the snapshot URL already spelled out two lines below.
The java_version axis now has 7 values, so 7 jobs left the matrix exactly at
capacity: every row was pinned to a distinct Java version and no slack remained
for the other axes to pair against. One extra row lifts pair coverage from
38.9% to 44.4%.
Gradle 10 rejects Develocity plugin versions below 4.0. The buildScan DSL used
here (termsOfUseUrl, termsOfUseAgree, publishing.onlyIf) is unchanged in 4.x.
…ndencies

Gradle 10 removes DependencyHandler.create(group, name, version). Verified the
generated testng POM still lists the same dependencies (testng-asserts, slf4j,
jcommander, jquery, plus optional guice and snakeyaml).
nmcp's publishAllProjectsProbablyBreakingProjectIsolation() feeds Project
objects to the dependency handler, a notation Gradle 10 removes. Apply
com.gradleup.nmcp in the maven-publish convention and list the aggregated
projects with the String path notation instead.

A projectsEvaluated guard fails the build if a project applies 'maven-publish'
without being aggregated, so the explicit list cannot silently drift and drop a
module from a release.

Also bumps nmcp 1.4.1 -> 1.6.1. Verified the aggregation zip still holds the
same 24 entries (testng jar, sources, javadoc, module, pom and checksums), and
that the guard fires when the aggregation entry is removed.
Gradle 10 removes 'val x by configurations.creating', 'val x by
tasks.registering(T::class)' and 'val x: T by project'. Rewritten as
configurations.create(name), tasks.register<T>(name) and
project.the<T>()/findProperty(name).

Mechanical: verified the shaded testng-all jar still holds the same 464 Java 11
classes, the generated POM the same 6 dependencies, and the OSGi test module
(which registers tasks through the rewritten delegates) still passes from a
clean build.
Error Prone 2.46.0 onwards requires the compiler to be passed
-XDaddTypeAnnotationsToSymbol=true, which the plugin only started doing in
4.4.0. The build runs error_prone_core 2.50.0 on plugin 4.1.0, so it was below
what the checker expects.

None of the 5.0.0 breaking changes apply here: the minimum Gradle version is
7.1 (we run 9.6.1), the minimum JDK is 11 (we build on 25), and the
isEnabled/isCompilingTestOnlyCode rename touches no property this build sets.
- org.sonarqube 4.4.1.3373 (2023) -> 7.3.1.8318
- Kotlin plugin and kotlin-bom 2.3.0 -> 2.4.10
- assertj-core 3.23.1 -> 3.27.7
- foojay-resolver-convention 0.8.0 -> 1.0.0
- org.gradlex.build-parameters 1.4.4 -> 1.4.5

Autostyle is deliberately left at 4.0: 4.0.1 bundles a google-java-format that
cannot reach com.sun.tools.javac.file under JPMS, and fails every module with
IllegalAccessError on JDK 25.

Verified with a full clean build on JDK 25 (12528 tests, 0 failures); the Java
and Kotlin bytecode still targets Java 11 (major version 55).
juherr added 8 commits July 27, 2026 11:15
actions/checkout writes the job token into .git/config by default, leaving it
readable by anything the build runs. None of these jobs push through the
checked-out remote: update-gradle-wrapper sets up its own git credentials from
repo-token before pushing, and the rest only read the tree.
jdkBuildVersion=0 is documented as "use the JVM running Gradle", and the main
build handles it. The build-logic scripts fed it straight to
JavaVersion.toVersion(), which failed the build with 'Index -1 out of bounds
for length 31'.
The check fires 16 times, all in test fixtures such as
assertThat("abc").isEqualTo("abc"), which exist only to give the runner a
passing method. Disabling it for every JavaCompile task also silenced it on
main sources for no reason.
4.0.0 replaces the AbstractTestTask.afterTest/afterSuite(Closure) calls that
Gradle 10 removes, so the last deprecation coming from a third-party plugin is
gone. Verified the testng POM and Gradle Module Metadata are unchanged.
The pom is assembled from feature variants plus a shaded jar, so a wiring
mistake silently changes what consumers resolve; until now that was only ever
checked by hand. Pin the expected set and fail the build when it drifts.

Versions are ignored on purpose, so dependency bumps do not require touching
the list.
Gradle 10 removes registerFeature(name) { usingSourceSet(sourceSets["main"]) },
and its prescribed replacement does not fit: these features carry no code, only
extra dependencies attached to the main artifact. A dedicated source set would
publish an empty jar under the feature capability and pull the dependencies off
main's compile classpath.

Declare the two consumable variants by hand instead, keeping the main jar as
the artifact and mapping them to optional pom dependencies. The aggregated
testng module keeps variants with no file of their own, as before.

Verified the feature variants of testng, testng-core and testng-core-api are
unchanged (same capabilities, attributes and artifacts), and that the published
pom, Gradle Module Metadata and shaded jar are identical to the baseline. The
only difference is the loss of the 'classes'/'resources' secondary variants,
which are a compile-avoidance optimisation with no consumer here.

Approach suggested by @vlsi, after pgjdbc 49d4598.
@juherr
juherr force-pushed the build/gradle-9-jdk-25 branch from d0ff5d2 to 1e0b64c Compare July 27, 2026 09:21

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt (2)

16-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comments likely cite the wrong Gradle version for the registerFeature/usingSourceSet(main) removal.

Both comments say "Gradle 10 removes registerFeature(name) { usingSourceSet(main) }". Per Gradle's deprecation notes, this pattern was scheduled to "fail with an error in Gradle 9.0" (not 10) — which is consistent with this PR needing the fix now, for the Gradle 9.6.1 upgrade, rather than pre-emptively for a future major version.

  • build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt#L16-L23: update the KDoc to reference Gradle 9 (or drop the specific version and just say "Gradle removed this form").
  • build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt#L74-L90: update the matching inline comment likewise.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt` around
lines 16 - 23, Correct the Gradle version reference for the removed
registerFeature/usingSourceSet(main) form: update the KDoc near
OptionalFeatureVariants to say Gradle 9 or omit the specific version, and make
the matching inline comment near OptionalFeaturesExtension consistent. Modify
both specified files; no code behavior changes are needed.

61-77: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use the Provider-based addVariantsFromConfiguration overload to stay lazy.

Gradle 9.2+ adds AdhocComponentWithVariants.addVariantsFromConfiguration(Provider<ConsumableConfiguration>, Action) specifically so configurations "are only realized when needed for publishing." Here apiElements.get() / runtimeElements.get() force eager realization every time publishOptionalFeature runs under plugins.withId("maven-publish"), even for tasks that never touch these variants.

♻️ Proposed fix using the lazy overload
     plugins.withId("maven-publish") {
         components.named<AdhocComponentWithVariants>("java") {
-            addVariantsFromConfiguration(apiElements.get()) {
+            addVariantsFromConfiguration(apiElements) {
                 mapToMavenScope("compile")
                 mapToOptional()
             }
-            addVariantsFromConfiguration(runtimeElements.get()) {
+            addVariantsFromConfiguration(runtimeElements) {
                 mapToMavenScope("runtime")
                 mapToOptional()
             }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt` around
lines 61 - 77, Update publishOptionalFeature to pass apiElements and
runtimeElements directly to the Provider-based addVariantsFromConfiguration
overload instead of calling .get(). Preserve the existing compile/runtime scope
mapping and mapToOptional actions while ensuring both configurations remain
lazily realized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt`:
- Around line 16-23: Correct the Gradle version reference for the removed
registerFeature/usingSourceSet(main) form: update the KDoc near
OptionalFeatureVariants to say Gradle 9 or omit the specific version, and make
the matching inline comment near OptionalFeaturesExtension consistent. Modify
both specified files; no code behavior changes are needed.
- Around line 61-77: Update publishOptionalFeature to pass apiElements and
runtimeElements directly to the Provider-based addVariantsFromConfiguration
overload instead of calling .get(). Preserve the existing compile/runtime scope
mapping and mapToOptional actions while ensuring both configurations remain
lazily realized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d7e27622-3fa1-43bb-bab5-da5752341590

📥 Commits

Reviewing files that changed from the base of the PR and between 8fd8a73 and d0ff5d2.

📒 Files selected for processing (6)
  • build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt
  • build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt
  • build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts
  • testng-core-api/testng-core-api-build.gradle.kts
  • testng-core/testng-core-build.gradle.kts
  • testng/testng-build.gradle.kts
🚧 Files skipped from review as they are similar to previous changes (1)
  • build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/CI_TEST_WORKFLOW.md (1)

549-560: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the documented matrix-job defaults consistent.

Line [549] sets MATRIX_JOBS to 8, but the workflow-dispatch example still uses default: '7' at Line [560]. Update the dispatch default to 8, or explain why the two paths intentionally differ.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/CI_TEST_WORKFLOW.md` around lines 549 - 560, Update the
workflow_dispatch input default for matrix_jobs in the MATRIX_JOBS documentation
example from 7 to 8 so it matches the documented MATRIX_JOBS value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docs/CI_TEST_WORKFLOW.md`:
- Around line 549-560: Update the workflow_dispatch input default for
matrix_jobs in the MATRIX_JOBS documentation example from 7 to 8 so it matches
the documented MATRIX_JOBS value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c4d21fd-ed19-4671-bfd7-6745145f30f2

📥 Commits

Reviewing files that changed from the base of the PR and between d0ff5d2 and 1e0b64c.

⛔ Files ignored due to path filters (1)
  • gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
📒 Files selected for processing (45)
  • .github/CONTRIBUTING.md
  • .github/workflows/combine-prs.yml
  • .github/workflows/gradle-wrapper-validation.yml
  • .github/workflows/label-commenter.yml
  • .github/workflows/matrix.mjs
  • .github/workflows/publish-maven-central.yml
  • .github/workflows/publish-snapshot.yml
  • .github/workflows/test.yml
  • .github/workflows/update-gradle-wrapper.yml
  • CHANGES.txt
  • build-logic-commons/gradle-plugin/build.gradle.kts
  • build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts
  • build-logic/basics/src/main/kotlin/testng.reproducible-builds.gradle.kts
  • build-logic/build-parameters/build.gradle.kts
  • build-logic/code-quality/build.gradle.kts
  • build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts
  • build-logic/code-quality/src/main/kotlin/testng.testing.gradle.kts
  • build-logic/jvm/build.gradle.kts
  • build-logic/jvm/src/main/kotlin/buildlogic/OptionalFeatureVariants.kt
  • build-logic/jvm/src/main/kotlin/testng.java-library.gradle.kts
  • build-logic/jvm/src/main/kotlin/testng.kotlin-library.gradle.kts
  • build-logic/publishing/build.gradle.kts
  • build-logic/publishing/src/main/kotlin/buildlogic/DependencyHandlerExtensions.kt
  • build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt
  • build-logic/publishing/src/main/kotlin/testng.local-maven-repo.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.maven-publish.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.signing.gradle.kts
  • build-logic/settings.gradle.kts
  • build.gradle.kts
  • docs/BUILD_SYSTEM.md
  • docs/CI_TEST_WORKFLOW.md
  • docs/JAVA_VERSIONS_QUICK_REFERENCE.md
  • docs/LOCAL_TESTING_GUIDE.md
  • docs/README.md
  • docs/RELEASE_PROCESS.md
  • gradle.properties
  • gradle/wrapper/gradle-wrapper.properties
  • gradlew
  • gradlew.bat
  • settings.gradle.kts
  • testng-core-api/testng-core-api-build.gradle.kts
  • testng-core/testng-core-build.gradle.kts
  • testng-test-osgi/testng-test-osgi-build.gradle.kts
  • testng/testng-build.gradle.kts
🚧 Files skipped from review as they are similar to previous changes (34)
  • build-logic/code-quality/src/main/kotlin/testng.testing.gradle.kts
  • build-logic/publishing/src/main/kotlin/buildlogic/DependencyHandlerExtensions.kt
  • build-logic/basics/src/main/kotlin/testng.reproducible-builds.gradle.kts
  • build-logic/publishing/build.gradle.kts
  • build-logic/jvm/build.gradle.kts
  • gradle/wrapper/gradle-wrapper.properties
  • build-logic/code-quality/build.gradle.kts
  • CHANGES.txt
  • .github/workflows/publish-maven-central.yml
  • settings.gradle.kts
  • .github/workflows/label-commenter.yml
  • build-logic/publishing/src/main/kotlin/testng.signing.gradle.kts
  • build-logic/jvm/src/main/kotlin/testng.java-library.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.local-maven-repo.gradle.kts
  • gradlew.bat
  • build-logic/build-parameters/build.gradle.kts
  • build-logic/jvm/src/main/kotlin/testng.kotlin-library.gradle.kts
  • testng-core-api/testng-core-api-build.gradle.kts
  • docs/README.md
  • build-logic/publishing/src/main/kotlin/testng.maven-publish.gradle.kts
  • build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts
  • build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt
  • testng-test-osgi/testng-test-osgi-build.gradle.kts
  • .github/CONTRIBUTING.md
  • build-logic-commons/gradle-plugin/build.gradle.kts
  • .github/workflows/matrix.mjs
  • build.gradle.kts
  • .github/workflows/publish-snapshot.yml
  • docs/LOCAL_TESTING_GUIDE.md
  • testng/testng-build.gradle.kts
  • testng-core/testng-core-build.gradle.kts
  • gradlew
  • build-logic/code-quality/src/main/kotlin/testng.errorprone.gradle.kts
  • build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts

Also state the Gradle versions precisely: registerFeature with the main source
set is deprecated since Gradle 8 and becomes an error in Gradle 10.
@juherr

juherr commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

@vlsi Thanks for the links. I've addressed both of your suggestions. Could you please check if everything looks good?

@juherr
juherr merged commit cd61def into testng-team:master Jul 28, 2026
11 checks passed
@juherr
juherr deleted the build/gradle-9-jdk-25 branch July 28, 2026 11:35
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.

3 participants