Skip to content

Fix build-ordering race: track javaagent config as run-task input#376

Merged
ryandens merged 2 commits into
mainfrom
worktree-fix-run-task-race
Jul 6, 2026
Merged

Fix build-ordering race: track javaagent config as run-task input#376
ryandens merged 2 commits into
mainfrom
worktree-fix-run-task-race

Conversation

@ryandens

@ryandens ryandens commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Problem

JavaagentApplicationRunPlugin wires the run task's -javaagent arguments through a CommandLineArgumentProvider (in JavaForkOptionsConfigurer) that resolves the javaagent configuration's files at execution time. That provider is not a tracked task input, so Gradle established no dependency on the tasks that build the agent jars.

Under parallel execution the run task could launch with -javaagent:<jar> before that jar had been produced:

Error opening zip file or JAR manifest missing : .../simple-agent.jar

Reproducible 100% locally and intermittently on CI:

./gradlew :plugin:functionalTest --tests "*can attach to application run task*" --rerun-tasks

Fix

Register the javaagent configuration as a tracked input of the run task:

it.inputs.files(javaagentConfiguration)

A Configuration is Buildable, so input-tracking makes the run task depend on the tasks that build the agent jars — the same way JavaagentApplicationDistributionPlugin already wires the start-script task.

Downstream fix in the otel modification plugin

Input-tracking requires every artifact added to the javaagent configuration to carry its producing-task dependency (builtBy). JavaagentOTelModificationPlugin added the extendedAgent output via extendedAgent.outputs.files.singleFile, which resolves to a plain File and drops that provenance — so tracking the configuration as a run-task input tripped Gradle's strict implicit-dependency validation:

A problem was found with the configuration of task ':app:run' (JavaExec).
  Task ':app:run' uses this output of task ':app:extendedAgent' without
  declaring an explicit or implicit dependency.

Fixed at the source by adding it via the task provider, which carries the producing task as a builtBy dependency:

project.dependencies.add("javaagent", project.files(extendedAgent))

Consumers that track the configuration now establish the :extendedAgent dependency automatically.

Tests

  • Added a deterministic regression test that asserts, via --dry-run, that :simple-agent:jar is scheduled before :hello-world:run. It fails without this fix and passes with it — independent of task-scheduling timing.
  • The existing can attach to application run task test also reproduces the race (verified: 3/3 failures without the fix, 3/3 passes with it under --rerun-tasks).

Verification

  • ./gradlew :plugin:functionalTest --tests "*can attach to application run task*" --rerun-tasks — green, run repeatedly
  • ./gradlew :otel:functionalTest --rerun-tasks — green (implicit-dependency validation no longer tripped; reproduced the failure first, then confirmed the fix)
  • ./gradlew build — green (including spotless formatting)

🤖 Generated with Claude Code

Note

Fix build-ordering race by tracking javaagent config as a run task input

  • Registers the javaagent configuration as an explicit input on the run task in JavaagentApplicationRunPlugin, so Gradle orders agent-building tasks before the run task.
  • Fixes JavaagentOTelModificationPlugin to add the extendedAgent artifact via project.files(extendedAgent) instead of resolving it to a plain File, preserving TaskProvider provenance and its builtBy dependency.
  • Adds a functional test that uses --dry-run to assert :simple-agent:jar is scheduled before :hello-world:run.

Macroscope summarized 4dafe32.

Comment thread plugin/src/main/kotlin/com/ryandens/javaagent/JavaagentApplicationRunPlugin.kt Outdated
@ryandens ryandens changed the title Fix build-ordering race in run task via dependsOn(javaagent) Fix build-ordering race: track javaagent config as run-task input Jul 6, 2026
@ryandens

ryandens commented Jul 6, 2026

Copy link
Copy Markdown
Owner Author

Addressed in 4b96150. Switched the run task to inputs.files(javaagentConfiguration) (matching the distribution plugin's start-script wiring) and fixed the downstream otel error at its source: JavaagentOTelModificationPlugin now adds the extendedAgent output via project.files(extendedAgent) instead of extendedAgent.outputs.files.singleFile, so the producing task's builtBy is carried into the javaagent configuration. Reproduced the :app:run implicit-dependency validation failure first, then confirmed :otel:functionalTest --rerun-tasks is green with the fix.

@ryandens ryandens marked this pull request as ready for review July 6, 2026 16:42
ryandens and others added 2 commits July 6, 2026 10:00
JavaagentApplicationRunPlugin configured the run task's -javaagent arguments via
a CommandLineArgumentProvider that resolves the javaagent configuration's files
at execution time. That provider is not a tracked task input, so Gradle
established no dependency on the tasks that build the agent jars. Under parallel
execution the run task could launch with -javaagent:<jar> before that jar had
been produced, failing with "Error opening zip file or JAR manifest missing"
(reproducible 100% of the time locally via `--rerun-tasks`, and intermittently
on CI).

Use `dependsOn(javaagentConfiguration)`: a Configuration is Buildable, so this
adds the artifact-building task dependencies and fixes the ordering. Unlike
`inputs.files(...)`, it does not register the agent jars as tracked inputs, so
it avoids tripping Gradle's strict implicit-dependency validation for agent jars
that are added to the configuration without a declared builtBy (e.g. the otel
example's :app:extendedAgent jar, which is added via project.files(singleFile)
and loses its task provenance). A run task is never up-to-date, so input
tracking would provide no benefit here anyway.

Add a deterministic regression test that asserts, via --dry-run, that
:simple-agent:jar is scheduled before :hello-world:run. The existing
`can attach to application run task` test also reproduces the race, but only
intermittently depending on task scheduling; the new test catches the ordering
regression independent of timing (it fails without this fix, passes with it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per review feedback, register the javaagent configuration as a tracked input of
the run task (inputs.files) rather than a bare dependsOn, matching how
JavaagentApplicationDistributionPlugin wires the start-script task. Input
tracking requires every artifact in the javaagent configuration to carry its
producing-task dependency.

Fix the otel example at the source: JavaagentOTelModificationPlugin added the
extendedAgent output via extendedAgent.outputs.files.singleFile, which resolves
to a plain File and drops the builtBy provenance, so tracking the configuration
as a run-task input tripped Gradle's implicit-dependency validation
(":app:run uses this output of task ':app:extendedAgent' without declaring ...").
Add it via project.files(extendedAgent) instead, so the producing task is
carried as a builtBy dependency and consumers that track the configuration
establish the dependency automatically.

Verified: :otel:functionalTest --rerun-tasks green, the run-task race tests green
across repeated --rerun-tasks runs, and ./gradlew build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ryandens ryandens force-pushed the worktree-fix-run-task-race branch from 4b96150 to 4dafe32 Compare July 6, 2026 17:02
@ryandens ryandens merged commit 19a6640 into main Jul 6, 2026
4 checks passed
@ryandens ryandens deleted the worktree-fix-run-task-race branch July 6, 2026 17:14
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