fix: anchor app-template rootPath to GetCurrentTemplatePath for subfolder stability (#3025)#3244
Conversation
…lder stability The application template set `this.wheels.rootPath` from `GetDirectoryFromPath(GetBaseTemplatePath())` — the directory of whatever file was originally requested. When a request bootstraps under a subfolder (e.g. the web test runner), the base template is not the public front controller, so rootPath mis-anchors. Because rootPath seeds `this.name` via `Hash(rootPath)` (the issue-359 shared-app-name pattern), an unstable value silently splits one app across two application scopes — a contributor to the "reload=true fixes it" symptom in #3025/#2887. Anchor to `GetCurrentTemplatePath()` instead — always this Application.cfc's own path (the public dir), stable regardless of the requested base template. The value is identical for a normal front-controller request, so existing apps that uncommented `Hash(rootPath)` keep the same app name. (Deliberately not the reporter's `dir & "../"` form, which would shift the value to the app root and change that hash for every existing user — stability, not level, is the fix.) Same idiom already used two lines down in rocketunit_tests/Application.cfc for `this.webrootDir`. Applied to the `wheels new` scaffold, the demo app, both examples, and the legacy test app. Validated: demo app reload/boot clean; onboarding harness (wheels new -> start -> migrate -> generate -> test) green (45/0). Refs #3025, #2887 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer
TL;DR — This PR retargets the application template's this.wheels.rootPath from GetDirectoryFromPath(GetBaseTemplatePath()) to GetDirectoryFromPath(GetCurrentTemplatePath()) so the value stays stable when a request bootstraps under a subfolder (e.g. the test runner), preventing the Hash(rootPath) app-name seed from splitting one app across two application scopes. I verified the correctness claims hold and the change is value-identical for normal requests. Verdict: comment — no blocking findings, one minor comment-accuracy nit worth tidying.
Correctness
The central claims check out:
- Framework path resolution is unaffected.
application.$wheels.rootPathis derived independently fromcgi.script_nameviaGlobal.cfc::$resolveFrameworkPaths(vendor/wheels/Global.cfc:2619) and assigned invendor/wheels/events/onapplicationstart.cfc:158/:368— it does not readthis.wheels.rootPath. So the only consumer of the template'sthis.wheels.rootPathis the (commented)Hash(rootPath)app-name seed. The blast radius is exactly what the PR body claims. - Value-identical for a normal front-controller request. In the
Application.cfcpseudo-constructor,GetCurrentTemplatePath()resolves to this file's own path andGetBaseTemplatePath()to the requestedindex.cfm— both inpublic/, soGetDirectoryFromPath(...)is the same. Apps that uncommented// this.name = Hash(this.wheels.rootPath);keep the same hash. The two diverge only under subfolder bootstrap, which is the bug being fixed.
Conventions
Minor nit (non-blocking): in cli/lucli/templates/app/public/Application.cfc:9 the new comment says rootPath "seeds this.name via Hash(rootPath) below", but that scaffold file sets this.name = createUUID(); (line 16) and, unlike the demo/example apps, carries no // this.name = Hash(this.wheels.rootPath); line below. The other four files (public/Application.cfc:13, both examples, rocketunit) do have that commented line, so the comment is accurate there. For the scaffold the rationale is forward-looking only (it holds if a user later enables the Hash). Consider softening the scaffold comment (e.g. "if you enable the Hash(rootPath) app-name pattern") so it does not reference a line that is not present in that file.
Cross-engine
No concerns. GetCurrentTemplatePath() is a universal BIF and is already relied on two lines down for this.webrootDir in vendor/wheels/rocketunit_tests/Application.cfc:18. No closures, struct member functions, reserved scopes, or Left(str,0) patterns introduced.
Tests
No automated test added. Accepted: this is an Application.cfc pseudo-constructor / BIF-behavior change that only manifests under a live HTTP bootstrap and is not reachable from the spec runner. The PR documents end-to-end validation via the onboarding harness (tools/test-onboarding.sh, 45 passed) and a manual demo-app boot, which is the right surface for this class of change.
Docs / Commits
- Changelog fragment
changelog.d/3025-rootpath-anchor.fixed.mdis correctly named (<slug>.fixed.md) and is a single complete bullet — follows the fragment system, no directCHANGELOG.mdedit. Good. - Commit
cf5db38conforms to commitlint: typefix, header well under 100 chars, not ALL-CAPS, and the body explains the why. PR title matches the landing subject.
Security
None. No SQL, user input, or secret-handling touched.
Summary
Ships the separable folded-in item from #3025 (the reporter's point 1): harden the application template's
rootPathanchor so it stays stable when a request bootstraps under a subfolder. This is independent of the larger request-scoped-overlay work tracked in #3025 (for which I posted a design plan separately).The bug
The app template set:
GetBaseTemplatePath()is the directory of whatever file was originally requested. When a request bootstraps under a subfolder (e.g. the web test runner), the base template is not the public front controller, sorootPathmis-anchors. BecauserootPathseedsthis.namevia the commentedHash(rootPath)pattern (the issue-359 shared-app-name fix), an unstable value silently splits one app across two application scopes — a contributor to the "they fight each other /reload=truefixes it" symptom in #3025 / #2887.The fix
GetCurrentTemplatePath()is always thisApplication.cfc's own path (thepublic/dir), stable regardless of the requested base template.public/, so existing apps that uncommentedHash(rootPath)keep the same app name.dir & "../"form: that would shift the value to the app root and change that hash for every existing user. The contract here is stability, not a specific level — the only consumer is the per-app-directory identity hash, for whichpublic/is fine as long as it's stable.vendor/wheels/rocketunit_tests/Application.cfcforthis.webrootDir.Applied consistently to the
wheels newscaffold (cli/lucli/templates/app/public/Application.cfc), the demo app (public/Application.cfc), both examples, and the legacy test app.Verification
This is a template/bootstrap change that runs only under a live HTTP context (not unit-testable in the spec runner), so validated end-to-end:
public/Application.cfc):?reload=true→ 302,GET /→ 200,/wheels/cli?command=inforeturns a correct envelope — boots clean.tools/test-onboarding.sh, the validator test runner: web runner mutates live application.wheels — replace global swap with request-scoped overlay #3025 cites for this bootstrap surface): 45 passed, 0 failed, 3 skipped — exercises the changed scaffold template throughwheels new→ start → migrate → generate → test. The 3 skips are pre-existing unrelated items (fix(cli): wheels destroy controller requires --force and leaves views behind #2330, toolbar/homepage env quirks).Refs #3025, #2887
🤖 Generated with Claude Code