Skip to content

CAMEL-23703: camel-launcher - JReleaser packaging and WinGet distribution#24915

Merged
davsclaus merged 23 commits into
apache:mainfrom
ammachado:CAMEL-23703-jreleaser-packaging
Jul 20, 2026
Merged

CAMEL-23703: camel-launcher - JReleaser packaging and WinGet distribution#24915
davsclaus merged 23 commits into
apache:mainfrom
ammachado:CAMEL-23703-jreleaser-packaging

Conversation

@ammachado

@ammachado ammachado commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Description

Adds JReleaser-driven package distribution for the camel-launcher module, generating installable packages for five package managers: Homebrew, SDKMAN, WinGet, Scoop, and Chocolatey.

Packaging

  • jreleaser.yml configures the distribution definitions, with custom templates under src/jreleaser/distributions/camel-cli/<packager>/.
  • src/jreleaser/bin/camel-package.sh is the single entry point, wrapping the JReleaser Maven plugin and mapping a release channel (stable / lts) onto a packager set, a Homebrew formula name, and the website manifests.
  • The JReleaser plugin is declared with <inherited>false</inherited> and bound to no lifecycle phase, so ordinary Camel builds neither prepare nor publish packages; only the wrapper invokes its goals.
  • Homebrew URLs use the search.maven.org/remotecontent redirector required by Homebrew's FormulaAudit::Urls rubocop rather than repo1.maven.org. All template URLs render through Mustache's unescaped triple-brace form, because the escaping form rewrites the = in ?filepath= as &#61; and yields a URL brew cannot download.
  • license-maven-plugin excludes cover the new .tpl templates, whose extension has no comment-style mapping (and JSON has no comment syntax at all); each exclude is documented inline in the pom.
  • camel-package.sh publish is intentionally unimplemented: it exits with an error rather than defaulting a destination, because which target each packager's artifacts should be pushed to (e.g. Homebrew's own tap vs. homebrew-core) is a decision for the Apache Camel PMC, not something the script should decide on its own. The Homebrew dual-formula gap noted in jreleaser.yml is blocked on that same decision.

WinGet payload

WinGet's portable installer type creates a symlink named camel.exe, which cannot be backed by a .bat. tooling/camel-exe cross-compiles two small native bootstraps that resolve that symlink, locate camel.bat beside the target, and forward the command line. Scoop and Chocolatey need no such binary and enter through camel.bat.

  • Those executables are confined to a dedicated camel-launcher-<version>-winget-bin.zip. The public -bin.zip / -bin.tar.gz no longer carry them.
  • That archive is built with attach=false, so Maven never installs or deploys it. It is released through the Apache distribution archive instead: etc/scripts/stage-winget-distro.sh stages a signed, checksummed candidate under dist/dev for the vote, and etc/scripts/release-distro.sh promotes the approved bytes unchanged to dist/release.
  • Promotion requires the SHA-512 published with the vote. Each candidate directory carries its own self-consistent .sha512 and .asc, so the candidate number alone cannot distinguish an approved candidate from a superseded one.
  • Before generating the WinGet manifest, camel-package.sh downloads the archived payload and refuses to proceed unless it is byte-identical to the local one.
  • Both archives draw their shared content from one assembly component (launcher-content.xml), so the WinGet package cannot silently diverge from the published archive.
  • The installer, default-locale, and version manifests are all locally overridden and pinned to WinGet schema 1.12.0, preventing JReleaser's embedded 1.9.0 templates from producing an invalid mixed-version manifest set.

CI gates

The native-exe workflow builds twice from clean output directories and compares SHA-256 of both executables and the WinGet archive, then deploys the reactor to a throwaway repository and fails if the WinGet payload or any of its sidecars appears there. Reproducibility required pinning the PE TimeDateStamp: it is a mandatory header field that lld fills with the current wall-clock time, so both cross-compiles pass -Wl,--no-insert-timestamp. Without it every build is byte-different and the archive SHA-256 published in the WinGet manifest cannot be independently regenerated by anyone verifying the release.

The launcher JAR itself needed a separate reproducibility fix, since it is the payload inside that same archive. RepackageMojo called Spring Boot's two-argument repackage, which leaves the last-modified time null; Spring Boot's packager then stamps every entry with the current wall-clock time and falls back to an unordered BOOT-INF/lib instead of a sorted one. RepackageMojo now passes through the pinned project.build.outputTimestamp. RepackageMojoTest's three placeholder assertTrue(true) cases were replaced with real assertions over the packaged JAR's structure (BOOT-INF/classes, BOOT-INF/lib, loader classes, manifest entries), though the reproducibility fix itself remains unproven by unit test since the fixture pins each library's first-entry time regardless of the fix.

Verification

Beyond the unit tests, jreleaser:config, jreleaser:prepare and jreleaser:package were run locally in dry-run for both channels and the generated output inspected: all three WinGet manifests at schema 1.12.0, both installer architectures resolving from one archive and checksum, the Homebrew formula for the unversioned and versioned cases, and the Scoop and Chocolatey manifests. Assembly changes were checked by diffing the archive entry list before and after. The complete PackagePlanTest class passes with 31 tests and none skipped.

Open questions for reviewers

  1. Does ASF policy require a package-manager-referenced artifact to be served from an apache.org host and covered by its own vote? If not, the WinGet archive could collapse back into the public one and the dist/dev staging step would go away.
  2. Is the ARM64 executable necessary, given WinGet's x64 applicability on ARM64 hosts and that the bootstrap only spawns cmd.exe?

This is part 3 of the split of the original CAMEL-23703 unified Camel CLI packaging work (see #24682, #24754, #24665, #24664). It targets apache/camel main and does not depend on unmerged content from any other part of the split.

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it).

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.

AI-assisted contributions

  • If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., Co-authored-by trailers) and the PR description identifies the AI tool used.

AI-assisted by Claude Code and Codex on behalf of Adriano Machado (ammachado).

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • docs
  • dsl/camel-jbang/camel-launcher
  • etc
  • tooling/camel-exe
  • tooling/maven/camel-repackager-maven-plugin

🔬 Scalpel shadow comparison — Scalpel: 6 tested, 0 compile-only — current: 3 all tested

Maveniverse Scalpel detected 6 affected modules (current approach: 3).

⚠️ Modules only in Scalpel (3)
  • camel-etc
  • camel-launcher
  • docs

Changed managed plugins: org.apache.maven.plugins:maven-release-plugin

Skip-tests mode would test 6 modules (5 direct + 1 downstream), skip tests for 0 (generated code, meta-modules)

Modules Scalpel would test (6)
  • camel-etc
  • camel-exe
  • camel-launcher
  • camel-launcher-container
  • camel-repackager-maven-plugin
  • docs

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

💡 Manual integration tests recommended:

You modified dsl/camel-jbang/camel-launcher. The related integration tests in dsl/camel-jbang/camel-jbang-it are excluded from CI. Consider running them manually:

mvn verify -f dsl/camel-jbang/camel-jbang-it -Djbang-it-test
All tested modules (6 modules)
  • Camel :: Docs
  • Camel :: Etc
  • Camel :: Exe
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Maven Plugins :: Repackager

⚙️ View full build and test results

@ammachado
ammachado marked this pull request as ready for review July 19, 2026 23:56

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR adds JReleaser-driven package distribution for the camel-launcher module, targeting five package managers (Homebrew, SDKMAN, WinGet, Scoop, and Chocolatey). The implementation is thorough and well-engineered, with exceptional attention to error handling, test coverage, and documentation of design decisions. The dual-wrapper approach (POSIX + Windows batch) with parity testing across both platforms is a notable strength.

What I Love

  1. Exhaustive inline documentation -- the jreleaser.yml header and the comments throughout both wrapper scripts read like a design document. Every decision is traced back to a confirmed JReleaser behavior, and the rationale for workarounds (the Homebrew AT naming convention, the Env. template prefix vs -D properties, the JAVA_BINARY vs BINARY type) is anchored to verifiable facts. Future maintainers will thank you for this.

  2. Defense-in-depth validation chain -- the scripts validate the channel, LTS line (against an allowlist with expiry-date gating), snapshot version rejection, artifact existence, and copy integrity before invoking JReleaser. The test-mode safety guards (CAMEL_PACKAGE_TEST_MODE=true required for CAMEL_PACKAGE_TEST_VERSION) prevent accidental production skips. This level of rigor is uncommon in release tooling scripts.

  3. Test architecture -- the PackagePlanTest uses nested @DisabledOnOs(WINDOWS) / @EnabledOnOs(WINDOWS) classes to exercise both wrappers on their native platform, with mvn stubs to isolate JReleaser invocation from the plan logic. The synthetic LTS fixture (supportEnds: "9999-12-31") decouples tests from real LTS calendar dates. The test that deliberately reads the real supported-lts.yml for 4.22 catches accidental deletions. Thoughtful design.

Findings

Important

Scoop autoupdate hash URL may 404 (manifest.json.tpl, autoupdate.hash.url):

The Scoop manifest configures autoupdate hash verification as:

"hash": {
    "url": "$url.sha512"
}

I verified that Maven Central does not publish .sha512 sidecar files for camel-launcher-4.21.0-bin.zip (returns HTTP 404). Only .sha1 and .md5 are present. This means that when Scoop's checkver -u runs, it will fail to fetch the hash for the new version, breaking auto-update.

Options:

  • Use "url": "$url.sha1" (universally available on Maven Central)
  • Omit the hash block entirely (Scoop will download the artifact and compute the hash itself during checkver -u)
  • Verify that the 4.22.0 release process (via Sonatype Central Portal) will generate .sha512 sidecar files for classified artifacts -- if it will, this is fine for future releases but needs documentation noting it won't work for retroactive verification of pre-4.22 artifacts

Could you confirm which approach is correct here? The Sonatype Central Portal's SHA-512 requirement may apply differently to classified artifacts.

Suggestions

[Suggestion] formula_opt_bin is Homebrew private API (formula.rb.tpl, line in def install):

CAMEL_FALLBACK_JAVA: "#{formula_opt_bin("openjdk")}/java"

Homebrew's Ruby API documentation marks formula_opt_bin as private API ("This method is part of a private API. This method may only be used in the Homebrew/brew repository"). While JReleaser's own default JAVA_BINARY template uses this same pattern (so it is a well-established convention), it could break without warning in a future Homebrew release.

The public-API equivalent would be:

CAMEL_FALLBACK_JAVA: "#{Formula["openjdk"].opt_bin}/java"

This is not blocking since JReleaser uses this pattern widely and Homebrew has maintained the method for years, but worth being aware of.

[Nit] Unquoted $SNAPSHOT_PATTERN_ARG in camel-package.sh (line ~859):

  $SNAPSHOT_PATTERN_ARG \

The intentional word-splitting (empty string becomes no argument) is correct behavior, but ShellCheck will flag this. A brief comment like # shellcheck disable=SC2086 -- intentional: empty var must vanish would prevent future contributors from "fixing" it.

[Nit] WinGet manifest schema version (installer.yaml.tpl):

# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json
ManifestVersion: 1.9.0

Declaring ManifestVersion: 1.9.0 is fine and forward-looking. Just noting it should be verified against what the winget-pkgs community repository currently accepts, since newer schema versions sometimes require fields that older winget validate versions do not enforce.

Critical

None -- nice work!

Questions

[Question] Network dependency in test-mode Homebrew formula patching (camel-package.sh, lines ~912-960):

The test-mode code downloads a known-good release from Maven Central to patch the generated formula. This is a clever approach that exercises a real brew install end-to-end. However, it introduces a hard network dependency -- if Maven Central is unreachable during CI, the packaging validation fails even though the packaging logic itself is correct.

Is this expected to run in CI, or only in manual pre-release validation? If CI, would it make sense to have a fallback (e.g., skip the patch with a warning when Maven Central is unreachable, since the formula content correctness is already validated by the non-network tests)?

Overall

This is a high-quality, well-documented piece of release engineering work. The attention to platform parity, error handling, and test isolation is impressive. The Scoop autoupdate hash URL is the one item I would want confirmed before merge. Everything else is solid and ready.

Claude Code on behalf of gnodet -- AI-generated review

@ammachado
ammachado marked this pull request as draft July 20, 2026 02:05
@ammachado ammachado changed the title CAMEL-23703: camel-launcher - JReleaser package generation for five packagers CAMEL-23703: camel-launcher - JReleaser packaging and WinGet distribution Jul 20, 2026
@ammachado
ammachado marked this pull request as ready for review July 20, 2026 05:59
@ammachado
ammachado requested review from davsclaus and gzurowski July 20, 2026 05:59

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: CAMEL-23703 JReleaser package generation (14 new commits)

This re-review covers the 14 new commits added since the initial review. Every finding from the previous review has been addressed, and the new changes represent a significant improvement in both design and robustness.

What stands out in this iteration

Vote-digest candidate promotion (release-distro.sh + stage-winget-distro.sh): The three-layer verification chain — vote-email digest match, .sha512 self-consistency check, GPG signature verification — is a genuinely strong security design. The insight that the candidate number alone cannot distinguish the approved candidate from a superseded one (each RC carries its own self-consistent .sha512 and .asc) is non-obvious and the digest-based approach solves it cleanly. The test coverage in WingetDistroScriptsTest validates all three failure modes (tampered payload, wrong candidate, missing digest) with fully stubbed dependencies.

