Skip to content
Draft
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
48 changes: 38 additions & 10 deletions .github/workflows/package-native-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -81,14 +83,24 @@ 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
# (see the `paths:` filters above), so the plugin's own sources are never what's under test here.
- 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
Expand All @@ -98,14 +110,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"
Expand Down Expand Up @@ -142,14 +154,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:
Expand All @@ -169,7 +183,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
Expand All @@ -179,11 +193,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
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
</includes>
<excludes>
<exclude>original-*.jar</exclude>
<exclude>*-sources.jar</exclude>
<exclude>*-javadoc.jar</exclude>
</excludes>
</fileSet>
<fileSet>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 and javadoc
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" /c:"-javadoc.jar" > nul
if errorlevel 1 if not defined LAUNCHER_JAR set "LAUNCHER_JAR=%%~fi"
)

@REM Execute Camel CLI, preserving arguments and the child exit code
for %%e in ("%JAVACMD%") do set "_JAVACMD_EXT=%%~xe"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ then
REPO="$BASEDIR"
fi

# Find the launcher JAR
# Find the launcher JAR, excluding sources and javadoc
LAUNCHER_JAR=
for f in "$BASEDIR"/camel-launcher-*.jar; do
case "$f" in *-sources.jar|*-javadoc.jar) continue ;; esac
if [ -f "$f" ]; then
LAUNCHER_JAR="$f"
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,25 @@ private Path currentJavaHome() {
return javaHome;
}

@Test
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<String, String> env = isolatedEnvironment();
env.put("JAVACMD", java.toString());

FakeJava.Result r = FakeJava.run(h.resolve("camel.bat"), env, "hawtio");

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
void acceptsJava17ViaJavacmd(@TempDir Path base) throws Exception {
Path h = home(base);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ private String pathWithNonqualifyingJava(Path base) throws Exception {
return pathDir + ":/usr/bin:/bin";
}

@Test
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<String, String> env = new HashMap<>();
env.put("LC_ALL", "C");
env.put("JAVACMD", java.toString());

FakeJava.Result r = FakeJava.run(h.resolve("camel.sh"), env, "hawtio");

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
void acceptsJava17ViaJavacmd(@TempDir Path base) throws Exception {
Path h = home(base);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
}

Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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());
}
}

Expand Down
Loading
Loading