Repository navigation
fix(cli): fresh-VM onboarding fixes (F1, F2, F4, F5, F6, F11) - #2353
Merged
Merged
Conversation
prepare-core.sh sed-substitutes the latest commit subject into BuildInfo.cfc inside a double-quoted CFML string. # is CFML's variable-interpolation delimiter, so a commit subject ending in a parenthesized PR ref like "(#2352)" produces a syntactically broken .cfc that crashes Lucee on the first request — bricking every fresh brew/chocolatey install. Onboarding finding F1. Escape both # → ## and " → "" before the substitution, then escape sed replacement specials (\, &) so the value transports through the existing sed pipeline cleanly. Add an inline post-substitution check that fails the build if any unescaped # or " remains in the produced commitSubject literal — a regression-stop without standing up a new test harness. Verified end-to-end: with COMMIT_SUBJECT="feat(config): introduce BuildInfo.cfc as the runtime version source (#2352)", the produced BuildInfo.cfc reads `commitSubject: "...source (##2352)",` (valid CFML); reverting the escape pipeline triggers the self-check with exit 1.
generateScaffold() takes a singular model name (e.g. "Post") and
correctly derives a pluralized form for the controller, views, and
migration — but updateRoutes() was being passed the raw singular,
producing .resources("post") in routes.cfm. Onboarding finding F4.
Wheels' resource convention is plural (PostsController maps to
.resources("posts")), so the singular variant either creates a phantom
second route that conflicts with any hand-added plural route, or
silently fails to bind to the controller scaffolded a few lines
earlier. The scaffold runs through cleanly otherwise — this is the one
piece that drifted from the convention.
Pass pluralName, which is already computed for the model/controller/
view generation right above. Add a regression spec that asserts
routes.cfm contains .resources("articles") (plural) and not
.resources("article") (singular) after scaffolding "Article".
The previous fix (#2349) staged the SQLite JDBC driver into every Lucee Express install's lib/ext/ — Tomcat's parent classpath. That satisfies Class.forName("org.sqlite.JDBC") but Lucee 7's datasource resolver consults its OSGi bundle loader for driver instantiation, not the Tomcat classloader. Without the bundle in <server>/lucee-server/bundles/, the very first cfquery against a SQLite datasource fails — and the Wheels migrator goes through cfquery, so `wheels migrate latest` blows up on every fresh app. Onboarding finding F2. Other engines (MySQL, Postgres) avoid the issue because their bundles ship inside lucee-7.x.x.jar and Lucee auto-deploys them; the SQLite extension is third-party and doesn't get the same treatment, so seeding bundles/ is on us. Add BundleStager.stageIntoServerBundles() alongside the existing stageIntoLibExt() — same idempotent / best-effort contract, walks LUCLI_HOME/servers/<name>/lucee-server/bundles/. Wire it into $ensureWheelsBundles() so every wheels start populates both targets. The OSGi-symbolic-name filename (org.xerial.sqlite-jdbc-3.49.1.0.jar) is required for Lucee's bundle loader to recognize it. 5 new specs cover detection, idempotency, missing serversRoot, missing bundleSrc, and partial-server-context tolerance. CLI suite 462 → 467 pass on this worktree.
LuCLI's dev-server Lucee config defaults to `inspectTemplate=once`, which means Lucee compiles each CFC once, caches the .class on disk under `<server>/lucee-server/context/cfclasses/`, and never rechecks the source timestamp. Wheels' `?reload=true` resets framework state via applicationStop() but does not invalidate Lucee's template cache, so source edits to models, controllers, and config silently keep returning the old compiled class. Even `wheels stop && wheels start` doesn't help — the .class on disk persists. The only path that picks up changes is physically wiping cfclasses. Onboarding finding F5; the single-biggest time sink across the tutorial run. `wheels reload` now physically wipes the cfclasses dir for the project's registered server before triggering the HTTP reload. Order matters: purge first, then reload — the application reset on the next request gets the fresh compile. Extract the deletion into CfclassesPurger.cfc (matching the BundleStager extraction pattern) so the logic is unit-testable without touching a live server. 5 new specs cover happy path, dir preservation, missing path, empty path, and idempotency. Best-effort: per-file failures (Lucee-locked .class mid-compile, Windows file lock) are swallowed rather than aborting the reload — the user is better served by a partial-purge than a full crash.
LuCLI's `server start` and `server stop` derive the server name from
the cwd basename when no registered server matches. Running `wheels
start` or `wheels stop` from a parent directory or unrelated dir
(easy to do by accident, especially in shell sessions where cwd
resets between commands) silently registers a phantom server context
named after that dir. The user ends up with ghost entries like `ws`
or `Downloads` in `wheels server list`, with `<unknown>` webroots
and no path to clean up. Onboarding finding F6.
Add a `$isWheelsProjectDir(path)` helper that checks for
`config/settings.cfm` — the file `wheels new` always writes and that
no other tool creates (box.json and Application.cfc are not specific
enough).
- `wheels start` refuses up-front when not in a Wheels project,
pointing the user at `wheels new`.
- `wheels stop` already had a guard for the case where the cwd has
no registered server but other servers are running (reports them);
extend it to also refuse fallthrough when no servers are running
at all, so we never reach LuCLI's name-from-cwd code path.
Two bugs in `wheels browser test` made it useless from the app for
running browser specs. Onboarding finding F11.
- Default directory was `wheels.tests.specs.wheelstest`, the
framework's internal browser-DSL test directory. Apps live at
`tests/specs/browser/`, so the runner reported 0 tests against
a fresh app spec.
- Test URL hit `/wheels/core/tests`, the framework's core test
runner. That endpoint only knows about specs under
`vendor/wheels/tests/specs/`. App-side specs are mounted by
`/wheels/app/tests`, the app test runner.
Default directory is now `tests.specs.browser` and the runner hits
`/wheels/app/tests`. `--directory=...` still overrides for advanced
use.
Two related sub-issues from the same finding (running browser specs
corrupts the dev server's cfclasses cache for other specs;
WHEELS_ENV=test wheels migrate latest looks for an unconfigured
`wheelstestdb_sqlite` datasource) are out of scope for this fix and
tracked separately.
2 of 3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six framework/CLI fixes from a fresh-VM tutorial run, addressing every blocker that prevented a brand-new user from completing the Getting Started guide. Each finding gets its own commit so the diff reads cleanly per-issue.
BuildInfo.cfchad an unescaped#from a PR-suffixed commit subject — bricked every freshbrew install wheelson first requestprepare-core.shCFML-escapes#/"inCOMMIT_SUBJECTbefore sed substitution; inline self-check fails the build on regressionlib/ext/but Lucee 7's datasource resolver only sees OSGibundles/—wheels migrate latestfailed on every fresh appBundleStager.stageIntoServerBundles();wheels startnow stages into both targetswheels generate scaffold Postwrote.resources("post")(singular) toconfig/routes.cfm, conflicting with the conventionpluralNametoupdateRouteswheels reloaddidn't actually reload — Lucee'sinspectTemplate=oncekeeps the cached.class, so?reload=trueonly resets framework stateCfclassesPurgerservice;wheels reloadphysically wipes the server's cfclasses cache before triggering reloadwheels stopfrom a parent / unrelated dir silently registered phantom servers (named after cwd) via LuCLI fallthrough$isWheelsProjectDirhelper;start/stoprefuse the fallthrough when noconfig/settings.cfmis presentwheels browser testran 0 tests — defaulted to the framework's internal browser-DSL dir and hit the framework test endpointtests.specs.browseragainst/wheels/app/testsWhat's in scope and what isn't
@in nested params) — reproduced and traced: that was a curl + Lucee form-parsing interaction, not a Wheels bug. Browser submits work fine. Will be addressed in the follow-up tutorial PR.findOne(values=…)) — that named-bind syntax was never implemented; the tutorial just claims it does. Framework behaves correctly. Tutorial PR will rewrite the auth chapter to use a working approach (dynamic finder).WHEELS_ENV=test wheels migrate latestlooks for awheelstestdb_sqlitedatasource thatwheels newdoesn't configureTest plan
prepare-core.shagainst a temp git repo with the bug-triggering commit subjectfeat(config): introduce BuildInfo.cfc as the runtime version source (#2352)→ produced BuildInfo.cfc reads(##2352)(valid CFML); reverting the escape pipeline triggers the self-check withexit 1~/.wheels/express/<v>/lib/ext/AND~/.wheels/servers/<name>/lucee-server/bundles/afterwheels start