Skip to content

feat(jvm): add Maven, Gradle, and Ant support with output filters#1241

Open
ryanmurf wants to merge 12 commits intortk-ai:developfrom
murphytek:master
Open

feat(jvm): add Maven, Gradle, and Ant support with output filters#1241
ryanmurf wants to merge 12 commits intortk-ai:developfrom
murphytek:master

Conversation

@ryanmurf
Copy link
Copy Markdown

Summary

Adds native rtk mvn, rtk gradle, and rtk ant subcommands with output
filtering, validated against 29 real-world Spring/Java/Kotlin projects.

The JVM ecosystem was the largest gap in rtk — Maven and Gradle builds are
notoriously verbose, and AI agents working in Java codebases waste enormous
amounts of context on download progress, reactor chatter, repeated stack
frames, and plugin noise.

What's added

Three new tools

  • rtk mvn {build,package,clean,install,test,verify} + passthrough
  • rtk gradle {build,test,assemble,clean,check,bootRun} + passthrough
  • rtk ant {build,clean,test,compile,package,install} + passthrough

Wrapper detection

resolved_build_command() (in core/utils.rs) prefers project-local ./mvnw
and ./gradlew over system binaries — this matches what the developer
actually runs and respects pinned tool versions.

Five compression mechanisms

  1. Quiet flag injection--no-transfer-progress + -B for Maven (with
    per-binary version probe so it's not injected on Maven < 3.6.1),
    --console=plain for Gradle. Suppresses the bulk of noise at the source.
  2. PGP signature collapsepgpverify-maven-plugin's 354 lines (178
    artifacts × 2 lines) collapse to one summary; non-OK results preserved
    verbatim for diagnosis.
  3. Stack-trace dedup (stack_dedup.rs) — identical repeated trace blocks
    collapse with (... repeated N more times ...).
  4. Stack-frame trimming (stack_trim.rs) — within a single trace, runs
    of noise frames (Groovy internals, JDK reflection, JUnit/Gradle/Surefire
    harness) collapse to (... N frames omitted ...) while keeping the
    header, first frame, last frame, and any user-code frames.
  5. Line-level dedup (line_dedup.rs) — adjacent runs of identical
    single lines collapse (e.g. > Configure project :app × 26).

Noise patterns added (corpus-discovered)

  • JVM 25 sun.misc.Unsafe deprecation footer
  • java.applet/legacy-deprecation compiler warnings
  • Maven reactor artifact headers, Surefire decorations
  • pgpverify-maven-plugin config block
  • OpenAPI generator chatter
  • Apache Cocoon resources plugin
  • Checkstyle audit messages
  • Gradle release highlights, JVM forking hints, configuration cache nags
  • Finished at: timestamps, skip non existing resourceDirectory no-ops

Bug fix in shared code

  • strip_ansi() in core/utils.rs now strips C0 control chars including
    \x08 backspace. Previously script/pty-captured output bypassed
    ^anchored regex matches because lines began with \x08\x08.

Real-world benchmark (29 projects)

Tool Projects Mean compression Best
Maven 14 70.0% lines / 74.6% bytes htmlunit 99.3%
Gradle 13 ~75% (with stack_trim) micrometer 94.8%
Ant 2 81.4% tomcat8_5 85.3%

100% exit-code parity across all 29 builds — the filter never masks a
real build failure.

Outliers in the bottom of the distribution were all tiny failed builds
(20–100 lines of nearly-pure error signal) where low compression is correct,
not a filter problem.

