Skip to content

feat: support native-aware wrapper install/update flows#2467

Open
maxandersen wants to merge 15 commits into
mainfrom
pr-8
Open

feat: support native-aware wrapper install/update flows#2467
maxandersen wants to merge 15 commits into
mainfrom
pr-8

Conversation

@maxandersen
Copy link
Copy Markdown
Collaborator

@maxandersen maxandersen commented May 18, 2026

Overview

Adds native binary support to JBang wrapper scripts and install/update mechanisms, enabling faster startup and better performance when GraalVM native images are available.

Closes #2458

Note: This PR should be rebased on top of #2472 and #2473 before merging.

Core Features

  • Native binary detection: Wrapper scripts (bash, PowerShell, CMD) detect and use native binaries when JBANG_USE_NATIVE=true
  • Intelligent fallback: Falls back to JAR execution if native binary unavailable
  • Platform-specific bundles: Downloads native platform-specific bundles (e.g. jbang-linux-x64.tar)
  • Automatic fallback: Falls back to generic bundle if native bundle not published yet
  • Safe updates: Uses .new file promotion mechanism for atomic updates

Version Handling

  • Both native and generic bundles respect JBANG_DOWNLOAD_VERSION
  • Supports versioned downloads (e.g., /download/v0.138.0/jbang-linux-x64.tar)
  • Defaults to /latest/download/ when version not specified
  • Consistent behavior between native and generic bundle downloads

Configuration

New environment variables control wrapper behavior:

Variable Default Description
JBANG_USE_NATIVE false If true, prefer native binary over JAR
JBANG_DOWNLOAD_BASEURL https://github.com/jbangdev/jbang/releases Override base URL (for testing/mirrors)
JBANG_DOWNLOAD_VERSION (unset, uses latest) Specific version to download during bootstrap

Script Changes

  • jbang (bash): Native bundle download/install functions, JBANG_USE_NATIVE support, .new file promotion for native binaries
  • jbang.ps1: Same native-aware logic ported to PowerShell
  • jbang.cmd: Native binary detection and .new promotion for Windows CMD

Java Changes

  • Settings.java: Shared getDownloadBaseUrl() respecting JBANG_DOWNLOAD_BASEURL
  • App.java: Native-aware installJBang() with platform detection and fallback
  • VersionChecker.java: Uses shared download base URL, fixed unreachable code

Code Quality Improvements

  • Fixed unreachable code in VersionChecker.retrieveLatestVersion()
  • Replaced Stream.forEach with for-loop (Java best practice for side effects)
  • Used Arrays.asList for Java 8 compatibility
  • Added cross-reference comments to keep Java/bash/PS1 implementations in sync
  • Null-safe string comparisons throughout

Documentation

  • Added comprehensive environment variable table to installation.adoc

Related PRs

PR Description Status
#2472 fix: bash retry loop with user feedback Prerequisite
#2473 build: CI native bundles + jreleaser 1.24.0 Prerequisite
#2467 feat: native-aware wrapper (this PR) Depends on above

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds platform-native bundle packaging and CI publishing, introduces JBANG_DOWNLOAD_BASEURL and Settings.getDownloadBaseUrl(), updates VersionChecker to use the configurable base URL, refactors App.java to prefer native bundles with fallback and atomic .new promotion, and enhances startup scripts (bash, cmd, ps1) to resolve, download, and install native or generic bundles.

Changes

Native Bundle Download & Update Support

Layer / File(s) Summary
Native bundle build and release infrastructure
build.gradle, .github/workflows/step-ci-build.yml, .github/workflows/tag-and-release.yml, jreleaser.yml
Gradle adds nativeDistZip/nativeDistTar tasks and helpers for native bundle naming; CI uploads per-OS native artifacts; tag-and-release job downloads staged distributions; JReleaser entries added for platform .zip/.tar artifacts.
Configurable download base URL and version checks
src/main/java/dev/jbang/Settings.java, src/main/java/dev/jbang/util/VersionChecker.java, docs/modules/ROOT/pages/installation.adoc
Settings exposes JBANG_DOWNLOAD_BASEURL and default releases URL with normalization; VersionChecker builds and validates the version.txt URL from Settings; docs document startup env vars controlling download base, version, native preference, and related settings.
Java installation with native bundle support and file promotion
src/main/java/dev/jbang/cli/App.java
Removes hardcoded jbangUrl; adds helpers to resolve native vs generic bundle URLs (honoring Settings and env vars); installation selects native bundle when requested with fallback to generic; clears cached urls, unpacks chosen bundle; refactors copying into copyFiles supporting .new promotions and nested .jbang/jbang.jar.
Linux/macOS bootstrap script native bundle support
src/main/scripts/jbang
Adds JBANG_DOWNLOAD_BASEURL docs, download retry helper, native exec/arch mapping, URL resolvers with JBANG_DOWNLOAD_URL precedence and latest handling, install_jbang_bundle implementing native-first then fallback behavior, install detection including native binary when enabled, and .new file promotion plus chmod for native binaries.
Windows batch bootstrap script native bundle support
src/main/scripts/jbang.cmd
Adds JBANG_DOWNLOAD_BASEURL default; updates needsInstall to check for native jbang.bin.exe when JBANG_USE_NATIVE=true; promotes jbang.bin.exe.new into place when appropriate.
Windows PowerShell bootstrap script native bundle support
src/main/scripts/jbang.ps1
Adds JBANG_DOWNLOAD_BASEURL docs; introduces helper functions for native naming/arch and URL resolution; adds Install-JBangBundle to fetch native or fallback bundles with extraction and gated .new promotion.
sequenceDiagram
  participant Installer as Installer (App / Scripts)
  participant Settings as Settings
  participant Remote as Release Host
  participant CI as CI/Artifacts
  Installer->>Settings: read JBANG_DOWNLOAD_BASEURL / version
  Installer->>Remote: request native bundle URL (native or fallback)
  CI->>Remote: upload per-OS artifacts (zip/tar)
  Remote-->>Installer: serve selected archive
  Installer->>Installer: extract, promote *.new, chmod/promote exe
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • jbangdev/jbang#2461: Overlapping changes to startup script download/install logic and retry-enabled download helpers; likely related to the script-side download/refetch behavior.

Poem

A rabbit of scripts, so nimble and spry,
Fetches native bundles from cloud or a file,
Hops through Gradle, CI, and releases on high,
Swaps .new with a flourish, then runs with a smile,
Native or JAR — the runner is versatile. 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: support native-aware wrapper install/update flows' accurately reflects the main changes: adding native binary support to wrapper scripts and install/update mechanisms.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from #2458: native bundle artifacts (nativeDistZip/nativeDistTar tasks), wrapper script detection of native binaries with platform-specific downloads, fallback to JAR, version handling, and environment variables (JBANG_USE_NATIVE, JBANG_DOWNLOAD_BASEURL, JBANG_DOWNLOAD_VERSION).
Out of Scope Changes check ✅ Passed All changes are directly related to native binary support in wrappers and CI/build infrastructure. Documentation updates to installation.adoc, Settings.java constants, VersionChecker refactoring, and JReleaser configuration all support the primary objective of native bundle handling and environment variable configuration.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr-8

Comment @coderabbitai help to get the list of available commands and usage tips.

@maxandersen
Copy link
Copy Markdown
Collaborator Author

@quintesse pushing this suggestion on adding native image to the scripts for early feedback + to run test on CI to see if the packaging of native works across the OS's.

Comment thread src/main/java/dev/jbang/cli/App.java Outdated
quintesse
quintesse previously approved these changes May 19, 2026
Copy link
Copy Markdown
Contributor

@quintesse quintesse left a comment

Choose a reason for hiding this comment

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

In general it looks okay, although somewhat verbose and with code duplication (eg download code gets repeated twice).

The only thing I think is actually wrong is that when you set JBANG_USE_NATIVE to true it requires a native binary to exist, if not it will try to download the binary release and install it... each time.

I think we should do that at most once.

We could either come up with some marker system that tells JBang to not try again, but that might get overly complex (you have to think about how you would ever clear those marks, to signal that you want to try again).

Or we simply say that we only try to install binaries for architectures that we know have binary releases.

This would mean that this code:

native_bundle_arch() {
  case "$arch" in
    x86_64|amd64)
      echo "x64"
      ;;
    aarch64|arm64)
      echo "aarch64"
      ;;
    *)
      echo "$arch"
      ;;
  esac
}

Should not print $arch in the default case but an empty string so the code knows not to try to download binaries for that architecture.

If in the future we start supporting that architecture then the moment the user updates to the latest jbang version the binaries will automatically be installed when they first run jbang after the update.

