From 4b9753ce47824b80e8c68cebd70c32896bab5f9f Mon Sep 17 00:00:00 2001 From: Federico Mariani Date: Wed, 14 Oct 2026 12:48:36 +0200 Subject: [PATCH 1/4] camel-launcher: skip sources jars in launch scripts Backport of 2f87c1e63da07c538d01e4066e8de95973d3767f to camel-4.22.x. --- .../src/main/resources/bin/camel.bat | 7 +++++-- .../src/main/resources/bin/camel.sh | 3 ++- .../jbang/launcher/CamelBatDiscoveryTest.java | 17 +++++++++++++++++ .../jbang/launcher/CamelShDiscoveryTest.java | 18 ++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat index 692dbea4de267..fb0fe19e1f886 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat +++ b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat @@ -49,9 +49,12 @@ set "JAVACMD=%CAMEL_JAVACMD%" @REM Set JVM options if specified if "%JAVA_OPTS%" == "" set JAVA_OPTS=-Xmx512m -@REM Find the launcher JAR +@REM Find the launcher JAR, excluding sources set "LAUNCHER_JAR=" -for %%i in ("%BASEDIR%\camel-launcher-*.jar") do set "LAUNCHER_JAR=%%i" +for %%i in ("%BASEDIR%\camel-launcher-*.jar") do ( + echo %%~nxi| findstr /i /e /l /c:"-sources.jar" > nul + if errorlevel 1 if not defined LAUNCHER_JAR set "LAUNCHER_JAR=%%i" +) @REM Execute Camel CLI, preserving arguments and the child exit code for %%e in ("%JAVACMD%") do set "_JAVACMD_EXT=%%~xe" diff --git a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh index c5f1fb6fd0c79..bc4a05426aa74 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh +++ b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh @@ -100,9 +100,10 @@ then REPO="$BASEDIR" fi -# Find the launcher JAR +# Find the launcher JAR, excluding sources LAUNCHER_JAR= for f in "$BASEDIR"/camel-launcher-*.jar; do + case "$f" in *-sources.jar) continue ;; esac if [ -f "$f" ]; then LAUNCHER_JAR="$f" break diff --git a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java index 4243115762756..761ee228e14c8 100644 --- a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java +++ b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -76,6 +77,22 @@ private Path currentJavaHome() { return javaHome; } + @Test + void skipsSourcesJars(@TempDir Path base) throws Exception { + Path h = home(Files.createDirectories(base.resolve("launcher with spaces"))); + Files.writeString(h.resolve("camel-launcher-9.9.9-sources.jar"), "sources"); + Files.writeString(h.resolve("camel-launcher-zzz-sources.jar"), "sources"); + Path java = FakeJava.writeFakeJava(base, "java", JAVA17, 0, "JAVACMD"); + Map env = isolatedEnvironment(); + env.put("JAVACMD", java.toString()); + + FakeJava.Result r = FakeJava.run(h.resolve("camel.bat"), env, "hawtio"); + + assertThat(r.exitCode()).as(r.stderr()).isZero(); + assertThat(r.stdout()).contains(h.resolve("camel-launcher-9.9.9.jar").toString()) + .doesNotContain("-sources.jar"); + } + @Test void acceptsJava17ViaJavacmd(@TempDir Path base) throws Exception { Path h = home(base); diff --git a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java index ad2735ed362ce..93d0b17b8443f 100644 --- a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java +++ b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java @@ -27,6 +27,7 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -56,6 +57,23 @@ private String pathWithNonqualifyingJava(Path base) throws Exception { return pathDir + ":/usr/bin:/bin"; } + @Test + void skipsSourcesJars(@TempDir Path base) throws Exception { + Path h = home(Files.createDirectories(base.resolve("launcher with spaces"))); + Files.writeString(h.resolve("camel-launcher-9.9.9-sources.jar"), "sources"); + Files.writeString(h.resolve("camel-launcher-zzz-sources.jar"), "sources"); + Path java = FakeJava.writeFakeJava(base, "java", JAVA17, 0, "JAVACMD"); + Map env = new HashMap<>(); + env.put("LC_ALL", "C"); + env.put("JAVACMD", java.toString()); + + FakeJava.Result r = FakeJava.run(h.resolve("camel.sh"), env, "hawtio"); + + assertThat(r.exitCode()).as(r.stderr()).isZero(); + assertThat(r.stdout()).contains(h.resolve("camel-launcher-9.9.9.jar").toString()) + .doesNotContain("-sources.jar"); + } + @Test void acceptsJava17ViaJavacmd(@TempDir Path base) throws Exception { Path h = home(base); From 8f3ae115cb66837210e362ffcab38f73a659fdd7 Mon Sep 17 00:00:00 2001 From: Federico Mariani Date: Wed, 14 Oct 2026 14:23:38 +0200 Subject: [PATCH 2/4] camel-launcher: exclude javadoc jars and omit auxiliary jars from distributions --- .../src/main/assembly/launcher-content.xml | 2 ++ .../camel-launcher/src/main/resources/bin/camel.bat | 4 ++-- .../camel-launcher/src/main/resources/bin/camel.sh | 4 ++-- .../dsl/jbang/launcher/CamelBatDiscoveryTest.java | 12 +++++++----- .../dsl/jbang/launcher/CamelShDiscoveryTest.java | 12 +++++++----- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dsl/camel-jbang/camel-launcher/src/main/assembly/launcher-content.xml b/dsl/camel-jbang/camel-launcher/src/main/assembly/launcher-content.xml index f648f2f9d0ea0..296373b1c0458 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/assembly/launcher-content.xml +++ b/dsl/camel-jbang/camel-launcher/src/main/assembly/launcher-content.xml @@ -56,6 +56,8 @@ original-*.jar + *-sources.jar + *-javadoc.jar diff --git a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat index fb0fe19e1f886..f2bd703ff71bc 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat +++ b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat @@ -49,10 +49,10 @@ set "JAVACMD=%CAMEL_JAVACMD%" @REM Set JVM options if specified if "%JAVA_OPTS%" == "" set JAVA_OPTS=-Xmx512m -@REM Find the launcher JAR, excluding sources +@REM Find the launcher JAR, excluding sources and javadoc set "LAUNCHER_JAR=" for %%i in ("%BASEDIR%\camel-launcher-*.jar") do ( - echo %%~nxi| findstr /i /e /l /c:"-sources.jar" > nul + echo %%~nxi| findstr /i /e /l /c:"-sources.jar" /c:"-javadoc.jar" > nul if errorlevel 1 if not defined LAUNCHER_JAR set "LAUNCHER_JAR=%%i" ) diff --git a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh index bc4a05426aa74..36a66c77efaba 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh +++ b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.sh @@ -100,10 +100,10 @@ then REPO="$BASEDIR" fi -# Find the launcher JAR, excluding sources +# Find the launcher JAR, excluding sources and javadoc LAUNCHER_JAR= for f in "$BASEDIR"/camel-launcher-*.jar; do - case "$f" in *-sources.jar) continue ;; esac + case "$f" in *-sources.jar|*-javadoc.jar) continue ;; esac if [ -f "$f" ]; then LAUNCHER_JAR="$f" break diff --git a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java index 761ee228e14c8..03883353c0eb6 100644 --- a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java +++ b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelBatDiscoveryTest.java @@ -35,7 +35,6 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -78,19 +77,22 @@ private Path currentJavaHome() { } @Test - void skipsSourcesJars(@TempDir Path base) throws Exception { + void skipsSourcesAndJavadocJars(@TempDir Path base) throws Exception { Path h = home(Files.createDirectories(base.resolve("launcher with spaces"))); Files.writeString(h.resolve("camel-launcher-9.9.9-sources.jar"), "sources"); Files.writeString(h.resolve("camel-launcher-zzz-sources.jar"), "sources"); + Files.writeString(h.resolve("camel-launcher-9.9.9-javadoc.jar"), "javadoc"); + Files.writeString(h.resolve("camel-launcher-zzz-javadoc.jar"), "javadoc"); Path java = FakeJava.writeFakeJava(base, "java", JAVA17, 0, "JAVACMD"); Map env = isolatedEnvironment(); env.put("JAVACMD", java.toString()); FakeJava.Result r = FakeJava.run(h.resolve("camel.bat"), env, "hawtio"); - assertThat(r.exitCode()).as(r.stderr()).isZero(); - assertThat(r.stdout()).contains(h.resolve("camel-launcher-9.9.9.jar").toString()) - .doesNotContain("-sources.jar"); + assertEquals(0, r.exitCode(), r.stderr()); + assertTrue(r.stdout().contains(h.resolve("camel-launcher-9.9.9.jar").toString()), r.stdout()); + assertFalse(r.stdout().contains("-sources.jar"), r.stdout()); + assertFalse(r.stdout().contains("-javadoc.jar"), r.stdout()); } @Test diff --git a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java index 93d0b17b8443f..08fac4adf315d 100644 --- a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java +++ b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelShDiscoveryTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -58,10 +57,12 @@ private String pathWithNonqualifyingJava(Path base) throws Exception { } @Test - void skipsSourcesJars(@TempDir Path base) throws Exception { + void skipsSourcesAndJavadocJars(@TempDir Path base) throws Exception { Path h = home(Files.createDirectories(base.resolve("launcher with spaces"))); Files.writeString(h.resolve("camel-launcher-9.9.9-sources.jar"), "sources"); Files.writeString(h.resolve("camel-launcher-zzz-sources.jar"), "sources"); + Files.writeString(h.resolve("camel-launcher-9.9.9-javadoc.jar"), "javadoc"); + Files.writeString(h.resolve("camel-launcher-zzz-javadoc.jar"), "javadoc"); Path java = FakeJava.writeFakeJava(base, "java", JAVA17, 0, "JAVACMD"); Map env = new HashMap<>(); env.put("LC_ALL", "C"); @@ -69,9 +70,10 @@ void skipsSourcesJars(@TempDir Path base) throws Exception { FakeJava.Result r = FakeJava.run(h.resolve("camel.sh"), env, "hawtio"); - assertThat(r.exitCode()).as(r.stderr()).isZero(); - assertThat(r.stdout()).contains(h.resolve("camel-launcher-9.9.9.jar").toString()) - .doesNotContain("-sources.jar"); + assertEquals(0, r.exitCode(), r.stderr()); + assertTrue(r.stdout().contains(h.resolve("camel-launcher-9.9.9.jar").toString()), r.stdout()); + assertFalse(r.stdout().contains("-sources.jar"), r.stdout()); + assertFalse(r.stdout().contains("-javadoc.jar"), r.stdout()); } @Test From 140a843259c59478d7e2289b4f88cc617cedcfab Mon Sep 17 00:00:00 2001 From: Federico Mariani Date: Wed, 14 Oct 2026 14:23:39 +0200 Subject: [PATCH 3/4] ci: validate launcher scripts with the pinned Maven wrapper and snapshot repositories Include the Windows validator timeout and process-handling fix from 7ab6c2946729 (CAMEL-24485). --- .../workflows/package-native-validation.yml | 38 ++++++++---- .../jbang/launcher/WebsiteInstallTest.java | 58 ++++++------------- .../launcher/WebsiteInstallerFixture.java | 33 ++++++++++- 3 files changed, 79 insertions(+), 50 deletions(-) diff --git a/.github/workflows/package-native-validation.yml b/.github/workflows/package-native-validation.yml index 4027b4b6c79a0..5d66244fcdfbc 100644 --- a/.github/workflows/package-native-validation.yml +++ b/.github/workflows/package-native-validation.yml @@ -41,6 +41,8 @@ concurrency: permissions: contents: read +# Use the project Maven wrapper throughout: runner-provided Maven versions can differ +# from the version supported by this branch's build extensions. jobs: # ── macOS / Linux (POSIX validators) ────────────────────────────── posix-validator: @@ -88,7 +90,7 @@ jobs: - name: Install camel-repackager-maven-plugin (build-time plugin, unresolvable from this reactor pass) shell: bash run: | - mvn -B -P apache-snapshots -ntp \ + ./mvnw -B -P apache-snapshots -ntp \ -pl tooling/maven/camel-repackager-maven-plugin install -DskipTests - name: Build camel-launcher distribution @@ -98,14 +100,14 @@ jobs: # published snapshot instead of being rebuilt from source, which is what previously turned # this into a ~626-module reactor build. -P apache-snapshots is required for that resolution. run: | - mvn -B -P apache-snapshots -ntp \ + ./mvnw -B -P apache-snapshots -ntp \ -pl buildingtools,dsl/camel-jbang/camel-launcher install -DskipTests - name: Stage a synthetic release version for offline validation shell: bash run: | cd dsl/camel-jbang/camel-launcher - REAL_VERSION=$(mvn -q -B -ntp org.apache.maven.plugins:maven-help-plugin:3.5.1:evaluate \ + REAL_VERSION=$(../../../mvnw -q -B -P apache-snapshots -ntp org.apache.maven.plugins:maven-help-plugin:3.5.1:evaluate \ -Dexpression=project.version -DforceStdout) TEST_VERSION="${REAL_VERSION%-SNAPSHOT}" echo "TEST_VERSION=$TEST_VERSION" >> "$GITHUB_ENV" @@ -142,14 +144,16 @@ jobs: # WebsiteInstallTest's PosixShell suite only gets targeted CI coverage today as a side # effect of the generic incremental-build check noticing a direct change under # dsl/camel-jbang/camel-launcher; a PR that only touches an upstream dependency wouldn't - # trigger it. Run it here explicitly so this dedicated validation workflow covers it too. + # trigger it. Run it here explicitly, together with the POSIX launcher script tests. run: | cd dsl/camel-jbang/camel-launcher - mvn -B -ntp test -pl . -Dtest=PackageNativeValidationTest,WebsiteInstallTest + ../../../mvnw -B -P apache-snapshots -ntp test -pl . -Dtest=PackageNativeValidationTest,WebsiteInstallTest,CamelShDiscoveryTest,FakeJavaTest - # ── Windows (install.ps1 unit tests) ────────────────────────────── + # ── Windows (installer and launcher script unit tests) ────────────────────────────── windows-validator: runs-on: windows-latest + # Caps a genuine hang well below GitHub's 6h default; a passing run has taken up to ~25 minutes. + timeout-minutes: 60 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -169,7 +173,7 @@ jobs: - name: Install camel-repackager-maven-plugin (build-time plugin, unresolvable from this reactor pass) shell: bash run: | - mvn -B -P apache-snapshots -ntp \ + ./mvnw -B -P apache-snapshots -ntp \ -pl tooling/maven/camel-repackager-maven-plugin install -DskipTests - name: Build camel-launcher module @@ -179,11 +183,25 @@ jobs: # published snapshot instead of being rebuilt from source, which is what previously turned # this into a ~626-module reactor build. -P apache-snapshots is required for that resolution. run: | - mvn -B -P apache-snapshots -ntp \ + ./mvnw -B -P apache-snapshots -ntp \ -pl buildingtools,dsl/camel-jbang/camel-launcher install -DskipTests - - name: Run Windows installer unit tests + - name: Run Windows installer and launcher script unit tests shell: bash + # The PowerShell suite has taken 10-20 minutes on windows-latest, straddling camel-launcher's 1200s + # surefire fork timeout, so slow-but-passing runs were killed. Give this job's fork more room. run: | cd dsl/camel-jbang/camel-launcher - mvn -B -ntp test -pl . -Dtest=WebsiteInstallTest + ../../../mvnw -B -P apache-snapshots -ntp test -pl . -Dtest=WebsiteInstallTest,CamelBatDiscoveryTest,FakeJavaTest -Dcamel.surefire.forkTimeout=2700 + + # Per-test timings for the PowerShell suite, which runs close to camel-launcher's surefire fork timeout. + # A fork killed on that timeout writes no XML report; the per-process timings the fixture prints to the + # build log cover that case. + - name: Archive surefire reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: windows-validator-surefire-reports + path: dsl/camel-jbang/camel-launcher/target/surefire-reports + retention-days: 7 + if-no-files-found: ignore diff --git a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallTest.java b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallTest.java index 0fbda6496e9d0..a514a7c108287 100644 --- a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallTest.java +++ b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallTest.java @@ -34,7 +34,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; @@ -152,11 +151,8 @@ private static void assertVersionInstalled(Path home, String version) throws Exc Path versionDir = home.resolve(".local/share/camel-cli/versions/" + version); assertTrue(Files.isDirectory(versionDir), "expected version directory " + versionDir); - Process check = new ProcessBuilder("/bin/sh", shim.toString(), "version") - .redirectErrorStream(true) - .start(); - String output = new String(check.getInputStream().readAllBytes(), StandardCharsets.UTF_8); - check.waitFor(); + String output = WebsiteInstallerFixture.execute( + new ProcessBuilder("/bin/sh", shim.toString(), "version").redirectErrorStream(true)).stdout(); assertTrue(output.contains("Camel " + version), output); } @@ -672,9 +668,8 @@ void restoreUserPath() throws Exception { + "if ($path) { $kind = $k.GetValueKind('Path'); " + "$kept = ($path -split ';') | Where-Object { $_ -and (-not ($dirs -icontains $_)) }; " + "$k.SetValue('Path', ($kept -join ';'), $kind) } $k.Close() }"); - Process cleanup = new ProcessBuilder("powershell", "-NoProfile", "-Command", script.toString()) - .redirectErrorStream(true).start(); - cleanup.waitFor(30, TimeUnit.SECONDS); + WebsiteInstallerFixture.execute( + new ProcessBuilder("powershell", "-NoProfile", "-Command", script.toString()).redirectErrorStream(true)); } private WebsiteInstallerFixture.Result install( @@ -716,11 +711,8 @@ private static void assertVersionInstalled(Path home, String version) throws Exc assertTrue(Files.isDirectory(versionDir(home, version)), "expected version directory " + versionDir(home, version)); - Process check = new ProcessBuilder("cmd.exe", "/c", shim.toString(), "version") - .redirectErrorStream(true) - .start(); - String output = new String(check.getInputStream().readAllBytes(), StandardCharsets.UTF_8); - check.waitFor(30, TimeUnit.SECONDS); + String output = WebsiteInstallerFixture.execute( + new ProcessBuilder("cmd.exe", "/c", shim.toString(), "version").redirectErrorStream(true)).stdout(); assertTrue(output.contains("Camel " + version), output); } @@ -740,13 +732,10 @@ private void assertMaliciousZipRejected(Path temp, MaliciousArchive archiveBuild } private static String queryEnvironmentPath(String scope) throws Exception { - Process p = new ProcessBuilder( + return WebsiteInstallerFixture.execute(new ProcessBuilder( "powershell", "-NoProfile", "-Command", "[Environment]::GetEnvironmentVariable('Path','" + scope + "')") - .redirectErrorStream(true).start(); - String out = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim(); - p.waitFor(30, TimeUnit.SECONDS); - return out; + .redirectErrorStream(true)).stdout().trim(); } private static long countOccurrencesCaseInsensitive(String path, String dir) { @@ -760,15 +749,12 @@ private static long countOccurrencesCaseInsensitive(String path, String dir) { // Reads HKCU\Environment\Path without expanding %VAR% references, returning "" when unset. private static String readUserPathRaw() throws Exception { - Process p = new ProcessBuilder( + return WebsiteInstallerFixture.execute(new ProcessBuilder( "powershell", "-NoProfile", "-Command", "$k = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment'); " + "if ($k) { [Console]::Out.Write($k.GetValue('Path', '', " - + "[Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)); $k.Close() }") - .start(); - String out = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8); - p.waitFor(30, TimeUnit.SECONDS); - return out; + + "[Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)); $k.Close() }")) + .stdout(); } // Writes HKCU\Environment\Path as a REG_EXPAND_SZ value; the value is passed via the environment @@ -781,21 +767,18 @@ private static void writeUserPathExpandable(String value) throws Exception { + "[Microsoft.Win32.RegistryValueKind]::ExpandString); $k.Close()") .redirectErrorStream(true); pb.environment().put("CAMEL_TEST_USERPATH", value); - Process p = pb.start(); - String out = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8); - if (!p.waitFor(30, TimeUnit.SECONDS) || p.exitValue() != 0) { - throw new IllegalStateException("failed to seed user PATH: " + out); + WebsiteInstallerFixture.Result r = WebsiteInstallerFixture.execute(pb); + if (r.exit() != 0) { + throw new IllegalStateException("failed to seed user PATH: " + r.stdout()); } } private static void deleteUserPath() throws Exception { - Process p = new ProcessBuilder( + WebsiteInstallerFixture.execute(new ProcessBuilder( "powershell", "-NoProfile", "-Command", "$k = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true); " + "if ($k) { $k.DeleteValue('Path', $false); $k.Close() }") - .redirectErrorStream(true).start(); - p.getInputStream().readAllBytes(); - p.waitFor(30, TimeUnit.SECONDS); + .redirectErrorStream(true)); } @Test @@ -1039,14 +1022,11 @@ void forwardsArgumentsAndExitCodeThroughShim(@TempDir Path temp) throws Exceptio // ("bar baz") trips cmd.exe's quote-stripping rule (it only preserves quotes when there are // exactly two), which would unquote the space in "Apache Camel" and break the call. A bare // command name resolved through PATH+PATHEXT avoids that quirk and mirrors real usage. - Process argsProc = shimProcess(binDir, "echo-args", "foo", "bar baz").start(); - String argsOut = new String(argsProc.getInputStream().readAllBytes(), StandardCharsets.UTF_8); - argsProc.waitFor(30, TimeUnit.SECONDS); + String argsOut = WebsiteInstallerFixture.execute(shimProcess(binDir, "echo-args", "foo", "bar baz")) + .stdout(); assertTrue(argsOut.contains("foo") && argsOut.contains("bar baz"), argsOut); - Process exitProc = shimProcess(binDir, "exit-code", "7").start(); - exitProc.waitFor(30, TimeUnit.SECONDS); - assertEquals(7, exitProc.exitValue()); + assertEquals(7, WebsiteInstallerFixture.execute(shimProcess(binDir, "exit-code", "7")).exit()); } } diff --git a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallerFixture.java b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallerFixture.java index a94857d9b708a..996e2eddf74f1 100644 --- a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallerFixture.java +++ b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/WebsiteInstallerFixture.java @@ -64,6 +64,8 @@ final class WebsiteInstallerFixture implements AutoCloseable { private static final String KEY_ALIAS = "camel-installer-test"; private static final String DEFAULT_BASE_VERSION = "9.9.9"; private static final Duration PROCESS_TIMEOUT = Duration.ofSeconds(60); + private static final Path POWERSHELL_MODULE_ANALYSIS_CACHE + = Path.of("target", "powershell", "ModuleAnalysisCache").toAbsolutePath(); record Result(int exit, String stdout, String stderr) { } @@ -446,6 +448,20 @@ Result run(List command, Map env) throws Exception { if (pathExt != null) { pb.environment().put("PATHEXT", pathExt); } + // Without TEMP/TMP, .NET falls back to USERPROFILE (the fresh per-test home) for the scratch + // files Add-Type's C# compile writes on every install.ps1 run. install.ps1 itself never stages + // anything under the temp directory, so passing the runner's through does not affect isolation. + for (String name : List.of("TEMP", "TMP")) { + String value = System.getenv(name); + if (value != null) { + pb.environment().put(name, value); + } + } + // PowerShell keeps its module analysis cache under LOCALAPPDATA, which every test points at a + // fresh home, so each powershell.exe launch would rebuild it from scratch when auto-loading + // Expand-Archive / Get-FileHash. Share one cache file across the whole test run instead. + Files.createDirectories(POWERSHELL_MODULE_ANALYSIS_CACHE.getParent()); + pb.environment().put("PSModuleAnalysisCachePath", POWERSHELL_MODULE_ANALYSIS_CACHE.toString()); } pb.environment().putAll(env); String home = env.get("HOME"); @@ -454,7 +470,16 @@ Result run(List command, Map env) throws Exception { // wherever the test JVM happens to be running from. pb.directory(Path.of(home).toFile()); } + return execute(pb); + } + /** + * Runs {@code pb} with stdout and stderr drained on background threads, so a chatty or stuck child can never block + * the test past {@link #PROCESS_TIMEOUT}: reading a stream to EOF before {@code waitFor} would wait for as long as + * the child (or any grandchild inheriting its handles) keeps the pipe open. + */ + static Result execute(ProcessBuilder pb) throws Exception { + long start = System.nanoTime(); Process process = pb.start(); ExecutorService collectors = Executors.newFixedThreadPool(2); try { @@ -463,7 +488,7 @@ Result run(List command, Map env) throws Exception { process.getOutputStream().close(); if (!process.waitFor(PROCESS_TIMEOUT.toSeconds(), TimeUnit.SECONDS)) { process.destroyForcibly(); - throw new IllegalStateException("installer did not exit in time"); + throw new IllegalStateException("process did not exit in time: " + pb.command()); } String out = new String(await(stdout), StandardCharsets.UTF_8); String err = new String(await(stderr), StandardCharsets.UTF_8); @@ -474,6 +499,12 @@ Result run(List command, Map env) throws Exception { } collectors.shutdownNow(); collectors.awaitTermination(10, TimeUnit.SECONDS); + // Surefire streams test stdout to the build log as it happens, so these timings survive even when + // the fork is killed by forkedProcessTimeoutInSeconds and no XML report is ever written. + String command = String.join(" ", pb.command()); + System.out.printf("[WebsiteInstallerFixture] %d ms: %s%n", + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start), + command.length() > 160 ? command.substring(0, 160) + "..." : command); } } From f9d1f4c6866c68f89afc5a3f9844f32debdd5177 Mon Sep 17 00:00:00 2001 From: Federico Mariani Date: Wed, 14 Oct 2026 16:14:55 +0200 Subject: [PATCH 4/4] camel-launcher: normalize Windows jar path and pin packaging Maven subprocesses --- .github/workflows/package-native-validation.yml | 10 ++++++++++ .../camel-launcher/src/main/resources/bin/camel.bat | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/package-native-validation.yml b/.github/workflows/package-native-validation.yml index 5d66244fcdfbc..27de46e58c2d4 100644 --- a/.github/workflows/package-native-validation.yml +++ b/.github/workflows/package-native-validation.yml @@ -83,6 +83,16 @@ jobs: java-version: '21' cache: 'maven' + # Packaging scripts invoke mvn themselves, so pin their PATH entry as well as + # the direct workflow commands. Keep the snapshot profile active in subprocesses. + - name: Pin Maven for packaging subprocesses + shell: bash + run: | + CAMEL_CI_MAVEN_HOME=$(./mvnw -q -B -P apache-snapshots -ntp -N org.apache.maven.plugins:maven-help-plugin:3.5.1:evaluate \ + -Dexpression=maven.home -DforceStdout) + echo "$CAMEL_CI_MAVEN_HOME/bin" >> "$GITHUB_PATH" + echo "MAVEN_ARGS=${MAVEN_ARGS:-} -P apache-snapshots" >> "$GITHUB_ENV" + # camel-repackager-maven-plugin builds the launcher's self-executing JAR. It is not in the # -pl list below, so without this step Maven resolves it from the Apache snapshot repository. # This workflow only triggers on dsl/camel-jbang/camel-launcher and tooling/camel-exe changes diff --git a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat index f2bd703ff71bc..9d485f9a39f99 100644 --- a/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat +++ b/dsl/camel-jbang/camel-launcher/src/main/resources/bin/camel.bat @@ -53,7 +53,7 @@ if "%JAVA_OPTS%" == "" set JAVA_OPTS=-Xmx512m set "LAUNCHER_JAR=" for %%i in ("%BASEDIR%\camel-launcher-*.jar") do ( echo %%~nxi| findstr /i /e /l /c:"-sources.jar" /c:"-javadoc.jar" > nul - if errorlevel 1 if not defined LAUNCHER_JAR set "LAUNCHER_JAR=%%i" + if errorlevel 1 if not defined LAUNCHER_JAR set "LAUNCHER_JAR=%%~fi" ) @REM Execute Camel CLI, preserving arguments and the child exit code