gnodet opened a new pull request, #13249: URL: https://github.com/apache/maven/pull/13249
## Overview This PR introduces four inter-related features for Maven 4.1.0, developed together because they share the same extension points and data model: 1. **`reactor.xml`** — a single `.mvn/reactor.xml` file that consolidates CLI defaults (replaces `.mvn/maven.config`), core extensions (replaces `.mvn/extensions.xml`), named build aliases, and lifecycle phase injections. 2. **Named build aliases** — a short name on the command line that expands to a full argument list, defined in `reactor.xml`. 3. **Lifecycle execution filter** — a `maven.lifecycle.filter` user property that skips mojo executions matching a coordinate or phase predicate. 4. **Structured build report** — a JSON file written to `target/build-reports/` at the end of each build, capturing timing, status, per-mojo log output, and environment info; accessible via `Session.buildEnvironment()` and a new `BuildReport` API. --- ## Feature 1: `reactor.xml` **Location:** `.mvn/reactor.xml` (project root directory) **Schema:** `api/maven-api-core/src/main/mdo/reactor.mdo` — generates StAX reader/writer via Modello. **Format:** ```xml <reactor xmlns="http://maven.apache.org/REACTOR/1.0.0"> <!-- Replaces .mvn/maven.config --> <options>-T4 --no-transfer-progress</options> <!-- Replaces .mvn/extensions.xml --> <extensions> <extension groupId="io.takari.maven" artifactId="takari-smart-builder" version="0.6.1"/> </extensions> <!-- Named build aliases (see Feature 2) --> <aliases> <alias name="ci"> <content>verify -Dmaven.test.skip=true</content> </alias> <alias name="release"> <args><arg>-Prelease</arg><arg>deploy</arg></args> </alias> </aliases> <!-- Custom lifecycle phase injections --> <phases> <phase name="pre-integration" after="test" before="integration-test"/> </phases> </reactor> ``` **Key design decisions:** - `reactor.xml` replaces both `maven.config` and `extensions.xml` when present. Legacy files are still read when `reactor.xml` is absent, preserving full backwards compatibility. - `<alias name="...">` uses an XML attribute (not a child element) because aliases are short and the attribute form is more readable for what is expected to be a common file. - `<options>` is a whitespace/quote-aware inline string (same tokenization as shell); `<optionArgs>` provides a structured alternative when values contain spaces. - Parsing happens in `BaseParser` before any DI container is started, so reactor.xml config (including extensions) is available at the earliest possible point. --- ## Feature 2: Named Build Aliases **Parsing:** `MavenParser.expandAlias()` — pure string manipulation before any Maven DI or session setup. **Semantics:** - Every argument in the args list is checked against the alias map in order; any match is replaced by the alias expansion tokens. - Expansion is **not recursive** — tokens produced by an alias are passed through as-is. - Aliases can be flag-style (e.g. `--skip-tests`) to provide shorthand for long `-D` expressions. - An empty alias expansion (no `<content>` and no `<args>`) is valid — the alias token is simply removed. **Validation:** - Blank alias names are rejected at expansion time with `IllegalArgumentException`. - Unclosed quotes in `<content>` strings throw `IllegalArgumentException` (via `ArgumentTokenizer`). **Example:** ``` # reactor.xml defines alias "ci" → "verify -Dmaven.test.skip=true" mvn ci -B # is equivalent to: mvn verify -Dmaven.test.skip=true -B ``` **Tests:** `MavenParserAliasTest`, `ArgumentTokenizerTest`, IT `mng-12537-reactor-xml-alias`. --- ## Feature 3: Lifecycle Execution Filter **Activation:** `-Dmaven.lifecycle.filter=<expression>` (property name: `Constants.MAVEN_LIFECYCLE_FILTER`) **Expression syntax** (comma-separated, OR-ed predicates): | Token | Meaning | |---|---| | `*` | Skip every mojo execution | | `:A` | Skip by artifactId (e.g. `:maven-enforcer-plugin`) | | `G:A` | Skip by groupId:artifactId | | `P` | Skip by goal prefix (e.g. `enforcer`) | | `P:v:g` | Skip by prefix + version + goal | | `P:v:g@e` | Skip by prefix + version + goal + executionId | | `phase(name)` | Skip all mojos bound to the named phase | **Implementation:** `MojoExecutionFilter` (parser) + `CoordinatePredicate` / `PhasePredicate` (matchers). Applied in `MojoExecutor` before each mojo execution; skipped mojos fire a `MojoSkipped` execution event. A `DefaultMojoExecution` with a `null` session is created for predicate evaluation — only string fields (goal, groupId, artifactId, prefix, executionId, phase) are accessed; no session-bound fields are touched. **Example:** ``` # Skip all test-phase mojos and the enforcer plugin mvn verify -Dmaven.lifecycle.filter=phase(test),:maven-enforcer-plugin ``` **Tests:** `CoordinatePredicateTest`, `MojoExecutionFilterTest`, `PhasePredicateTest`, IT `mng-12538-lifecycle-filter`. --- ## Feature 4: Structured Build Report **Output location:** `<topDirectory>/target/build-reports/` - `build-report-<timestamp>Z.json` — timestamped per-build file - `build-report-latest.json` — symlink (or copy on Windows) always pointing to the most recent report **Content:** ```jsonc { "formatVersion": 1, "status": "SUCCESS", // SUCCESS | FAILURE | ERROR | SKIPPED "startTime": "2025-07-29T14:30:00Z", "duration": "PT1M23.456S", "mavenVersion": "4.1.0-SNAPSHOT", "javaVersion": "25.0.1", "goals": ["verify"], "multiModule": true, "threads": 4, "environment": { "os": "Linux 6.1.0", "arch": "amd64", "userProperties": { "-Dmaven.test.skip": "true" }, "systemProperties": { "java.version": "25.0.1", ... } }, "modules": [ { "groupId": "org.apache.maven", "artifactId": "maven-core", "status": "SUCCESS", "duration": "PT12.3S", "mojos": [ { "groupId": "org.apache.maven.plugins", "artifactId": "maven-compiler-plugin", "goal": "compile", "executionId": "default-compile", "phase": "compile", "status": "SUCCESS", "duration": "PT2.1S", "output": [ { "level": "INFO", "timestamp": "...", "message": "Compiling 42 source files" } ] } ] } ], "failures": [...], "output": [...] // build-level log lines not attributed to a module } ``` **Implementation:** - `BuildReportCollector` — `@Singleton` `ExecutionListener` that captures timings and routes log events via `ProjectBuildLogAppender`. Thread-safe: uses `ConcurrentHashMap` + per-list `synchronized` blocks (properly guarded — no synchronization on the shared `Collections.emptyList()` singleton). - `BuildReportJsonWriter` — hand-written JSON serializer (no external JSON dependency; produces UTF-8 output compatible with `jq`). - `DefaultBuildEnvironment` — captures OS, JVM, Maven version, and user/system properties (sensitive keys redacted to `***`). **API access:** `Session.buildEnvironment()` returns a `BuildEnvironment` (lazy, volatile, thread-safe). **New API types** (`api/maven-api-core`, package `org.apache.maven.api.build.report`): - `BuildReport` — top-level report - `ModuleReport` — per-module timing and mojo list - `MojoReport` — per-mojo timing and captured log output - `LogEvent` — a single captured log line (level + timestamp + message + optional throwable) - `FailureReport` — structured failure info (module, mojo, message, stack trace excerpt) - `BuildStatus` — enum: `SUCCESS`, `FAILURE`, `ERROR`, `SKIPPED` - `BuildEnvironment` — OS/JVM/Maven/property snapshot All API types are immutable records with noun-style accessors (`groupId()`, `status()`, `duration()`) and full Javadoc. Lists are defensively copied in compact constructors. **Tests:** `BuildReportCollectorTest`, `BuildReportIntegrationTest`. --- ## What Was Not Changed - `.mvn/maven.config` and `.mvn/extensions.xml` continue to be read when `reactor.xml` is absent — no migration required. - No existing behaviour changes when these features are not activated. - The `maven.lifecycle.filter` property is not set by default — zero overhead when unused. - Build reports are written on a best-effort basis (failure to write logs a `WARN`, build is not aborted). --- ## Checklist - [x] Unit tests for all new classes (`ArgumentTokenizerTest`, `MavenParserAliasTest`, `CoordinatePredicateTest`, `MojoExecutionFilterTest`, `PhasePredicateTest`, `BuildReportCollectorTest`, `BuildReportIntegrationTest`) - [x] Integration tests (`mng-12537-reactor-xml-alias`, `mng-12538-lifecycle-filter`) - [x] `spotless:apply` applied - [x] No new compiler warnings - [x] API types follow Maven 4 immutable-type conventions (noun accessors, compact constructors, `List.copyOf()` at construction time) - [x] Concurrency: `BuildReportCollector` reviewed — `ConcurrentHashMap` + per-list `synchronized`, no spurious synchronization on shared singletons -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