@maxandersen
Copy link
Copy Markdown
Collaborator Author

@quintesse adjusted based on your suggestions and issues found locally - see commits for rationales for the fixes.

@maxandersen maxandersen marked this pull request as ready for review May 22, 2026 05:37
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/scripts/jbang.cmd (2)

47-59: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Promote jbang.bin.exe.new before the early native jump.

Line 47 jumps straight to :run_with_cli whenever binaryPath is already set, so Lines 56-59 never run in the normal native path. That means staged native updates are never promoted and the old binary keeps running.

Suggested fix
+if "%JBANG_USE_NATIVE%"=="true" if exist "%~dp0jbang.bin.exe.new" (
+  copy /y "%~dp0jbang.bin.exe.new" "%~dp0jbang.bin.exe" > nul 2>&1
+  del /f /q "%~dp0jbang.bin.exe.new"
+)
+
 if not "!binaryPath!"=="" goto :run_with_cli
 if not exist "%jarPath%" goto :run_with_cli
 
 if exist "%jarPath%.new" (
   rem a new jbang version was found, we replace the old one with it
   copy /y "%jarPath%.new" "%jarPath%" > nul 2>&1
   del /f /q "%jarPath%.new"
 )
-
-if "%JBANG_USE_NATIVE%"=="true" if exist "%~dp0jbang.bin.exe.new" (
-  copy /y "%~dp0jbang.bin.exe.new" "%~dp0jbang.bin.exe" > nul 2>&1
-  del /f /q "%~dp0jbang.bin.exe.new"
-)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.cmd` around lines 47 - 59, The early jump to
:run_with_cli when %binaryPath% is set prevents promoting staged native updates;
move the promotion block that checks "%JBANG_USE_NATIVE%"=="true" and the
existence of "%~dp0jbang.bin.exe.new" to run before the conditional goto
:run_with_cli so the staged native binary (jbang.bin.exe.new) is copied into
place and deleted prior to any early native-path exit; update the script so the
promotion logic referencing %JBANG_USE_NATIVE% and "%~dp0jbang.bin.exe.new"
executes before the check that jumps to :run_with_cli.

35-44: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Treat the generic fallback as a valid installed state.

If JBANG_USE_NATIVE=true and the native ZIP is missing, the delegated PowerShell install can still succeed with the generic bundle and leave jbang.bin.exe absent. Line 38 then makes needsInstall true on every subsequent launch, so this path keeps re-running the bootstrap instead of using the installed JAR fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.cmd` around lines 35 - 44, The current bootstrap marks
needsInstall=true whenever JBANG_USE_NATIVE=="true" and jbang.bin.exe is
missing, which forces reinstall loops even if the generic jbang.jar is present;
change the logic so the presence of "%JBDIR%\bin\jbang.jar" alone counts as a
valid installed state and only require installation when the jar is missing
(i.e., check for jbang.jar first and set needsInstall=true if it does not exist;
only consider the native jbang.bin.exe requirement if the jar is absent or you
explicitly need to prefer native, otherwise do not set needsInstall just because
jbang.bin.exe is missing). Ensure you update the checks around the needsInstall
variable and the JBANG_USE_NATIVE gate (variables/binaries referenced:
needsInstall, "%JBDIR%\bin\jbang.jar", "%JBDIR%\bin\jbang.bin.exe",
JBANG_USE_NATIVE, jbang.ps1, jbang.cmd).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/tag-and-release.yml:
- Around line 81-83: The workflow uses the deprecated ::set-output command to
export RELEASE_VERSION; update the step that sets RELEASE_VERSION (the
RELEASE_VERSION variable assignment and the echo "::set-output
name=RELEASE_VERSION::$RELEASE_VERSION" line) to write the output to
$GITHUB_OUTPUT instead by emitting a line in the format RELEASE_VERSION=<value>
into $GITHUB_OUTPUT (append using shell redirection) so downstream steps can
read the output.
- Around line 72-77: The download step "Download native bundles" tries to fetch
artifacts matching "*-jbang-native-bundles-*" but no such upload step exists;
add a fail-fast guard so the step only runs when the producer upload step
completed successfully (referencing the existing upload step named
"jreleaser-release"). Update the "Download native bundles" step to include an
if: conditional (e.g., if: ${{ steps.jreleaser-release.outcome == 'success' }})
so the download only runs when the producer step has run and succeeded, or
alternatively change the download pattern to match the actual uploaded artifact
name ("jreleaser-release") if that was intended.

In `@build.gradle`:
- Around line 332-343: The archive tasks nativeDistZip and nativeDistTar
currently rely on commonSpec which uses a configuration-time check for
jbang.bin* existence, so in clean builds the nativeImage output can be missed;
change commonSpec (or the archive task definitions) to include the nativeImage
outputs via task providers instead of a file-exists check — e.g., remove the
config-time File.exists guard and use provider-based inclusion like from({
tasks.named('nativeImage').flatMap { it.outputs.files } }) or
from(tasks.named('nativeImage').map { it.outputs.files }) and keep
dependsOn('nativeImage') so the jbang.bin* produced by nativeImage is always
captured into nativeDistZip/nativeDistTar.

In `@docs/modules/ROOT/pages/installation.adoc`:
- Around line 52-54: Update the documentation entry for the environment variable
JBANG_DOWNLOAD_BASEURL in the "Startup Script Environment Variables" table on
installation.adoc so the documented default matches runtime behavior: change the
shown default from https://github.com/jbangdev/jbang/releases to
https://www.jbang.dev/releases and ensure the descriptive text still explains
this is the base URL used for downloading JBang releases (for testing/offline
mirrors).

In `@src/main/java/dev/jbang/cli/App.java`:
- Around line 256-262: The resolveDownloadUrl method builds URLs with a
hardcoded "v" prefix which causes double "v" when JBANG_DOWNLOAD_VERSION already
includes a leading "v"; update resolveDownloadUrl to normalize the version
string (trim and strip any leading 'v' or 'V') before concatenating so that
resolveDownloadUrl(String version, String bundleName) returns baseUrl +
"/download/v" + normalizedVersion + "/" + bundleName and preserves the existing
latest branch when version is null/empty; reference the resolveDownloadUrl
method to locate and change the normalization logic.

In `@src/main/java/dev/jbang/Settings.java`:
- Around line 47-53: In getDownloadBaseUrl(), the environment override stored in
baseUrl is only checked with trim() for emptiness but the untrimmed value is
returned and normalized, which can produce invalid URLs like " https://... ";
fix by trimming the env value once it's read (assign baseUrl = baseUrl.trim())
before the emptiness check/normalization so replaceAll("/+$", "") runs against
the trimmed string; references: getDownloadBaseUrl(), ENV_DOWNLOAD_BASEURL,
DEFAULT_RELEASES_URL, variable baseUrl.

In `@src/main/scripts/jbang`:
- Around line 385-390: The current check sets needs_install="true" whenever
JBANG_USE_NATIVE=true and the native binary ($native_name) is missing, which
ignores a successful generic bundle install; modify the logic around the
needs_install determination in the same block (symbols: JBANG_USE_NATIVE,
native_name, needs_install, install_jbang_bundle) so that installation is
triggered only if neither the native binary nor the generic jbang launcher/JAR
(e.g., jbang.bin or whatever generic artifact your script uses) exists;
alternatively, after calling install_jbang_bundle("$JBANG_DOWNLOAD_VERSION",
"$JBANG_USE_NATIVE") verify which artifact was actually installed and only treat
missing launchers as needing install on subsequent runs (i.e., check for either
the native binary OR the generic jbang launcher before leaving
needs_install=true).

In `@src/main/scripts/jbang.ps1`:
- Around line 223-228: The script re-triggers installation because when
Install-JBangBundle falls back to the generic jbang.zip it doesn't create the
native executable, so the $needsInstall check (using Get-NativeExecName) stays
true; fix by updating Install-JBangBundle to create the expected native shim at
"$JBDIR\bin\$(Get-NativeExecName)" (or a small launcher/executable-stub)
whenever it falls back to the generic JAR so subsequent runs find the native
exec and $needsInstall becomes false, or alternatively adjust the $needsInstall
logic to consider a successful generic fallback (presence of jbang.jar and
jbang.ps1) as sufficient even when JBANG_USE_NATIVE is "true".
- Around line 153-170: The script uses PowerShell 7 ternary syntax in the
interpolations $($ReleaseVersion ? $ReleaseVersion : 'latest'), which breaks
under the declared PowerShell 5 requirement; replace both occurrences with a
PowerShell 5–compatible conditional that yields $ReleaseVersion when it is
set/non-empty and 'latest' otherwise (either by computing a local variable like
$displayVersion before the WriteLine calls or by using an -if/-else expression),
and keep the surrounding logic in the native/generic download blocks
(references: $ReleaseVersion, Get-NativeBundleUrl, Get-FallbackBundleUrl, the
WriteLine calls).

---

Outside diff comments:
In `@src/main/scripts/jbang.cmd`:
- Around line 47-59: The early jump to :run_with_cli when %binaryPath% is set
prevents promoting staged native updates; move the promotion block that checks
"%JBANG_USE_NATIVE%"=="true" and the existence of "%~dp0jbang.bin.exe.new" to
run before the conditional goto :run_with_cli so the staged native binary
(jbang.bin.exe.new) is copied into place and deleted prior to any early
native-path exit; update the script so the promotion logic referencing
%JBANG_USE_NATIVE% and "%~dp0jbang.bin.exe.new" executes before the check that
jumps to :run_with_cli.
- Around line 35-44: The current bootstrap marks needsInstall=true whenever
JBANG_USE_NATIVE=="true" and jbang.bin.exe is missing, which forces reinstall
loops even if the generic jbang.jar is present; change the logic so the presence
of "%JBDIR%\bin\jbang.jar" alone counts as a valid installed state and only
require installation when the jar is missing (i.e., check for jbang.jar first
and set needsInstall=true if it does not exist; only consider the native
jbang.bin.exe requirement if the jar is absent or you explicitly need to prefer
native, otherwise do not set needsInstall just because jbang.bin.exe is
missing). Ensure you update the checks around the needsInstall variable and the
JBANG_USE_NATIVE gate (variables/binaries referenced: needsInstall,
"%JBDIR%\bin\jbang.jar", "%JBDIR%\bin\jbang.bin.exe", JBANG_USE_NATIVE,
jbang.ps1, jbang.cmd).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e78e114b-d701-472a-95fa-1820167d62b5

📥 Commits

Reviewing files that changed from the base of the PR and between 90dd464 and a1ccb97.

📒 Files selected for processing (11)
  • .github/workflows/step-ci-build.yml
  • .github/workflows/tag-and-release.yml
  • build.gradle
  • docs/modules/ROOT/pages/installation.adoc
  • jreleaser.yml
  • src/main/java/dev/jbang/Settings.java
  • src/main/java/dev/jbang/cli/App.java
  • src/main/java/dev/jbang/util/VersionChecker.java
  • src/main/scripts/jbang
  • src/main/scripts/jbang.cmd
  • src/main/scripts/jbang.ps1

Comment thread .github/workflows/tag-and-release.yml
Comment thread .github/workflows/tag-and-release.yml
Comment on lines +52 to +54
|`JBANG_DOWNLOAD_BASEURL`
|`https://github.com/jbangdev/jbang/releases`
|Base URL for downloading JBang releases (used for testing and offline mirrors)
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the documented default for JBANG_DOWNLOAD_BASEURL to match runtime behavior.

Line 53 documents GitHub releases as default, but the Java default is https://www.jbang.dev/releases. This inconsistency can cause confusing troubleshooting outcomes.

Proposed fix
 |`JBANG_DOWNLOAD_BASEURL`
-|`https://github.com/jbangdev/jbang/releases`
+|`https://www.jbang.dev/releases`
 |Base URL for downloading JBang releases (used for testing and offline mirrors)
As per coding guidelines, "New environment variables must be documented in the reference table in `docs/modules/ROOT/pages/installation.adoc` ("Startup Script Environment Variables")".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/modules/ROOT/pages/installation.adoc` around lines 52 - 54, Update the
documentation entry for the environment variable JBANG_DOWNLOAD_BASEURL in the
"Startup Script Environment Variables" table on installation.adoc so the
documented default matches runtime behavior: change the shown default from
https://github.com/jbangdev/jbang/releases to https://www.jbang.dev/releases and
ensure the descriptive text still explains this is the base URL used for
downloading JBang releases (for testing/offline mirrors).

Comment on lines +256 to +262
private static String resolveDownloadUrl(String version, String bundleName) {
String baseUrl = getDownloadBaseUrl();
if (version == null || version.trim().isEmpty()) {
return baseUrl + "/latest/download/" + bundleName;
} else {
return baseUrl + "/download/v" + version + "/" + bundleName;
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize JBANG_DOWNLOAD_VERSION before adding the v prefix.

With the current logic, setting JBANG_DOWNLOAD_VERSION=v0.138.0 generates /download/vv0.138.0/..., which breaks downloads.

Proposed fix
 private static String resolveDownloadUrl(String version, String bundleName) {
 	String baseUrl = getDownloadBaseUrl();
 	if (version == null || version.trim().isEmpty()) {
 		return baseUrl + "/latest/download/" + bundleName;
 	} else {
-		return baseUrl + "/download/v" + version + "/" + bundleName;
+		String normalizedVersion = version.trim();
+		if (normalizedVersion.startsWith("v")) {
+			normalizedVersion = normalizedVersion.substring(1);
+		}
+		return baseUrl + "/download/v" + normalizedVersion + "/" + bundleName;
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static String resolveDownloadUrl(String version, String bundleName) {
String baseUrl = getDownloadBaseUrl();
if (version == null || version.trim().isEmpty()) {
return baseUrl + "/latest/download/" + bundleName;
} else {
return baseUrl + "/download/v" + version + "/" + bundleName;
}
private static String resolveDownloadUrl(String version, String bundleName) {
String baseUrl = getDownloadBaseUrl();
if (version == null || version.trim().isEmpty()) {
return baseUrl + "/latest/download/" + bundleName;
} else {
String normalizedVersion = version.trim();
if (normalizedVersion.startsWith("v")) {
normalizedVersion = normalizedVersion.substring(1);
}
return baseUrl + "/download/v" + normalizedVersion + "/" + bundleName;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/dev/jbang/cli/App.java` around lines 256 - 262, The
resolveDownloadUrl method builds URLs with a hardcoded "v" prefix which causes
double "v" when JBANG_DOWNLOAD_VERSION already includes a leading "v"; update
resolveDownloadUrl to normalize the version string (trim and strip any leading
'v' or 'V') before concatenating so that resolveDownloadUrl(String version,
String bundleName) returns baseUrl + "/download/v" + normalizedVersion + "/" +
bundleName and preserves the existing latest branch when version is null/empty;
reference the resolveDownloadUrl method to locate and change the normalization
logic.

Comment thread src/main/java/dev/jbang/Settings.java
Comment thread src/main/scripts/jbang
Comment thread src/main/scripts/jbang.ps1 Outdated
Comment on lines +153 to +170
if ($NativeRequested -eq "true") {
$jburl = Get-NativeBundleUrl $ReleaseVersion
[Console]::Error.WriteLine("Downloading JBang $($ReleaseVersion ? $ReleaseVersion : 'latest') native bundle...")
try { Invoke-WebRequest "$jburl" -OutFile $bundlePath; $ok=$? } catch {
$ok=$false
$err=$_
}
if (-not $ok) {
[Console]::Error.WriteLine("WARNING: Native JBang bundle not available from $jburl, falling back to generic bundle")
$jburl = Get-FallbackBundleUrl $ReleaseVersion
try { Invoke-WebRequest "$jburl" -OutFile $bundlePath; $ok=$? } catch {
$ok=$false
$err=$_
}
}
} else {
$jburl="https://github.com/jbangdev/jbang/releases/download/v$env:JBANG_DOWNLOAD_VERSION/jbang.zip";
}
[Console]::Error.WriteLine("Downloading JBang $env:JBANG_DOWNLOAD_VERSION...")
try { Invoke-WebRequest "$jburl" -OutFile "$TDIR\urls\jbang.zip"; $ok=$? } catch {
$ok=$false
$err=$_
$jburl = Get-FallbackBundleUrl $ReleaseVersion
[Console]::Error.WriteLine("Downloading JBang $($ReleaseVersion ? $ReleaseVersion : 'latest')...")
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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Windows PowerShell 5.1 support the ternary operator (condition ? expr1 : expr2), or was it introduced in PowerShell 7?

💡 Result:

Windows PowerShell 5.1 does not support the ternary operator [1][2]. The ternary operator (condition ? expr1 : expr2) was introduced in PowerShell 7.0 [1][3]. While users of Windows PowerShell 5.1 often use if-else statements as the standard alternative for conditional logic [2], they can also utilize expression-based if or switch statements, provided the results are wrapped in subexpressions $() and properly handled to avoid issues with null or empty collections [3]. Official Microsoft documentation confirms that the ternary operator syntax is a feature of PowerShell 7 and later versions [1][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '130,190p' src/main/scripts/jbang.ps1 | nl -ba | sed -n '1,120p'

Repository: jbangdev/jbang

Length of output: 101


🏁 Script executed:

python3 - <<'PY'
path="src/main/scripts/jbang.ps1"
start=130
end=190
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f, start=1):
        if start <= i <= end:
            print(f"{i:4d}:{line.rstrip()}")
PY

Repository: jbangdev/jbang

Length of output: 2560


🏁 Script executed:

python3 - <<'PY'
path="src/main/scripts/jbang.ps1"
with open(path,'r',encoding='utf-8') as f:
    for i in range(1,60):
        line=f.readline()
        if not line: break
        print(f"{i:3d}:{line.rstrip()}")
PY

Repository: jbangdev/jbang

Length of output: 2728


Remove PowerShell 7 ternary syntax from this PowerShell 5 script

src/main/scripts/jbang.ps1 uses $($ReleaseVersion ? $ReleaseVersion : 'latest') at lines 155 and 170, but the script declares #Requires -Version 5, and Windows PowerShell 5.1 cannot parse the condition ? expr1 : expr2 ternary operator (it’s a PowerShell 7+ feature). Replace with a PowerShell 5-compatible conditional.

Suggested fix
 function Install-JBangBundle {
     param([string]$ReleaseVersion, [string]$NativeRequested)
 
     $bundlePath = "$TDIR\urls\jbang.zip"
+    $displayVersion = if ($ReleaseVersion) { $ReleaseVersion } else { "latest" }
     New-Item -ItemType Directory -Force -Path "$TDIR\urls" >$null 2>&1
 
     if ($NativeRequested -eq "true") {
         $jburl = Get-NativeBundleUrl $ReleaseVersion
-        [Console]::Error.WriteLine("Downloading JBang $($ReleaseVersion ? $ReleaseVersion : 'latest') native bundle...")
+        [Console]::Error.WriteLine("Downloading JBang $displayVersion native bundle...")
         try { Invoke-WebRequest "$jburl" -OutFile $bundlePath; $ok=$? } catch {
           $ok=$false
           $err=$_
         }
@@
     } else {
         $jburl = Get-FallbackBundleUrl $ReleaseVersion
-        [Console]::Error.WriteLine("Downloading JBang $($ReleaseVersion ? $ReleaseVersion : 'latest')...")
+        [Console]::Error.WriteLine("Downloading JBang $displayVersion...")
         try { Invoke-WebRequest "$jburl" -OutFile $bundlePath; $ok=$? } catch {
           $ok=$false
           $err=$_
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.ps1` around lines 153 - 170, The script uses
PowerShell 7 ternary syntax in the interpolations $($ReleaseVersion ?
$ReleaseVersion : 'latest'), which breaks under the declared PowerShell 5
requirement; replace both occurrences with a PowerShell 5–compatible conditional
that yields $ReleaseVersion when it is set/non-empty and 'latest' otherwise
(either by computing a local variable like $displayVersion before the WriteLine
calls or by using an -if/-else expression), and keep the surrounding logic in
the native/generic download blocks (references: $ReleaseVersion,
Get-NativeBundleUrl, Get-FallbackBundleUrl, the WriteLine calls).

Comment thread src/main/scripts/jbang.ps1
Adds native binary support to JBang wrapper scripts and install/update mechanisms.

- Wrapper scripts (bash, PowerShell, CMD) now detect and use native binaries when JBANG_USE_NATIVE=true
- Falls back to JAR execution if native binary unavailable
- Download native platform-specific bundles (jbang-{os}-{arch}.tar/zip)
- Automatic fallback to generic bundle if native bundle missing
- Safe update mechanism using .new files promoted on next wrapper run

- Both native and generic bundles respect JBANG_DOWNLOAD_VERSION
- Supports versioned downloads (e.g., /download/v0.138.0/jbang-linux-x64.tar)
- Defaults to /latest/download/ when version not specified

- JBANG_USE_NATIVE: prefer native binary over JAR
- JBANG_DOWNLOAD_BASEURL: override release base URL (for testing/mirrors)
- JBANG_DOWNLOAD_VERSION: specific version to download during bootstrap

- Added nativeDistZip and nativeDistTar Gradle tasks
- CI builds and uploads native bundles for Linux, macOS, Windows
- JReleaser configured to include native bundle artifacts

- Fixed unreachable code in VersionChecker.retrieveLatestVersion()
- Improved exception logging in native bundle fallback
- Replaced Stream.forEach with for-loop for side effects
- Used Arrays.asList for Java 8 compatibility
- Added cross-reference comments to keep Java/bash/PS1 in sync
- Null-safe string comparisons throughout

- Added environment variable table to installation.adoc
- Documents JBANG_USE_NATIVE, JBANG_DOWNLOAD_BASEURL, JBANG_DOWNLOAD_VERSION

Closes #2458
The retval variable was set by download() but never updated after
the tar extraction. If tar failed (corrupted archive, disk full),
the error was silently ignored because retval still held the
download exit code (0). Now we capture $? after tar.
The variable was declared but no longer used after the download
logic was refactored to reuse jburl for the fallback path.
Align getNativeBundleArch() in build.gradle with the same function
in App.java, jbang (bash), and jbang.ps1 — all of which now return
empty/null for unrecognized architectures to prevent attempting
native bundle downloads on unsupported platforms.
The copyFiles refactor inadvertently changed jbang.jar to use .new
promotion on all platforms. The original code only used .new for
jbang.jar on Windows (to work around locked-file issues). Native
binaries (jbang.bin/jbang.bin.exe) correctly use .new on all
platforms since a running binary cannot be replaced in place.
Floating tags (e.g. @v4, @v2) are vulnerable to supply-chain
attacks. Restore the original pinned SHA references for
actions/download-artifact and jreleaser/release-action.
Both App.java and VersionChecker.java duplicated the same
DEFAULT_JBANG_RELEASES_URL constant and env-var-reading logic
for JBANG_DOWNLOAD_BASEURL. Centralise in Settings to avoid
divergence.
Comment thread src/main/java/dev/jbang/Settings.java Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@build.gradle`:
- Around line 332-343: The native bundle tasks nativeDistZip and nativeDistTar
rely on commonSpec which references build/tmp/version.txt but only declare
dependsOn('nativeImage'), so running these tasks on a clean workspace can
produce bundles missing version.txt; update the task registrations for
nativeDistZip and nativeDistTar to add an explicit dependency on the task or
file that generates version.txt (e.g., dependsOn('versionTxt') or
dependsOn(tasks.named('versionTxt'))), ensuring versionTxt is executed before
packaging while keeping existing nativeImage dependency.

In `@src/main/scripts/jbang`:
- Around line 70-73: The case statement that maps native bundle architectures
(the case on variable "arch") only matches "x86_64|amd64" and therefore misses
when "arch" was already normalized to "x64"; update the pattern in that case arm
to also match "x64" (e.g., change the pattern to "x86_64|amd64|x64") so the
branch echoes "x64" for normalized inputs and the native bundle download logic
uses the correct architecture string.

In `@src/main/scripts/jbang.cmd`:
- Around line 37-39: The current check sets needsInstall when JBANG_USE_NATIVE
is true and jbang.bin.exe is missing, which forces reinstall even if a generic
fallback (jbang.jar + jbang.cmd) is already present; change the condition that
sets needsInstall for native mode to require both that jbang.bin.exe is missing
AND that the generic fallback (jbang.cmd or jbang.jar) is also missing — i.e.,
update the logic around JBANG_USE_NATIVE, jbang.bin.exe, jbang.cmd and jbang.jar
so the script does not mark needsInstall=true for native when only the native
binary is absent but a working fallback exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d346eb7-88d5-4f10-9b6d-5ed64859b2f0

📥 Commits

Reviewing files that changed from the base of the PR and between a1ccb97 and 84086c9.

📒 Files selected for processing (11)
  • .github/workflows/step-ci-build.yml
  • .github/workflows/tag-and-release.yml
  • build.gradle
  • docs/modules/ROOT/pages/installation.adoc
  • jreleaser.yml
  • src/main/java/dev/jbang/Settings.java
  • src/main/java/dev/jbang/cli/App.java
  • src/main/java/dev/jbang/util/VersionChecker.java
  • src/main/scripts/jbang
  • src/main/scripts/jbang.cmd
  • src/main/scripts/jbang.ps1
✅ Files skipped from review due to trivial changes (1)
  • docs/modules/ROOT/pages/installation.adoc

Comment thread build.gradle
Comment on lines +332 to +343
tasks.register('nativeDistZip', Zip) {
dependsOn('nativeImage')
with commonSpec
archiveFileName = "${getNativeBundleBaseName()}.zip"
}

tasks.register('nativeDistTar', Tar) {
dependsOn('nativeImage')
with commonSpec
compression = Compression.NONE
archiveFileName = "${getNativeBundleBaseName()}.tar"
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add versionTxt as an explicit dependency for native bundle tasks.

commonSpec includes build/tmp/version.txt, but these tasks only depend on nativeImage. Running them directly on a clean workspace can package bundles without version.txt.

Proposed fix
 tasks.register('nativeDistZip', Zip) {
-	dependsOn('nativeImage')
+	dependsOn('nativeImage', 'versionTxt')
 	with commonSpec
 	archiveFileName = "${getNativeBundleBaseName()}.zip"
 }
 
 tasks.register('nativeDistTar', Tar) {
-	dependsOn('nativeImage')
+	dependsOn('nativeImage', 'versionTxt')
 	with commonSpec
 	compression = Compression.NONE
 	archiveFileName = "${getNativeBundleBaseName()}.tar"
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tasks.register('nativeDistZip', Zip) {
dependsOn('nativeImage')
with commonSpec
archiveFileName = "${getNativeBundleBaseName()}.zip"
}
tasks.register('nativeDistTar', Tar) {
dependsOn('nativeImage')
with commonSpec
compression = Compression.NONE
archiveFileName = "${getNativeBundleBaseName()}.tar"
}
tasks.register('nativeDistZip', Zip) {
dependsOn('nativeImage', 'versionTxt')
with commonSpec
archiveFileName = "${getNativeBundleBaseName()}.zip"
}
tasks.register('nativeDistTar', Tar) {
dependsOn('nativeImage', 'versionTxt')
with commonSpec
compression = Compression.NONE
archiveFileName = "${getNativeBundleBaseName()}.tar"
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build.gradle` around lines 332 - 343, The native bundle tasks nativeDistZip
and nativeDistTar rely on commonSpec which references build/tmp/version.txt but
only declare dependsOn('nativeImage'), so running these tasks on a clean
workspace can produce bundles missing version.txt; update the task registrations
for nativeDistZip and nativeDistTar to add an explicit dependency on the task or
file that generates version.txt (e.g., dependsOn('versionTxt') or
dependsOn(tasks.named('versionTxt'))), ensuring versionTxt is executed before
packaging while keeping existing nativeImage dependency.

Comment thread src/main/scripts/jbang
Comment on lines +70 to +73
case "$arch" in
x86_64|amd64)
echo "x64"
;;
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix x64 mapping in native bundle architecture resolution.

Line 70 only matches x86_64|amd64, but arch is normalized to x64 earlier (Line 334). On x64 hosts this returns empty arch and skips native bundle download.

Suggested patch
 native_bundle_arch() {
   case "$arch" in
-    x86_64|amd64)
+    x86_64|amd64|x64)
       echo "x64"
       ;;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "$arch" in
x86_64|amd64)
echo "x64"
;;
case "$arch" in
x86_64|amd64|x64)
echo "x64"
;;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang` around lines 70 - 73, The case statement that maps
native bundle architectures (the case on variable "arch") only matches
"x86_64|amd64" and therefore misses when "arch" was already normalized to "x64";
update the pattern in that case arm to also match "x64" (e.g., change the
pattern to "x86_64|amd64|x64") so the branch echoes "x64" for normalized inputs
and the native bundle download logic uses the correct architecture string.

Comment on lines +37 to +39
if not exist "%JBDIR%\bin\jbang.jar" set needsInstall=true
if "%JBANG_USE_NATIVE%"=="true" if not exist "%JBDIR%\bin\jbang.bin.exe" set needsInstall=true
if "!needsInstall!"=="true" (
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid install loop after successful generic fallback in native mode.

Line 38 makes install mandatory whenever jbang.bin.exe is missing. If fallback installed only jbang.jar + jbang.cmd, this keeps reinstalling every run.

Suggested patch
   set needsInstall=false
   if not exist "%JBDIR%\bin\jbang.jar" set needsInstall=true
-  if "%JBANG_USE_NATIVE%"=="true" if not exist "%JBDIR%\bin\jbang.bin.exe" set needsInstall=true
+  if not exist "%JBDIR%\bin\jbang.cmd" set needsInstall=true
+  if "%JBANG_USE_NATIVE%"=="true" if not exist "%JBDIR%\bin\jbang.bin.exe" if not exist "%JBDIR%\bin\jbang.cmd" set needsInstall=true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.cmd` around lines 37 - 39, The current check sets
needsInstall when JBANG_USE_NATIVE is true and jbang.bin.exe is missing, which
forces reinstall even if a generic fallback (jbang.jar + jbang.cmd) is already
present; change the condition that sets needsInstall for native mode to require
both that jbang.bin.exe is missing AND that the generic fallback (jbang.cmd or
jbang.jar) is also missing — i.e., update the logic around JBANG_USE_NATIVE,
jbang.bin.exe, jbang.cmd and jbang.jar so the script does not mark
needsInstall=true for native when only the native binary is absent but a working
fallback exists.

@maxandersen
Copy link
Copy Markdown
Collaborator Author

@quintesse I rebased with main so this has retry logic - what I then noticed that when url is unreachable jbang just looks stuck so I added printing of url on download + a retry line printed on retry.

With the latest env options its now easier to test and validate before we go live, you can test it using:

ensure no global jbang.jar available:

rm -rf ~/.jbang/bin/jbang.jar

run with specific url:

JBANG_DOWNLOAD_URL=notthere.jar ./src/main/scripts/jbang

or change just version:

JBANG_DOWNLOAD_VERSION=0.118 ./src/main/scripts/jbang

so output is now like this:

JBANG_DOWNLOAD_VERSION=0.118.0not ./src/main/scripts/jbang
Downloading JBang 0.118.0not from https://github.com/jbangdev/jbang/releases/download/v0.118.0not/jbang.tar...
Download attempt 1/6 failed, retrying in 1 second(s)...
Download attempt 2/6 failed, retrying in 2 second(s)...
Download attempt 3/6 failed, retrying in 4 second(s)...
Download attempt 4/6 failed, retrying in 8 second(s)...

Meaning retries are visible but also that url is shown on each download to user which in the past we kept hidden.

@quintesse wdyt? would like second set of eyes before pushing this :)

curl's --retry and wget's --tries flags operate silently when combined
with -s/-q, giving no indication that retries are happening. This makes
it impossible to diagnose download failures in the field.

Replace the built-in retry delegation with a manual retry loop (matching
the approach already used in jbang.ps1's Invoke-Download), providing:
- Retry attempt feedback: 'Download attempt 1/6 failed, retrying in 1 second(s)...'
- Exponential backoff by default (1s, 2s, 4s, 8s...), or fixed delay via JBANG_DOWNLOAD_RETRY_DELAY
- URL shown on the initial download line for both bash and PS1 scripts

Also adds JBANG_DOWNLOAD_URL support to resolve_download_url() (bash)
and Resolve-DownloadUrl (PS1) as a full URL override, integrated with
the native-aware install refactoring.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Max Rydahl Andersen <max.andersen@gmail.com>
@maxandersen
Copy link
Copy Markdown
Collaborator Author

fixed the failure in ps1 due to bad/different exit behaviour than tests expected.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/scripts/jbang.ps1 (1)

130-137: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The new ARM64 mapping is unreachable with the current architecture initialization.

Line 46 still hard-codes $arch = 'x64', so this function never sees arm64 on Windows ARM64 hosts. The wrapper will keep resolving x64 native bundles and x64 JDK downloads instead of the aarch64 variants described in the PR.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.ps1` around lines 130 - 137, Get-NativeBundleArch
never sees "arm64" because $arch is currently hard-coded to 'x64'; fix by
replacing the hard-coded $arch initialization with runtime detection (e.g.,
check $env:PROCESSOR_ARCHITECTURE or use WMI/Get-CimInstance to detect the
OS/processor arch) so $arch can be set to the actual value
("ARM64"/"arm64"/"aarch64" or "AMD64"/"x86_64") before calling
Get-NativeBundleArch; ensure the normalization in Get-NativeBundleArch still
maps "arm64" to "aarch64" so Windows ARM64 hosts resolve aarch64 bundles and
JDKs.
♻️ Duplicate comments (4)
src/main/scripts/jbang.ps1 (2)

188-197: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Avoid PowerShell 7 ternary syntax in a PowerShell 5 script.

#Requires -Version 5 means these interpolations are parsed by Windows PowerShell 5.1, where ? : is unsupported. As written, the script fails before it can run.

Suggested fix
 function Install-JBangBundle {
     param([string]$ReleaseVersion, [string]$NativeRequested)
 
     $bundlePath = "$TDIR\urls\jbang.zip"
+    $displayVersion = if ($ReleaseVersion) { $ReleaseVersion } else { "latest" }
     New-Item -ItemType Directory -Force -Path "$TDIR\urls" >$null 2>&1
 
     if ($NativeRequested -eq "true") {
         $jburl = Get-NativeBundleUrl $ReleaseVersion
-        [Console]::Error.WriteLine("Downloading JBang $($ReleaseVersion ? $ReleaseVersion : 'latest') native bundle from $jburl...")
+        [Console]::Error.WriteLine("Downloading JBang $displayVersion native bundle from $jburl...")
         $ok = Invoke-Download "$jburl" $bundlePath
         if (-not $ok) {
           [Console]::Error.WriteLine("WARNING: Native JBang bundle not available from $jburl, falling back to generic bundle")
           $jburl = Get-FallbackBundleUrl $ReleaseVersion
           $ok = Invoke-Download "$jburl" $bundlePath
         }
     } else {
         $jburl = Get-FallbackBundleUrl $ReleaseVersion
-        [Console]::Error.WriteLine("Downloading JBang $($ReleaseVersion ? $ReleaseVersion : 'latest') from $jburl...")
+        [Console]::Error.WriteLine("Downloading JBang $displayVersion from $jburl...")
         $ok = Invoke-Download "$jburl" $bundlePath
     }
Does Windows PowerShell 5.1 support the ternary operator (`condition ? expr1 : expr2`), or was it introduced in PowerShell 7?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.ps1` around lines 188 - 197, The string interpolation
uses PowerShell 7 ternary syntax ($($ReleaseVersion ? $ReleaseVersion :
'latest')) which breaks on Windows PowerShell 5. Replace the inline ternary by
computing a local variable (e.g., $ver) before the download calls: set $ver = if
($ReleaseVersion) { $ReleaseVersion } else { 'latest' } and then use $ver in the
messages and when calling Get-FallbackBundleUrl/Invoke-Download (references:
$ReleaseVersion, Invoke-Download, Get-FallbackBundleUrl).

246-249: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't redownload after a successful generic fallback.

With JBANG_USE_NATIVE=true, a fallback to jbang.zip leaves jbang.bin.exe absent by design. This check makes $needsInstall true again on every launch, so callers that rely on the bootstrap script keep reinstalling instead of reusing the JAR fallback.

Suggested fix
-  if ($env:JBANG_USE_NATIVE -eq "true" -and -not (Test-Path "$JBDIR\bin\$(Get-NativeExecName)")) {
+  if ($env:JBANG_USE_NATIVE -eq "true" `
+      -and -not (Test-Path "$JBDIR\bin\$(Get-NativeExecName)") `
+      -and (-not (Test-Path "$JBDIR\bin\jbang.jar") -or -not (Test-Path "$JBDIR\bin\jbang.ps1"))) {
     $needsInstall = $true
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang.ps1` around lines 246 - 249, The launch loop currently
forces reinstallation when JBANG_USE_NATIVE="true" if the native binary from
Get-NativeExecName is missing even though a fallback JAR (jbang.jar) was
successfully installed; update the native-check branch to only mark
$needsInstall = $true when the native binary is missing AND the fallback JAR is
also missing (i.e., add a Test-Path "$JBDIR\bin\jbang.jar" negative check to the
condition). This change touches the $needsInstall variable and the if that
checks $env:JBANG_USE_NATIVE and Get-NativeExecName so the script will reuse the
JAR fallback instead of redownloading.
src/main/scripts/jbang (2)

70-73: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Recognize normalized x64 here.

Line 334 already normalizes x86_64|amd64 to x64, so this helper returns an empty arch on the common path and native mode always falls back to the generic bundle.

Suggested fix
 native_bundle_arch() {
   case "$arch" in
-    x86_64|amd64)
+    x86_64|amd64|x64)
       echo "x64"
       ;;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang` around lines 70 - 73, The helper that computes a
platform-specific arch is missing recognition for the already-normalized "x64"
value, so when "x86_64|amd64" is normalized to "x64" earlier the helper returns
empty and native mode falls back to generic; update the helper's case block (the
one using pattern case "$arch" in and matching x86_64|amd64) to also accept
"x64" (e.g., add x64 to the pattern alongside x86_64|amd64) so the normalized
value returns the expected arch string instead of empty.

412-414: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't force a reinstall after a successful generic fallback.

If JBANG_USE_NATIVE=true but the native bundle was unavailable, a valid generic install still leaves $native_name absent. This block flips needs_install back to true on every launch, so the bootstrap script keeps downloading instead of reusing the JAR fallback.

Suggested fix
-  if [[ "$JBANG_USE_NATIVE" == "true" && ! -f "$JBDIR/bin/$native_name" ]]; then
+  if [[ "$JBANG_USE_NATIVE" == "true" && ! -f "$JBDIR/bin/$native_name" \
+        && ( ! -f "$JBDIR/bin/jbang.jar" || ! -f "$JBDIR/bin/jbang" ) ]]; then
     needs_install="true"
   fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/scripts/jbang` around lines 412 - 414, The current check sets
needs_install="true" whenever JBANG_USE_NATIVE=="true" and the native binary
"$JBDIR/bin/$native_name" is missing, which forces reinstall even when a working
generic fallback is present; change the condition in that if (the one
referencing JBANG_USE_NATIVE, JBDIR and native_name) to only set
needs_install="true" if the native binary is missing AND there is no usable
generic fallback (e.g. test that the fallback executable/jar such as
"$JBDIR/jbang" or "$JBDIR/jbang.jar" is also missing); in short, add a second
negative check for the generic fallback before flipping needs_install so an
existing JAR fallback isn’t overridden.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/main/scripts/jbang.ps1`:
- Around line 130-137: Get-NativeBundleArch never sees "arm64" because $arch is
currently hard-coded to 'x64'; fix by replacing the hard-coded $arch
initialization with runtime detection (e.g., check $env:PROCESSOR_ARCHITECTURE
or use WMI/Get-CimInstance to detect the OS/processor arch) so $arch can be set
to the actual value ("ARM64"/"arm64"/"aarch64" or "AMD64"/"x86_64") before
calling Get-NativeBundleArch; ensure the normalization in Get-NativeBundleArch
still maps "arm64" to "aarch64" so Windows ARM64 hosts resolve aarch64 bundles
and JDKs.

---

Duplicate comments:
In `@src/main/scripts/jbang`:
- Around line 70-73: The helper that computes a platform-specific arch is
missing recognition for the already-normalized "x64" value, so when
"x86_64|amd64" is normalized to "x64" earlier the helper returns empty and
native mode falls back to generic; update the helper's case block (the one using
pattern case "$arch" in and matching x86_64|amd64) to also accept "x64" (e.g.,
add x64 to the pattern alongside x86_64|amd64) so the normalized value returns
the expected arch string instead of empty.
- Around line 412-414: The current check sets needs_install="true" whenever
JBANG_USE_NATIVE=="true" and the native binary "$JBDIR/bin/$native_name" is
missing, which forces reinstall even when a working generic fallback is present;
change the condition in that if (the one referencing JBANG_USE_NATIVE, JBDIR and
native_name) to only set needs_install="true" if the native binary is missing
AND there is no usable generic fallback (e.g. test that the fallback
executable/jar such as "$JBDIR/jbang" or "$JBDIR/jbang.jar" is also missing); in
short, add a second negative check for the generic fallback before flipping
needs_install so an existing JAR fallback isn’t overridden.

In `@src/main/scripts/jbang.ps1`:
- Around line 188-197: The string interpolation uses PowerShell 7 ternary syntax
($($ReleaseVersion ? $ReleaseVersion : 'latest')) which breaks on Windows
PowerShell 5. Replace the inline ternary by computing a local variable (e.g.,
$ver) before the download calls: set $ver = if ($ReleaseVersion) {
$ReleaseVersion } else { 'latest' } and then use $ver in the messages and when
calling Get-FallbackBundleUrl/Invoke-Download (references: $ReleaseVersion,
Invoke-Download, Get-FallbackBundleUrl).
- Around line 246-249: The launch loop currently forces reinstallation when
JBANG_USE_NATIVE="true" if the native binary from Get-NativeExecName is missing
even though a fallback JAR (jbang.jar) was successfully installed; update the
native-check branch to only mark $needsInstall = $true when the native binary is
missing AND the fallback JAR is also missing (i.e., add a Test-Path
"$JBDIR\bin\jbang.jar" negative check to the condition). This change touches the
$needsInstall variable and the if that checks $env:JBANG_USE_NATIVE and
Get-NativeExecName so the script will reuse the JAR fallback instead of
redownloading.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d1d9981-449e-4ee2-a1ff-826467f894c7

📥 Commits

Reviewing files that changed from the base of the PR and between 84086c9 and ee7051b.

📒 Files selected for processing (2)
  • src/main/scripts/jbang
  • src/main/scripts/jbang.ps1

@quintesse
Copy link
Copy Markdown
Contributor

One thing I see is a difference in output between WIndows and Linux/Mac: "Error downloading JBang from notthere.jar to C:\Users\tako.jbang\cache\urls\jbang.zip" vs "Error downloading JBang from notthere.jar"

@quintesse
Copy link
Copy Markdown
Contributor

The other thing is that when both JBANG_DOWNLOAD_VERSION and JBANG_DOWNLOAD_URL are defined we might want to prioritize the URL, right now it takes the VERSION.

@quintesse
Copy link
Copy Markdown
Contributor

Also, we might want to check that the values aren't empty strings. On Powershell it was much easier for me to set the env vars to empty than to delete them and it tried to download from "".

@quintesse
Copy link
Copy Markdown
Contributor

Didn't have time to figure out why exactly, but this doesn't work:

$ BANG_DOWNLOAD_URL="https://github.com/jbangdev/jbang/releases/download/v0.138.0/jbang-0.138.0.tar" ./src/main/scripts/jbang
Downloading JBang latest from https://github.com/jbangdev/jbang/releases/download/v0.138.0/jbang-0.138.0.tar...
Installing JBang...
cp: cannot stat '/c/Users/tako/.jbang/cache/urls/jbang/bin/*': No such file or directory
./src/main/scripts/jbang: line 419: /c/Users/tako/.jbang/bin/jbang: No such file or directory

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/step-ci-build.yml:
- Around line 298-307: The jreleaser-dry-run job should explicitly restrict
GITHUB_TOKEN permissions and disable credential persistence on checkout: add a
job-level permissions block to the jreleaser-dry-run job (e.g., permissions:
contents: read, actions: read, packages: write — adjust to the actual minimal
scopes you need for artifact download/upload) and set persist-credentials: false
on the actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd step to prevent
the token from being left in the workspace.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ed62319-b2da-4be9-bf3a-a37dd1338d2f

📥 Commits

Reviewing files that changed from the base of the PR and between 7da832a and e794ace.

📒 Files selected for processing (1)
  • .github/workflows/step-ci-build.yml

Comment on lines +298 to +307
jreleaser-dry-run:
needs: [build-shared, build-test-native-image]
if: always() && needs.build-shared.result == 'success'
runs-on: ubuntu-latest
env:
JRELEASER_VERSION: 1.19.0
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/step-ci-build.yml"
echo "== File exists? =="
ls -l "$FILE" || exit 1

echo "== Show context around lines 280-360 =="
nl -ba "$FILE" | sed -n '280,360p'

echo "== Find all 'permissions:' occurrences in workflow =="
rg -n "^\s*permissions\s*:" "$FILE" || true

echo "== Find all actions/checkout uses in workflow =="
rg -n "uses:\s*actions/checkout@" "$FILE" -n || true

echo "== Show checkout step options near jreleaser-dry-run =="
# Extract the jreleaser-dry-run block roughly
python3 - <<'PY'
import re, itertools, pathlib
p=pathlib.Path(".github/workflows/step-ci-build.yml")
txt=p.read_text().splitlines()
start=None
for i,line in enumerate(txt):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', line):
        start=i
        break
if start is None:
    print("jreleaser-dry-run not found")
    raise SystemExit(0)
# print next 80 lines
for j in range(start, min(start+120, len(txt))):
    print(f"{j+1:4d}: {txt[j]}")
PY

Repository: jbangdev/jbang

Length of output: 264


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/step-ci-build.yml"
echo "== Show context around lines 280-360 =="
nl -ba "$FILE" | sed -n '280,360p'

echo "== Find all 'permissions:' occurrences in workflow =="
rg -n "^\s*permissions\s*:" "$FILE" || true

echo "== Find all actions/checkout uses in workflow =="
rg -n "uses:\s*actions/checkout@" "$FILE" || true

echo "== Extract jreleaser-dry-run block =="
python3 - <<'PY'
import re, pathlib
p=pathlib.Path(".github/workflows/step-ci-build.yml")
lines=p.read_text().splitlines()
start=None
for idx,l in enumerate(lines):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', l):
        start=idx
        break
if start is None:
    print("jreleaser-dry-run not found")
    raise SystemExit(0)
end=min(start+140,len(lines))
for i in range(start,end):
    print(f"{i+1:4d}: {lines[i]}")
PY

Repository: jbangdev/jbang

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/step-ci-build.yml"
echo "== Show context around lines 280-360 =="
nl -ba "$FILE" | sed -n '280,360p'

echo "== Find all 'permissions:' occurrences in workflow =="
rg -n "^\s*permissions\s*:" "$FILE" || true

echo "== Find all actions/checkout uses in workflow =="
rg -n "uses:\s*actions/checkout@" "$FILE" || true

echo "== Extract jreleaser-dry-run block =="
python3 - <<'PY'
import re, pathlib
p=pathlib.Path(".github/workflows/step-ci-build.yml")
lines=p.read_text().splitlines()
start=None
for idx,l in enumerate(lines):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', l):
        start=idx
        break
if start is None:
    print("jreleaser-dry-run not found")
    raise SystemExit(0)
end=min(start+160,len(lines))
for i in range(start,end):
    print(f"{i+1:4d}: {lines[i]}")
PY

Repository: jbangdev/jbang

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/step-ci-build.yml"

nl -ba "$FILE" | sed -n '280,360p'
rg -n "^\s*permissions\s*:" "$FILE" || true
rg -n "uses:\s*actions/checkout@" "$FILE" || true

python3 - <<'PY'
import re, pathlib
p=pathlib.Path(".github/workflows/step-ci-build.yml")
lines=p.read_text().splitlines()
start=None
for idx,l in enumerate(lines):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', l):
        start=idx
        break
if start is None:
    raise SystemExit("jreleaser-dry-run not found")
end=min(start+160,len(lines))
for i in range(start,end):
    print(f"{i+1:4d}: {lines[i]}")
PY

Repository: jbangdev/jbang

Length of output: 101


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/step-ci-build.yml"
echo "== Context 280-360 =="
nl -ba "$FILE" | sed -n '280,360p'

echo "== Permissions occurrences =="
rg -n "^\s*permissions\s*:" "$FILE" || true

echo "== Checkout uses =="
rg -n "uses:\s*actions/checkout@" "$FILE" || true

echo "== jreleaser-dry-run block (full-ish) =="
python3 - <<'PY'
import re, pathlib
p=pathlib.Path(".github/workflows/step-ci-build.yml")
lines=p.read_text().splitlines()
start=None
for idx,l in enumerate(lines):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', l):
        start=idx
        break
if start is None:
    print("not found")
    raise SystemExit(0)
end=min(start+200,len(lines))
for i in range(start,end):
    print(f"{i+1:4d}: {lines[i]}")
PY

Repository: jbangdev/jbang

Length of output: 141


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/step-ci-build.yml"
nl -ba "$FILE" | sed -n '280,360p'
rg -n "^\s*permissions\s*:" "$FILE" || true
rg -n "uses:\s*actions/checkout@" "$FILE" || true
python3 - <<'PY'
import re, pathlib
p=pathlib.Path(".github/workflows/step-ci-build.yml")
lines=p.read_text().splitlines()
start=None
for idx,l in enumerate(lines):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', l):
        start=idx
        break
if start is None:
    print("jreleaser-dry-run not found")
    raise SystemExit(0)
end=min(start+200,len(lines))
for i in range(start,end):
    print(f"{i+1:4d}: {lines[i]}")
PY

Repository: jbangdev/jbang

Length of output: 101


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/step-ci-build.yml"

echo "== permissions occurrences =="
rg -n "^\s*permissions\s*:" "$FILE" || true

echo "== actions/checkout occurrences =="
rg -n "uses:\s*actions/checkout@" "$FILE" || true

echo "== jreleaser-dry-run block with line numbers (approx) =="
python3 - <<'PY'
import pathlib, re
p=pathlib.Path(".github/workflows/step-ci-build.yml")
lines=p.read_text().splitlines()
start=None
for i,l in enumerate(lines):
    if re.match(r'^\s*jreleaser-dry-run:\s*$', l):
        start=i
        break
if start is None:
    raise SystemExit("jreleaser-dry-run not found")
end=min(len(lines), start+180)
for idx in range(start,end):
    print(f"{idx+1:4d}: {lines[idx]}")
PY

Repository: jbangdev/jbang

Length of output: 8433


Harden token exposure in the jreleaser-dry-run job (.github/workflows/step-ci-build.yml:298-307)

  • This job has no explicit job-level permissions: (currently none in the workflow), so scope the GITHUB_TOKEN to least privilege—artifact download/upload in this job typically requires more than just contents: read.
  • Disable credential persistence on the actions/checkout@93cb6efe... step by setting persist-credentials: false.
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 305-307: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[warning] 298-342: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/step-ci-build.yml around lines 298 - 307, The
jreleaser-dry-run job should explicitly restrict GITHUB_TOKEN permissions and
disable credential persistence on checkout: add a job-level permissions block to
the jreleaser-dry-run job (e.g., permissions: contents: read, actions: read,
packages: write — adjust to the actual minimal scopes you need for artifact
download/upload) and set persist-credentials: false on the
actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd step to prevent the
token from being left in the workspace.

On Windows (Git Bash/MSYS2/Cygwin), the downloaded bundle is a .zip
file, but install_jbang_bundle() unconditionally used tar to extract it,
causing 'This does not look like a tar archive' errors.

Now uses unzip on Windows, consistent with the unpack() function used
for JDK extraction.
@maxandersen maxandersen force-pushed the pr-8 branch 2 times, most recently from 37dbf30 to 28325ba Compare May 22, 2026 08:24
The jreleaser dry-run step in CI was failing because signing is
configured as active: ALWAYS and the GPG/Maven Central env vars
are not available in PR builds. Provide placeholder values since
signing is not actually performed during dry-run.
The nativeDistZip/nativeDistTar tasks were eagerly evaluating
project.version via getNativeBundleBaseName() during task registration,
before the nisse afterEvaluate block had set the version. This caused
native bundles to be named jbang-unspecified-<os>-<arch> instead of
jbang-<version>-<os>-<arch>.

Using provider {} defers the evaluation until task execution, when the
version has been resolved.

Also require native image builds to succeed before running jreleaser
dry-run, and provide dummy signing env vars for dry-run.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 23, 2026
CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.
maxandersen added a commit that referenced this pull request May 25, 2026
…2473)

* build: update CI and release for native bundles and latest jreleaser

CI (step-ci-build.yml):
- Build native bundles (nativeDistZip/nativeDistTar) per platform
- Upload native bundles as artifacts
- Add jreleaser dry-run job to validate release process on PRs

Release (tag-and-release.yml):
- Reuse step-ci-build.yml as a called workflow for build/test/native bundles
- Release job downloads artifacts from CI and runs JReleaser
- Update jreleaser from 1.19.0 to 1.24.0
- Fix deprecated set-output to use GITHUB_OUTPUT

Build (build.gradle):
- Add nativeDistZip and nativeDistTar tasks
- Add getNativeBundleOs/Arch/BaseName helpers for consistent naming
- Fix commonSpec native image inclusion to be lazy (not config-time)

JReleaser (jreleaser.yml):
- Add native bundle artifacts (mac-aarch64, linux-x64, windows-x64)
  as optional release files (active: RELEASE, skipArtifact on missing)

Split from #2467.

* ci: retrigger CI

* fix: use cleanInstallDist instead of rm -rf for native image install

* fix: support platform-named native binaries in IntegrationManager, Wrapper and App

IntegrationManager now resolves jbang.jar by directory instead of
suffix replacement, so jbang-linux-x64.bin finds jbang.jar correctly.
Wrapper and App checks now accept any .bin/.bin.exe suffix instead of
hardcoded jbang.bin.

* fix: update VersionIT regex to accept -SNAPSHOT suffix

* fix: map arm64 to aarch64 in jbang script to match native binary naming

macOS uname -m returns 'arm64' but Gradle names the binary with
'aarch64'. Merge the two cases so the script finds jbang-mac-aarch64.bin.

* fix: include expected binary names in native binary not-found warnings

* fix: disable signing in jreleaser dry-run to avoid keyring init failure

* fix: correct jreleaser signing property prefix

* build: use jreleaser early-access to fix signing issue

* fix: use --yolo for jreleaser dry-run instead of manual signing overrides
@quintesse
Copy link
Copy Markdown
Contributor

Just tried 0.138.0.19 and many commands worked but a simple version with verbose turned on gave me:

PS C:\Temp\jbang> .\bin\jbang version --verbose
Exception in thread "main" org.graalvm.nativeimage.MissingReflectionRegistrationError: Cannot reflectively access the class 'sun.text.resources.cldr.ext.FormatData_en_001'. To allow this operation, add the following to the 'reflection' section of 'reachability-metadata.json' and rebuild the native image:

  {
    "type": "sun.text.resources.cldr.ext.FormatData_en_001"
  }

The 'reachability-metadata.json' file should be located in 'META-INF/native-image/<group-id>/<artifact-id>/' of your project. For further help, see https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.reportClassAccess(MissingReflectionRegistrationUtils.java:50)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:321)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:297)
        at java.base@25.0.2/java.lang.Class.forName(DynamicHub.java:1708)
        at java.base@25.0.2/java.lang.Class.forName(DynamicHub.java:1682)
        at java.base@25.0.2/java.lang.Class.forName(DynamicHub.java:1667)
        at jdk.localedata@25.0.2/sun.util.resources.provider.LocaleDataProvider.loadResourceBundle(LocaleDataProvider.java:53)
        at jdk.localedata@25.0.2/sun.util.resources.provider.LocaleDataProvider.getBundle(LocaleDataProvider.java:39)
        at java.base@25.0.2/sun.util.resources.Bundles.loadBundleFromProviders(Bundles.java:264)
        at java.base@25.0.2/sun.util.resources.Bundles.findBundleOf(Bundles.java:199)
        at java.base@25.0.2/sun.util.resources.Bundles.findBundleOf(Bundles.java:158)
        at java.base@25.0.2/sun.util.resources.Bundles.loadBundleOf(Bundles.java:143)
        at java.base@25.0.2/sun.util.resources.Bundles.of(Bundles.java:64)
        at java.base@25.0.2/sun.util.resources.LocaleData.getBundle(LocaleData.java:179)
        at java.base@25.0.2/sun.util.resources.LocaleData.getNumberFormatData(LocaleData.java:175)
        at java.base@25.0.2/sun.util.locale.provider.LocaleResources.getDecimalFormatSymbolsData(LocaleResources.java:223)
        at java.base@25.0.2/java.text.DecimalFormatSymbols.initialize(DecimalFormatSymbols.java:829)
        at java.base@25.0.2/java.text.DecimalFormatSymbols.<init>(DecimalFormatSymbols.java:115)
        at java.base@25.0.2/sun.util.locale.provider.DecimalFormatSymbolsProviderImpl.getInstance(DecimalFormatSymbolsProviderImpl.java:85)
        at java.base@25.0.2/java.text.DecimalFormatSymbols.getInstance(DecimalFormatSymbols.java:183)
        at java.base@25.0.2/java.util.Formatter.getDecimalFormatSymbols(Formatter.java:2031)
        at java.base@25.0.2/java.util.Formatter.getZero(Formatter.java:2039)
        at java.base@25.0.2/java.util.Formatter$FormatSpecifier.localizedMagnitude(Formatter.java:4678)
        at java.base@25.0.2/java.util.Formatter$FormatSpecifier.print(Formatter.java:3511)
        at java.base@25.0.2/java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:3196)
        at java.base@25.0.2/java.util.Formatter$FormatSpecifier.print(Formatter.java:3155)
        at java.base@25.0.2/java.util.Formatter.format(Formatter.java:2761)
        at java.base@25.0.2/java.util.Formatter.format(Formatter.java:2698)
        at java.base@25.0.2/java.lang.String.format(String.java:4455)
        at dev.jbang.util.Util.getMsgHeader(Util.java:557)
        at dev.jbang.util.Util.verboseMsg(Util.java:485)
        at dev.jbang.cli.JBang$3.handle(JBang.java:159)
        at dev.jbang.cli.JBang$3.handle(JBang.java:156)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2425)
        at picocli.CommandLine.execute(CommandLine.java:2174)
        at dev.jbang.Main.main(Main.java:33)
        at java.base@25.0.2/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support native install/update/run in scripts

2 participants