Skip to content

fix: Gradle JAR resolution, JaCoCo agent coverage, and multi-module settings.gradle parsing#2013

Closed
mashraf-222 wants to merge 8 commits intomainfrom
fix/gradle-tracer-jar-resolution
Closed

fix: Gradle JAR resolution, JaCoCo agent coverage, and multi-module settings.gradle parsing#2013
mashraf-222 wants to merge 8 commits intomainfrom
fix/gradle-tracer-jar-resolution

Conversation

@mashraf-222
Copy link
Copy Markdown
Contributor

@mashraf-222 mashraf-222 commented Apr 6, 2026

Problems Fixed

1. Gradle/tracer JAR resolution fails when not in Maven cache

When the codeflash-runtime JAR wasn't in the local Maven cache, Gradle projects had no fallback.

2. JaCoCo coverage times out on large Gradle projects

The previous JaCoCo implementation used a Gradle init script + jacocoTestReport task. On large multi-module projects (e.g., eureka), this added 5-10 minutes of Gradle overhead, causing persistent timeouts.

3. Multi-line settings.gradle includes not parsed

The regex only captured the first line of multi-line include statements, causing test_module=None and Gradle running tests across ALL modules.

Solutions

1. Maven Central HTTP fallback

Added download_from_maven_central_http() for both runtime and tracer JARs.

2. JaCoCo agent approach (replaces Gradle plugin)

  • Downloads jacocoagent.jar and jacococli.jar from Maven Central (cached in ~/.codeflash/java_agents/)
  • Injects -javaagent:jacocoagent.jar via JAVA_TOOL_OPTIONS env var
  • Converts .exec to .xml using JaCoCo CLI after tests (~2 seconds)
  • No Gradle plugin, init script, or jacocoTestReport task needed

3. Multi-line settings.gradle regex fix

New regex handles comma-continued lines across multiple lines.

4. Coverage timeout increase (900s)

Large multi-module Gradle projects with --no-daemon (cold JVM startup + compilation + test execution) can take 10+ minutes. Increased min_timeout from 120s to 900s for coverage runs.

Code Changes

File Changes
gradle_strategy.py Removed init script/plugin approach. Added JaCoCo agent injection via JAVA_TOOL_OPTIONS, .exec.xml CLI conversion, agent/CLI JAR download helpers
maven_strategy.py Added download_from_maven_central_http() fallback for runtime JAR
test_runner.py Coverage min_timeout 120→900s, simplified coverage logging
test_build_tools.py Updated coverage path expectations, replaced init script tests with agent download tests
test_java_test_paths.py Updated multi-line settings.gradle test expectations

E2E Validation

Fibonacci (Maven — regression check)

Full pipeline healthy. ~16,000x memoization speedup found. No regressions from Gradle changes.

eureka toEnum (Gradle — validates all 3 fixes)

Checkpoint Result
Multi-module detection (eureka-core) PASS
JAR downloaded from Maven Central PASS
JaCoCo agent injected via JAVA_TOOL_OPTIONS PASS
No jacocoTestReport task in Gradle command PASS
test.exec generated (699KB) PASS
test.xml converted via CLI (806KB) PASS
Behavioral baseline (21 results) PASS
Full pipeline completed without timeout (900s) PASS

…tion

Gradle projects and the tracer flow could not resolve codeflash-runtime
when no local JAR was cached — the bundled JAR was removed in PR #1817
but the download fallback was only added to MavenStrategy. This adds a
direct HTTP download from Maven Central (no mvn binary required) as a
fallback in GradleStrategy.ensure_runtime() and find_agent_jar().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 6, 2026

Claude finished @mashraf-222's task in 2s —— View job


PR Review Summary

Prek Checks

Clean — ruff and mypy report no issues on changed files.


Code Review

Bug: Partial download not cleaned up in JaCoCo JAR fetchers [gradle_strategy.py:955-966, 978-988]

get_jacoco_agent_jar() and get_jacoco_cli_jar() use urllib.request.urlretrieve() without cleanup on failure. If the download is interrupted, a partial/corrupt file is left at the target path. On the next run, agent_jar.exists() returns True and the corrupt JAR is returned, causing the JaCoCo agent to fail silently.

