slachiewicz opened a new issue, #49:
URL: https://github.com/apache/maven-executor/issues/49

   ### New feature, improvement proposal
   
   Grounded in six integration-test suites migrated off maven-verifier on 
2026-09-21 (apache/maven-ear-plugin#556, 
apache/maven-remote-resources-plugin#303, apache/maven-apache-resources#131, 
apache/maven-surefire#3484 and #3485, apache/maven-build-cache-extension#536, 
apache/maven-integration-testing#445 and #446; about 1,500 test files, about 
2,100 lines of helper code written six times over), the executor issues those 
migrations produced (#44, #45, #46, #47), the guide corrections in 
apache/maven-verifier#217, and a source survey of the five runtime consumers of 
maven-invoker (deprecated by apache/maven-invoker#164, last release 3.3.0). 
Line references are to tag `executor-1.0.0`.
   
   ## P1 — blocks migrations
   
   1. **Embedded Maven 4 must not hang, and hangs must fail (#47).** All three 
`Mode.AUTO` consumers hung on every 4.0.0-rc-6 cell and were cancelled at the 
CI timeout; all now force `FORKED`. Start with what 
`ToolboxExecutorTool.mojo()` already does for 4.x (`--raw-streams`, line 129) 
plus a closed stdin, and honour `executionTimeout` in embedded mode: 
`EmbeddedMavenExecutor.execute()` never reads it, only 
`ProcessBuilderExecutorSupport` does (lines 79-95). `Mode.AUTO` should refuse 
embedded when it cannot work rather than select it; maven-verifier's silent 
fork-on-failure is what kept these suites green for two years.
   2. **Local-repository and artifact paths without a plugin (#44).** The only 
`ExecutorTool` runs `eu.maveniverse.maven.plugins:toolbox` through the Maven 
under test; the core IT suite alone has about 440 path call sites 
(`deleteArtifacts` 302, `verifyArtifactPresent` 84, `getArtifactPath` 29, 
metadata 28). Ship a default tool that resolves the repository 
(`-Dmaven.repo.local` argument, then the system property, then settings, then 
`~/.m2/repository`) and computes default-layout artifact and metadata paths. 
Keep the toolbox tool for `dump()` and opt-in use.
   
   ## P2 — core correctness, each hit by more than one port
   
   3. **Stream ownership (#45).** Pump threads close caller streams 
(`ProcessBuilderExecutorSupport.java:124,137`); embedded does not; one sink for 
both streams races two closes. Flush, never close, and state that `stdOut()` 
and `stdErr()` may be the same object.
   4. **Lifetime (#46).** The constructor-time `System` snapshot forces a 
JVM-lifetime executor; a per-execution executor exhausts the heap after about 
100 builds (apache/maven-surefire#3484, run 35594937350) even though `close()` 
does dispose realms (`EmbeddedMavenExecutor.java:189-193`). Snapshot in 
`execute()`, and make `close()` reclaim enough to allow per-test executors.
   5. **`toBuilder()` throws on `argument()` (#16 item 1).** `Impl.arguments()` 
is unmodifiable (`ExecutorRequest.java:394-396`), `Builder(List)` stores it 
(`:221`), `argument()` calls `add` (`:239`). This blocks the 
one-template-request-per-test-class pattern.
   6. **`MAVEN_ARGS` leaks into the forked child (#16 item 2).** `env.remove` 
acts on the local map (`ForkedMavenExecutor.java:143`) while `pb.environment()` 
inherits the parent's; a CI runner that sets it changes every IT's arguments.
   7. **`Builder.logFile(Path)`** tee-ing both streams in arrival order, 
compatible with `grabOutputAsString`. Six ports wrote this; build-cache reads 
`log.txt` from 13 files, surefire's `OutputValidator` and the core ITs' 
`loadLogLines` read it too.
   8. **`userProperty(k, v)`** appending `-Dk=v`, with Javadoc on 
`jvmSystemProperty` saying it lands in `MAVEN_OPTS` 
(`ForkedMavenExecutor.java:125-141`). Three ports and the guide's first version 
mapped `setSystemProperty` to the wrong one.
   
   ## P3 — test-support module, so the seventh port writes nothing
   
   9. **`maven-executor-verifier`**: Java 8, depends only on core. Autoclean 
and `-e -B` defaults, `verifyErrorFreeLog` (`[ERROR]` scan minus Velocity 
noise), `verifyTextInLog`/`NotInLog`, `verifyFile*`, 
`verifyArtifact*`/`deleteArtifacts` on item 2, 
`loadLines`/`loadProperties`/`filterFile`/`newDefaultFilterMap`, 
`ResourceExtractor`, a per-execution log file. Seed it from 
apache/maven-integration-testing#445 (Java 8, toolbox-free), not from 
apache/maven master's helper (Java 17, toolbox-bound).
   10. **JUnit 5 extension (#20)** shaped like build-cache's 
`IntegrationTestExtension`: per-test project copy, injected Verifier, log in 
the test directory. That shape moved 130 test files with zero edits.
   11. **Installation discovery** behind an explicit factory, never silent. 
`discoverInstallationDirectory()` throws without `maven.home`, and three of 
four plugin ports had to forward it to Failsafe through 
`systemPropertyVariables`.
   
   ## P4 — runtime replacement for maven-invoker
   
   Five consumers embed maven-invoker at compile scope to launch Maven from 
inside a running Maven: maven-invoker-plugin (`AbstractInvokerMojo`), 
maven-archetype (`IntegrationTestMojo`, `CreateProjectFromArchetypeMojo`, 
`FilesetArchetypeCreator`), maven-release (`InvokerMavenExecutor`), 
maven-javadoc-plugin (`JavadocUtil`); maven-gpg-plugin uses it at test scope 
(`InvokerTestUtils`). The union of what they call, verified per file:
   
   | Invoker API | maven-executor 1.0.0 | Status |
   |---|---|---|
   | `setBaseDirectory` / `setPomFile` | `cwd(Path)` / `-f` argument | direct |
   | `setGoals`, `addArg(s)`, `setProperties` | `arguments(...)`, `-Dk=v` | 
composable; item 8 |
   | `setBatchMode`, `setDebug`, `setOffline`, `setShowErrors`, 
`setShowVersion`, `setUserSettingsFile`, `setGlobalSettingsFile`, 
`setToolchainsFile`, `setGlobalToolchainsFile` | `-B -X -o -e -V -s -gs -t -gt` 
arguments | composable; a typed builder would stop nine ports re-deriving the 
flags |
   | `setLocalRepositoryDirectory` (all five) | `-Dmaven.repo.local=` | 
composable |
   | `setMavenHome` (invoker-plugin, release, javadoc, gpg) | 
`ForkedMavenExecutor(Path)` | direct |
   | `setJavaHome` (invoker-plugin, release, javadoc); `setMavenOpts` (javadoc) 
| `environmentVariable("JAVA_HOME"/"MAVEN_OPTS", ...)`, as invoker does 
(`MavenCommandLineBuilder.java:191,195`) | composable |
   | `addShellEnvironment` (release, gpg) | `environmentVariable` | direct |
   | `setInputStream(System.in)` (release interactive prompts, gpg) | 
`stdIn(InputStream)` | direct in forked; #45 must not close `System.in` |
   | `setOutputHandler` / `setErrorHandler` (all five, line-oriented) | raw 
`OutputStream` only | **missing**: a line consumer (`Consumer<String>`) so mojo 
logs stay line-buffered; today each port writes a splitting stream |
   | `setTimeoutInSeconds` (gpg) | `executionTimeout(Duration)` | direct, 
forked only |
   | `setMavenExecutable` (`mvnw` relative to basedir, 
`AbstractInvokerMojo.java:514`; resolution in 
`MavenCommandLineBuilder.java:513-527`) | none: forked always runs 
`<installation>/bin/mvn` (`ForkedMavenExecutor.java:104-106`) | **missing**: 
`forExecutable(Path)`; the invoker plugin's `invoker.mavenExecutable` and 
wrapper-based ITs have no path otherwise |
   | `Invoker.setLogger` | none | missing, low value; a `Consumer<String>` 
debug hook suffices |
   | `getExitCode()` / `getExecutionException()` (launch failure vs build 
failure; release `:121-129`, invoker-plugin `:2176-2184`) | `exitCode()` / 
unchecked `ExecutorException` | equivalent; document that `ExecutorException` 
is the launch-failure channel |
   
   Two runtime facts set the constraints. The outer `mvn` script passes 
`-Dmaven.home` (`bin/mvn:323`), so the running installation is discoverable 
without configuration. Embedded mode inside a running Maven is not viable: 
`EmbeddedMavenExecutor` replaces `System` properties, `System.out` and the 
context classloader for the duration (`:149-162, 237-239, 289, 330`), which is 
a second Maven mutating the first's JVM state. The runtime story is therefore 
forked only; say so in Javadoc and never let `Mode.AUTO` pick embedded when 
`maven.home` equals the current process's.
   
   Runtime priorities: (a) `forExecutable(Path)` and `mvnw` support, without 
which maven-invoker-plugin cannot migrate; (b) line-oriented output consumers; 
(c) a `stdIn` that is not closed (#45); (d) a typed `MavenRequest.Builder` with 
the flag conveniences from the table; (e) confirmed `executionTimeout` and 
`destroyForcibly` behaviour, for a plugin that must not orphan a child on 
Ctrl-C.
   
   ## Where things live
   
   Runtime conveniences (a)-(e) belong in core: they are executor concerns, 
dependency-free, and the plugins cannot take a test module at compile scope. 
The Verifier-shaped helpers (P3) belong in a separate module in this 
repository, not in a maven-verifier 3.0: apache/maven-verifier#186 exists to 
close that repository; the README already plans a 2.x line "with extra features 
on top of the executors"; the helpers version with `ExecutorHelper` and item 2; 
and apache/maven's own ITs would consume it.
   
   Not recommended: helpers in the core artifact; the toolbox tool as the 
default path provider in an ASF test artifact; a `verifier.forkMode`-style 
system property in place of the explicit `Mode` API.
   
   ## Sequencing
   
   - 1.0.1: items 1, 3, 5, 6; runtime (c).
   - 1.1: items 2, 4, 7, 8; runtime (a), (b), (d).
   - 2.x: items 9, 10, 11.
   
   Items 2 and 9 together let the core ITs and build-cache delete their in-repo 
Verifier copies. Runtime (a) and (b) let maven-invoker-plugin drop 
maven-invoker, which is the migration that retires that repository.
   
   Happy to split this into one issue per item once the shape is agreed.
   


-- 
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]

Reply via email to