fix: improve multi-module Gradle detection for dynamic settings.gradle.kts#2015
fix: improve multi-module Gradle detection for dynamic settings.gradle.kts#2015HeshamHM28 wants to merge 11 commits intomainfrom
Conversation
…e.kts - Parse listOf(...) patterns in settings.gradle.kts for projects that build include lists dynamically (e.g. OpenRewrite) - Use word boundary in include regex to avoid matching variable names like 'includedProjects' - Break module voting ties using codeflash.toml module-root config, so the function's own module is preferred over cross-module tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Claude finished @mashraf-222's task in 4s —— View job PR Review SummaryPrek Checks✅ Code ReviewBug:
|
| File | Coverage |
|---|---|
gradle_strategy.py |
33% |
test_runner.py |
57% |
line_profiler.py |
58% |
tracer.py |
82% |
Low coverage on gradle_strategy.py is expected — subprocess execution paths (Gradle commands, JaCoCo conversion) cannot easily be unit tested. The critical new logic (_find_top_level_block, _ensure_maven_central_repo, _update_existing_codeflash_dependency, add_codeflash_dependency) is well covered by the 20 new unit tests.
Missing test: The tie-breaking logic in _find_multi_module_root has no test that verifies codeflash.toml module-root actually breaks a tie. The only tie test (test_tied_modules_fallback) just asserts test_module in ("clients", "streams") without a config file present.
Summary
The core work is solid and well-tested: Maven Central dependency management (replacing local JAR copy), JaCoCo via runtime agent, multi-line include detection, parent-module walking, and timeout bumps are all correct. Two items to address before merging:
- Bug (functional):
_read_config_module_rootneedsPath(value).parts[0]to extract the module name from the path — without this, the tie-breaking feature silently doesn't work. - Design (minor): the
setup_coveragecall atrun_behavioral_tests:527is dead code for Gradle and should be cleaned up to avoid confusing future readers.
⚡️ Codeflash found optimizations for this PR📄 26% (0.26x) speedup for
|
The optimization pre-compiles three regex patterns at module load time (`_INCLUDE_PATTERN`, `_LISTOF_PATTERN`, `_QUOTED_PATTERN`) instead of recompiling them on every function call, eliminating the ~1 ms pattern-compilation overhead that line profiler shows dominated the original version (44.3% of total time in the first `re.findall` alone). The second major change replaces the O(n) `if stripped not in modules` list scan with a set-based `if stripped not in seen` check, which cuts the deduplication cost from ~288 ns to ~72 ns per check when the fallback listOf branch executes. Runtime improves from 2.35 ms to 1.91 ms (23% faster) with no behavioral regressions.
⚡️ Codeflash found optimizations for this PR📄 23% (0.23x) speedup for
|
…date logic - Generalize _find_top_level_dependencies_block() into _find_top_level_block(name) so it can find any top-level block (dependencies, repositories, etc.) - Rewrite _ensure_maven_central_repo() to use tree-sitter instead of regex, preventing false matches inside buildscript/subprojects/allprojects blocks - Add _update_existing_codeflash_dependency() to replace stale versions or old files() format with the current Maven Central coordinate - Wire version update into add_codeflash_dependency() and add_codeflash_dependency_multimodule() so old entries get updated instead of silently skipped Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Phase 1 committed: tree-sitter repositories block + version update logic Changes
Validation
Remaining phases
|
Gradle resolves the codeflash-runtime JAR to ~/.gradle/caches/, not ~/.m2/. Add an optional classpath parameter to find_agent_jar() that searches the resolved classpath for the JAR before falling back to the existing ~/.m2 / resources / dev-build chain. Thread the parameter through build_javaagent_arg, build_agent_env, instrument_source_for_line_profiler, and line_profiler_step so the optimization pipeline passes the resolved classpath automatically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Gradle JaCoCo plugin approach (jacocoTestReport task) fails on
multi-module projects and adds 5-10 min overhead. Replace with:
1. Inject -javaagent:{runtime_jar}=destfile={exec} via JAVA_TOOL_OPTIONS
(AgentDispatcher routes destfile= args to JaCoCo PreMain)
2. Run tests without jacocoTestReport task
3. Convert .exec to .xml via shaded JaCoCo CLI in the runtime JAR
This eliminates the "jacocoTestReport not found" error on eureka and
similar multi-module Gradle projects, and removes build file mutation
for coverage setup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Phase 2 & 3 committed and pushed. Phase 2 — classpath hint for JAR resolution:
Phase 3 — JaCoCo coverage via runtime JAR agent:
Running E2E on eureka to validate. |
…ource_for_line_profiler
…2026-04-07T11.40.43 ⚡️ Speed up function `_extract_modules_from_settings_gradle` by 23% in PR #2015 (`fix/gradle-maven-central-dependency`)
|
This PR is now faster! 🚀 @claude[bot] accepted my optimizations from: |
The regex for extracting modules from settings.gradle only matched single-line include statements. Multi-line includes like eureka's (include 'a',\n 'b',\n 'c') only captured the first module, causing test_module to be None and breaking multi-module path resolution (e.g., classfiles lookup for JaCoCo coverage conversion). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
E2E Validation — Eureka (Multi-Module Gradle Project)All five commits validated on eureka ( Phase 1: Maven Central Dependency (
|
Gradle --no-daemon on multi-module projects (e.g., eureka) needs cold JVM startup + dependency resolution + compilation + test execution + JaCoCo agent overhead, which exceeds 300s. At 300s the process is killed mid-execution, producing partial results that the pipeline can't use for behavioral baseline. Ported from PR #2013 which validated 300s→600s→900s progression on eureka (build takes ~10 min, 900s provides safe headroom). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…adle Two changes to prevent cold-build timeouts on large multi-module Gradle projects (e.g., eureka ~16 min cold build): 1. install_multi_module_deps now compiles testClasses instead of just classes, so the test execution timeout only covers running tests, not compilation. 2. Pre-install compilation timeout increased from 300s to 900s to accommodate cold Gradle --no-daemon builds on large projects. Combined with the coverage min_timeout of 900s (previous commit), compilation and test execution each get their own 900s budget instead of sharing one. Ported from PR #2013 experience where 300s/600s were validated as insufficient for eureka cold builds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…builds Gradle --no-daemon on multi-module projects forces a full JVM cold start for every invocation. On eureka, configuration + dependency resolution alone takes ~10 min before tests even start. 900s was still getting killed at the boundary. 1200s (20 min) provides headroom for: cold Gradle startup (~10 min) + test execution (~5 min) + JaCoCo overhead + safety margin. PR #2013 iterated through 300→600→900s and found 900s sufficient only when build caches were warm from prior invocations in the same session. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
E2E Validation — Full Pipeline Pass on EurekaAll commits validated end-to-end on eureka ( Timeout Fixes (commits
|
| Phase | Evidence | Status |
|---|---|---|
| Maven Central dep | Added codeflash-runtime dependency to build.gradle (tree-sitter) |
✅ |
| Multi-line include | Detected multi-module project. Module votes: {'eureka-core': 2} |
✅ |
| Pre-compile | :eureka-core:testClasses -x test → installed successfully |
✅ |
| Build cache reuse | :eureka-core:testClasses UP-TO-DATE |
✅ |
| JaCoCo agent | JaCoCo coverage XML generated (806KB) |
✅ |
| Behavioral baseline | return code 1, Parsed 22 results from SQLite |
✅ |
| Candidate evaluation | 6 candidates tested | ✅ |
| Full pipeline | Completed without timeout | ✅ |
Session Logs
/home/ubuntu/e2e-sessions/2026-04-07_16-13_gradle-1200s-timeout/logs/eureka/toEnum.log
|
@claude review this PR |
|
Claude finished @mashraf-222's task in 4m 36s —— View job PR ReviewTasks
SummaryThis PR makes three large improvements: (1) switch from local JAR file resolution to Maven Central dependency, (2) replace regex-based JaCoCo setup with a runtime JAR agent approach, and (3) improve multi-module detection for dynamic Unaddressed Issues from Prior ReviewDead code at
|
|
Claude finished @mashraf-222's task in 4m 17s —— View job PR Review
This is a solid, well-motivated PR with good test coverage and thorough E2E validation. The three core changes (Maven Central instead of file-local JARs, JaCoCo via runtime agent instead of Gradle plugin, and dynamic Bugs1. _INCLUDE_PATTERN = re.compile(r"""(?:^|(?<=\s))include\s*\(?[^)]*\)?""", re.MULTILINE | re.DOTALL)The PR description says this fixes false matches on With _INCLUDE_PATTERN = re.compile(r"""\binclude\b\s*\(?[^)]*\)?""", re.MULTILINE | re.DOTALL)2. When an existing Contrast with # add_codeflash_dependency_multimodule — missing this after updating:
updated = _ensure_maven_central_repo(build_file, updated)Design Issues3. Dead if enable_coverage:
coverage_xml_path = strategy.setup_coverage(...) # line 527 — result immediately overwritten
...
if enable_coverage:
result, result_xml_path, coverage_xml_path = strategy.run_tests_with_coverage(...) # line 534
4. if not modules or not any("/" not in m and "." not in m for m in modules):The intent is: "fall back to For OpenRewrite specifically this probably works because their inline 5. Any existing line with Minor6. if "mavenCentral()" in content:
return contentThis also matches 7. Test path objects missing Per What's well done
|
Summary
listOf(...)patterns insettings.gradle.ktsfor projects that build include lists dynamically (e.g. OpenRewrite usesval allProjects = listOf(...)+include(*(allProjects + ...).toTypedArray()))includeregex to use word boundary, avoiding false matches on variable names likeincludedProjectscodeflash.tomlmodule-rootconfig so the function's own module is preferred over cross-module tests_read_config_module_roothelper to read module-root from codeflash.toml/pyproject.toml_infer_module_from_configand multi-module detection for sub-module project rootsProblem
When running
codeflash --file rewrite-core/src/main/java/.../StringUtils.java --function trimIndenton OpenRewrite (a 30+ module Gradle project):_extract_modules_from_settings_gradle()returned empty becausesettings.gradle.ktsuseslistOf(...)+ spread operator instead of directinclude("module-a")syntaxtestglobally instead of:rewrite-core:test, building all 30+ modules and failing on unrelated compilation errorsTest plan
tests/test_languages/test_java/test_java_test_paths.py)_extract_modules_from_settings_gradlecorrectly finds 29 modules from OpenRewrite's dynamicsettings.gradle.ktsrewrite-corewhen configured asmodule-rootcodeflash --file ... --function trimIndenton OpenRewrite repo🤖 Generated with Claude Code