Skip to content

feat(sbt): add SBT (Scala Build Tool) support - #752

Merged
aeppling merged 8 commits into
rtk-ai:developfrom
randomBrainstormer:feat/sbt/add-sbt-scala-build-tool-support
Jul 21, 2026
Merged

feat(sbt): add SBT (Scala Build Tool) support#752
aeppling merged 8 commits into
rtk-ai:developfrom
randomBrainstormer:feat/sbt/add-sbt-scala-build-tool-support

Conversation

@randomBrainstormer

@randomBrainstormer randomBrainstormer commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds rtk sbt test — ScalaTest output filtering with 90% token reduction (compact single-line on pass, failure details on fail)
  • Adds rtk sbt compile — strips SBT noise, keeps errors with 75% token reduction (source count + time on success)
  • Adds rtk sbt run — light filtering, strips SBT preamble, passes through program output
  • Adds rtk sbt <other> — passthrough for any unsupported subcommand

Implementation

Follows the established go_cmd.rs pattern:

  • lazy_static! regex for noise/summary/error line detection
  • crate::tee::tee_and_hint for full output recovery on failure
  • Exit code propagation for CI/CD compatibility
  • Discovery rules updated (src/discover/rules.rs) with 80% savings estimate

Test plan

  • 11 unit tests with token savings assertions (>=60% on test pass, >=40% on test fail, >=30% on compile error)
  • 3 real-output fixtures: sbt_test_pass.txt, sbt_test_fail.txt, sbt_compile_error.txt
  • cargo fmt --all --check — clean
  • cargo clippy --all-targets — clean
  • cargo test --all — 990 passed, 0 failed
  • Manual: rtk sbt test in a real Scala project — pending, will validate on a Scala workspace

Documentation

  • README.md — added rtk sbt to build commands section and command rewrite table
  • CHANGELOG.md — added entry under [Unreleased]

Signed-off-by: Ivan Severino 4858703+randomBrainstormer@users.noreply.github.com

@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch from fd6de19 to f6c44ec Compare March 20, 2026 16:32
@CLAassistant

CLAassistant commented Mar 20, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@aeppling

Copy link
Copy Markdown
Contributor

Hey

We are cleaning up the codebase and improving the project structure for better onboarding. As part of this effort, PR #826 reorganizes src/ from a flat layout into subfolders.

No logic changes — only file moves and import path updates.

What you need to do

Rebase your branch on develop when receiving this comment:

git fetch origin && git rebase origin/develop

Git detects renames automatically. If you get import conflicts, update the paths:

use crate::git;        // now: use crate::cmds::git::git;
use crate::tracking;   // now: use crate::core::tracking;
use crate::config;     // now: use crate::core::config;
use crate::init;       // now: use crate::hooks::init;
use crate::gain;       // now: use crate::analytics::gain;

Need help rebasing? Tag @aeppling

@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch from f6c44ec to c985d7b Compare March 28, 2026 18:00
@randomBrainstormer

Copy link
Copy Markdown
Contributor Author

Hey, thanks for the heads-up! @aeppling , I've rebased on develop and adapted to the new structure. Let me know if anything looks off!

@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch 2 times, most recently from f7dc838 to 10ff209 Compare April 6, 2026 06:19
@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch from f650ab6 to 86c89f5 Compare April 9, 2026 14:42
@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch from 86c89f5 to 1722d50 Compare April 17, 2026 19:43
@Angel-O

Angel-O commented May 8, 2026

Copy link
Copy Markdown

hey @aeppling any update on this ? do you think it can be merged ?

@dannylinden

Copy link
Copy Markdown

Please merge before my wallet getting empty due to our scala project :D

@pszymkowiak

Copy link
Copy Markdown
Collaborator

Hi @randomBrainstormer! Re-ran CI on the rebased branch and it hit two regressions from develop that landed after your last green build (2026-04-17):

  1. Missing Debug derive on SbtCommandsCommands enum on develop now requires Debug on all sub-enums:
error[E0277]: `SbtCommands` doesn't implement `std::fmt::Debug`
  1. Clippy 0.1.95 flags 5 unnecessary >= y + 1 patterns — clippy expanded that lint since you submitted. Should rewrite as x > y instead of x >= y + 1.

