Skip to content

[LAUNCHER] Fix nested jar url handling #91

[LAUNCHER] Fix nested jar url handling

[LAUNCHER] Fix nested jar url handling #91

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Cross-compiles camel.exe (x64 + arm64) on Linux using clang/llvm-mingw.
# Transitional safety net, remove once a real release has exercised the
# -Prelease auto-trigger path successfully.
name: Camel launcher native exe build
on:
push:
branches:
- main
paths:
- 'tooling/camel-exe/**'
- 'dsl/camel-jbang/camel-launcher/**'
- '.github/actions/setup-llvm-mingw/**'
- '.github/workflows/camel-launcher-native-exe.yml'
pull_request:
branches:
- main
paths:
- 'tooling/camel-exe/**'
- 'dsl/camel-jbang/camel-launcher/**'
- '.github/actions/setup-llvm-mingw/**'
- '.github/workflows/camel-launcher-native-exe.yml'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
camel-exe: ${{ steps.changes.outputs.camel-exe }}
camel-launcher: ${{ steps.changes.outputs.camel-launcher }}
camel-launcher-upstream: ${{ steps.changes.outputs.camel-launcher-upstream }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- id: changes
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "camel-exe=true" >> "$GITHUB_OUTPUT"
echo "camel-launcher=true" >> "$GITHUB_OUTPUT"
echo "camel-launcher-upstream=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${{ github.event_name }}" = "pull_request" ]; then
CHANGED="$(git diff --name-only "origin/${{ github.base_ref }}"...HEAD)"
else
CHANGED="$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}")"
fi
echo "Changed files:"
echo "$CHANGED"
camel_exe=false
camel_launcher=false
camel_launcher_upstream=false
if echo "$CHANGED" | grep -qE '^(tooling/camel-exe/|\.github/actions/setup-llvm-mingw/|\.github/workflows/camel-launcher-native-exe\.yml)'; then
camel_exe=true
fi
if echo "$CHANGED" | grep -qE '^(dsl/camel-jbang/camel-launcher/|tooling/camel-exe/|\.github/actions/setup-llvm-mingw/|\.github/workflows/camel-launcher-native-exe\.yml)'; then
camel_launcher=true
fi
# camel-launcher-native normally resolves camel-jbang-core (and the launcher's other
# in-repo dependencies) from the Apache Snapshots repo instead of building them here (see
# the "Build launcher with native camel.exe" step below). That assumption breaks when a PR
# changes camel-jbang-core itself, since the new code isn't on the snapshot repo yet.
if echo "$CHANGED" | grep -qE '^dsl/camel-jbang/camel-jbang-core/'; then
camel_launcher_upstream=true
fi
echo "camel-exe=$camel_exe" >> "$GITHUB_OUTPUT"
echo "camel-launcher=$camel_launcher" >> "$GITHUB_OUTPUT"
echo "camel-launcher-upstream=$camel_launcher_upstream" >> "$GITHUB_OUTPUT"
camel-exe:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.camel-exe == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up JDK 25
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- uses: ./.github/actions/setup-llvm-mingw
- name: Build and test native camel.exe (x64 + arm64)
# Plain mvn: this reactor is only a handful of modules, so the mvnd daemon
# brings no parallelism benefit and adds a cold-start failure mode.
run: |
mvn -B -pl buildingtools,tooling/camel-exe -am verify -Dcamel.exe.build=true -DskipTests
- name: Upload Windows x64 camel.exe
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: camel-x64-exe
path: tooling/camel-exe/target/camel-x64.exe
if-no-files-found: error
camel-exe-windows-smoke:
needs:
- detect-changes
- camel-exe
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.camel-exe == 'true'
runs-on: windows-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up JDK 25
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- name: Download Windows x64 camel.exe
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: camel-x64-exe
path: tooling/camel-exe/target
- name: Smoke test Windows x64 camel.exe
shell: bash
run: |
mvn -B -ntp -pl buildingtools,tooling/camel-exe -am test \
-Dtest=CamelExeBootstrapTest -Dsurefire.failIfNoSpecifiedTests=false
camel-launcher-native:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.camel-launcher == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up JDK 25
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- name: Verify launcher snapshot dependencies are published
# Without -am the launcher's upstream modules are not built here; they must
# already be deployed as the current version's snapshot on the Apache
# snapshot repository. camel-launcher is the terminal module, deployed after
# its dependencies, so its presence stands in as a sentinel for the whole
# upstream tree. Fail early with a clear message instead of a deep Maven
# resolution error mid-build.
run: |
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout -N)
META="https://repository.apache.org/snapshots/org/apache/camel/camel-launcher/${VERSION}/maven-metadata.xml"
echo "Checking ${META}"
if ! curl -fsSL --proto '=https' -o /dev/null "${META}"; then
echo "ERROR: camel-launcher ${VERSION} is not on the Apache snapshot repository."
echo "This job resolves the launcher's upstream modules from there (no -am build)."
echo "Wait for a main-branch snapshot deploy of ${VERSION}, or rebuild with -am."
exit 1
fi
echo "Launcher upstream snapshots for ${VERSION} are available."
- name: Install camel-jbang-core from source
# camel-jbang-core is one of the launcher's in-repo dependencies but isn't in the -pl
# list below, so it normally resolves as the current version's snapshot from Apache
# Snapshots. When this PR changes camel-jbang-core itself, that snapshot doesn't have
# the new code yet, so build and install just this one module from source instead.
#
# No -am: camel-jbang-core depends on camel-catalog, which aggregates all ~460
# components (camel-allcomponents) so the catalog can enumerate them. -am here would
# pull in almost the entire reactor. camel-jbang-core's own dependencies keep resolving
# from Apache Snapshots; only camel-jbang-core itself is rebuilt.
if: needs.detect-changes.outputs.camel-launcher-upstream == 'true'
run: |
mvn -B -P apache-snapshots -ntp \
-pl dsl/camel-jbang/camel-jbang-core install -DskipTests
- uses: ./.github/actions/setup-llvm-mingw
- name: Install the launcher's in-repo build plugins
# 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
# and any change made to it in this PR is silently not exercised. Maven cannot consume a
# plugin produced by the same reactor invocation, hence the separate installation.
run: |
mvn -B -P apache-snapshots -ntp \
-pl tooling/maven/camel-repackager-maven-plugin install -DskipTests
- name: Build launcher with native camel.exe (x64 + arm64)
# No -am: build only the listed modules. The launcher's other in-repo dependencies
# (camel-jbang-core, its jbang plugins, camel-core, ...) resolve as the current
# version's snapshot from Apache snapshots, or from the local repo if the step above
# just installed camel-jbang-core from source (local repo takes precedence).
#
# Plain mvn: once -am is gone this reactor is only a few modules, so the mvnd
# daemon brings no parallelism benefit and adds a cold-start failure mode.
# -P apache-snapshots is required here so the upstream modules resolve.
run: |
mvn -B -P apache-snapshots \
-pl buildingtools,tooling/camel-exe,dsl/camel-jbang/camel-launcher verify \
-Dcamel.exe.build=true -DskipTests
- name: Assert archive carries both exe files
run: |
ZIP=$(find dsl/camel-jbang/camel-launcher/target -maxdepth 1 -name 'camel-launcher-*-winget-bin.zip' -print -quit)
echo "Checking archive: $ZIP"
ENTRIES=$(unzip -l "$ZIP" | grep -Ec '/bin/camel-(x64|arm64)\.exe$')
if [ "$ENTRIES" -ne 2 ]; then
echo "ERROR: expected both bin/camel-x64.exe and bin/camel-arm64.exe in archive"
unzip -l "$ZIP" | grep -i exe
exit 1
fi
echo "Both bin/camel-x64.exe and bin/camel-arm64.exe found in archive"
- name: Verify reproducible native executables and WinGet archive
run: |
HASH_FILE="$RUNNER_TEMP/winget-first-build.sha256"
sha256sum \
tooling/camel-exe/target/camel-x64.exe \
tooling/camel-exe/target/camel-arm64.exe \
dsl/camel-jbang/camel-launcher/target/camel-launcher-*-winget-bin.zip \
> "$HASH_FILE"
# 'clean verify' wipes target, so keep the first-build outputs around; on a mismatch the
# differing byte offsets and zip entry listing say far more than a bare FAILED.
FIRST_BUILD="$RUNNER_TEMP/winget-first-build"
mkdir -p "$FIRST_BUILD"
cp tooling/camel-exe/target/camel-x64.exe \
tooling/camel-exe/target/camel-arm64.exe \
dsl/camel-jbang/camel-launcher/target/camel-launcher-*-winget-bin.zip "$FIRST_BUILD"
mvn -B -P apache-snapshots \
-pl buildingtools,tooling/camel-exe,dsl/camel-jbang/camel-launcher clean verify \
-Dcamel.exe.build=true -DskipTests
if ! sha256sum --check "$HASH_FILE"; then
for ARCH in x64 arm64; do
echo "=== differing bytes in camel-${ARCH}.exe (first 40) ==="
cmp -l "$FIRST_BUILD/camel-${ARCH}.exe" \
"tooling/camel-exe/target/camel-${ARCH}.exe" | head -40 || true
done
# 'unzip -v' lists each entry's CRC and timestamp, so the diff distinguishes an entry
# whose contents changed from one that merely got a new modification time.
echo "=== differing WinGet archive entries ==="
ZIP=$(find dsl/camel-jbang/camel-launcher/target -maxdepth 1 -name '*-winget-bin.zip' -print -quit)
diff <(unzip -v "$FIRST_BUILD"/*-winget-bin.zip) <(unzip -v "$ZIP") || true
# The launcher JAR is itself an archive, so a CRC change above says nothing about which of
# its thousands of entries moved. Unpack both and diff one level down.
echo "=== differing entries inside the launcher JAR ==="
unzip -qo "$FIRST_BUILD"/*-winget-bin.zip '*/bin/camel-launcher-*.jar' -d "$RUNNER_TEMP/jar-first"
unzip -qo "$ZIP" '*/bin/camel-launcher-*.jar' -d "$RUNNER_TEMP/jar-second"
diff <(unzip -v "$(find "$RUNNER_TEMP/jar-first" -name '*.jar' -print -quit)") \
<(unzip -v "$(find "$RUNNER_TEMP/jar-second" -name '*.jar' -print -quit)") \
| head -60 || true
exit 1
fi
- name: Verify WinGet archive is not deployed by Maven
run: |
MAVEN_REPO="$RUNNER_TEMP/winget-maven-repo"
mvn -B -P apache-snapshots \
-pl buildingtools,tooling/camel-exe,dsl/camel-jbang/camel-launcher deploy \
-Dcamel.exe.build=true -DskipTests \
-DaltSnapshotDeploymentRepository="winget-check::file://$MAVEN_REPO"
# Matches the ZIP and every sidecar Maven may generate alongside it
# (.sha1, .md5, .sha256, .sha512, .asc), not just the two seen locally.
DEPLOYED_WINGET_FILES=$(find "$MAVEN_REPO" -type f -name '*-winget-bin.zip*' -print)
if [ -n "$DEPLOYED_WINGET_FILES" ]; then
echo "ERROR: Maven deployed the local-only WinGet payload:"
echo "$DEPLOYED_WINGET_FILES"
exit 1
fi