Shared assembly content (launcher-content.xml): Extracting the common archive content into a shared component descriptor and having both bin.xml and winget-bin.xml reference it is the right structural choice. The test bothArchivesShareOneContentDefinition ensures this invariant survives future refactoring — a detail that makes this more than just "clean code."

CI defense-in-depth for Maven non-deployment: The CI step that deploys to a throwaway local repo and then fails if any *-winget-bin.zip* file appears is an effective guard against accidental Maven attachment. Combined with attach=false in the assembly execution, this provides two independent layers of protection.

Batch wrapper removal: The removal of camel-package.bat was clean — no remaining references anywhere in the diff, and install.ps1 was properly updated to use camel.bat (architecture-neutral) instead of the native executables for the public archive.

Findings

See inline comments. No blocking issues found.

Scanner coverage

  • shellcheck: ran on all .sh files. Findings on pre-existing code in release-distro.sh (unquoted variables, [ -o ] usage); the new code added by this PR uses proper quoting throughout. One informational note on stage-winget-distro.sh (SC2015: A && B || C) which is safe here since usage exits.
  • gitleaks: ran on all release scripts — no leaks found.
  • semgrep, pmd, ast-grep: available but not run (no Java source files with sufficient complexity to warrant it for this review; the test files are well-structured).

Previous review items — status

Previous finding Status
Scoop autoupdate hash URL may 404 Fixedhash key removed from autoupdate block; Scoop computes SHA-256 itself. Test scoopAutoupdateComputesSha256RatherThanReusingTheSha1Sidecar enforces this.
formula_opt_bin is Homebrew private API Acknowledged — still present (was a suggestion, not blocking).

Claude Code on behalf of gnodet — AI-generated review

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Comment thread etc/scripts/release-distro.sh
Comment thread pom.xml

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code on behalf of Claus Ibsen (davsclaus)

Nice work on this PR — the packaging infrastructure is thorough, well-tested, and security-conscious. A few observations:

Findings

Minor

  1. Wildcard import in PackagePlanTest.javaimport java.util.*; will likely be expanded by impsort:sort, generating an uncommitted change that fails CI. See inline comment.

  2. supported-lts.yml only lists 4.22 — the file only contains line "4.22". Since this drives package distribution (not security patches), this seems intentional, but worth confirming it's not an omission of 4.14/4.18.

Questions

  • The publish subcommand in camel-package.sh returns exit 2 with "not yet implemented (Phase 5)". Is there a follow-up task tracked?
  • The jreleaser.yml notes a "KNOWN GAP" about producing both unversioned and versioned Homebrew formulae from a single --channel stable --lts-line X.Y run. Is a follow-up task filed?
  • The jreleaser-plugin-version (1.25.0) is declared as a module-level property rather than centralized. Since <inherited>false</inherited> is set this works fine, but is that intentional?

Strengths

  • Thorough test coverage: 31+ tests in PackagePlanTest, plus WingetDistroScriptsTest with stubbed svn/gpg/curl
  • Security-conscious: attach=false prevents WinGet ZIP from reaching Maven Central; vote-approved SHA-512 digest guards against promoting a superseded candidate
  • CI reproducibility gates (build twice, compare SHA-256) and deploy verification
  • Clean assembly refactoring via shared launcher-content.xml with test invariants enforcing it
  • Correct symlink resolution in camel.c with proper fallback
  • Upgrade guide and release guide updates present

This review covers project rules and conventions. It does not replace specialized review tools such as CodeRabbit, Sourcery, or SonarCloud.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New commit d6ea308 (make the repackaged launcher JAR reproducible) looks good — clean follow-up to the PE timestamp pinning.

What works well:

  • Root cause is clearly explained in the commit message: the 2-arg Repackager.repackage leaves last-modified null, which causes both wall-clock entry timestamps and insertion-order BOOT-INF/lib ordering. Using the 3-arg overload with parseOutputTimestamp fixes both at once.
  • Using MavenArchiver.parseBuildOutputTimestamp is the canonical Maven API — handles ISO-8601, epoch-seconds, single-char disable convention, and SOURCE_DATE_EPOCH fallback correctly.
  • Good defensive design: parseOutputTimestamp returns null when reproducible output is not configured, preserving backward compatibility.
  • Thorough tests cover all four edge cases (ISO, epoch, unset/disabled, malformed).
  • The testInvalidOutputTimestampFailsLoudly test documents an important invariant — a malformed value must fail rather than silently degrading to non-reproducible output.
  • Workflow enhancement (diffing unzip -v output) is a nice diagnostic improvement for distinguishing content changes from timestamp changes.

