Skip to content

Switch native smoke tests to the smoke-test plugin#11468

Draft
bric3 wants to merge 8 commits into
masterfrom
bdu/migrate-native-smoke-tests-to-smoke-test-plugin
Draft

Switch native smoke tests to the smoke-test plugin#11468
bric3 wants to merge 8 commits into
masterfrom
bdu/migrate-native-smoke-tests-to-smoke-test-plugin

Conversation

@bric3
Copy link
Copy Markdown
Contributor

@bric3 bric3 commented May 27, 2026

What Does This Do

Migrates dd-smoke-tests/spring-boot-3.0-native and dd-smoke-tests/quarkus-native from raw Exec against $rootDir/gradlew to the dd-trace-java.smoke-test-app plugin (added in #11405).

The nested native builds now run through the Gradle Tooling API on a pinned Gradle 8.14.5.

To support the native flavor, the plugin and testJvmConstraints gain a few things:

  • SmokeTestAppExtension.javaLauncher — pin the nested daemon JDK explicitly when an inner plugin chain requires it.
  • NestedGradleBuild.environment — extra env vars merged on top of the outer process environment, used to forward GRAALVM_HOME etc. to the nested daemon.
  • NestedGradleBuild is now @CacheableTask, so the expensive native-image output is reusable across CI smoke-test slots — the prior Exec-based wiring set outputs.cacheIf { true } to opt in explicitly.
  • testJvmConstraints.nativeImageCapable allows to ensure the testJvm provides native-image.
  • SmokeTestAppExtension.projectJar(name, project, configuration) overload, so consumers can target a non-default variant like the agent's shadow configuration.

Motivation

Same root cause as #11407: as soon as the root project moves to Gradle 9, the Spring Boot Gradle plugin pre-3.5 inside the nested application/ builds breaks (Configuration.getUploadTaskName() is gone). While running the nested project on Gradle 8.14.5 works.

The native modules also carried two extra hazards worth fixing in the same pass:

  • Cross-project task reach-through (project(':dd-java-agent').tasks.shadowJar.archiveFile.get())
    It is evaluated eagerly at configuration time, and might break under Gradle 9's stricter project isolation. It has been replaced with consumer-configuration-based wiring through projectJar(...).
  • Daemon JDK / native-image plugin compat
    The old code forked gradlew directly, which on the GraalVM 21 slot meant running Gradle 9 (post-bump) on a JDK 21 GraalVM. This is now decoupled via the smoke test plugin.

For Spring Boot 3.0 specifically, the inner app's compileJava now sets options.release = 17 so it emits major-61 class files regardless of the daemon JDK ; indeed Spring Boot 3.0's bundled ASM 9.4 can't read major-65 (Java 21) classes, so without this the inner :resolveMainClassName task fails with Unsupported class file major version 65 (observed in CI on both graalvm17 and graalvm21 slots).

Additional Notes

bric3 and others added 8 commits May 27, 2026 15:13
IN the nested project, spring boot uses a removed API in In Gradle 9.5.
This changes uses managed Gradle 8.14.5 build via our custom smoke test
plugin.

The Test JVM Constraints was adapted to support request for native-image
capability.

The smoke test plugin allowes to source a jar from a specific project
configuration, and also, to pass environment to the managed Gradle.
…mokeTestApp

Mirrors the spring-boot-3.0-native migration: switches
`dd-smoke-tests:quarkus-native` from raw `Exec` against `$rootDir/gradlew`
to the `dd-trace-java.smoke-test-app` plugin's `NestedGradleBuild` task,
running the nested build through the Tooling API on Gradle 8.14.5.

Removes the cross-project eager task reach-through
(`project(':dd-java-agent').tasks.shadowJar.archiveFile.get()`) that breaks
under Gradle 9's stricter project isolation, replacing it with
`projectJar('agentJar', project(':dd-java-agent'), 'shadow')` which wires
the agent jar through a resolvable, non-transitive consumer configuration.

Slot gating uses `testJvmConstraints { minJavaVersion = 21; maxJavaVersion
= 21; nativeImageCapable = true }`: Quarkus 3.12.1 requires Mandrel/GraalVM
23.1.0 (JDK 21-based) and rejects older distributions at the
native-image step ("You are using an older version of GraalVM or Mandrel").
`onlyIf` on `quarkusNativeBuild` and `compileTestGroovy` skips the build
itself when an unsupported `testJvm` is selected.

The Quarkus inner application script now reads `-PagentJar=...` and sets
`quarkus.native.additional-build-args` itself, keeping agent jar path
resolution inside the nested build instead of composing the argument
string in the outer script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spring Boot 3.0's bundled ASM 9.4 only parses class files up to major 64
(Java 20), so the inner app's `compileJava` must emit class files no
newer than that. Set `options.release = 17` so the nested build doesn't
need a separate Java 17 toolchain and can run on the daemon JDK regardless
of which GraalVM slot drives the outer build.

This fixes CI runs where `-PtestJvm=graalvm17` or `-PtestJvm=graalvm21`
were failing at `resolveMainClassName` with "Unsupported class file major
version 65" — the daemon's Java 21 javac was emitting major-65 classes
that SB 3.0's ASM rejects.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Annotate the task with `@CacheableTask` so Gradle can store and reuse its
output directory across builds. All inputs already carry the path-sensitivity
metadata the build cache needs:

  - `applicationSources`: `@PathSensitive(RELATIVE)` (source tree)
  - `NestedBuildProjectJar.file`: `@PathSensitive(NONE)` (forwarded jars,
    same content at different absolute paths across CI runners)

Without this, the expensive native-image build re-runs from scratch on every
CI smoke-test job even when declared inputs are identical — the prior
`Exec`-based wiring set `outputs.cacheIf { true }` to opt in explicitly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`testJvmConstraints { nativeImageCapable = true }` only checks the launcher's
native-image capability; it doesn't require a `-PtestJvm` slot to be selected.
On a daemon JVM that happens to be GraalVM 21, the constraint lets the Test
task run with no testJvm even though `isSupportedGraalvm` is false and the
matching native build was skipped — the Test would then exercise stale or
missing artifacts.

Add an `onlyIf(isSupportedGraalvm)` to the Test tasks of both
`spring-boot-3.0-native` and `quarkus-native` so they only run when an
explicit, supported `-PtestJvm` was provided.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quarkus 3.12.1 only requires Mandrel/GraalVM **23.1** (JDK 21) at minimum
for native-image — newer GraalVM distributions are accepted. Verified
locally that the smoke test builds and runs cleanly on `graalvm25` as well
as `graalvm21`, so relax the upper bound from JDK 21 to JDK 25.

Constraint becomes:

  testJvmConstraints {
    minJavaVersion = JavaVersion.VERSION_21
    maxJavaVersion = JavaVersion.VERSION_25
    nativeImageCapable = true
  }

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`testJvmConstraints` only needs `minJavaVersion = 21` and
`nativeImageCapable = true` — let any newer GraalVM slot through instead
of hard-capping at JDK 25. The lower bound + native-image check is what
actually enforces compatibility with Quarkus 3.12.1's `native-image`
requirements.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`test_smoke_graalvm` now also runs `:dd-smoke-tests:quarkus-native:test`
alongside `:dd-smoke-tests:spring-boot-3.0-native:test` across the
`graalvm17`, `graalvm21`, and `graalvm25` slots. The Quarkus module
gates itself on `minJavaVersion = 21` + `nativeImageCapable`, so the
`graalvm17` slot will simply skip the Quarkus task at the constraint
layer rather than fail inside the nested native-image build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant