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 @@ -22,6 +22,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo

### Fixed

- Linux `.deb` / `.rpm` packages double-nested the framework at `/opt/wheels/module/vendor/wheels/wheels/` instead of `/opt/wheels/module/vendor/wheels/`. `wheels-core-VER.zip` carries a top-level `wheels/` directory that `unzip` preserves; the nfpm `type: tree` rule then copied the entire `build/framework/` tree (wrapper and all) into the destination, leaving `Injector.cfc` one level too deep. Every fresh `wheels new` install on Ubuntu/Fedora then crashed on first request with `could not find component or class with name [wheels.Injector]`, cascading into the cryptic `The key [WO] does not exist.` error in `onError`. The brew formula handles this correctly via `(share/"wheels/framework/wheels").install Dir["*"]`; the Linux nfpm configs now pin `src` at `./build/framework/wheels/` to match. Regression spec at `vendor/wheels/tests/specs/cli/LinuxPackageStagingSpec.cfc` (#2773)
- `onError` in the generated app template and demo `public/Application.cfc` now guards `application.wo` with `StructKeyExists(application, "wo")` after the recovery try/catch. When `new wheels.Injector(...)` fails during `onApplicationStart` (e.g. a stale `/wheels` mapping under Lucee Express 7), the original error is preserved via a minimal HTML fallback instead of cascading into the cryptic "The key [WO] does not exist" exception that hit "Your First 15 Minutes" tutorial users on fresh installs

----
Expand Down
9 changes: 8 additions & 1 deletion tools/distribution-drafts/linux-packages/nfpm-wheels-be.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ contents:
- src: ./build/module/
dst: /opt/wheels/module/
type: tree
- src: ./build/framework/
# Framework source — see nfpm-wheels.yaml for the full explainer of why src
# MUST be ./build/framework/wheels/ (with trailing /wheels/), not bare
# ./build/framework/. Short version: nfpm `type: tree` copies src contents
# into dst, and the wheels-core-VER.zip preserves a top-level wheels/ dir,
# so without the inner /wheels/ we get a double-nested
# /opt/wheels/module/vendor/wheels/wheels/ that breaks runtime resolution
# of `wheels.Injector`. See issue #2773.
- src: ./build/framework/wheels/
dst: /opt/wheels/module/vendor/wheels/
type: tree
- src: ./build/sqlite-jdbc.jar
Expand Down
11 changes: 10 additions & 1 deletion tools/distribution-drafts/linux-packages/nfpm-wheels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@ contents:
dst: /opt/wheels/module/
type: tree
# Framework source — staged into vendor/wheels/ in scaffolded apps.
- src: ./build/framework/
# IMPORTANT: src points at ./build/framework/wheels/ (the inner directory),
# NOT ./build/framework/. wheels-core-VER.zip has a top-level wheels/ dir
# that `unzip` preserves, leaving the framework at ./build/framework/wheels/.
# nfpm's `type: tree` copies the *contents* of src into dst, so without the
# trailing /wheels/ on src, the framework double-nests as
# /opt/wheels/module/vendor/wheels/wheels/Injector.cfc and Lucee can't
# resolve `wheels.Injector` at app startup. See issue #2773. The brew
# formula handles this the equivalent way — see homebrew-wheels
# Formula/wheels.rb's `(share/"wheels/framework/wheels").install Dir["*"]`.
- src: ./build/framework/wheels/
dst: /opt/wheels/module/vendor/wheels/
type: tree
# SQLite JDBC — required at first run by the wrapper script (cliff fix from
Expand Down
52 changes: 52 additions & 0 deletions vendor/wheels/tests/specs/cli/LinuxPackageStagingSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,58 @@ component extends="wheels.WheelsTest" {
);
});

it("stages framework src from ./build/framework/wheels/ so contents flatten under vendor/wheels/", () => {
var src = fileRead(t.path);
// wheels-core-VER.zip has a top-level `wheels/` directory inside it
// (the smoke test asserts this at tools/ci/smoke-test-module.sh:112).
// nfpm `type: tree` copies the *contents* of src into dst, so if src
// points at ./build/framework/ (one level above the inner wheels/),
// the entire wheels/ subdirectory itself lands at dst — producing
// /opt/wheels/module/vendor/wheels/wheels/Injector.cfc instead of
// /opt/wheels/module/vendor/wheels/Injector.cfc. The framework then
// never loads at runtime ("could not find component or class with
// name [wheels.Injector]" — see issue ##2773).
//
// The brew formula handles this by explicitly re-introducing the
// wheels/ wrapper at stage time — see homebrew-wheels Formula/wheels.rb:62
// — (share/"wheels/framework/wheels").install Dir["*"]. The .deb/.rpm
// equivalent is to point src at the inner wheels/ directory directly.
//
// `[[:space:]]+` matches across the YAML line break between the src
// value and `dst:` — POSIX `[[:space:]]` resolves to Java's `\s` in
// both Lucee and Adobe CF, which includes `\n`.
var hasFixedPair = reFindNoCase(
"src:[[:space:]]+\./build/framework/wheels/[[:space:]]+dst:[[:space:]]+/opt/wheels/module/vendor/wheels/",
src
) > 0;
expect(hasFixedPair).toBeTrue(
t.label & " must declare `src: ./build/framework/wheels/` (with the "
& "trailing /wheels/) for the framework contents entry. Without the "
& "inner /wheels/ segment, nfpm's `type: tree` double-nests the "
& "framework at /opt/wheels/module/vendor/wheels/wheels/, and Lucee "
& "fails to resolve `wheels.Injector` at app startup. See issue ##2773."
);

// Negative guard: the buggy bare-framework form must not coexist
// with the fixed form. A future copy-paste could leave both entries
// in the file, and nfpm would happily stage both — the bare one
// reintroduces the double-nesting. Pairs with the toBeTrue above
// per the dual-assertion pattern already used by the wrapper-routing
// checks at lines 60-68 / 81-106.
var hasBuggyPair = reFindNoCase(
"src:[[:space:]]+\./build/framework/[[:space:]]+dst:[[:space:]]+/opt/wheels/module/vendor/wheels/",
src
) > 0;
expect(hasBuggyPair).toBeFalse(
t.label & " must NOT declare `src: ./build/framework/` (without "
& "the trailing /wheels/) for any contents entry targeting "
& "/opt/wheels/module/vendor/wheels/. If both the bare and the "
& "/wheels/-suffixed entries coexist, nfpm stages the inner "
& "wheels/ wrapper as a subdirectory and the framework "
& "double-nests. See issue ##2773."
);
});

});
})(target);
}
Expand Down
Loading