Could you push a quick fix? After that CI should go green and we can merge.

Full job log: https://github.com/rtk-ai/rtk/actions/runs/24583551517/job/76078806371

@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch 2 times, most recently from 2b89d93 to b360283 Compare May 14, 2026 22:08
@randomBrainstormer

Copy link
Copy Markdown
Contributor Author

@pszymkowiak Done! Rebased onto develop and pushed a fix for both:

  • Added #[derive(Debug)] to SbtCommands
  • The 5 int_plus_one warnings in stream.rs were already fixed by 1bc4713 on develop, they came in automatically with the rebase

CI should be green now. Let me know if anything else comes up before merge.

@pszymkowiak

Copy link
Copy Markdown
Collaborator

Thanks for the quick rebase fix — CI is green now (verified, 11 checks pass on all 3 OS).

I built the branch and tested rtk sbt against real Scala projects in a container. sbt compile and sbt run work well. One real gap on sbt test:

filter_sbt_test only recognizes ScalaTest output. Tested against typelevel/cats (kernelLawsJVM/test, 1701 tests) and a fresh munit project — both use munit (via discipline-munit), whose summary format is:

[info] Passed: Total 1701, Failed 0, Errors 0, Passed 1701

TEST_SUMMARY_RE does not match this, so has_summary stays false and the output falls back to sbt test: parse error (sbt_cmd.rs:409) — even though it then recovers the summary from the [error] lines. munit is the default test framework in many modern Scala 3 templates, so this is not an edge case.

Two suggestions:

  1. Add a munit branch to filter_sbt_test (Total N, Failed N, Errors N, Passed N), and ideally specs2/ZIO Test later.
  2. When the fallback does recover a summary, it should not print parse error — that header is misleading.

Minor: sbt update falls through to passthrough (run_other) — fine for now, but on a real project dependency resolution is very verbose and would benefit from a filter later.

compile/run are solid. Up to you whether you want to extend test framework coverage in this PR or land it as-is and follow up — your call.

@randomBrainstormer

randomBrainstormer commented May 17, 2026

Copy link
Copy Markdown
Contributor Author

@pszymkowiak good catch on munit , it's the default in Scala 3 templates so definitely not an edge case. Fix was small enough to fold in here:

  • Added MUNIT_SUMMARY_RE for the Passed: Total N, Failed N, Errors N, Passed N format. Maps succeeded = Passed, failed = Failed + Errors, sets has_summary = true. The *** FAILED *** detail blocks work the same as ScalaTest so those came for free. Added pass/fail fixtures to cover both frameworks.

  • Also renamed the fallback label from parse error to errors, agreed it was misleading.

sbt update and specs2/ZIO Test I'll leave for a follow-up as you suggested.

@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch from 7619c61 to 327a4e1 Compare May 21, 2026 16:19
@randomBrainstormer

Copy link
Copy Markdown
Contributor Author

@pszymkowiak hey, just checking in — munit fix is in, CI is green. Any blockers on your end before this can be merged?

randomBrainstormer and others added 5 commits May 26, 2026 10:00
Adds `rtk sbt` command with three subcommands:

- `rtk sbt test`    — ScalaTest output filtering (90% token reduction):
                      compact single-line on pass, failure details on fail
- `rtk sbt compile` — Strips SBT noise, keeps errors (75% token reduction):
                      success summary with source count + time
- `rtk sbt run`     — Light filtering, strips SBT preamble, keeps program output
- `rtk sbt <other>` — Passthrough for unsupported subcommands

Implementation follows the established go_cmd.rs pattern with lazy_static
regex, tee output recovery on failure, and exit code propagation for CI/CD.

Includes 3 real-output fixtures (pass, fail, compile error) and 11 unit tests
with token savings assertions. Discovery rules updated with 80% savings estimate.

Signed-off-by: Ivan Severino <4858703+randomBrainstormer@users.noreply.github.com>
…dle Mockito/ScalaMock

- filter_sbt_test now uses a state machine to capture [info] detail lines
  that follow a *** FAILED *** marker — covers native ScalaTest assertion
  messages, Mockito Scala verification failures (WantedButNotInvoked,
  TooManyActualInvocations), and ScalaMock expectation failures
  (Unexpected call, Unsatisfied expectation)