download_from_maven_central_http() in maven_strategy.py correctly handles this with M2_JAR_PATH.unlink(missing_ok=True) in the except block. The same pattern is missing in both JaCoCo helpers.

Fix this →

Bug: Multi-line include fix is incomplete — gradle_strategy.py not updated

_extract_modules_from_settings_gradle in test_runner.py was fixed to handle multi-line includes (used by _find_multi_module_root). However, _extract_gradle_include_modules in gradle_strategy.py (used by _discover_gradle_submodule_rootsget_project_info) still uses the old single-line _RE_INCLUDE = re.compile(r"""include\s*\(?([^)\n]+)\)?""").

For a project like Eureka with:

include 'eureka-client',
        'eureka-server',
        'eureka-core'

Source/test root discovery in get_project_info would still only find eureka-client (the first line), missing the other modules.

Fix this →

Dead code: setup_coverage() call is now a no-op [test_runner.py:424]

if enable_coverage:
    coverage_xml_path = strategy.setup_coverage(build_root, test_module, project_root)  # ← result unused
...
if enable_coverage:
    result, result_xml_path, coverage_xml_path = strategy.run_tests_with_coverage(...)  # ← overrides above

GradleStrategy.setup_coverage() no longer has side effects (it just computes a path), so the result assigned on line 424 is immediately clobbered by line 433. The call at line 424 is dead code for Gradle. It still matters for MavenStrategy (which does write coverage setup to pom.xml), so it can't simply be removed — but the current structure is misleading.

Design: JAVA_TOOL_OPTIONS affects ALL JVM processes, not just the forked test JVM [gradle_strategy.py:888]

JAVA_TOOL_OPTIONS is a JVM-level env var inherited by every JVM spawned by the process — including the Gradle daemon itself. Both the Gradle daemon and the forked test JVM will load the JaCoCo agent and try to write to the same test.exec file. While JaCoCo handles concurrent writes via append=true (the default), this adds spurious Gradle-internal coverage data to the report, and could cause issues if the Gradle daemon's classpath doesn't include jacocoagent.jar.

A cleaner approach: pass the JaCoCo agent via the existing javaagent_arg parameter of run_tests_via_build_tool() (already used by the line profiler), which injects it into the test task JVM args via the Gradle init script. Since E2E validation passed, this is a design note rather than a blocker.

Tests hit the network: TestJacocoAgentDownload makes real HTTP requests [test_build_tools.py:784-806]

test_get_jacoco_agent_jar_downloads_to_codeflash_home, test_get_jacoco_agent_jar_is_cached, and test_get_jacoco_cli_jar_downloads all call get_jacoco_agent_jar()/get_jacoco_cli_jar() without mocking urllib.request.urlretrieve. They actually download JaCoCo JARs from Maven Central during test runs. These tests will be slow and flaky in offline/CI environments.

Fix this →


Duplicate Detection

HIGH confidence_extract_gradle_include_modules (gradle_strategy.py:320) and _extract_modules_from_settings_gradle (test_runner.py:202) are near-duplicate functions doing the same thing: extracting module names from settings.gradle content. They use different regex approaches and are now inconsistently maintained (this PR fixed one but not the other). These should be consolidated into a single function.

No other duplicates detected.


Test Coverage

  • gradle_strategy.py: 34% (new functions get_jacoco_agent_jar, get_jacoco_cli_jar, convert_jacoco_exec_to_xml are covered but error paths are mostly untested)
  • maven_strategy.py: 47%
  • test_runner.py: 58%

13 test failures observed in test_comparator.py — these are pre-existing failures unrelated to this PR (that file has no changes in this diff).


Last updated: 2026-04-07

…rage

The previous approach failed on multi-module Gradle projects (e.g., eureka)
where the root project doesn't have the java plugin — appending
`jacocoTestReport` as a root-level task caused "Task not found" errors.

Three changes:
1. Fixed _JACOCO_INIT_SCRIPT to guard with `plugins.withType(JavaPlugin)`
   so JaCoCo only applies to projects that have the java plugin
