Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -32,6 +32,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo

### Fixed

- `paginationLinks()` now emits a one-time per-request `WriteLog(type="warning", ...)` deprecation notice pointing 3.x → 4.x upgraders at `paginationNav()` (the all-in-one helper) and the individual `firstPageLink`/`previousPageLink`/`pageNumberLinks`/`nextPageLink`/`lastPageLink` composables. `wheels upgrade check --to=4.0.0` now also greps `app/views/` for `paginationLinks(` and flags every hit with a remediation pointer, closing the silent-rot gap surfaced by titan Phase 2.4 (#2714)
- `wheels` `.deb` / `.rpm` Linux packages now ship the lucli-native `wheels-module` artifact, version + channel stamps, and a wrapper that routes through the bundled module — fixing the three v4.0.0 rpm regressions that broke `wheels start` on Rocky Linux during the titan production cutover. (1) `build-linux-packages.sh` now untars `wheels-module-${WHEELS_VERSION}.tar.gz` into `/opt/wheels/module/` instead of unzipping the CommandBox-shaped `wheels-cli-${WHEELS_VERSION}.zip`. (2) The LuCLI binary is staged as `/opt/wheels/wheels` so `basename(argv[0])` is `wheels` when the wrapper execs it — mirroring the brew formula and making LuCLI's module dispatcher resolve `wheels start` against the bundled module. (3) `nfpm-wheels.yaml` and `nfpm-wheels-be.yaml` now declare `/opt/wheels/.version` and `/opt/wheels/.channel` under `contents:` so `wheels --version` no longer returns `unknown (stable)`. (4) `tar` is declared as an rpm + deb runtime dependency since Rocky Linux 10 minimal cloud images do not ship it and any role that unpacks a tarball payload fails silently without it (#2700)
- `wheels.middleware.RateLimiter` now validates `windowSeconds > 0` and `maxRequests >= 0` at construction. Previously, `windowSeconds = 0` leaked a generic CFML `You cannot divide by zero` exception out of the `fixedWindow` and `tokenBucket` strategies (and let every request through on `slidingWindow`), with no pointer back to the misconfigured `set(middleware = [...])` line. The constructor now throws `Wheels.RateLimiter.InvalidConfiguration` with a message naming the bad parameter — matching the pattern already used for `strategy`, `storage`, and `proxyStrategy`. `maxRequests = 0` remains legal (kill-switch idiom for "block every request") (#2693)
- `wheels deploy --version=v1.2.3` (the form documented in the Kamal migration guide) no longer fails with `Invalid value for option '--version': 'v1.2.3' is not a boolean`. picocli treats `--version` as a `versionHelp = true` root flag and absorbs it during arg parsing before `Module.cfc` ever sees the subcommand, so the literal Kamal form was unreachable. The deploy parser now accepts `--release` as a picocli-safe alias (extracted into `cli/lucli/services/deploy/cli/DeployArgsParser.cfc` for unit-testability), and the brew/scoop wrappers rewrite `--version[=val]` → `--release[=val]` when `deploy` is the first positional — so the documented `--version` form keeps working on a current-channel wrapper, and users on an older wrapper can pass `--release` directly (#2674)
Expand Down
15 changes: 15 additions & 0 deletions cli/lucli/Module.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -3733,6 +3733,21 @@ component extends="modules.BaseModule" {
extensions: "cfm,cfc",
fix: "Missing manifest entries throw Wheels.ViteAssetNotFound in production. Rebuild assets during deploy (npm run build) or set(viteStrictManifest=false) to restore 3.x silent fallback."
});
// paginationLinks() is deprecated in favor of paginationNav()
// (#1930) and now emits a one-time per-request warning at
// runtime (#2714). Flag any view still calling the deprecated
// helper so 3.x apps see the rename during `wheels upgrade check`.
// NOTE: '#' is doubled in the `fix` string because CFML strings
// interpret a bare '#' as expression interpolation; we want a
// literal '#' in the user-visible remediation text.
arrayAppend(checks, {
description: "Deprecated paginationLinks() helper (renamed to paginationNav() in 4.0)",
pattern: "paginationLinks\s*\(",
checkType: "grep",
scanDir: "app/views",
extensions: "cfm,cfc",
fix: "Replace paginationLinks() with paginationNav() (the all-in-one nav helper) or compose firstPageLink/previousPageLink/pageNumberLinks/nextPageLink/lastPageLink directly. See CLAUDE.md ## Pagination View Helpers and issue ##1930."
});
}

// Run checks
Expand Down
87 changes: 87 additions & 0 deletions vendor/wheels/tests/specs/view/paginationLinksDeprecationSpec.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Regression for issue ##2714: `paginationLinks()` is documented as
* deprecated in favor of `paginationNav()` (per ##1930), but emits no
* runtime warning and is invisible to `wheels upgrade check`.
*
* Two assertions:
*
* 1. The first call to `paginationLinks()` within a request sets a
* request-scoped guard flag (`request.wheels.$paginationLinksDeprecationLogged`).
* The flag is what gates the one-time `WriteLog(type="warning", ...)`
* call so the deprecation surfaces once per request, not on every
* render in a loop.
*
* 2. The 3.x -> 4.x scan rules in `cli/lucli/Module.cfc` include a
* `paginationLinks` grep so `wheels upgrade check --to=4.0.0`
* flags apps still calling the deprecated helper. Mirrors the
* static-inspection pattern used by `UpgradeCheckCoverageSpec`.
*/
component extends="wheels.WheelsTest" {

function run() {

g = application.wo

describe("paginationLinks deprecation surface", () => {

beforeEach(() => {
_params = {controller = "dummy", action = "dummy"}
_controller = g.controller("dummy", _params)
g.set(functionName = "paginationLinks", encode = false)
structDelete(request.wheels, "$paginationLinksDeprecationLogged")
})

afterEach(() => {
g.set(functionName = "paginationLinks", encode = true)
structDelete(request.wheels, "$paginationLinksDeprecationLogged")
})

describe("runtime warning", () => {

it("sets a request-scoped guard flag on first call", () => {
g.model("author").findAll(page = 2, perPage = 3, order = "lastName")
expect(structKeyExists(request.wheels, "$paginationLinksDeprecationLogged")).toBeFalse()
_controller.paginationLinks()
expect(structKeyExists(request.wheels, "$paginationLinksDeprecationLogged")).toBeTrue()
expect(request.wheels.$paginationLinksDeprecationLogged).toBeTrue()
})

it("does not re-log when called multiple times in the same request", () => {
g.model("author").findAll(page = 2, perPage = 3, order = "lastName")
_controller.paginationLinks()
request.wheels.$paginationLinksDeprecationLogged = "first"
_controller.paginationLinks()
expect(request.wheels.$paginationLinksDeprecationLogged).toBe("first")
})

})

describe("upgrade-check coverage", () => {

it("3.x -> 4.x scan rules in Module.cfc grep for paginationLinks", () => {
// expandPath("/wheels") resolves to vendor/wheels via the
// configured Lucee mapping; the repo root is two levels above.
var repoRoot = expandPath("/wheels/../..")
var modulePath = repoRoot & "/cli/lucli/Module.cfc"
expect(fileExists(modulePath)).toBeTrue("Missing: " & modulePath)

var moduleSource = fileRead(modulePath)
var start = find("currentMajor <= 3 && targetMajor >= 4", moduleSource)
expect(start > 0).toBeTrue("3.x -> 4.x branch not found in Module.cfc")

var endIdx = find("// Run checks", moduleSource, start)
var sliceLen = endIdx > 0 ? endIdx - start : len(moduleSource) - start + 1
var block = sliceLen > 0 ? mid(moduleSource, start, sliceLen) : ""

expect(findNoCase("paginationLinks", block) > 0).toBeTrue(
"3.x -> 4.x checks should grep app views for paginationLinks( so apps still using the deprecated helper get flagged by 'wheels upgrade check --to=4.0.0'. See issue ##2714."
)
})

})

})

}

}
13 changes: 13 additions & 0 deletions vendor/wheels/view/links.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,19 @@ component {
boolean pageNumberAsParam,
any encode
) {
// Issue #2714: paginationLinks() is deprecated in favor of paginationNav().
// Emit a one-time per-request warning so 3.x → 4.x upgraders see the
// signal without flooding the log on a paginated view that renders
// the helper many times. Follows the $checkPluginsDeprecation() pattern
// in vendor/wheels/Plugins.cfc.
if (!StructKeyExists(request.wheels, "$paginationLinksDeprecationLogged")) {
request.wheels.$paginationLinksDeprecationLogged = true;
WriteLog(
type = "warning",
text = "[Wheels] paginationLinks() is deprecated and will be removed in a future release. Use paginationNav() instead (or compose the individual helpers: firstPageLink/previousPageLink/pageNumberLinks/nextPageLink/lastPageLink). See https://github.com/wheels-dev/wheels/issues/1930"
);
}

/* To fix the bug below:
https://github.com/wheels-dev/wheels/issues/942

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ See [Packages](/v4-0-0/digging-deeper/packages/) for manifest fields, per-method

### Monolithic `paginationLinks()`

**CHANGELOG:** composable pagination helpers added (#1930). The 3.x `paginationLinks()` is retained. New code should use `paginationNav()` or compose the individual helpers (`paginationInfo`, `firstPageLink`, `previousPageLink`, `pageNumberLinks`, `nextPageLink`, `lastPageLink`).
**CHANGELOG:** composable pagination helpers added (#1930). The 3.x `paginationLinks()` is retained but deprecated. As of #2727, calling `paginationLinks()` emits a one-time per-request `WriteLog(type="warning")` pointing at the replacement so apps see the signal without flooding the log. `wheels upgrade check --to=4.0.0` also greps `app/views/` for `paginationLinks(` and flags every hit with a remediation pointer. New code should use `paginationNav()` or compose the individual helpers (`paginationInfo`, `firstPageLink`, `previousPageLink`, `pageNumberLinks`, `nextPageLink`, `lastPageLink`).

### RocketUnit test style

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ See [Packages](/v4-0-1-snapshot/digging-deeper/packages/) for manifest fields, p

### Monolithic `paginationLinks()`

**CHANGELOG:** composable pagination helpers added (#1930). The 3.x `paginationLinks()` is retained. New code should use `paginationNav()` or compose the individual helpers (`paginationInfo`, `firstPageLink`, `previousPageLink`, `pageNumberLinks`, `nextPageLink`, `lastPageLink`).
**CHANGELOG:** composable pagination helpers added (#1930). The 3.x `paginationLinks()` is retained but deprecated. As of #2727, calling `paginationLinks()` emits a one-time per-request `WriteLog(type="warning")` pointing at the replacement so apps see the signal without flooding the log. `wheels upgrade check --to=4.0.0` also greps `app/views/` for `paginationLinks(` and flags every hit with a remediation pointer. New code should use `paginationNav()` or compose the individual helpers (`paginationInfo`, `firstPageLink`, `previousPageLink`, `pageNumberLinks`, `nextPageLink`, `lastPageLink`).

### RocketUnit test style

Expand Down
Loading