- run_other detects integration test commands (it:test, IntegrationTest/test,
  integration-test/test, and any *:test / */test variant) and applies
  filter_sbt_test instead of raw passthrough
- sbt boilerplate cleaned from failure output: TestsFailedException,
  Total time, and compileIncremental noise removed; failed suite class
  names retained for navigation
- add fixtures: sbt_test_mockito_fail.txt, sbt_test_scalamock_fail.txt,
  sbt_it_test_pass.txt
- 18 tests (was 11), all passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…quirement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add MUNIT_SUMMARY_RE to recognize the munit/discipline-munit summary format:
  [info] Passed: Total N, Failed N, Errors N, Passed N
  [info] Failed: Total N, Failed N, Errors N, Passed N

munit is the default test framework in Scala 3 templates and is used by
major libraries (typelevel/cats via discipline-munit). Without this,
filter_sbt_test fell through to the error-recovery path for every munit
project, which also printed a misleading "sbt test: parse error" header.

Changes:
- Add MUNIT_SUMMARY_RE regex and parse branch in filter_sbt_test
- Change fallback label from "sbt test: parse error" to "sbt test: errors"
- Add munit pass/fail fixtures and tests covering both Scala 2 (ScalaTest)
  and Scala 3 (munit) output formats
@randomBrainstormer
randomBrainstormer force-pushed the feat/sbt/add-sbt-scala-build-tool-support branch from f272aee to 189409b Compare May 26, 2026 08:45
@max-leuthaeuser

Copy link
Copy Markdown

Thanks for this PR! I am a heavy Scala/SBT user and would really like to finally get this feature.

@randomBrainstormer

randomBrainstormer commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Hi @pszymkowiak @aeppling , bumping this one. All your review points are addressed (munit fix in, last CI green). Anything still blocking, or is it good to merge?

…cala-build-tool-support

# Conflicts:
#	src/main.rs

@pszymkowiak pszymkowiak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-approving on the current head (a521234) after a full local verification — the earlier approval predated later commits, so I wanted to confirm the code as it stands now.

Verified locally:

  • cargo build ✅, cargo test (30 sbt tests) ✅
  • The 8 unwrap() calls are all lazy_static! { Regex::new(...).unwrap() } — the sanctioned RTK pattern, not production panics.
  • End-to-end on a real Scala/SBT project (installed SBT, ran a project with one passing + one failing ScalaTest): rtk sbt test correctly summarized 1 passed, 1 failed, surfaced the failing test (add is broken on purpose / CalcSpec), propagated the non-zero exit code, and wrote the full output to the tee log. Token savings ~88% (154 → 18 words).

Clean, well-tested, real fixtures, no scope creep. LGTM.

Minor non-blocking follow-up idea: including the assertion detail (e.g. 4 did not equal 5) alongside the failing test name would help agents debug without opening the tee log — but the log covers it, so not a blocker.

@pszymkowiak
pszymkowiak requested a review from aeppling July 18, 2026 13:58
@randomBrainstormer

Copy link
Copy Markdown
Contributor Author

Thanks for the review and approval! @pszymkowiak
The CI workflow is sitting in action_required state, it needs a maintainer to click approve. Could someone with write access kick that off?

@KuSh KuSh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please follow CONTRIBUTING.md, especially the Transparency section.

Also, two tests (test_filter_sbt_compile_empty_input and test_filter_sbt_test_empty_input) appear to conflict with guard.rs’s own test suite (never_worse("", "0 matches") returns ""), so empty input must yield empty output

Comment thread src/discover/rules.rs Outdated
Comment thread src/main.rs Outdated
Comment thread CHANGELOG.md Outdated
@TaKO8Ki

TaKO8Ki commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Following up on pszymkowiak’s real-project testing, I also tested sbt test, sbt compile, and sbt run with Cats and official Scala/Typelevel example projects against the current head a521234.

Each command was run once directly with SBT and once through RTK. The filtered outputs and reduction figures below are produced by the SBT filter implemented in this PR.

Results:

  • typelevel/cats kernelLawsJVM/test: 1,701 tests passed; native 831 words → filtered 4 words
  • typelevel/cats kernelJVM/compile: passed; native 288 words → filtered 4 words
  • scala/scala3-example-project run: passed and preserved the application output; native 179 words → filtered 174 words