LGTM ✅

Claude Code on behalf of gnodet — AI-generated review

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New commit 422afeb (replace placeholder repackager tests with real ones) is a significant quality improvement — well done.

What's great:

  • The old tests were all `assertTrue(true, "Placeholder test")` — now they create actual repackaged JARs and verify real invariants:
    • Structure: BOOT-INF/classes, BOOT-INF/lib, root loader classes all present
    • Manifest: Main-Class = JarLauncher, Start-Class = CamelLauncher (swapping these produces a silently broken JAR)
    • Dependency filtering: compile-scope included, test-scope excluded, native exe excluded
    • Reproducibility: two runs produce byte-identical output — the most important invariant given this PR's theme
  • The `writeJar()` fixture uses fixed entry times (`FIXED_ENTRY_TIME`) so the test fixture itself can't introduce the non-determinism the reproducibility test is checking for — good attention to detail.
  • The workflow fix (installing `camel-repackager-maven-plugin` before the launcher build) is a real correctness improvement — without it, changes to the plugin in the same PR would be silently ignored because Maven resolves the plugin from the snapshot repo.
  • Enhanced diagnostic: on mismatch, now also unpacks the launcher JAR from both builds and diffs entries one level deeper.
  • Using `protected` instead of `private` on mojo fields is the standard Maven plugin testing pattern.

LGTM ✅

Claude Code on behalf of gnodet — AI-generated review

ammachado and others added 12 commits July 20, 2026 10:08
…ackagers

Add jreleaser.yml and camel-package.sh/.bat, driving Homebrew, SDKMAN, WinGet,
Scoop, and Chocolatey distribution from a channel-plan + supported-LTS allowlist.
Stages the website installer output (install.sh, install.ps1, release manifest)
alongside the packages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… findings

- Remove stale supported-lts.yml "4.18" entry (package-manager LTS support
  starts at 4.22)
- Add CAMEL_PACKAGE_TEST_SUPPORTED_LTS test override so LTS-expiry tests are
  decoupled from real production supportEnds dates (mirrors
  CAMEL_PACKAGE_TEST_VERSION), and cover the previously-untested
  "line found but expired" rejection branch
- Fail loudly instead of silently succeeding when the LTS Homebrew-formula
  rename step finds zero generated .rb files (sh and bat)
- Replace deprecated wmic date lookup in camel-package.bat with PowerShell,
  and hard-fail if the resolved date is empty/malformed instead of silently
  skipping the LTS expiry check
- Correct jreleaser.yml NOTE and README.md docs that no longer matched
  shipped behavior (unpinned openjdk dependency, ARM64 Chocolatey handling,
  Windows camel.bat usage)
- Gate the Homebrew test-mode formula patch (and the JReleaser snapshot-pattern
  override) on CAMEL_PACKAGE_TEST_VERSION in addition to CAMEL_PACKAGE_TEST_MODE,
  so a genuine release run can never silently repoint the generated formula at a
  different already-published version, and JReleaser keeps its own snapshot guard
  on real releases
- camel-package.bat: fail loudly when `mvn evaluate` yields no project.version
  (reset the variable, then check errorlevel and empty), and when the formula
  rename `move` fails, matching the sh script's set -e behavior
- Distinguish a missing/malformed supported-lts.yml from an unknown LTS line in
  both wrappers, with a dedicated test
- Chocolatey/Scoop native-exe removal: use -ErrorAction Stop behind a Test-Path
  guard instead of -ErrorAction SilentlyContinue, so a failed removal fails
  loudly rather than leaving a stray exe shimmed on PATH
- Scoop manifest checkver/autoupdate: point at Maven Central metadata and the
  real artifact name instead of unset JReleaser template placeholders
- Note in camel-package.bat that the Homebrew test-mode patch is intentionally
  POSIX-only (Homebrew validation runs on macOS/Linux)
- Soften "empirically confirmed"/version-pinned comments to reference the pinned
  JReleaser plugin version, and fix the openjdk-dependency rationale to reflect
  that formula.rb.tpl declares depends_on explicitly
