Repository navigation
refactor(cli): migrate console + test to ArgSpec, dropping the argv round-trip (#2861) - #2874
Conversation
…ound-trip (#2861) Migrate `console` and `test` off the getArgs()/argsFromCollection() argv round trip onto ArgSpec, consuming LuCLI's structured argCollection directly. Adds private parseConsoleArgs / parseTestArgs helpers and 13 server-free specs in CommandArgParsingSpec. Fixes the latent arg1-gate that silently dropped named-only invocations (`wheels console --password=x`, `wheels test --core`). console reads --password=<value>; test reads --filter (+ --directory alias), --reporter, --db (tracked explicit), --verbose / --ci / --core, --no-test-db, a bare positional filter, and -v (delivered as a positional). Deliberate delta: the space-separated option forms (--filter models) give way to --filter=models, since LuCLI hands a space-separated value over as a bare flag + a positional. getArgs() / argsFromCollection() stay as the shim for the remaining commands — the dispatchers (generate, create, db, browser), the parser-delegating deploy / packages / migrate, and the LuCLI-passthrough start; shim removal follows once those are converted. Refs #2861, #2862, #2855. Signed-off-by: Peter Amiri <peter@alurium.com>
There was a problem hiding this comment.
Wheels Bot — Reviewer A
This PR continues the ArgSpec migration (#2861) by moving console and test off the getArgs()/argsFromCollection() argv round-trip. The implementation is clean, the documented behavioral deltas are honest, and the 13 new specs give solid coverage of the main paths. I'm approving; two minor test-coverage gaps are noted below.
Correctness
No logic bugs found.
dbExplicit detection (Module.cfc line 595) uses structKeyExists(arguments.coll, "db") rather than checking the parsed value. This is the correct approach — it distinguishes a user-supplied --db=sqlite from the silent default, which is exactly what the runner needs.
--directory precedence (Module.cfc line 564):
var filter = len(parsed.directory) ? parsed.directory : parsed.filter;When both --filter and --directory are supplied simultaneously, --directory wins. The old round-trip had non-deterministic behavior here (struct iteration order in argsFromCollection), so the new behavior is strictly more predictable. No objection — but the test suite doesn't cover this combination (see Tests below).
Positional + named filter together: when {filter: "models", arg1: "security"} arrives, parsed.filter is set first and the !len(filter) guard on line 582 prevents the positional from overriding it. The old argsFromCollection put positionals before named flags in the reconstructed argv, so named flags also won in the old code. The semantics are preserved. ✓
Cross-engine
The CLI test suite runs on LuCLI (Lucee 7), and the added code uses only standard CFML: reFindNoCase, mid, left, len, val, trim, struct/array operations. No bracket-notation calls inside closures, no application-scope function members, no Left(str, 0) Lucee 7 crash, no struct member-function collisions. ✓
The positional-collection walk (Module.cfc lines 572–584) uses standard for (var key in struct) iteration, not a closure, so the closure-scope capture and obj["key"]() anti-patterns don't apply. ✓
Tests
13 new specs in CommandArgParsingSpec.cfc via ModuleArgvProbe. Happy path, defaults, each named option, each flag, the -v-as-positional edge case, and the -v models index-gap case are all covered. Two gaps worth filling in a follow-up:
-
Both
--filterand--directorysupplied simultaneously. The current behavior (--directorywins) is deterministic and better than the old code, but there is no spec to pin it. A one-liner would do:it("directory alias wins when both --filter and --directory are supplied", () => { var o = probe.$parseTestArgs({filter: "models", directory: "controllers"}); expect(o.filter).toBe("controllers"); }); -
Named
--filter+ bare positional combined. The!len(filter)guard on line 582 correctly makes the named option win, but there is no spec asserting this. A test like{filter: "models", arg1: "security"}→filter == "models"would lock in the precedence rule.
Neither gap is a blocker; both are easily added to CommandArgParsingSpec independently.
Docs
CHANGELOG [Unreleased] entry is present. The entry is lengthy but matches the verbose style established by the prior eight-leaf-command entry directly above it. No change to user-facing guides is required (CLI-internal argument sourcing). ✓
Commits
Single commit: refactor(cli): migrate console + test to ArgSpec, dropping the argv round-trip (#2861) — 88-character header, valid refactor type, cli scope, not ALL-CAPS. DCO Signed-off-by: Peter Amiri <peter@alurium.com> is present. ✓
The commit body honestly flags the two behavioral deltas (named-only invocations fixed; space-separated forms dropped for --key=value). Good.
Security
No SQL, no user-input reflection, no secrets in fixtures. ✓
Wheels Bot — Reviewer B (round 1)A's review is substantive and well-grounded. The correctness analysis tracks the actual diff, the cross-engine checklist is accurate, and the two flagged test gaps are real. One minor factual error in the commit-header count (A wrote 88 chars; the title is 86), but this does not affect the conclusion. I align with the approve verdict. SycophancyNone detected. Each approval is backed by a specific code citation or a direct comparison to the old behavior. False positivesOne minor count error worth noting, not a material false positive:
No other false positives. A's claims about Missed issuesNone. The two test-coverage gaps A identified (simultaneous Verdict alignmentApprove is consistent with the findings: no logic bugs, clean cross-engine profile, adequate coverage with two non-blocking gaps that are easy follow-ups to ConvergenceAligned. A and I agree: the PR is correct, the behavioral deltas are documented and intentional, and the two missing specs are minor enough not to block. Joint recommendation is approve. |
Summary
Continues the #2861 ArgSpec migration (foundation in #2862; eight leaf commands in the prior batch) by moving two more CLI subcommands —
consoleandtest— off thegetArgs()/argsFromCollection()argv round-trip and ontocli.lucli.services.ArgSpec, consuming LuCLI's structuredargCollectiondirectly.These are the two genuinely leaf commands left (they fully consume their args). The remaining
getArgs()callers all forward raw argv onward and are deferred: the dispatchers (generate,create,db,browser), the parser-delegatingdeploy/packages/migrate, and the LuCLI-passthroughstart. This drops thegetArgs()callers from 10 → 8.What changed
parseConsoleArgs(coll)—--password=<value>viaArgSpec.option; an empty result lets the command auto-detect the reload password.parseTestArgs(coll)—--filter(+ documented--directoryalias),--reporter,--db(tracked explicit so the runner distinguishes an implicit default from a chosen one),--verbose/--ci/--core,--no-test-db, a bare positional filter, and-v(delivered by LuCLI as a positional). Preserves$normalizeTestFilterand the fullrunTests(...)argument set.console()/test()now callparse…Args(structuredArgs(arguments)); downstream logic is unchanged (same variable names).$parseConsoleArgs,$parseTestArgs) + 13 new server-free specs inCommandArgParsingSpec.cfc.Behavioral notes
arg1-gate the round trip masked: named-only invocations (wheels console --password=x,wheels test --corewith no positional) now take effect instead of silently running with defaults.wheels console --password secret,wheels test --filter models) give way to the--key=valueforms — LuCLI delivers a space-separated value as a bare flag + a separate positional, never a named value. Consistent with the leaf-command batch.Verification
TDD (red → green) on Lucee 7 via the CLI suite (
/wheels/cli/tests):CommandArgParsingSpec: 38 → 51 pass, 0 fail, 0 error (the 13 new specs).SshClientSpec,SshPoolSpec,ServerCommandsSpec) are pre-existing, server/SSH-dependent, and pass in CI (the full CLI suite is green ondevelop— 681/0/0 — before this change, which adds 13). Confirmed via stash-isolate-rerun.Type of Change
Feature Completeness Checklist
Signed-off-by:on the commitCommandArgParsingSpec.cfc[Unreleased]Refs #2861, #2862, #2855.