2. Pass the init script via --init-script (was defined but never used)
3. Qualify jacocoTestReport with module prefix (`:module:jacocoTestReport`)
   for multi-module projects
4. Simplified setup_coverage() — init script handles plugin injection,
   no more build file modification needed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mashraf-222 mashraf-222 changed the title fix: add Maven Central HTTP fallback for Gradle and tracer JAR resolution fix: add Maven Central HTTP fallback for Gradle/tracer JAR and fix JaCoCo multi-module coverage Apr 6, 2026
mashraf-222 and others added 3 commits April 6, 2026 23:55
Gradle with --no-daemon + coverage on multi-module projects (e.g., eureka)
needs cold startup + configuration + compilation + tests + JaCoCo report
generation, which exceeds 300s. Validated on eureka: build was killed at
exactly 300s, now gets 600s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes for Gradle multi-module project support:

1. _extract_modules_from_settings_gradle regex now handles multi-line
   include statements (comma-continued across lines). Previously only
   the first line was captured, causing module detection to miss most
   modules in projects like eureka. This made Gradle run `test` at root
   level instead of `:eureka-core:test`, triggering all-module compilation.

2. Increased coverage min_timeout from 300s to 900s — Gradle --no-daemon
   on large projects needs ~10min for cold startup + compilation + tests,
   plus ~3min for JaCoCo report generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous approach used a Gradle init script to inject the JaCoCo plugin
and ran jacocoTestReport as a Gradle task. On large multi-module projects
(e.g., eureka), this added 5-10 minutes of Gradle overhead for report
generation, causing persistent timeouts.

Switch to the JaCoCo Java agent approach:
- Inject -javaagent:jacocoagent.jar via JAVA_TOOL_OPTIONS env var
- Coverage data collected during test execution with near-zero overhead
- Convert .exec to .xml using JaCoCo CLI after tests complete (~2 seconds)
- No Gradle plugin, init script, or jacocoTestReport task needed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mashraf-222 mashraf-222 changed the title fix: add Maven Central HTTP fallback for Gradle/tracer JAR and fix JaCoCo multi-module coverage fix: Gradle JAR resolution, JaCoCo agent coverage, and multi-module settings.gradle parsing Apr 7, 2026
github-actions bot and others added 3 commits April 7, 2026 01:27
Eureka-core with --no-daemon takes ~10 min for cold startup + compilation +
test execution. The 600s timeout was too tight — tests completed but Gradle
was killed during shutdown. 900s provides sufficient buffer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mashraf-222
Copy link
Copy Markdown
Contributor Author

E2E Validation

Setup

# Branch: fix/gradle-tracer-jar-resolution (commit 6c40422f)
# Cleared ~/.m2/repository/com/codeflash/codeflash-runtime/
# Local BE services running (CF API :3001, AI Service :8000)
export CODEFLASH_CFAPI_SERVER="local"
export CODEFLASH_AIS_SERVER="local"

Run 1: Fibonacci (Maven regression check)

cd code_to_optimize/java/
uv run codeflash --file src/main/java/com/example/Fibonacci.java --function fibonacci --verbose --no-pr

Result: PASS — Full Maven pipeline healthy, ~16,000x memoization speedup found. No regressions.

Run 2: eureka toEnum (Gradle — validates all fixes)

cd /home/ubuntu/code/eureka/
uv run codeflash --file eureka-core/src/main/java/com/netflix/eureka/Version.java --function toEnum --verbose --no-pr
Checkpoint Result
Multi-module detection (eureka-core) PASS
JAR downloaded from Maven Central PASS
JaCoCo agent injected via JAVA_TOOL_OPTIONS PASS
No jacocoTestReport task in Gradle command PASS
test.exec generated (699KB) PASS
test.xml converted via CLI (801KB) PASS
Pipeline completed without timeout (900s limit) PASS
Behavioral baseline: 21 tests passed PASS

Result: PASS — All 3 Gradle fixes validated. No optimization found (expected for this function), but full pipeline completed correctly.

@mashraf-222 mashraf-222 closed this Apr 7, 2026
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