Test coverage

  • 31 new unit tests in src/cmds/jvm/
  • 12 integration tests in tests/jvm_filter_integration.rs against 4
    realistic captured-output fixtures (tests/fixtures/jvm/*.txt)
  • Inline TOML filter tests in mvn-build.toml, gradle.toml, ant.toml
  • Total: 1,386 unit tests passing, 12 integration tests passing

Architecture

raw output
   → strip_ansi (ANSI escapes + C0 control chars)
   → dedupe_stack_traces (whole repeated trace blocks)
   → trim_stack_noise (intra-trace framework frames)
   → dedupe_repeated_lines (adjacent identical lines)
   → collapse_pgp_block (Maven only)
   → filter_mvn_build / filter_gradle_* / filter_ant_* (line-by-line)
   → truncate
final filtered output

Test plan

  • cargo test --bin rtk — 1386 passed
  • cargo test --test jvm_filter_integration — 12 passed
  • cargo build --release — clean
  • rtk mvn build against a real Spring Boot project — verify wrapper used
  • rtk gradle build against a real Kotlin Gradle project — verify wrapper used
  • rtk ant build against an Ant project (e.g. Apache Derby)
  • rtk gain reports nonzero savings after running the above

🤖 Generated with Claude Code

aeppling and others added 9 commits April 6, 2026 12:54
feat(): batch fixs + aws extended + filter quality batch
…master--components--rtk

chore(master): release 0.35.0
- New rtk mvn subcommand with build/package/clean/install/test/verify
- src/cmds/jvm/mod.rs and src/cmds/jvm/mvn_cmd.rs
- Expanded mvn-build.toml filter (Scanning, Reactor, Download, Progress, jansi/SLF4J/JDK warnings)
- Combined mvn/mvnw rewrite rule in src/discover/rules.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New rtk gradle subcommand with build/test/assemble/clean/check/bootRun
- src/cmds/jvm/gradle_cmd.rs (mod.rs already created by mvn agent)
- Expanded gradle.toml filter (progress bars, daemon chatter, native-platform warnings)
- Combined gradle/gradlew rewrite rule in src/discover/rules.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/cmds/mod.rs
#	src/main.rs
Adds native rtk mvn and rtk gradle subcommands with output filtering,
plus wrapper script detection (./mvnw, ./gradlew, mvnw, gradlew).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rt, line dedup, frame trimming

Major improvements to JVM support after corpus benchmarking on 29 real
Maven/Gradle/Ant projects:

Critical fixes:
- Wrapper detection: rtk now uses ./mvnw and ./gradlew when present (was
  silently falling back to system mvn/gradle, breaking projects pinned
  to specific versions). resolved_build_command() in core/utils.rs.
- Maven version probe: --no-transfer-progress only injected on Maven >=
  3.6.1 (older versions reject it and fail the build). Per-binary cache.
- strip_ansi() now strips C0 control chars including \x08 backspace
  (script/pty wrappers were defeating ^anchored regex matches).

New features:
- Quiet flag injection: --no-transfer-progress + -B for Maven, --console=
  plain for Gradle. Suppresses noise at the source.
- PGP signature verify collapse: pgpverify-maven-plugin's per-artifact
  chatter (354 lines for 178 artifacts on htmlunit) collapses to a single
  summary, preserving non-OK results verbatim.
- Stack-trace dedup: identical repeated traces (e.g. SpotBugs 340x flood)
  collapse to first + '(... repeated N more times ...)'.
- Stack-frame trimming: noise frames (groovy internals, jdk reflection,
  test runner harness) within a single trace collapse to '(... N frames
  omitted ...)' while keeping the header, first frame, last frame, and
  any user-code frames.
- Line-level dedup: adjacent runs of identical single lines collapse
  (e.g. 26x '> Configure project :app').
- Ant build tool support: rtk ant {build,clean,test,compile,package,
  install} with target chatter stripping.

New noise patterns added (corpus-discovered):
- JVM 25 sun.misc.Unsafe deprecation footer
- java.applet/legacy-deprecation compiler warnings
- Maven reactor artifact headers and Surefire decorations
- pgpverify-maven-plugin config block
- OpenAPI generator file-writing chatter
- Apache Cocoon resources-plugin lines
- Checkstyle audit start/done/0-violations messages
- Gradle release highlights and JVM forking hints

Test fixtures: 4 realistic captured-output files in tests/fixtures/jvm/
plus a 12-test integration suite (tests/jvm_filter_integration.rs) that
asserts >=50% compression with signal preservation.

Test count: 1386 unit tests passing, 12 integration tests passing.

Corpus benchmark (29 real projects, 14 Maven + 13 Gradle + 2 Ant):
- Maven: 70.0% mean / 78.2% median line compression
- Gradle: ~50% mean (Groovy stack noise, now addressed by stack_trim)
- Ant: 81.4% mean
- 100% exit-code parity (filter never masks failures)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 12, 2026 04:43
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 12, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 3 committers have signed the CLA.

✅ aeppling
❌ github-actions[bot]
❌ ryanmurf
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class JVM build-tool support to rtk by introducing native rtk mvn, rtk gradle, and rtk ant subcommands plus supporting filter/rewriter infrastructure and fixtures/tests to validate output compression.

Changes:

  • Added new JVM command modules (Maven/Gradle/Ant) with stack-trace trimming/dedup and line dedup helpers.
  • Expanded/added built-in TOML filters for Maven/Gradle/Ant output and updated built-in filter counts.
  • Added discovery rewrite rules plus new integration tests and realistic captured-output fixtures.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
tests/jvm_filter_integration.rs Adds JVM integration tests using captured fixtures and helper filters.
tests/common/mod.rs Adds duplicated “test helper” filter logic for integration tests.
tests/fixtures/jvm/mvn_test_failure.txt Maven failure fixture for integration testing.
tests/fixtures/jvm/mvn_clean_compile_with_pgp.txt Maven multi-module + PGP fixture for integration testing.
tests/fixtures/jvm/gradle_test_with_spotbugs_flood.txt Gradle failure + repeated stack traces fixture.
tests/fixtures/jvm/ant_compile.txt Ant javac failure fixture.
src/main.rs Wires new Mvn, Gradle, and Ant clap subcommands into the CLI.
src/cmds/mod.rs Exposes new jvm command module namespace.
src/cmds/jvm/mod.rs Adds automod entry for JVM command modules.
src/cmds/jvm/mvn_cmd.rs Implements rtk mvn execution, quiet-flag injection, and Maven filtering (incl. PGP collapse).
src/cmds/jvm/gradle_cmd.rs Implements rtk gradle execution, quiet-flag injection, and Gradle filtering pipeline.
src/cmds/jvm/ant_cmd.rs Implements rtk ant execution and Ant filtering.
src/cmds/jvm/stack_dedup.rs Adds repeated stack-trace block deduplication.
src/cmds/jvm/stack_trim.rs Adds intra-trace “noise frame” trimming.
src/cmds/jvm/line_dedup.rs Adds adjacent repeated-line deduplication.
src/core/utils.rs Enhances strip_ansi() and adds resolved_build_command() wrapper preference logic.
src/discover/rules.rs Adds rewrite rules for mvn/mvnw, gradle/gradlew, and ant.
src/filters/mvn-build.toml Expands Maven TOML filter patterns and inline tests.
src/filters/gradle.toml Expands Gradle TOML filter patterns and limits.
src/filters/ant.toml Adds new Ant TOML filter and inline tests.
src/core/toml_filter.rs Updates built-in filter-count assertions for the new filter(s).

Comment on lines +3 to +7
//! Each test:
//! - Reads a fixture file from tests/fixtures/jvm/
//! - Runs it through the appropriate filter function (via tests/common/mod.rs)
//! - Asserts filtered output is at least 50% smaller than raw input
//! - Asserts critical signal lines are preserved (errors, BUILD result)
Comment on lines +38 to +46
#[test]
fn test_mvn_compile_with_pgp_reduces_by_50pct() {
let raw = fixture("mvn_clean_compile_with_pgp.txt");
let filtered = common::filter_mvn_build(&raw);

assert!(
reduced_by_at_least(&raw, &filtered, 40),
"expected ≥40% reduction, got:\nraw={} bytes, filtered={} bytes\n---\n{}",
raw.len(),
Comment on lines +359 to +373
// Unix-style wrapper (works on macOS, Linux, and Git Bash on Windows).
let unix_wrapper = std::path::Path::new(&wrapper);
if unix_wrapper.exists() {
return Command::new(format!("./{}", wrapper));
}

// Windows native wrappers.
#[cfg(target_os = "windows")]
{
for ext in &["cmd", "bat"] {
let win_wrapper = format!("{}.{}", wrapper, ext);
if std::path::Path::new(&win_wrapper).exists() {
return Command::new(format!(".\\{}", win_wrapper));
}
}
Comment on lines +38 to +45
# Plugin chatter that's never actionable
"^\\[INFO\\] skip non existing resourceDirectory",
"^\\[INFO\\] Finished at:",
# Java compiler legacy-deprecation noise (e.g. java.applet.*)
"^\\[WARNING\\].*java\\.applet\\.",
"^\\[WARNING\\].*has been deprecated and marked for removal",
# JVM 25 sun.misc.Unsafe deprecation footer block (4 lines)
"^WARNING: sun\\.misc\\.Unsafe::objectFieldOffset",
Comment on lines +155 to +172
// Always preserve these.
if RE_KEEP_BUILD_RESULT.is_match(line)
|| RE_KEEP_TOTAL_TIME.is_match(line)
|| RE_KEEP_JAVAC_ERROR.is_match(line)
|| RE_KEEP_ERROR.is_match(line)
|| RE_KEEP_FAILED.is_match(line)
{
kept.push(truncate(line.trim_end(), 240).to_string());
continue;
}

// Strip these patterns.
if RE_STRIP_BUILDFILE.is_match(line)
|| RE_STRIP_TARGET.is_match(line)
|| RE_STRIP_TASK_CHATTER.is_match(line)
|| RE_STRIP_JAVAC_PREFIX.is_match(line)
{
continue;
// JVM-MVN BEGIN
RtkRule {
pattern: r"^mvn\s+(compile|package|clean|install)\b",
pattern: r"^(?:mvn|mvnw)\s+(compile|package|clean|install|test|verify)\b",
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks — verified empirically that this works as-is. strip_absolute_path() in src/discover/registry.rs:263 normalizes any first-word containing / (including ./mvnw) down to its basename (mvnw) before classification, so the regex never sees the ./ prefix:

$ rtk rewrite "./mvnw clean test"
rtk mvn clean test    (exit 3)
$ rtk rewrite "mvnw verify"
rtk mvn verify        (exit 3)

The same normalization handles ./gradlew (see the related comment thread). Leaving the patterns as-is to avoid regex duplication that would diverge from the existing absolute-path normalization.

Comment on lines +663 to +667
// Matches `gradle ...`, `./gradlew ...` and `gradlew ...`
// (strip_absolute_path normalizes `./gradlew` → `gradlew` for classify,
// but rewrite_segment uses the un-normalized cmd, so both prefixes are
// listed in rewrite_prefixes below).
pattern: r"^(?:gradle|gradlew)\s+(build|test|assemble|clean|check|bootRun)\b",
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same as the Maven thread above — verified working via strip_absolute_path():

$ rtk rewrite "./gradlew test"
rtk gradle test    (exit 3)
$ rtk rewrite "gradlew assemble"
rtk gradle assemble (exit 3)

Note: I did fix a separate broken regex in src/filters/gradle.toml's match_command (was ^(gradle|gradlew|\./)gradlew?\b requiring gradlegradle, now ^(?:\./)?(?:gradle|gradlew)\b) — that was a real bug from a different code path.

Comment on lines +345 to +384
/// Patterns stripped from Maven output. Conservative — we never drop lines
/// starting with `[ERROR]`, `[WARNING]`, `Tests run:`, or `BUILD ...`.
fn is_mvn_noise(line: &str) -> bool {
lazy_static::lazy_static! {
static ref PATTERNS: Vec<Regex> = {
let raw = [
// Initial scan / reactor announcements
r"^Scanning for projects",
r"^\[INFO\] Scanning for projects",
r"^\[INFO\] Reactor",
r"^\[INFO\] -+$",
r"^\[INFO\] =+$",
r"^\[INFO\] $",
r"^\[INFO\]\s*$",
r"^\[INFO\] ---",
r"^\[INFO\] Building\s",
// Dependency I/O
r"^Downloading from\s",
r"^Downloaded from\s",
r"^\[INFO\] Downloading\s",
r"^\[INFO\] Downloaded\s",
r"^Downloading:",
r"^Downloaded:",
r"^Progress \(\d+\)",
r"^Progress ",
// jansi / SLF4J / JDK restricted-method warnings
r"^WARNING: A restricted method",
r"^WARNING: java\.lang\.System::load",
r"^WARNING: Use --enable-native-access",
r"^WARNING: Restricted methods will be blocked",
// JVM 25 sun.misc.Unsafe deprecation footer block (4 lines)
r"^WARNING: A terminally deprecated method in sun\.misc\.Unsafe",
r"^WARNING: sun\.misc\.Unsafe::objectFieldOffset",
r"^WARNING: This is a critical method",
// pgpverify-maven-plugin config-block chatter
r"^\[INFO\] Key server\(s\)",
r"^\[INFO\] Create cache directory for PGP keys:",
r"^\[INFO\] Resolved \d+ artifact\(s\)",
r"^\[INFO\] Artifacts were already validated",
// Corpus-discovered patterns (resources, checkstyle, OpenAPI gen)
Comment on lines +1 to +4
//! Thin test helpers that replicate filter logic from src/cmds/jvm/ for use
//! in integration tests. These are intentionally minimal — they use the same
//! regex patterns as the production code but don't depend on the binary crate's
//! internal module graph (which is inaccessible from tests/ in a bin-only crate).
Comment on lines 1 to 4
[filters.gradle]
description = "Compact Gradle build output — strip progress, keep tasks and errors"
match_command = "^(gradle|gradlew|\\./)gradlew?\\b"
strip_ansi = true
@ryanmurf ryanmurf changed the base branch from master to develop April 12, 2026 04:52
ryanmurf and others added 3 commits April 11, 2026 22:56
- jvm_filter_integration: rename test fns to *_reduces_by_40pct and update
  module doc to match the actual 40% threshold (1, 2)
- core/utils: prefer .cmd/.bat wrappers FIRST on Windows (shebang ./mvnw
  doesn't execute under CreateProcess), fall through to Unix wrapper only
  on non-Windows (3)
- mvn-build.toml: drop 'Finished at:' from inline test expected output to
  match the strip pattern (4)
- ant_cmd: replace blanket [javac]-prefix strip with narrow informational
  pattern (Compiling/Note/Compiled/options-warning) so user-actionable
  diagnostic context (source snippets, carets, symbol/location, error
  count summaries) survives the filter (5)
- main.rs: clarify 'rtk ant build' help text — it runs the literal 'build'
  target, not the project's default target (6)
- mvn_cmd: align is_mvn_noise regex set with mvn-build.toml strip patterns
  (reactor headers, Surefire decorations, Finished at, applet deprecation,
  SLF4J) so the native filter and TOML filter behave consistently (9)
- tests/common/mod.rs: docstring acknowledges this is an approximation of
  the production filter, not a byte-for-byte mirror — drift is possible (10)
- gradle.toml: fix broken match_command alternation. Was
  '^(gradle|gradlew|\\./)gradlew?\\b' which required 'gradlegradle';
  now '^(?:\\./)?(?:gradle|gradlew)\\b' (11)

Items 7 and 8 (Copilot's claim that ./mvnw and ./gradlew patterns don't
match) are false positives — strip_absolute_path() in src/discover/registry.rs
already normalizes ./mvnw → mvnw before classification. Verified via
'rtk rewrite "./mvnw clean test"' returning 'rtk mvn clean test' (exit 3).
Will leave a polite reply on those review threads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds two new safeguards for flag injection:

1. RTK_NO_INJECT_FLAGS env var — global kill-switch. When set to any
   non-empty, non-'0' value, rtk passes args through unchanged.
   Intended as a one-line escape hatch for users hitting unexpected
   build-tool quirks where the injected flags break their build.

2. Gradle version probe (symmetric with the existing Maven probe) —
   only inject --console=plain when the resolved Gradle reports
   major version >= 5. Older versions had a less-mature plain console
   that didn't reliably suppress noise; falling back to no injection
   on Gradle <5 keeps the tiny number of legacy projects working
   (1 of 213 in the cataloged corpus).

Both probes fail-CLOSED: if probing fails (binary missing, output
unparseable), no flag is injected. Better to lose a small win than
to break someone's build.

Verified via smoke tests:
- RTK_NO_INJECT_FLAGS=1 rtk mvn build → 'compile' (no flags)
- Default rtk mvn build → '-B compile' on synthetic wrapper
- Real Maven 3.9.8 (cve-doc-generator) → BUILD SUCCESS exit 0
- Real Gradle 8.12.1 (Kotlin compiler) → exit 0

Co-Authored-By: Claude Opus 4.6 (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.

4 participants