Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo

### Fixed

- The Scoop `wheels.cmd` wrapper (both `wheels` and `wheels-be` channels) now dispatches LuCLI via `"%JAVA_HOME%\bin\java.exe" -client -jar "%~dp0lucli-<ver>.bat" %*` instead of `call "%~dp0lucli-<ver>.bat" %*`. The `lucli-<ver>.bat` artifact is a bat-jar concatenation (small bat preamble + raw JAR ZIP bytes, ~915 KB), and cmd.exe pre-parses the entire file looking for labels before running it — on at least Windows 11 10.0.26200.8457 that pre-parse trips on a byte sequence in the JAR tail and aborts with `The filename, directory name, or volume label syntax is incorrect.` before LuCLI ever executes. Invoking java directly bypasses the bat-file pre-parser; java reads the JAR via stream and skips the bat preamble. The wrapper now also resolves `JAVA_HOME` from the openjdk21 dependency declared via `depends: java/openjdk21` (preferring `%SCOOP%\apps\openjdk21\current`, falling back to the sibling-app layout `%~dp0..\..\openjdk21\current`) and fails fast with an actionable install hint when neither is found. The same fix is applied to the `:deploy_dispatch` branch added by #2691. The published bucket needs republishing for existing installs to pick up the fix on their next `scoop update`. Closes #2765
- Release artifacts (`wheels-core`, `wheels-cli`, `wheels-base-template`, `wheels-starter-app`) now ship `*.zip.sha512` / `*.zip.md5` checksum sidecars (was `*.sha512` / `*.md5`) so the scoop-wheels `autoupdate` config — which expects the `.zip.sha512` shape via `$url.sha512` substitution — no longer 404s on every non-module artifact. `wheels-module` already used the correct shape; this brings the other four artifacts and both release workflows (`release.yml`, `release-candidate.yml`, plus the `snapshot.yml` reusable-workflow chain) into line. Closes the Windows install regression reported in #2758 + scoop-wheels#2 (#2761)
- Docs: Windows install steps in `start-here/installing.mdx` and `command-line-tools/installation.mdx` now call out `scoop bucket add java` as a prerequisite. Scoop's `depends:` declaration does not auto-add the dependency bucket on the user's behalf, so users hit `Couldn't find manifest for 'openjdk21' from 'java' bucket` before they could proceed (#2761)
- `$viteResolveAssets()` on Adobe CF 2023/2025 returned empty `preloads` and `styles` arrays when the manifest included transitive imports with CSS chunks. Root cause: Adobe CF copies arrays by value when they are passed directly from a struct literal — `$viteWalkImports(preloads = local.rv.preloads, styles = local.rv.styles, ...)` handed the walker independent copies on Adobe CF, so every `ArrayAppend(arguments.preloads, ...)` inside the recursion wrote to garbage and `local.rv` came back empty. Lucee and BoxLang share the array references, so the bug was Adobe-only. Fix: pass the parent `rv` struct and mutate `arguments.rv.preloads` / `arguments.rv.styles` — struct references are shared on every engine (Cross-Engine Invariant #6). Affects every helper that walks transitive imports: `viteScriptTag`, `viteStyleTag`, `vitePreloadTag`, and `$viteHtmlHead`. Existing viteSpec assertions on transitive-import walk, diamond-dependency dedup, and cyclic-import termination serve as the regression catch (#2756)
Expand Down
49 changes: 48 additions & 1 deletion tools/distribution-drafts/scoop/build-manifests.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,54 @@ def cmd_wrapper(channel: str) -> list[str]:
" )",
")",
"",
f'call "%~dp0lucli-{LUCLI_VERSION}.bat" %*',
# JAVA_HOME resolver (issue #2765). The dispatch below invokes
# java.exe directly instead of `call "%~dp0lucli-<ver>.bat"`,
# because cmd.exe pre-parses bat files for labels/control flow
# and trips on bytes inside the lucli bat-jar's ZIP tail. On at
# least Windows 11 10.0.26200.8457 that pre-parse aborts the
# script with `The filename, directory name, or volume label
# syntax is incorrect.` before the bat ever runs.
":: Resolve JAVA_HOME from the openjdk21 dependency declared via `depends: java/openjdk21`.",
":: The wrapper invokes java.exe directly (not `call lucli-<ver>.bat`) to bypass cmd.exe's",
":: bat-file pre-parser, which trips on bytes in the lucli bat-jar's ZIP tail and aborts",
":: the script with ERROR_INVALID_NAME on at least Windows 11 10.0.26200.8457 (issue #2765).",
"if not defined JAVA_HOME (",
r' if defined SCOOP if exist "%SCOOP%\apps\openjdk21\current\bin\java.exe" set "JAVA_HOME=%SCOOP%\apps\openjdk21\current"',
")",
"if not defined JAVA_HOME (",
r' for %%I in ("%~dp0..\..\openjdk21\current") do if exist "%%~fI\bin\java.exe" set "JAVA_HOME=%%~fI"',
")",
"if not defined JAVA_HOME (",
' echo Wheels CLI: cannot resolve JAVA_HOME. Install Java with: scoop install java/openjdk21 1>&2',
" exit /b 1",
")",
"",
":: `wheels deploy ...` arg rewrite (issue #2674). picocli absorbs --version",
":: as a root-level flag even after a subcommand, so the documented Kamal form",
":: `wheels deploy --version=v1.2.3` blows up before Module.cfc runs. Rewrite to",
":: --release here; Module.cfc accepts both flags.",
'if /I "%~1"=="deploy" goto :deploy_rewrite',
"",
r'"%JAVA_HOME%\bin\java.exe" -client -jar "%~dp0lucli-' + LUCLI_VERSION + r'.bat" %*',
"exit /b %ERRORLEVEL%",
"",
":deploy_rewrite",
'set "WHEELS_DEPLOY_ARGS=deploy"',
"shift",
":deploy_arg_loop",
'if "%~1"=="" goto :deploy_dispatch',
'set "ARG=%~1"',
'if "!ARG!"=="--version" (',
' set "WHEELS_DEPLOY_ARGS=!WHEELS_DEPLOY_ARGS! --release"',
') else if "!ARG:~0,10!"=="--version=" (',
' set "WHEELS_DEPLOY_ARGS=!WHEELS_DEPLOY_ARGS! --release=!ARG:~10!"',
") else (",
' set "WHEELS_DEPLOY_ARGS=!WHEELS_DEPLOY_ARGS! %~1"',
")",
"shift",
"goto :deploy_arg_loop",
":deploy_dispatch",
r'"%JAVA_HOME%\bin\java.exe" -client -jar "%~dp0lucli-' + LUCLI_VERSION + r'.bat" !WHEELS_DEPLOY_ARGS!',
"exit /b %ERRORLEVEL%",
"",
":show_version",
Expand Down
21 changes: 18 additions & 3 deletions tools/distribution-drafts/scoop/wheels-be.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,28 @@
"$lines.Add(' )')",
"$lines.Add(')')",
"$lines.Add('')",
"$lines.Add(':: `wheels deploy …` arg rewrite (issue #2674). picocli absorbs --version')",
"$lines.Add(':: Resolve JAVA_HOME from the openjdk21 dependency declared via `depends: java/openjdk21`.')",
"$lines.Add(':: The wrapper invokes java.exe directly (not `call lucli-<ver>.bat`) to bypass cmd.exe''s')",
"$lines.Add(':: bat-file pre-parser, which trips on bytes in the lucli bat-jar''s ZIP tail and aborts')",
"$lines.Add(':: the script with ERROR_INVALID_NAME on at least Windows 11 10.0.26200.8457 (issue #2765).')",
"$lines.Add('if not defined JAVA_HOME (')",
"$lines.Add(' if defined SCOOP if exist \"%SCOOP%\\apps\\openjdk21\\current\\bin\\java.exe\" set \"JAVA_HOME=%SCOOP%\\apps\\openjdk21\\current\"')",
"$lines.Add(')')",
"$lines.Add('if not defined JAVA_HOME (')",
"$lines.Add(' for %%I in (\"%~dp0..\\..\\openjdk21\\current\") do if exist \"%%~fI\\bin\\java.exe\" set \"JAVA_HOME=%%~fI\"')",
"$lines.Add(')')",
"$lines.Add('if not defined JAVA_HOME (')",
"$lines.Add(' echo Wheels CLI: cannot resolve JAVA_HOME. Install Java with: scoop install java/openjdk21 1>&2')",
"$lines.Add(' exit /b 1')",
"$lines.Add(')')",
"$lines.Add('')",
"$lines.Add(':: `wheels deploy ...` arg rewrite (issue #2674). picocli absorbs --version')",
"$lines.Add(':: as a root-level flag even after a subcommand, so the documented Kamal form')",
"$lines.Add(':: `wheels deploy --version=v1.2.3` blows up before Module.cfc runs. Rewrite to')",
"$lines.Add(':: --release here; Module.cfc accepts both flags.')",
"$lines.Add('if /I \"%~1\"==\"deploy\" goto :deploy_rewrite')",
"$lines.Add('')",
"$lines.Add('call \"%~dp0lucli-0.3.7.bat\" %*')",
"$lines.Add('\"%JAVA_HOME%\\bin\\java.exe\" -client -jar \"%~dp0lucli-0.3.7.bat\" %*')",
"$lines.Add('exit /b %ERRORLEVEL%')",
"$lines.Add('')",
"$lines.Add(':deploy_rewrite')",
Expand All @@ -110,7 +125,7 @@
"$lines.Add('shift')",
"$lines.Add('goto :deploy_arg_loop')",
"$lines.Add(':deploy_dispatch')",
"$lines.Add('call \"%~dp0lucli-0.3.7.bat\" !WHEELS_DEPLOY_ARGS!')",
"$lines.Add('\"%JAVA_HOME%\\bin\\java.exe\" -client -jar \"%~dp0lucli-0.3.7.bat\" !WHEELS_DEPLOY_ARGS!')",
"$lines.Add('exit /b %ERRORLEVEL%')",
"$lines.Add('')",
"$lines.Add(':show_version')",
Expand Down
21 changes: 18 additions & 3 deletions tools/distribution-drafts/scoop/wheels.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,28 @@
"$lines.Add(' )')",
"$lines.Add(')')",
"$lines.Add('')",
"$lines.Add(':: `wheels deploy …` arg rewrite (issue #2674). picocli absorbs --version')",
"$lines.Add(':: Resolve JAVA_HOME from the openjdk21 dependency declared via `depends: java/openjdk21`.')",
"$lines.Add(':: The wrapper invokes java.exe directly (not `call lucli-<ver>.bat`) to bypass cmd.exe''s')",
"$lines.Add(':: bat-file pre-parser, which trips on bytes in the lucli bat-jar''s ZIP tail and aborts')",
"$lines.Add(':: the script with ERROR_INVALID_NAME on at least Windows 11 10.0.26200.8457 (issue #2765).')",
"$lines.Add('if not defined JAVA_HOME (')",
"$lines.Add(' if defined SCOOP if exist \"%SCOOP%\\apps\\openjdk21\\current\\bin\\java.exe\" set \"JAVA_HOME=%SCOOP%\\apps\\openjdk21\\current\"')",
"$lines.Add(')')",
"$lines.Add('if not defined JAVA_HOME (')",
"$lines.Add(' for %%I in (\"%~dp0..\\..\\openjdk21\\current\") do if exist \"%%~fI\\bin\\java.exe\" set \"JAVA_HOME=%%~fI\"')",
"$lines.Add(')')",
"$lines.Add('if not defined JAVA_HOME (')",
"$lines.Add(' echo Wheels CLI: cannot resolve JAVA_HOME. Install Java with: scoop install java/openjdk21 1>&2')",
"$lines.Add(' exit /b 1')",
"$lines.Add(')')",
"$lines.Add('')",
"$lines.Add(':: `wheels deploy ...` arg rewrite (issue #2674). picocli absorbs --version')",
"$lines.Add(':: as a root-level flag even after a subcommand, so the documented Kamal form')",
"$lines.Add(':: `wheels deploy --version=v1.2.3` blows up before Module.cfc runs. Rewrite to')",
"$lines.Add(':: --release here; Module.cfc accepts both flags.')",
"$lines.Add('if /I \"%~1\"==\"deploy\" goto :deploy_rewrite')",
"$lines.Add('')",
"$lines.Add('call \"%~dp0lucli-0.3.7.bat\" %*')",
"$lines.Add('\"%JAVA_HOME%\\bin\\java.exe\" -client -jar \"%~dp0lucli-0.3.7.bat\" %*')",
"$lines.Add('exit /b %ERRORLEVEL%')",
"$lines.Add('')",
"$lines.Add(':deploy_rewrite')",
Expand All @@ -113,7 +128,7 @@
"$lines.Add('shift')",
"$lines.Add('goto :deploy_arg_loop')",
"$lines.Add(':deploy_dispatch')",
"$lines.Add('call \"%~dp0lucli-0.3.7.bat\" !WHEELS_DEPLOY_ARGS!')",
"$lines.Add('\"%JAVA_HOME%\\bin\\java.exe\" -client -jar \"%~dp0lucli-0.3.7.bat\" !WHEELS_DEPLOY_ARGS!')",
"$lines.Add('exit /b %ERRORLEVEL%')",
"$lines.Add('')",
"$lines.Add(':show_version')",
Expand Down
154 changes: 154 additions & 0 deletions vendor/wheels/tests/specs/cli/ScoopWrapperSpec.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
component extends="wheels.WheelsTest" {

// Regression: the Scoop-installed wheels.cmd shipped on at least one
// real Windows 11 build (10.0.26200.8457) failed on every invocation
// with two compounding bugs, both reported in issue ##2765.
//
// Bug 1 -- the wrapper set `JAVA_HOME=%~dp0share\jdk`, a path Scoop
// never produces. A Scoop install only populates `share\module` and
// `share\framework`; no `share\jdk`. The brew formula resolves
// JAVA_HOME from openjdk@21's opt_prefix (a real path), but the Scoop
// wrapper was written assuming a parallel layout that doesn't exist
// in a Scoop install. That dead assignment was already removed from
// the in-repo template before this fix, but the published bucket
// carried it.
//
// Bug 2 -- the wrapper invoked lucli with
// `call "%~dp0lucli-<ver>.bat"`. The lucli-<ver>.bat artifact is a
// bat-jar concatenation (small bat preamble + `:JAR_BOUNDARY` + raw
// JAR ZIP bytes, ~915 KB). cmd.exe pre-parses the entire bat file
// for labels/control flow before running it, and on this Windows
// build the pre-parse trips on a byte sequence in the JAR tail with
// `The filename, directory name, or volume label syntax is
// incorrect.` The bat never executes. Bypassing the bat preamble by
// invoking java directly --
// `"%JAVA_HOME%\bin\java.exe" -client -jar "%~dp0lucli-<ver>.bat" %*`
// -- works because java reads the JAR via stream and skips the bat
// preamble in front of the ZIP central directory.
//
// This spec pins both bucket manifests AND the source-of-truth
// build-manifests.py against both regressions, plus the implied
// JAVA_HOME resolver that the direct-java dispatch needs.

function run() {

describe("Scoop wrapper (build-manifests.py output)", () => {

// expandPath("/wheels") resolves to vendor/wheels via the
// configured Lucee mapping; the repo root is two levels above.
var repoRoot = expandPath("/wheels/../..");
var beManifest = repoRoot & "/tools/distribution-drafts/scoop/wheels-be.json";
var stableManifest = repoRoot & "/tools/distribution-drafts/scoop/wheels.json";
var script = repoRoot & "/tools/distribution-drafts/scoop/build-manifests.py";

var manifestTargets = [
{path: beManifest, label: "wheels-be.json (bleeding-edge channel)"},
{path: stableManifest, label: "wheels.json (stable channel)"}
];

for (var target in manifestTargets) {
// IIFE to capture loop variable for closure binding.
(function(t) {
describe(t.label, () => {

it("does not dispatch lucli via `call ""%~dp0lucli-<ver>.bat""`", () => {
expect(fileExists(t.path)).toBeTrue("Missing file: " & t.path);
var src = fileRead(t.path);
// Both the normal dispatch and the deploy
// arg-rewrite dispatch are subject to the
// cmd.exe bat-jar parser regression. Either
// `call` line is a re-introduction of bug 2.
var hasBatCall = findNoCase("call \""%~dp0lucli-", src) > 0;
expect(hasBatCall).toBeFalse(
t.label & " must not dispatch lucli via `call ""%~dp0lucli-<ver>.bat""`. "
& "cmd.exe pre-parses the bat-jar tail and aborts before lucli runs on at "
& "least Windows 11 10.0.26200.8457. Invoke java directly instead. "
& "See issue ##2765."
);
});

it("dispatches lucli via direct `java.exe -client -jar`", () => {
var src = fileRead(t.path);
// Substring match on the JSON-encoded form of
// the wrapper line. The raw CMD line is
// `"%JAVA_HOME%\bin\java.exe" -client -jar "%~dp0lucli-<ver>.bat" %*`;
// after PS single-quote wrap and JSON quote
// escape, the file contains the run below.
var hasDirectJava = findNoCase(
"\""%JAVA_HOME%\\bin\\java.exe\"" -client -jar \""%~dp0lucli-",
src
) > 0;
expect(hasDirectJava).toBeTrue(
t.label & " must invoke lucli via "
& """%JAVA_HOME%\bin\java.exe"" -client -jar ""%~dp0lucli-<ver>.bat"" "
& "to bypass cmd.exe's bat-file pre-parser. See issue ##2765."
);
});

it("does not set JAVA_HOME to %~dp0share\jdk", () => {
var src = fileRead(t.path);
// Bug 1: a Scoop install never populates
// share\jdk -- share holds only module and
// framework subdirs.
var hasBrokenJavaHome = findNoCase(
"JAVA_HOME=%~dp0share\\jdk",
src
) > 0;
expect(hasBrokenJavaHome).toBeFalse(
t.label & " must not set JAVA_HOME=%~dp0share\jdk -- Scoop installs "
& "populate share/module and share/framework only. Resolve JAVA_HOME "
& "from the openjdk21 dependency declared via `depends: java/openjdk21`. "
& "See issue ##2765."
);
});

it("resolves JAVA_HOME from the openjdk21 dependency", () => {
var src = fileRead(t.path);
// The direct-java dispatch above requires
// JAVA_HOME to be set. Scoop installs the
// openjdk21 dependency under
// `%SCOOP%\apps\openjdk21\current` (or the
// sibling `%~dp0..\..\openjdk21\current`).
// Match `openjdk21` inside a `$lines.Add(...)`
// call rather than the bare top-level
// `depends: java/openjdk21` field -- only the
// wrapper-template emission counts.
var hasOpenjdk21InWrapper = reFindNoCase(
"\$lines\.Add\([^)]*openjdk21",
src
) > 0;
expect(hasOpenjdk21InWrapper).toBeTrue(
t.label & " must resolve JAVA_HOME from Scoop's openjdk21 install "
& "(declared via `depends: java/openjdk21`) inside the wrapper "
& "template, not just as a top-level dependency. Without it, the "
& "direct-java dispatch fails when JAVA_HOME isn't already set. "
& "See issue ##2765."
);
});

});
})(target);
}

describe("build-manifests.py (source of truth)", () => {

it("emits the direct-java dispatch line", () => {
expect(fileExists(script)).toBeTrue("Missing file: " & script);
var src = fileRead(script);
// Source-script + JSON drift would silently
// re-introduce the regression on the next regen. Pin
// both ends.
var hasJavaHomeRef = findNoCase("%JAVA_HOME%", src) > 0;
expect(hasJavaHomeRef).toBeTrue(
"build-manifests.py must reference %JAVA_HOME% in the wrapper template; "
& "otherwise a regen wipes the fix from both JSONs. See issue ##2765."
);
});

});

});

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ Wheels ships on Windows through [Scoop](https://scoop.sh) — a portable, sandbo
# the Scoop installer, which is why this works from a totally fresh shell.
scoop install git

# The java bucket provides the openjdk21 dependency declared by both packages.
# Scoop's `depends:` does not auto-add the dependency bucket.
scoop bucket add java
scoop bucket add wheels https://github.com/wheels-dev/scoop-wheels

# Pick a channel:
Expand All @@ -132,7 +135,7 @@ scoop install wheels-be # bleeding-edge - tracks every develop merge
wheels --version
```

Both packages bundle OpenJDK 21 inline — no separate `java` bucket, no `JAVA_HOME` setup. Both expose the same `wheels` PATH shim, so Scoop refuses to install both at once. To switch channels:
Both packages install OpenJDK 21 as a Scoop dependency (`java/openjdk21`) — the `java` bucket added above is required. The `wheels.cmd` wrapper resolves `JAVA_HOME` from the installed dependency automatically, so no manual setup is needed. Both expose the same `wheels` PATH shim, so Scoop refuses to install both at once. To switch channels:

```powershell
scoop uninstall wheels && scoop install wheels-be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,14 @@ The legacy `wheels` package on chocolatey.org is the CommandBox-based v1.x relea
3. Add the Wheels bucket and install a channel:

```powershell title="PowerShell"
scoop bucket add java
scoop bucket add wheels https://github.com/wheels-dev/scoop-wheels
scoop install wheels # stable - tracks v4.0.0 GA tags
# OR:
scoop install wheels-be # bleeding-edge - tracks every develop merge
```

Both packages inline OpenJDK 21 — no separate `java` bucket or `JAVA_HOME` setup required. Both expose the same `wheels` shim on PATH, so Scoop refuses to install both at once. See [Release Channels](/v4-0-0/start-here/release-channels/) for the comparison.
Both packages install OpenJDK 21 as a Scoop dependency (`java/openjdk21`) from the `java` bucket added above — no manual `JAVA_HOME` setup needed; the wrapper resolves it automatically. Both expose the same `wheels` shim on PATH, so Scoop refuses to install both at once. See [Release Channels](/v4-0-0/start-here/release-channels/) for the comparison.

4. Verify:

Expand Down
Loading
Loading