- Add tests covering the LTS Homebrew formula rename, the test-mode formula-patch
  gate, malformed LTS metadata, the documented 4.22 support window, and stronger
  assertions on the JReleaser goal/packager/snapshot-pattern invocation

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fold the standalone "Chocolatey ARM64" README section into the "Native
bootstrap executables" paragraph, and drop the imprecise "native ARM64
support not available" / "works without emulation" wording. choco#1803
tracks per-architecture declaration in Chocolatey's packaging framework
(so a package cannot ship and auto-select per-arch native exes the way
WinGet does), not whether camel.bat runs on ARM64 (it does). Align the
chocolateyinstall.ps1.tpl comment with the same precise wording.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
ammachado and others added 9 commits July 20, 2026 10:08
The include-camel-exe and build-native-exe profiles activate on the
camel.exe.build property, and the release profile sets it in <properties>.
Maven's property activator only reads user and system properties, so a
profile that sets a property cannot activate profiles keyed on it. Verified
with help:active-profiles: under -Prelease alone neither profile is active.

A release therefore built no native Windows executables and no WinGet
payload at all, so the staging step in the release guide could never find
camel-launcher-<version>-winget-bin.zip.

Pass -Dcamel.exe.build=true as a real user property via
maven-release-plugin's <arguments>, and correct the two comments and the
upgrade-guide sentence that claimed -Prelease was sufficient on its own.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Found by invoking jreleaser:config/prepare/package locally in dry-run,
which nothing had done before: every test stubs mvn, so none of these
were reachable by the existing suite.

jreleaser.yml: the second project icon declared a url with no width or
height. JReleaser rejects that, so jreleaser:config failed outright and
camel-package.sh prepare died at its first JReleaser goal.

Templates: JReleaser HTML-escapes double-brace placeholders, rewriting the
"=" in the Homebrew redirector's "?filepath=" as "&apache#61;" and producing a
URL brew cannot download. Render URLs with the triple-brace unescaped form.
Only Homebrew was affected today, but the other packagers use the same form
so a future query parameter cannot silently break them.

Scoop: drop autoupdate.hash. Maven Central publishes only a SHA-1 sidecar
for these artifacts (.sha256 and .sha512 both 404), so pointing autoupdate
at one would downgrade future versions from the pinned SHA-256 to SHA-1.
Without it Scoop downloads the artifact and computes SHA-256 itself.

camel-package.sh: a value-taking option with no value hit `shift 2` with one
argument left, which fails under `set -e` and killed the script with a bare
exit 1 and no message. Report the usage error instead. Treat a missing
Homebrew formula directory on the LTS path as an error rather than skipping
the rename, which would otherwise ship JReleaser's kebab-cased filename.
Delete the test-mode block that rewrote the generated formula's url,
version and sha256: it supported a camel-validate.sh that was never
written, nothing invoked it, and it put network fetches and sed rewriting
of a release artifact into the release path.

camel.c: fall back to the module path when symlink resolution fails, so a
directly-invoked exe still finds camel.bat instead of failing outright.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every dist/dev candidate carries its own self-consistent .sha512 and .asc,
so exporting rc3 when the vote approved rc4 passed both checks and silently
promoted a superseded payload to dist/release. The candidate number cannot
distinguish them; only the digest published with the vote can.

