Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,9 @@ private void executeStep(BuildStep step) throws IOException, LifecycleExecutionE
* them can only be seen by looking at the failures themselves.
*
* @param failures The failures collected for a single project
* @return {@code true} if the build must be halted
* @return {@code true} if the build must be halted; checked exceptions (ordinary plugin failures) are
* soft and allow the reactor to continue with other projects, while {@link RuntimeException}s
* and {@link Error}s indicate an unexpected JVM or framework state and halt the build
*/
private static boolean isFatal(List<Throwable> failures) {
return failures.stream().anyMatch(t -> t instanceof RuntimeException || !(t instanceof Exception));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,34 @@ void exceptionThrownByBuildStepIsRecordedAsBuildFailure() throws Exception {
assertTrue(session.getResult().getBuildSummary(project) instanceof org.apache.maven.execution.BuildFailure);
}

/**
* A checked exception thrown by a build step is a soft failure: the reactor must not be halted, and
* other projects must still be built when {@code REACTOR_FAIL_AT_END} is in effect. This pins the
* soft-failure path of {@code isFatal} so a future change does not silently break {@code --fail-at-end}
* for ordinary plugin failures.
*/
@Test
void checkedExceptionThrownByBuildStepDoesNotHaltReactor() throws Exception {
MavenProject project = newProject();
MavenSession session = newSession(project);
session.getRequest().setReactorFailureBehavior(MavenExecutionRequest.REACTOR_FAIL_AT_END);

ReactorContext reactorContext = execute(session, project, event -> {}, plan -> {
BuildStep step = plan.step(project, "validate")
.orElseThrow(() -> new IllegalStateException("no validate step in the plan"));
// LifecycleExecutionException is a checked exception — the soft-failure case for isFatal.
step.exception = new LifecycleExecutionException("plugin failure");
step.status.set(BuildStep.FAILED);
});

assertTrue(
session.getResult().getBuildSummary(project) instanceof org.apache.maven.execution.BuildFailure,
"the project must be recorded as a failure");
assertTrue(
!reactorContext.getReactorBuildStatus().isHalted(),
"a checked exception must not halt the reactor when REACTOR_FAIL_AT_END is set");
}

/**
* A project can end up with more than one failure: when a build step fails, the matching after:* step is
* still run for cleanup and may fail on its own. Those failures are reported through a wrapper exception,
Expand Down
Loading