diff --git a/AGENTS.md b/AGENTS.md index 5c724c0e7..6ecd3740b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,7 +20,7 @@ - Logging/output: use `dev.jbang.util.Util` helpers (e.g., `infoMsg`, `verboseMsg`). - Commits: use conventional/semantic format — `feat:`, `fix:`, `build:`, `docs:`, etc. PR titles follow the same convention. - Startup scripts live in `src/main/scripts/`: `jbang` (bash), `jbang.cmd` (CMD), `jbang.ps1` (PowerShell). The CMD script delegates downloads and JDK installs to `jbang.ps1`. Changes affecting downloads or bootstrap must be applied consistently across all three. Behavior (e.g., retry backoff) must be consistent across tools — watch for tool-specific quirks like `curl --retry-delay 0` meaning exponential backoff while `wget --waitretry=0` meaning no delay. -- Environment variables follow `JBANG_*` naming. New env vars must have defaults in all three scripts, be documented in the reference table in `installation.adoc` ("Startup Script Environment Variables"), and behave identically across platforms. +- Environment variables follow `JBANG_*` naming. New env vars must have defaults in each script that actually uses them (e.g., `jbang.cmd` delegates downloads to `jbang.ps1`, so download-related vars only need defaults in `jbang` and `jbang.ps1`). Document new vars in `installation.adoc` ("Startup Script Environment Variables") and ensure consistent behavior across platforms. - Documentation is AsciiDoc under `docs/modules/ROOT/pages/` (e.g., `installation.adoc`, `troubleshooting.adoc`). - Test infrastructure: `BaseTest` includes WireMock for HTTP mocking (records/replays requests). `BaseIT` provides `shell()` helpers for running CLI commands. Use `assumeTrue` for conditionally skipping tests (e.g., `assumeTrue(isCommandAvailable("bash"))` or `assumeTrue(isCommandAvailable("pwsh"))`). - Script tests should run the real scripts from `src/main/scripts/` — not synthetic copies or extracted functions. Use env var overrides (e.g., `JBANG_DOWNLOAD_URL`) to point real scripts at WireMock. diff --git a/src/main/scripts/jbang b/src/main/scripts/jbang index d8effa46d..12d66433f 100755 --- a/src/main/scripts/jbang +++ b/src/main/scripts/jbang @@ -9,6 +9,11 @@ # The Java version to install when it's not installed on the system yet javaVersion=${JBANG_DEFAULT_JAVA_VERSION:-17} +# Base URL for downloading JBang releases. +# Override for testing or corporate mirrors. +# Example: JBANG_DOWNLOAD_BASEURL=http://localhost:18080 +jbangDownloadBaseUrl=${JBANG_DOWNLOAD_BASEURL:-https://github.com/jbangdev/jbang/releases} + # Number of retry attempts for downloads (curl/wget) downloadRetry=${JBANG_DOWNLOAD_RETRY:-5} downloadRetryDelay=${JBANG_DOWNLOAD_RETRY_DELAY:-0} @@ -25,20 +30,35 @@ script_dir() { } download() { - if [ -x "$(command -v curl)" ]; then - curl -sLf --retry "$downloadRetry" --retry-delay "$downloadRetryDelay" -o "$2" "$1" - retval=$? - elif [ -x "$(command -v wget)" ]; then + local attempt=0 + local maxAttempts=$((downloadRetry + 1)) + while true; do + attempt=$((attempt + 1)) + if [ -x "$(command -v curl)" ]; then + curl -sLf -o "$2" "$1" + retval=$? + elif [ -x "$(command -v wget)" ]; then + wget -q -O "$2" "$1" + retval=$? + else + echo "Error: curl or wget not found, please make sure one of them is installed" 1>&2 + exit 1 + fi + if [ $retval -eq 0 ] || [ $attempt -ge $maxAttempts ]; then + break + fi if [ "$downloadRetryDelay" -gt 0 ] 2>/dev/null; then - wget -q --tries="$downloadRetry" --waitretry="$downloadRetryDelay" -O "$2" "$1" + sleepSeconds=$downloadRetryDelay else - wget -q --tries="$downloadRetry" -O "$2" "$1" + # Exponential backoff: 1, 2, 4, 8, ... + sleepSeconds=$((1 << (attempt - 1))) fi - retval=$? - else - echo "Error: curl or wget not found, please make sure one of them is installed" 1>&2 - exit 1 - fi + echo "Download $attempt/$maxAttempts failed. Retry in $sleepSeconds second(s)..." 1>&2 + if [ $attempt -eq 1 ]; then + echo "(Set JBANG_DOWNLOAD_RETRY=0 to disable retries)" 1>&2 + fi + sleep $sleepSeconds + done } unpack() { @@ -258,15 +278,15 @@ if [[ -z "$binaryPath" ]]; then fi if [[ -z "$binaryPath" && -z "$jarPath" ]]; then if [[ ! -f "$JBDIR/bin/jbang.jar" || ! -f "$JBDIR/bin/jbang" ]]; then - echo "Downloading JBang $JBANG_DOWNLOAD_VERSION..." 1>&2 mkdir -p "$TDIR/urls" if [ -n "$JBANG_DOWNLOAD_URL" ]; then jburl="$JBANG_DOWNLOAD_URL"; elif [ -z "$JBANG_DOWNLOAD_VERSION" ]; then - jburl="https://github.com/jbangdev/jbang/releases/latest/download/jbang.tar"; + jburl="${jbangDownloadBaseUrl}/latest/download/jbang.tar"; else - jburl="https://github.com/jbangdev/jbang/releases/download/v$JBANG_DOWNLOAD_VERSION/jbang.tar"; + jburl="${jbangDownloadBaseUrl}/download/v$JBANG_DOWNLOAD_VERSION/jbang.tar"; fi + echo "Downloading JBang ${JBANG_DOWNLOAD_VERSION:-latest} from $jburl..." 1>&2 download "$jburl" "$TDIR/urls/jbang.tar" if [ $retval -ne 0 ]; then echo "Error downloading JBang from $jburl" 1>&2; exit $retval; fi echo "Installing JBang..." 1>&2 diff --git a/src/main/scripts/jbang.ps1 b/src/main/scripts/jbang.ps1 index 2cc5a2142..d613ef9a6 100644 --- a/src/main/scripts/jbang.ps1 +++ b/src/main/scripts/jbang.ps1 @@ -60,6 +60,11 @@ if (-not (Test-Path env:JBANG_DIR)) { $JBDIR="$env:userprofile\.jbang" } else { if (-not (Test-Path env:JBANG_CACHE_DIR)) { $TDIR="$JBDIR\cache" } else { $TDIR=$env:JBANG_CACHE_DIR } if (-not (Test-Path env:JBANG_USE_NATIVE)) { $env:JBANG_USE_NATIVE="false" } +# Base URL for downloading JBang releases. +# Override for testing or corporate mirrors. +# Example: $env:JBANG_DOWNLOAD_BASEURL='http://localhost:18080' +if (-not (Test-Path env:JBANG_DOWNLOAD_BASEURL)) { $jbangDownloadBaseUrl='https://github.com/jbangdev/jbang/releases' } else { $jbangDownloadBaseUrl=$env:JBANG_DOWNLOAD_BASEURL } + # Number of retry attempts for downloads if (-not (Test-Path env:JBANG_DOWNLOAD_RETRY)) { $downloadRetry=5 } else { $downloadRetry=[int]$env:JBANG_DOWNLOAD_RETRY } if (-not (Test-Path env:JBANG_DOWNLOAD_RETRY_DELAY)) { $downloadRetryDelay=0 } else { $downloadRetryDelay=[int]$env:JBANG_DOWNLOAD_RETRY_DELAY } @@ -82,7 +87,10 @@ function Invoke-Download { # Exponential backoff: 1, 2, 4, 8, ... $sleepSeconds = [Math]::Pow(2, $attempt - 1) } - [Console]::Error.WriteLine("Download attempt $attempt/$($downloadRetry + 1) failed, retrying in $sleepSeconds second(s)...") + [Console]::Error.WriteLine("Download $attempt/$($downloadRetry + 1) failed. Retry in $sleepSeconds second(s)...") + if ($attempt -eq 1) { + [Console]::Error.WriteLine("(Set JBANG_DOWNLOAD_RETRY=0 to disable retries)") + } Start-Sleep -Seconds $sleepSeconds } } @@ -142,11 +150,12 @@ if (-not $binaryPath -and -not $jarPath) { if (Test-Path env:JBANG_DOWNLOAD_URL) { $jburl=$env:JBANG_DOWNLOAD_URL } elseif (-not (Test-Path env:JBANG_DOWNLOAD_VERSION)) { - $jburl="https://github.com/jbangdev/jbang/releases/latest/download/jbang.zip" + $jburl="$jbangDownloadBaseUrl/latest/download/jbang.zip" } else { - $jburl="https://github.com/jbangdev/jbang/releases/download/v$env:JBANG_DOWNLOAD_VERSION/jbang.zip"; + $jburl="$jbangDownloadBaseUrl/download/v$env:JBANG_DOWNLOAD_VERSION/jbang.zip"; } - [Console]::Error.WriteLine("Downloading JBang $env:JBANG_DOWNLOAD_VERSION...") + $dlVersion = if ($env:JBANG_DOWNLOAD_VERSION) { $env:JBANG_DOWNLOAD_VERSION } else { 'latest' } + [Console]::Error.WriteLine("Downloading JBang $dlVersion from $jburl...") $ok = Invoke-Download "$jburl" "$TDIR\urls\jbang.zip" if (-not ($ok)) { [Console]::Error.WriteLine("Error downloading JBang from $jburl to $TDIR\urls\jbang.zip")