Skip to content

Commit 8553740

Browse files
committed
fix(cli): completions resilient, improve help text for destructive commands
- Replace completions stubs with Cliffy CompletionsCommand for bash/zsh/fish - Add best-effort stack-name completion with graceful fallback on missing config - Add detailed help descriptions for down (destructive warning), secrets (sops/age dependency), sync (drift detection), reload (in-place redeploy) - Add stub exports for in-progress env features to unblock compilation - Add tests for completions shells and enhanced help text
1 parent 2df3881 commit 8553740

4 files changed

Lines changed: 676 additions & 6 deletions

File tree

src/cli/mod.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,11 @@ export function buildCli(): Command {
565565

566566
// --- sync (issue #6) ---
567567
cli.command("sync", "Full sync pipeline: generate, render, and deploy stacks.")
568+
.description(
569+
"Performs drift detection by comparing generated stack content against\n" +
570+
"canonical files. Only deploys stacks that have changed. Use --dry-run\n" +
571+
"to preview without deploying.",
572+
)
568573
.option("--dry-run", "Preview sync without deploying.")
569574
.option("--config <path:string>", "Explicit config file path.")
570575
.option("--profile <name:string>", "Use a specific profile.")
@@ -762,7 +767,12 @@ export function buildCli(): Command {
762767
});
763768

764769
// --- reload (issue #9) ---
765-
cli.command("reload", "Re-render and redeploy only changed stacks without tearing them down.")
770+
cli.command("reload", "Re-render and redeploy stacks in-place without tearing them down.")
771+
.description(
772+
"Compares current deployed config against desired state. Only changed\n" +
773+
"stacks are updated, minimizing service disruption compared to a full\n" +
774+
"down-and-up cycle.",
775+
)
766776
.option("--skip-generate", "Only re-render and re-deploy, do not regenerate stacks.")
767777
.option("--follow-logs", "Stream logs for deployed stacks after reload.")
768778
.option("--stacks <names:string>", "Comma-separated list of stack names to reload.")
@@ -830,7 +840,15 @@ export function buildCli(): Command {
830840
});
831841

832842
// --- secrets (issue #7) ---
833-
const secretsCmd = cli.command("secrets", "Manage SOPS/age encrypted secrets.");
843+
const secretsCmd = cli.command(
844+
"secrets",
845+
"Manage SOPS/age encrypted secrets for stack env files.",
846+
)
847+
.description(
848+
"Requires sops (https://github.com/getsops/sops) and age\n" +
849+
"(https://github.com/FiloSottile/age) to be installed on the host.\n" +
850+
"Run ` stackctl secrets check ` to verify tooling availability.",
851+
);
834852

835853
secretsCmd.command("encrypt", "Encrypt .env files to encrypted output.")
836854
.arguments("[files...:string]")

src/cli/mod_test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,47 @@ Deno.test("buildCli version is set", () => {
4747
const cmd = buildCli();
4848
assertEquals(cmd.getVersion(), "0.1.0-dev");
4949
});
50+
51+
Deno.test("completions subcommand has bash, zsh, fish shells", () => {
52+
const cmd = buildCli();
53+
const completions = cmd.getCommand("completions");
54+
assertEquals(completions !== undefined, true, "completions command should exist");
55+
56+
// CompletionsCommand provides bash, zsh, fish subcommands
57+
const bash = completions!.getCommand("bash");
58+
const zsh = completions!.getCommand("zsh");
59+
const fish = completions!.getCommand("fish");
60+
61+
assertEquals(bash !== undefined, true, "bash completions should be available");
62+
assertEquals(zsh !== undefined, true, "zsh completions should be available");
63+
assertEquals(fish !== undefined, true, "fish completions should be available");
64+
});
65+
66+
Deno.test("enhanced help descriptions for destructive/external-tool commands", () => {
67+
const cmd = buildCli();
68+
69+
// down - should warn about destructive operation in full text
70+
const downCmd = cmd.getCommand("down");
71+
assertEquals(downCmd !== undefined, true);
72+
const downText = downCmd!.getDescription();
73+
assertEquals(downText.includes("WARNING"), true);
74+
assertEquals(downText.includes("destructive"), true);
75+
76+
// secrets - should mention sops/age dependency in full description
77+
const secretsCmd = cmd.getCommand("secrets");
78+
assertEquals(secretsCmd !== undefined, true);
79+
const secretsText = secretsCmd!.getDescription();
80+
assertEquals(secretsText.includes("sops"), true);
81+
82+
// sync - should mention drift detection in full description
83+
const syncCmd = cmd.getCommand("sync");
84+
assertEquals(syncCmd !== undefined, true);
85+
const syncText = syncCmd!.getDescription();
86+
assertEquals(syncText.includes("drift"), true);
87+
88+
// reload - short description mentions in-place; full description mentions in-place/disruption
89+
const reloadCmd = cmd.getCommand("reload");
90+
assertEquals(reloadCmd !== undefined, true);
91+
const reloadText = reloadCmd!.getDescription();
92+
assertEquals(reloadText.includes("minimizing service disruption"), true);
93+
});

0 commit comments

Comments
 (0)