I also ran the SBT-related unit tests, and they all passed.

Once the changes requested by @KuSh are addressed, LGTM.

Comment thread src/cmds/scala/sbt_cmd.rs Outdated
…code

- filter_sbt_test/compile now return empty output for empty input, and all
  run_* handlers emit via runner::print_with_hint (guard::never_worse), so RTK
  never emits tokens the underlying command didn't produce (CONTRIBUTING
  Transparency rule). Flip the two empty-input edge-case tests accordingly.
- run_run now preserves scoped tasks (e.g. `exampleJVM/run`) like run_test/
  run_compile, instead of forcing `sbt run <task>`.
- Anchor the discover/rewrite sbt rule with `(?:\s|$)` so it no longer matches
  beyond the intended subcommands (e.g. `sbt testify`).
- Remove preprocess_sbt_args + helpers and their tests: the rewrite rule never
  produces `sbt <global-flag> <task>`, so that path was unreachable.
- Drop the manually-added CHANGELOG entry (generated at release).
@randomBrainstormer

randomBrainstormer commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review @KuSh, and @TaKO8Ki for the scoped-task catch! All fixed in ef38103.

Everything passes locally: cargo fmt --all --check, cargo clippy --all-targets (no warnings), cargo test --all (2456 passed), and semgrep is clean against the PR base.

Back to you when you get a chance, mind kicking off the CI run (it needs a maintainer to approve it) and taking another look?

@aeppling

aeppling commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Hey @randomBrainstormer

Following up on the Transparency point KuSh raised, here are some point to address for better standardization, otherwise LGTM :)

  • run_test / run_compile / run_run: replace the manual .output() + track + exit blocks with the shared runner, same as go_cmd.rs
  runner::run_filtered(cmd, "sbt test", &args.join(" "), filter_sbt_test, RunOptions::with_tee("sbt_test"))
  • run_other it:test branch appends the tee hint without the never_worse cap. When filter_sbt_test doesn't recognize the output it returns it unchanged, so what gets printed is the full raw output plus the hint line, more than the raw command produced. print_with_hint (already used in the other three handlers) caps the total; going through the runner fixes it for free.

  • Propagate the exit codes to main (use runner::run_filtered and return its Result<i32>)

Refer to CONTRIBUTING.md and CODING_PRACTICES.md for the transparence guidelines

Address the standardization follow-up: replace the manual .output() + track +
process::exit blocks in run_test/run_compile/run_run with runner::run_filtered
(a small run_task helper), matching go_cmd.rs. The runner applies the
never_worse cap, tee hint, tracking, and exit-code propagation centrally.

- run_other's it:test branch now goes through the runner too, so an
  unrecognized output is capped by never_worse instead of being reprinted
  verbatim plus the tee hint (more than the raw command produced). The
  passthrough branch uses runner::run_passthrough.
- Handlers now return Result<i32> and the exit code is propagated to main
  instead of calling std::process::exit directly.
@randomBrainstormer

randomBrainstormer commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @aeppling, all three addressed in 59b604b:

  • run_test / run_compile / run_run now go through runner::run_filtered (via a small run_task helper) instead of the manual .output() + track + exit blocks — same shape as go_cmd.rs.
  • run_other's it:test branch also routes through the runner now, so the never_worse cap applies: an unrecognized output no longer gets reprinted verbatim plus the hint. The catch-all branch uses runner::run_passthrough.
  • Handlers return Result<i32> and the exit code is propagated to main (no more direct std::process::exit).

The net result is ~190 fewer lines in the SBT module 🚀 , also all green locally: cargo fmt --all --check, cargo clippy --all-targets (no warnings), cargo test --all (2456 passed), semgrep clean against the PR base.

@aeppling
aeppling merged commit 4f86523 into rtk-ai:develop Jul 21, 2026
11 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 21, 2026
@aeppling

Copy link
Copy Markdown
Contributor

Thanks for contributing to RTK @randomBrainstormer !

@greenhost87

Copy link
Copy Markdown

Minor problem - single test execution via sbt testOnly falls into rtk sbt <other>

@aeppling

Copy link
Copy Markdown
Contributor

@greenhost87 #3140

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.