Fixes #13135, ensure completude of the reactor summary but priviledge failures to be last to stay human efficient - #13136
rmannibucau wants to merge 3 commits into
Conversation
gnodet-bot
left a comment
There was a problem hiding this comment.
The goal of showing complete reactor summaries is sound — the old behavior of hiding successful modules on failure was indeed confusing. The grouping approach (skipped→success→failure) is clean. But there's a behavioral regression that needs addressing before merge.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
| } | ||
|
|
||
| if (shouldSkip) { | ||
| if (group == 0 && entry.buildSummary() == null) { |
There was a problem hiding this comment.
This condition unconditionally filters out all null-buildSummary entries regardless of whether the build succeeded or failed. The old code gated this on result.hasExceptions():
// Old
boolean shouldSkip = result.hasExceptions(); // only true when build failedWith the new code, running mvn -pl moduleA install (or any --also-make / --resume-from subset) on a multi-module project produces a reactor summary that completely omits the modules that were never built. Their buildSummary is null and they silently vanish — no SKIPPED line, no ... prefix, nothing. This is a regression from Maven 3 / pre-#11977 behavior.
Fix: carry result.hasExceptions() into ReactorSummaryRequest (or pass it as a parameter) and only suppress null-buildSummary entries when there are exceptions:
| if (group == 0 && entry.buildSummary() == null) { | |
| if (group == 0 && entry.buildSummary() == null && request.hasExceptions()) { |
…and add boolean hasExceptions to ReactorSummaryRequest, set from result.hasExceptions() at call site.
| } | ||
|
|
||
| buffer.append(project.getName()); | ||
| StringBuilder buffer = request.buffer(); |
There was a problem hiding this comment.
🔧 Minor: request.buffer() re-assigned on every iteration — misleading
The StringBuilder is fetched from the record on every loop iteration, producing a new local variable buffer each time that refers to the same shared object. It reads like a fresh allocation each time but is not — a reader might wonder why it's inside the loop rather than hoisted before it. Move it before the loop:
| StringBuilder buffer = request.buffer(); | |
| StringBuilder buffer = request.buffer(); |
(i.e., hoist this line to just before the for loop, and remove it from inside the loop body)
| inOrder.verify(logger).info("Reactor Summary for Maven Project artifact1 3.5.4-SNAPSHOT:"); | ||
| inOrder.verify(logger).info(""); | ||
| inOrder.verify(logger).info("..."); | ||
| inOrder.verify(logger).info("Maven Project artifact1 ............................ SUCCESS [ 1.000 s]"); |
There was a problem hiding this comment.
📋 Missing test: successful partial build with some modules not built
The only test scenarios covering null-buildSummary entries are failure scenarios. There's no test for:
mvn -pl moduleA install // moduleB, moduleC → null buildSummary, no exceptions
With the current implementation, those modules silently disappear from the summary (see the regression comment on logReactorSummaryGroup). A test like testSessionEndedSuccessWithSkippedModules would have caught it:
project1→BuildSuccessproject2→ no build summary (null)project3→BuildSuccess- No exception added
- Expected:
project1 SUCCESS,project2 SKIPPED,project3 SUCCESS(in build order)
|
Skipping modules for which the build passed was intentional, according to issue Now, when we have a project with hundreds of modules, we will have a full list and need to scroll a long way up to see the root error. Maybe this behavior should be parameterized? |
|
Another idea: show a summary with only the number of successful and skipped modules, and print the details only for the ones that failed. For pass build we print all details as is now. |
not at all, this PR solves the original issue by sorting by status, failures last. note that having a single module in the report for hundreds of module built is at least as much bothering than scrolling (looks like a bug in the execution and not only the report) guess the proposal solves both cases and don't need a parameter since you don't need to scroll anymore (and colors help) and you have the failures immediately at the end (where you terminal is if it autoscroll like most of them) on code feedback: will handle the hasException and buffer points |
c61e988 to
e2f14e2
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review after updated commit.
All three findings from the previous REQUEST_CHANGES review are addressed:
- ✅ Behavioral regression (null
buildSummaryalways suppressed): fixed —request.hasExceptions()now gates suppression correctly - ✅
bufferfetched inside the loop: fixed — hoisted before theforloop - ✅ Missing test for success-with-skipped-modules:
testSessionEndedSuccessWithSkippedModulesadded, and existing failure tests updated to reflect the new ordering
Two new observations below.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
slawekjaranowski
left a comment
There was a problem hiding this comment.
In original issue we have:
Can we have a way to skip reactor logging, i.e. the reactor build order and/or reactor summary.
I have hundreds of modules in my reactor build and I always have to scroll back pages to see the error message.
here we remove requirements of skipping ... so issue should be reopened or closed as not planed if we merge this one.
|
I am convinced that skipping correctly built modules when an error occurs is a good solution. for me -1 sorting/grouping introduce the problem again |
|
@slawekjaranowski ok I get your point but this is only relevant to a very particular case: monothreaded build, for all other cases it is counter productive. Also your two screenshots highlights the issue: "why did it build a single module? it is obviously why the test failed", so this is more misleading in most cases IMHO. I'd also like to emphasis we already had a solution to skip the summary: side note: sorting solves the issue "where is the failed module" when you have hundreds of module (camel, hop, tomee, nifi etc), doesnt solve "present me all errors" but we'll never solve this one by design until it is a trivial case = single module with a single simple error just cause the errors can take more than a screen and you can get a chain of errors (think JAXRS for ex), so don't think we should fight the issue you mention but only the summary one. |
…failures to be last to stay human efficient
e2f14e2 to
a36f758
Compare
|
Thanks for the fix. Worth noting that #12697 (and its dependency chain starting at #12695) is introducing new event loggers ( |
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review after updated commit.
Previous findings status:
- ✅ Behavioral regression (null
buildSummaryalways suppressed): fixed — gated onrequest.hasExceptions()✓ - ✅
bufferfetched inside the for loop: fixed — hoisted to top oflogReactorSummaryGroup✓ - ✅ Missing test for success-with-skipped-modules:
testSessionEndedSuccessWithSkippedModulesadded ✓ - ❌ Mutable
StringBuilderinsideReactorSummaryRequestrecord: still present (see inline) - ❌
UNKNOWNbuildSummary silently suppressed on failure: still present (see inline)
Note on design debate: @slawekjaranowski has a CHANGES_REQUESTED review open on the overall grouping/ordering approach. That is a design-level question between maintainers — not something I can adjudicate. The two open technical findings below apply regardless of which design direction is chosen.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
| } | ||
|
|
||
| private record ReactorSummaryRequest( | ||
| List<ReactorSummaryEntry> entries, StringBuilder buffer, boolean isSingleVersion, boolean hasExceptions) {} |
There was a problem hiding this comment.
🔧 Same mutable-StringBuilder-in-record issue as compat copy — apply the same fix (allocate locally in logReactorSummaryGroup, drop the field from ReactorSummaryRequest).
|
@gnodet good call, guess this will need some agreement on the target now before we get the new ones :(. Right now I needed to revert several builds to rc5 due to that so hope we dont take too much time 🤞 . |
|
@rmannibucau for a long running build |
|
@delanym the |
If there's a regression, we can fix it in rc7. The PR I pointed at are all for 4.1.0. |
But this issue does not advertise fixing #8027. It just links to it because it's related IIUC. |
|
@gnodet my understand of the issue was that there are too much summary lines so you dont see the failed one fast enough, this part is solved by this PR. Slawomir completed it was also a goal to see the test failure inline immediately so solve space -> this one I think will never really be solved by design until you do yet another "logger" (listener) which does filter and there we'll not get an agreement of the default so my proposal is to stay conservative on the default, keep a consistent output (all or nothing, sorted if it helps, but never a filtered summary which looks just like maven was broken for the run) and accept to scroll for test cause you will scroll anyway since the suite is executed in general (once again some edge cases would benefit from the code which led to the regression but these are corner cases). |
|
Thanks @rmannibucau for the fix — the sorting approach is the right direction. One issue remains in the latest commit: modules with if (group == 0 && entry.buildSummary() == null && request.hasExceptions()) {
lastWasSkipped = true;
continue; // ← these modules disappear from the summary
}This means the reactor summary is still incomplete on failure — just for a different category of modules than before. Users with a 100-module project where 80 built successfully and 19 were never reached won't see those 19 in the summary at all, which is exactly the confusion #13135 describes. The fix is simply to remove those three lines. With group-based sorting, Also, the |
|
Note that the new event logger infrastructure coming in 4.1.0 (see #12697 and its dependency chain starting at #12695 — |
gnodet-bot
left a comment
There was a problem hiding this comment.
The sorting approach is right — failures at the bottom of the terminal is exactly what we want. One remaining issue below.
| @@ -237,11 +269,11 @@ private void logReactorSummary(MavenSession session) { | |||
There was a problem hiding this comment.
This block silently suppresses modules that were never built (buildSummary == null), which is the same incompleteness that #13135 was trying to fix — just for a different category of modules. With the group-based sorting, these modules already land in group 0 and display as SKIPPED before the successes and failures. There's no reason to hide them.
Dropping these 4 lines gives a complete summary on failure: SKIPPED (not built) → SUCCESS → FAILURE, with failures always last where the terminal cursor sits.
Keep SKIPPED modules visible regardless of build failure and rely on group ordering (skipped, success, failure) to bring failures last. Remove the separate .detail logger added earlier as it brought no benefit, and drop the now unused lastWasSkipped placeholder handling.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review after latest commit (75f5320).
Previous findings status:
- ✅ Behavioral regression (null
buildSummaryalways suppressed): fully resolved — no suppression at all now, all entries always rendered - ✅ Missing test for successful partial build with skipped modules:
testSessionEndedSuccessWithSkippedModulesadded - ✅
UNKNOWN → group = 0suppression concern: moot — the concern was about the oldshouldSkipgating. With no suppression remaining, UNKNOWN entries always render in group 0 (before successes). Visible, not lost. - 🔄 Mutable
StringBuilderinsiderecord: still present — see inline comment below.
One open issue remains.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
| } | ||
|
|
||
| private record ReactorSummaryRequest( | ||
| List<ReactorSummaryEntry> entries, StringBuilder buffer, boolean isSingleVersion) {} |
There was a problem hiding this comment.
🔧 Mutable StringBuilder in record — still present, checkstyle argument doesn't hold
The author previously responded that the StringBuilder is in the record as a checkstyle workaround. But checkstyle method-length rules apply to the method body where the code lives — in this case the relevant restriction would be on logReactorSummary, which is already short. The StringBuilder allocation belongs in logReactorSummaryGroup, which is a newly added, short method — no checkstyle constraint applies there.
The fix:
| List<ReactorSummaryEntry> entries, StringBuilder buffer, boolean isSingleVersion) {} | |
| private record ReactorSummaryRequest( | |
| List<ReactorSummaryEntry> entries, boolean isSingleVersion) {} |
Then in logReactorSummaryGroup, change StringBuilder buffer = request.buffer(); to StringBuilder buffer = new StringBuilder(128);, and update the call site in logReactorSummary:
ReactorSummaryRequest request = new ReactorSummaryRequest(entries, isSingleVersion);A record carrying mutable shared state that is mutated by three consecutive callers is a correctness trap — any future refactor that calls logReactorSummaryGroup twice in parallel or reorders the calls will silently corrupt the buffer. Please fix.
| } | ||
|
|
||
| private record ReactorSummaryRequest( | ||
| List<ReactorSummaryEntry> entries, StringBuilder buffer, boolean isSingleVersion) {} |
There was a problem hiding this comment.
🔧 Same mutable-StringBuilder-in-record issue as the compat copy. Apply the same fix: drop StringBuilder buffer from ReactorSummaryRequest, allocate locally at the top of logReactorSummaryGroup.
|
are we good now like that and we revisit with the new listeners - one being summary-free maybe? |
|
Never mind of technical implementation, we should listen users what they need. |
|
@slawekjaranowski a very gently reminder I'm an user too and this is a bug for the user I am ;) - no technical concern there, if you want to do it using asm i'm fine |
|
@slawekjaranowski it sounds like bigger and better things are coming, so that's just fine. I'm playing the long game. |
|
FTR: don't get me wrong, I'm not against enabling that use case (this was the logger code in the PR I removed after Guillaume's feedback), but I'm against an impacting regression. 100% aligned what Guillaume prepared should cover everyone needs so stability short term, enhancement mid terms. |
There was a problem hiding this comment.
I think this is the correct way to go until we can do more in 4.1.0.
The problem here, not having the info, is worse than having too much info.
Before merging, the technical aspect needs to be finalized, and the other issue closed as not planned anymore.
|
@gnodet can you highlight the tech aspect you have in mind (happy to discuss on slack if it helps), latest report of claude review were not relevant from my point of view (or intended to be more exact) and the skipped line issue was fixed IIRC. About the other issue, do you reference #8027 ? Think we can keep open and see if the new listeners can fix it, no? |
That one seemed relevant, you disagree with the analysis ? |
|
@gnodet (on the phone) thought i fixed this one but yeah I agree |
I'll push a fix. |
You actually fixed it, you were right. LGTM |
slawekjaranowski
left a comment
There was a problem hiding this comment.
ok, in favor of new console modes ... I will not block it.
please be aware of similar change in 4.0.x and 3.10.x branches
some nit to consider or confirm that is intended:
- order: skipped, success, failure - is different that old
- with error log level - we have inconsistent indentation in the messages
|
@slawekjaranowski ordering is intended to help identify failed modules faster (not the error but which ones which is already a baby step forward), indentation is a side effect. I think the benefit > the lost this way but on my side no strong issue to revert the level if desired. |


Goal is to ensure the reactor summary stays complete so we see some modules were built and not just the one(s) failling.
Following this checklist to help us incorporate your
contribution quickly and easily:
Note that commits might be squashed by a maintainer on merge.
This may not always be possible but is a best-practice.
mvn verifyto make sure basic checks pass.A more thorough check will be performed on your pull request automatically.
If your pull request is about ~20 lines of code you don't need to sign an
Individual Contributor License Agreement if you are unsure
please ask on the developers list.
To make clear that you license your contribution under
the Apache License Version 2.0, January 2004
you have to acknowledge this by using the following check-box.