Conversation
bae1db9 to
5a5af1e
Compare
602fffb to
056dcf0
Compare
Add the --console CLI flag with four output modes: - plain: compact one-line-per-module output for CI - rich: JLine status bar with live reactor progress - verbose: full mojo-level output (current default) - machine: JSON lines for piping to external tools Part 3 of the #12572 split (depends on build report PR #12695). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
056dcf0 to
a1ee585
Compare
e64db31 to
de8044a
Compare
a1ee585 to
0f31ce2
Compare
Add the --console CLI flag with four output modes: - plain: compact one-line-per-module output for CI - rich: JLine status bar with live reactor progress - verbose: full mojo-level output (current default) - machine: JSON lines for piping to external tools Part 3 of the #12572 split (depends on build report PR #12695). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add the --console CLI flag with four output modes: - plain: compact one-line-per-module output for CI - rich: JLine status bar with live reactor progress - verbose: full mojo-level output (current default) - machine: JSON lines for piping to external tools Part 3 of the #12572 split (depends on build report PR #12695). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8083c89 to
170bd72
Compare
Add the --console CLI flag with four output modes: - plain: compact one-line-per-module output for CI - rich: JLine status bar with live reactor progress - verbose: full mojo-level output (current default) - machine: JSON lines for piping to external tools Part 3 of the #12572 split (depends on build report PR #12695). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
170bd72 to
af0cc72
Compare
Add the --console CLI flag with four output modes: - plain: compact one-line-per-module output for CI - rich: JLine status bar with live reactor progress - verbose: full mojo-level output (current default) - machine: JSON lines for piping to external tools Part 3 of the #12572 split (depends on build report PR #12695). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8baa65a to
02ac855
Compare
af0cc72 to
3658983
Compare
gnodet
left a comment
There was a problem hiding this comment.
Well-designed build report feature with clean API/impl separation and comprehensive tests. A few issues noted below.
Also noted:
- The architecture is solid: clean API interfaces in maven-api-core, record-based implementations in maven-core, EventSpy pattern for automatic discovery, thread-based log routing for parallel-build safety, atomic file writes with symlink swap.
- The PR correctly depends on PR #12694 (logging foundation) — should not be merged until #12694 lands.
- No test for the
captureLogEventrouting logic (mojo-level vs module-level vs build-level buffers). This is the core routing mechanism and warrants at least one test exercising the dispatch.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of gnodet
gnodet
left a comment
There was a problem hiding this comment.
Well-designed build report feature with clean API/impl separation, solid thread safety, and comprehensive tests. A few issues worth addressing:
Confirmed findings (verified independently):
-
[Medium]
BuildReportCollector.java— The Javadoc onMAX_LOG_EVENTS_PER_SCOPEstates "events are dropped and a truncation notice is appended," butcaptureLogEventsilently drops events without ever appending a truncation notice. Either implement the truncation notice (e.g., append a synthetic LogEvent like "... N events truncated") or correct the Javadoc to say events are silently dropped. -
[Low]
BuildReportJsonWriter.java— ThewriteNullableFieldmethod has an unusedboolean hasMoreparameter annotated with@SuppressWarnings("unused"). The parameter is never read and the method always emits a trailing comma regardless. Remove it to avoid confusion. -
[Low]
BuildReportJsonWriter.java—writeProblemusessb.lastIndexOf(",\n")to remove trailing commas (searches entire buffer backwards), whilewriteLogEventuses the dedicatedremoveTrailingCommahelper (checks only last two characters). UseremoveTrailingCommaconsistently — it's safer sincelastIndexOfcould theoretically match an earlier,\nif future refactors change field order.
This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
gnodet
left a comment
There was a problem hiding this comment.
Well-structured addition of a JSON build report feature with clean API design, good test coverage (17 tests), and defensive error handling. The zero-dependency JSON writer is appropriate for Maven's philosophy. A few design items:
Medium severity:
-
No opt-out mechanism (
BuildReportCollector.java): The collector is unconditionally active for every build — everymvninvocation writes a JSON file to disk with no system property to disable it. Consider adding-Dmaven.build.report.skip=truefor environments where this is undesirable (read-only filesystems, embedded invocations, CI runners). -
MojoSkipped events not handled (
BuildReportCollector.java):ExecutionEvent.Type.MojoSkipped(fired e.g. when a mojo requires online mode but Maven is offline) silently vanishes from the report. Inconsistent withProjectSkippedwhich IS handled. Skipped mojos should be tracked withBuildStatus.SKIPPED.
Low severity:
-
Failure timestamp inaccuracy (
BuildReportCollector.javaline 1110):failureTimestampis set toMonotonicClock.now()at report-assembly time, not at actual failure time. The mojo's timing data does capture the real timing — worth documenting in theFailureReport.timestamp()Javadoc. -
Inconsistent trailing comma removal (
BuildReportJsonWriter.java):writeProblemusessb.lastIndexOf(",\n")which searches backwards through the entire buffer, whilewriteLogEventuses the more robustremoveTrailingComma(sb)which checks only the end. Consider usingremoveTrailingCommaconsistently. -
Unused
hasMoreparameter (BuildReportJsonWriter.javaline 1553): Annotated@SuppressWarnings("unused")and never referenced. Either use it to control comma behavior or remove it. -
~70 lines of duplicated test helpers:
BuildReportCollectorTestandBuildReportIntegrationTestshare identicalcreateProject,createSession,createMojoExecution, andcreateEventmethods. Consider extracting to a shared test utility.
The API design (immutable interfaces in maven-api-core, record implementations in maven-core, @Experimental markers) follows Maven's established patterns. Thread safety approach is sound. The atomic file write + symlink pattern is well-implemented with proper fallbacks.
This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
3658983 to
c51ee87
Compare
02ac855 to
812a842
Compare
…enhancements - Add LogEvent interface (maven-api-core) with projectId/mojoId context fields - Add DefaultLogEvent record (maven-core) implementing LogEvent - Add MavenJulHandler (maven-logging): bridge JUL→SLF4J for plugin logging - Enhance DefaultLog (maven-core): carry LOG_API_METADATA for mojo log capture - Add ProjectBuildLogAppender (maven-core): MDC-aware log sink feeding LogEvent stream - Suppress JLine terminal-init DEBUG logs before activateLogging in quiet mode - Gate StackWalker behind hasReportCapture() for zero overhead in normal builds - Fix warn(Supplier<String>, Throwable) incorrectly calling logger.info() - Add @PARAM tags on trace(Supplier) overloads; fix sequenceNumber() @return javadoc - Fix LogEvent.message() @return javadoc copy-paste from formattedMessage() - Strengthen logApiMetadataIsClearedAfterCall() test to exercise the remove() path
1d9c90d to
abd26c7
Compare
869e497 to
1dae6bb
Compare
1dae6bb to
9be8b11
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of squash commit 1dae6bb2 — full build report feature (API + impl + collector + JSON writer + tests).
Resolved since prior reviews:
MAX_LOG_EVENTS_PER_SCOPEJavadoc now says "silently dropped" ✅writeNullableFieldhasMoreparameter removed ✅writeProblemusesremoveTrailingComma(sb)consistently ✅BuildEnvironment"not yet captured" section now only listsargs[]and implicit profiles ✅Session.buildEnvironment()Javadoc no longer has "same object" claim ✅MavenSimpleLoggerTest—LogSinktests added ✅
Two items remain.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of squash commit 9be8b118 — single formatting fixup: writeNullableField signature reflowed to one line.
Two prior findings remain unaddressed.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit cbc76239 — SessionStub.buildEnvironment() stub added.
The stub impl is consistent with the rest of SessionStub (e.g. getMavenVersion() also returns null despite @Nonnull). No new bugs introduced by this commit.
Two findings from the prior review remain unaddressed. See inline comments.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
cbc7623 to
f656ea9
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of squash commit f656ea9 — full build-report feature including BuildEnvironment API, BuildReportCollector, BuildReportJsonWriter, and wiring into DefaultSession, ApiRunner, SessionStub.
Resolved since prior reviews:
MAX_LOG_EVENTS_PER_SCOPEJavadoc fixed — now says "silently dropped" ✅hasMoreparameter removed fromwriteNullableField✅writeProblemnow usesremoveTrailingComma(sb)consistently ✅Session.buildEnvironment()Javadoc — "same object" claim removed, now says "immutable snapshot" ✅BuildEnvironment.java"not yet captured" section updated —batchModeandnoTransferProgressremoved from the list ✅
Two findings remain:
-
[Medium]
SessionStub.buildEnvironment()returnsnull, violating the@Nonnullcontract declared onSession.buildEnvironment(). Any test that callssession.buildEnvironment()through aSessionStubwill get an NPE when it dereferences the result. See inline comment. -
[Low]
ApiRunner.buildEnvironment()constructs a new anonymousBuildEnvironmentobject on every call with no caching. Since the values are all static defaults, this should be aprivate static finalconstant. See inline comment.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
(Note: ignore the preceding test review — posted in error.)
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of squash commit f656ea9567ce — full build report feature (API + impl + BuildReportCollector + BuildReportJsonWriter + MavenJulHandler + JUL quiet-mode fix + tests).
All prior findings resolved since ee2e6c44:
MAX_LOG_EVENTS_PER_SCOPEJavadoc now says "silently dropped" — truncation claim removed ✅- Dead
hasMoreparameter removed fromwriteNullableField✅ mojoSkipped()now callssetMojoId(null)✅- Stale
BuildEnvironmentJavadoc (batchMode/noTransferProgresslisted as "not yet captured") removed ✅ LogEvent.sequenceNumber()Javadoc corrected to "or-1if unavailable" ✅Session.buildEnvironment()no longer claims it returns "the same object" ✅writeProblemnow usesremoveTrailingCommaconsistently ✅
Two new issues.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 5a244435 — addresses the two findings from the prior f656ea9 review.
Resolved since prior review:
SessionStub.buildEnvironment()now returns a proper no-op implementation instead ofnull—@Nonnullcontract honored ✅ApiRunner.DefaultSession.buildEnvironment()extracted toprivate static final EMPTY_BUILD_ENVIRONMENT— no longer allocates a new anonymous object on every call ✅DefaultSession.buildEnvironment()uses full double-checked locking (volatilefield +synchronized(this)inner guard) — correct for a@ThreadSafeclass ✅
Minor nit (non-blocking): SessionStub’s anonymous BuildEnvironment uses fully-qualified java.util.List.of() / java.util.Map.of() even though the top-level class already imports java.util.List and java.util.Map (which inner anonymous classes inherit). ApiRunner’s EMPTY_BUILD_ENVIRONMENT correctly uses the unqualified form. Not worth a change request — just worth aligning if the class is touched again.
All prior findings resolved. The build-report feature (API, impl, collector, JSON writer, tests) is well-structured. Approved.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
Posted in error during testing — please disregard.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 1b48e9c1 — synchronize buildLogBuffer iteration in buildReport().
All prior findings resolved. This commit addresses the one remaining correctness gap:
buildLogBufferis aCollections.synchronizedList. Its Javadoc explicitly requires external synchronization during traversal.List.copyOf()iterates internally, so without asynchronized (buildLogBuffer)guard it was susceptible toConcurrentModificationExceptionon concurrent log appends duringonSessionEnded(). Fixed correctly — same pattern asmoduleLogBufferat line 519. ✅
Notes (non-blocking, pre-existing):
- The
size() < MAX_LOG_EVENTS_PER_SCOPEcheck-then-add()incaptureLogEvent(line ~306) is still an unsynchronized TOCTOU onbuildLogBuffer. For asynchronizedList, individualsize()andadd()calls are atomic but the compound check-then-act can allow slightly more thanMAX_LOG_EVENTS_PER_SCOPEevents under heavy concurrency. The cap is a soft limit, so the impact is bounded and benign — not worth a change request. Same pattern exists formojoLogBuffersandmoduleLogBuffers. mojoLogBuffers.remove(mKey)→List.copyOf(logBuffer)correctly needs no synchronization: once removed from the map,logBufferis a private reference with no concurrent writers.
The fix is correct, minimal, and well-explained in the commit message.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commits 5a244435 + 1b48e9c1 — all four findings from the previous review are resolved. One new low-severity finding.
Resolved since last review:
buildLogBufferiteration inbuildReport()is now synchronized ✅DefaultSession.buildEnvironment()now uses double-checked locking withvolatile— caches correctly ✅ApiRunner.DefaultSessionanonymousBuildEnvironmentextracted to static constantEMPTY_BUILD_ENVIRONMENT✅SessionStub.buildEnvironment()no longer returnsnull✅
One new finding (inline below).
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 32901219 — MAX_THROWABLE_DEPTH depth guard in MavenSimpleLogger + SessionStub.buildEnvironment() static constant.
Resolved since prior review:
SessionStub.buildEnvironment()now returnsEMPTY_BUILD_ENVIRONMENTstatic constant instead of per-call anonymous class ✅
One new low-severity finding (inline below).
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 3abb55be — three fix commits + one test commit since prior review (ee2e6c44).
All previous findings resolved ✅
Every finding raised in prior reviews has been addressed:
- ✅ Truncation Javadoc: now says "silently dropped to prevent unbounded memory growth"
- ✅ Dead
hasMoreparameter removed;writeNullableFieldsimplified to 4-arg signature - ✅
writeProblemnow usesremoveTrailingComma()consistently - ✅
mojoSkipped()now callssetMojoId(null)— mojo ID no longer leaked on skip - ✅
Session.buildEnvironment()Javadoc: "same object" claim removed - ✅
DefaultSession.buildEnvironment()cached via DCL +volatilefield - ✅
Log.javatrace methods remaindefault— binary compat preserved - ✅
hasReportCapture()guard restored inDefaultLog.withMetadata() - ✅
BuildEnvironmentstale Javadoc removed (batchMode/noTransferProgress) - ✅
MavenSimpleLoggerstack overflow guarded (MAX_THROWABLE_DEPTH=20with truncation notice) - ✅
LogEvent.sequenceNumber()Javadoc corrected to"or -1 if unavailable" - ✅
MavenJulHandlerTestrestored withsetInPublishForTesthook
Two low-severity issues in the new commits:
buildLogBufferTOCTOU (low) — see inlineisLoggablefilter path untested (low) — see inline
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of new commits since ee2e6c44 — BuildReportCollector TOCTOU fix, DefaultSession.buildEnvironment() double-checked locking, MavenSimpleLogger depth guard, SessionStub stub, and test additions.
Resolved since prior reviews:
Log.javatrace methods restored todefault— binary-compat issue fixed ✅LoggingExecutionListener.mojoSkipped()now callssetMojoId(null)✅LogEvent.sequenceNumber()Javadoc correctly saysor -1 if unavailable✅Session.javaJavadoc no longer claims "same object" ✅DefaultSession.buildEnvironment()caches with double-checked locking (volatile field + synchronized block) ✅BuildReportCollector.MAX_LOG_EVENTS_PER_SCOPEJavadoc now says "silently dropped" ✅BuildReportJsonWriter.writeNullableFielddeadhasMoreparameter removed ✅writeProblemnow usesremoveTrailingCommaconsistently ✅MavenSimpleLogger.writeThrowabledepth guard (MAX_THROWABLE_DEPTH=20) ✅FORKING_MOJO_IDwired inforkStarted/forkSucceeded/forkFailed✅buildLogBufferTOCTOU fixed with externalsynchronizedblock ✅buildLogBufferiteration inbuildReport()correctly synchronized ✅SessionStub.buildEnvironment()returns a non-null constant ✅MavenJulHandlerTestnow tests the filter path ✅
Two remaining items.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
test - ignore
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
test inline
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 6d646283 — seven fix commits addressing prior review findings.
Resolved since prior reviews (ee2e6c4 and earlier):
- Dead
hasMoreparameter removed fromwriteNullableField✅ MAX_LOG_EVENTS_PER_SCOPEJavadoc updated: now correctly says "silently dropped" ✅Session.buildEnvironment()Javadoc: "same object" claim removed, says "immutable snapshot" ✅DefaultSession.buildEnvironment(): DCL caching withvolatilefield added ✅BuildEnvironment.java:batchMode/noTransferProgressremoved from "What is not yet captured" ✅LogEvent.sequenceNumber()Javadoc: fixed to "or -1 if unavailable" ✅buildLogBuffersynchronization:Collections.synchronizedList+ explicitsynchronizedblock ✅MavenJulHandler: null logger-name guard added ✅
Still unresolved (1 finding): see inline comment.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 11205e68 — MojoSkipped handling added to BuildReportCollector.
All prior findings resolved:
MAX_LOG_EVENTS_PER_SCOPEJavadoc corrected to "silently dropped" ✅- Dead
hasMoreparameter removed fromwriteNullableField✅ BuildEnvironmentJavadoc updated:batchMode/noTransferProgressremoved from "not yet captured" ✅Session.buildEnvironment()Javadoc no longer claims "same object" ✅writeProblemswitched toremoveTrailingComma✅DefaultSession.buildEnvironment()now caches via DCL/volatile ✅mojoSkipped()inLoggingExecutionListenernow callssetMojoId(null)✅MojoSkippedevents now recorded inBuildReportCollectorviaonMojoSkipped✅ (this commit)
Review of this commit:
The onMojoSkipped implementation is correct. A skipped mojo has no preceding MojoStarted event, so:
- No
currentMojoByThreadentry to remove (correct — nothing was registered) - No log buffer to drain (correct — no
MojoStartedmeans no buffer was initialized) nowas start timestamp andDuration.ZEROis accurate and honestcomputeIfAbsenton the synchronized list is consistent withonMojoFinished
The test covers the critical path: MojoSkipped fires without a preceding MojoStarted, status is SKIPPED, duration is ZERO, output is empty. Static analysis (ast-grep, semgrep) clean.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review of commit 11205e680 — MojoSkipped handling in BuildReportCollector.
Resolved since prior review (6d646283):
MojoSkippedevents now routed to a newonMojoSkipped()handler — skipped mojos recorded withBuildStatus.SKIPPED,Duration.ZERO, and empty output ✅- Consistent with
ProjectSkippedhandling (both route to a "finished" handler) ✅ testMojoSkippedIsRecorded()regression guard added inBuildReportCollectorTest: firesMojoSkippedwithout a priorMojoStartedand asserts the report contains one entry with goal"test", statusSKIPPED, durationZERO, and empty output ✅
All prior findings resolved. The build report feature is complete and correct: clean API/impl separation, EventSpy-based collector with thread-safe log routing (ConcurrentHashMap + synchronizedList with proper external synchronization), atomic JSON file writes, and comprehensive coverage of all mojo lifecycle events (started, succeeded, failed, skipped).
This review was generated by an AI agent, Hermès on behalf of @gnodet.
- Add BuildReport, ModuleReport, MojoReport, FailureReport, BuildStatus API types (maven-api-core) - Add BuildEnvironment API type and Session.buildEnvironment() accessor - Implement BuildReportCollector in maven-core: captures per-mojo log output, timing, failures - Implement BuildReportJsonWriter: writes structured JSON report to file at build end - Integrate into MavenInvoker and DefaultMavenExecutionRequest - Extend MavenSimpleLogger and MavenJulHandler with LogSink support for log capture - Add SessionStub.buildEnvironment() stub for maven-testing - Add BuildReportCollectorTest, BuildReportJsonWriterTest, BuildReportIntegrationTest - Handle MojoSkipped events in BuildReportCollector - Fix ConcurrentModificationException in buildReport() log buffer iteration
11205e6 to
92a187f
Compare
Summary
Part 2 of the logging feature chain (depends on #12694 — logging foundation).
Adds a structured build report that captures per-module and per-mojo execution results, timing, log events, and failures as a JSON file at the end of every build. Also adds
BuildEnvironmentto the Maven API, exposed viaSession.buildEnvironment()and frozen inBuildReport.environment().What's in this PR
BuildReport,BuildStatus,ModuleReport,MojoReport,FailureReportBuildEnvironmento.a.m.apicapturing the invocation contextSession.buildEnvironment()DefaultBuildReport,DefaultModuleReport,DefaultMojoReport,DefaultFailureReport,DefaultBuildEnvironmentBuildReportCollectorEventSpythat tracks lifecycle events and captures log output viaLogEventSink, routing to mojo/module/build-level buffersBuildReportJsonWriterKey design decisions
BuildReportCollectoris a@Named @Singletonthat extendsAbstractEventSpy, discovered automatically — no wiring changes neededConcurrentHashMap<Long, String>(thread ID → mojo/project key) to associate log events with the correct scope in parallel buildsLogEventSink(4-arg) independently from the existingLogSink(5-arg) used byProjectBuildLogAppender— no interference with console outputbuild-report-latest.jsonsymlinkonSessionEndedwraps report generation in try-catch so report failures never crash the buildBuildEnvironment
BuildEnvironment(ino.a.m.api) captures the full invocation context atSessionStarted:password,token,secret,passphrase,apikey), curated system info (OS, JVM, Maven home, available processors)-Ponly), selected projects (-pl), resume-from (-rf)Session.buildEnvironment()gives plugins live access to the same data.BuildReport.environment()carries a frozen snapshot in the JSON output.Also fixes a gap in
MavenExecutionRequest:noTransferProgresswas consumed byMavenInvokerto pick aTransferListenerbut never stored on the request. AddedisNoTransferProgress()/setNoTransferProgress().What's NOT in this PR (deferred to later PRs)
--warning-modeCLI flag — Warning mode, diagnostic collector, BuilderProblem enrichments #12698--console=plain/rich/machine) — Console modes: --console=plain/rich/verbose/machine #13180mvnlogviewer tool — mvnlog: build log viewer, integration tests, script routing #12699args[]),--also-make/--also-make-dependentsinBuildEnvironment— pending further API workPR chain
mvnlogviewerTest plan
BuildEnvironment