release-distro.sh now takes the approved SHA-512 as a fourth argument,
mandatory whenever a candidate number is given, and aborts if the exported
ZIP does not match. The check runs before the sidecar and signature
verification so the error names the real problem rather than sending the
release manager after a signature that was never broken.
stage-winget-distro.sh prints the digest for the vote email, and honours
CAMEL_GPG_KEY for release managers holding more than one key (the
equivalent of maven-gpg-plugin's gpg.keyname).

The workflow's Maven-exclusion gate matched only the ZIP and its .sha1;
match every sidecar so a leaked .asc or .md5 is caught too.

WingetDistroScriptsTest now drives release-distro.sh against stubbed
wget/svn/gpg rather than asserting on its source text, covering promotion of
the approved candidate, a tampered payload, a superseded candidate, and a
missing digest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bin.xml and winget-bin.xml were near-duplicates differing only by the tar.gz
format and one fileSet. The two archives must deliver the same CLI, so a file
added to one could silently go missing from the other. Move the shared
dependencySet and fileSets into launcher-content.xml and reference it from
both descriptors, leaving each with only its id, formats and, for WinGet, the
native executables. Verified behaviour-preserving: the public archive's entry
list is byte-identical before and after, and the WinGet archive differs from
it by exactly bin/camel-x64.exe and bin/camel-arm64.exe.

PackagePlanTest: replace assertions that matched substrings in pom.xml,
jreleaser.yml, the assembly descriptors and a GitHub workflow with structural
XML, YAML and JSON parsing, so reformatting cannot break a test while a real
content change slips through. Includes resolve through the shared component,
so they describe what each archive carries rather than which file declares it.
Drop the assertion that compared two hardcoded dates, which could never fail,
in favour of running the wrapper against every line the production allowlist
advertises: that starts failing when an LTS line's support window lapses,
which is the intended signal. Drop the test asserting the workflow file's
contents and the one asserting the wrapper's shebang; CI running is the
verification for the former, and fifteen execution tests cover the latter.

Remove the agent planning and design documents committed under docs/, which
is the Antora source tree and not a home for working notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Codex <noreply@openai.com>
The PE format carries a mandatory TimeDateStamp, and lld fills it with the
current wall-clock time unless told otherwise. Two identical builds therefore
produced byte-different executables, and the difference propagated into the
WinGet archive whose SHA-256 is published in the manifest and voted on during
the release. Pass -Wl,--no-insert-timestamp to both cross-compiles.

Also stash the first-build executables before the reproducibility check runs
'clean verify', so a future mismatch reports the differing byte offsets rather
than a bare FAILED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RepackageMojo called the two-argument Repackager.repackage, which leaves the
last-modified time null. That null drives two separate mechanisms in Spring
Boot's packager: every entry is stamped with the current wall-clock time, and
BOOT-INF/lib falls back to a LinkedHashMap ordered by the dependency set's
iteration order rather than a sorted TreeMap.

Consecutive builds of the same source therefore produced different JARs, and
the JAR is the payload of the WinGet archive whose SHA-256 is published in the
manifest and voted on during the release. Pass the pinned
project.build.outputTimestamp instead, read through maven-archiver's
parseBuildOutputTimestamp so the ISO-8601 and epoch-second forms and the
SOURCE_DATE_EPOCH fallback all behave as Maven documents them.

The three-argument overload is @SInCE 4.0.0, so this only became available with
the recent Spring Boot upgrade.

Also extend the workflow's reproducibility diagnostic to diff the WinGet
archive's entry listing, which distinguishes changed content from a changed
timestamp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RepackageMojoTest carried three tests that asserted assertTrue(true) with a
comment describing what they would check one day. They could not fail, so they
reported coverage over exactly the JAR structure this branch is changing.

Drive the mojo over a staged source JAR instead and assert the properties that
matter: application classes under BOOT-INF/classes, dependencies under
BOOT-INF/lib, loader classes at the root, Main-Class pointing at the loader with
Start-Class pointing at the application, and the scope filter holding at the
packaged output rather than only at includeArtifact.

Mojo fields become protected so the test can set them, matching how
EndpointSchemaGeneratorMojoTest drives its mojo.

Two findings worth recording. Spring Boot adds spring-boot-jarmode-tools to
BOOT-INF/lib on its own, so the launcher ships it today; the test asserts around
it rather than silently encoding it as intended. And testRepackagingIsReproducible
still passes when the outputTimestamp fix is reverted: with a null time Spring
Boot derives each nested library's entry time from that library's first entry,
which the fixture pins, so the fixture cannot reproduce the real conditions. The
reproducibility fix therefore remains unproven by unit test.

Also make the workflow install camel-repackager-maven-plugin before the launcher
build. It is outside the -pl list, so the job was resolving it from the Apache
snapshot repository and never exercising the change, and extend the failure
diagnostic to diff the launcher JAR's own entry listing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bin.xml and winget-bin.xml were near-duplicates differing only by the tar.gz
format and one fileSet. The two archives must deliver the same CLI, so a file
added to one could silently go missing from the other. Move the shared
dependencySet and fileSets into launcher-content.xml and reference it from
both descriptors, leaving each with only its id, formats and, for WinGet, the
native executables. Verified behaviour-preserving: the public archive's entry
list is byte-identical before and after, and the WinGet archive differs from
it by exactly bin/camel-x64.exe and bin/camel-arm64.exe.

PackagePlanTest: replace assertions that matched substrings in pom.xml,
jreleaser.yml, the assembly descriptors and a GitHub workflow with structural
XML, YAML and JSON parsing, so reformatting cannot break a test while a real
content change slips through. Includes resolve through the shared component,
so they describe what each archive carries rather than which file declares it.
Drop the assertion that compared two hardcoded dates, which could never fail,
in favour of running the wrapper against every line the production allowlist
advertises: that starts failing when an LTS line's support window lapses,
which is the intended signal. Drop the test asserting the workflow file's
contents and the one asserting the wrapper's shebang; CI running is the
verification for the former, and fifteen execution tests cover the latter.

Remove the agent planning and design documents committed under docs/, which
is the Antora source tree and not a home for working notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ammachado
ammachado force-pushed the CAMEL-23703-jreleaser-packaging branch from 422afeb to cea612f Compare July 20, 2026 14:09
ammachado and others added 2 commits July 20, 2026 11:06
Batch mode (-B) avoids interactive-mode ANSI/progress noise in CI logs
for the exe-build and installation steps that were missing it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "Phase 5" references were tracked only in local notes, not on any
public tracker, and read like a scheduled implementation phase rather
than what they actually are: publish is blocked on the Apache Camel
PMC deciding where each packager's artifacts should go (e.g. Homebrew
to this project's own tap vs. homebrew-core). The Homebrew dual-formula
gap is blocked on that same decision, not a separate follow-up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ammachado

Copy link
Copy Markdown
Contributor Author

Claude Code on behalf of Adriano Machado (ammachado)

Replying to @davsclaus's review:

  • supported-lts.yml only listing 4.22 — not an omission. This file gates which --lts-line values the new CLI packaging pipeline in this PR accepts. 4.14/4.18 were never packaged through this pipeline (it didn't exist), so there's nothing to omit; 4.22 is simply the first line eligible.
  • jreleaser-plugin-version as a module-level property — intentional. jreleaser-maven-plugin is used in exactly this one pom, and the module already declares other single-use version properties locally (maven-version, plexus-*-version, etc.) rather than centralizing. <inherited>false</inherited> is correctly set alongside it.
  • publish / Homebrew dual-formula gap — reworded both comments (camel-package.sh, jreleaser.yml, and the SDKMAN release-notes.md note) to drop the "Phase 5" references, which were only ever tracked in local notes, not a public tracker. The actual blocker: where each packager's artifacts get published (e.g. Homebrew to this project's own tap vs. homebrew-core) is a decision for the Apache Camel PMC. The Homebrew dual-formula gap is blocked on that same decision, not a separate follow-up — the comments now say so explicitly.
  • Wildcard import — already fixed prior to this review; replied inline.

Pushed as two commits: batch-mode (-B) fix for the exe-build CI steps, and the comment rewording above.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New commit 1f0f9e17f602 reviewed — good cleanup. Replacing the opaque "Phase 5" references with the actual blocking reason (PMC publish-destination decision) makes the codebase much more readable for contributors who weren't part of the original planning.

Comment/documentation-only changes — prior approval still applies.

Claude Code on behalf of gnodet — AI-generated review

@ammachado

ammachado commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Status update on the pieces of CAMEL-23703:

Component Status
Java runtime discovery for camel.sh/camel.bat Merged (#24664)
Native camel.exe bootstrap (Windows) Merged (#24665)
Cross-compile camel.exe for x64/arm64 Merged (#24754)
Website installer scripts (install.sh/install.ps1) + release manifests Merged (#24682)
JReleaser packaging (Homebrew, SDKMAN, WinGet, Scoop, Chocolatey) Open, in review (this PR)
Package-native validation CI (installs/uninstalls each generated package and smoke-tests it) Prepared; PR will follow once this one merges

Not yet started:

  • Actual publishing to the package managers. Everything above only prepares/packages the artifacts locally; nothing pushes a release to Homebrew, SDKMAN, WinGet, Scoop, or Chocolatey yet. This needs a PMC decision on publish destinations first, e.g. whether Homebrew formulae go to this project's own tap or to homebrew-core, since that decision shapes how publishing gets wired up. It also blocks producing both the unversioned and versioned Homebrew formula from a single release.
  • Making install.sh/install.ps1 reachable at camel.apache.org. The scripts are done and tested, but nothing serves them yet, so curl -fsSL https://camel.apache.org/install.sh doesn't resolve. That requires a change in the camel-website repo; the tracking issue there (Add Install scripts for Camel Launcher camel-website#1487) is still open with no PR against it.

Claude Code on behalf of Adriano Machado (ammachado)

@davsclaus

Copy link
Copy Markdown
Contributor

LGTM

@davsclaus davsclaus added this to the 4.22.0 milestone Jul 20, 2026
@davsclaus davsclaus added the enhancement New feature or request label Jul 20, 2026
@davsclaus
davsclaus merged commit c30379b into apache:main Jul 20, 2026
10 checks passed
@ammachado
ammachado deleted the CAMEL-23703-jreleaser-packaging branch July 20, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants