Skip to content

Commit 32215a7

Browse files
authored
fix(cli): wheels deploy returns success summary in real mode (#2230) (#2238)
Every deploy subcommand in Module.cfc returned arrayToList(cli.dryRunOutput(), chr(10)) unconditionally. The buffer is only populated in --dry-run mode, so real deploys returned a blank string and the operator had no visible confirmation the deploy succeeded. Change every public verb in the 9 Deploy*Cli classes from void to string, ending with a private $renderResult(opts, summary) helper that returns the dry-run buffer when opts.dryRun is truthy and a user-facing summary otherwise. Simplify the 16 Module.cfc dispatch sites accordingly. Also fixes the same inline bug in DeployMainCli.audit() and details(). Tests: adds 9 regression specs across DeployMainCliSpec and DeployAppCliSpec verifying real-mode returns a non-empty summary and dry-run continues to return the buffered command list. Full CLI suite: 375 pass, 0 fail.
1 parent 584fc44 commit 32215a7

12 files changed

Lines changed: 336 additions & 105 deletions

cli/lucli/Module.cfc

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,40 +1314,33 @@ component extends="modules.BaseModule" {
13141314

13151315
switch (sub) {
13161316
case "deploy":
1317-
dmc.deploy(opts);
1318-
return arrayToList(dmc.dryRunOutput(), chr(10));
1317+
return dmc.deploy(opts);
13191318
case "redeploy":
1320-
dmc.redeploy(opts);
1321-
return arrayToList(dmc.dryRunOutput(), chr(10));
1319+
return dmc.redeploy(opts);
13221320
case "rollback":
13231321
if (arrayLen(positional) < 2) {
13241322
throw(message="rollback requires a version argument: wheels deploy rollback <version>");
13251323
}
13261324
opts.version = positional[2];
1327-
dmc.rollback(opts);
1328-
return arrayToList(dmc.dryRunOutput(), chr(10));
1325+
return dmc.rollback(opts);
13291326
case "config":
13301327
return dmc.config(opts);
13311328
case "init":
13321329
return dmc.init_stub(opts);
13331330
case "setup":
1334-
dmc.setup(opts);
1335-
return arrayToList(dmc.dryRunOutput(), chr(10));
1331+
return dmc.setup(opts);
13361332
case "version":
13371333
return dmc.version();
13381334
case "audit":
1339-
dmc.audit(opts);
1340-
return arrayToList(dmc.dryRunOutput(), chr(10));
1335+
return dmc.audit(opts);
13411336
case "docs":
13421337
// `docs [SECTION]` — section is the optional second positional.
13431338
opts.section = arrayLen(positional) >= 2 ? positional[2] : "";
13441339
return dmc.docs(opts);
13451340
case "details":
1346-
dmc.details(opts);
1347-
return arrayToList(dmc.dryRunOutput(), chr(10));
1341+
return dmc.details(opts);
13481342
case "remove":
1349-
dmc.remove(opts);
1350-
return arrayToList(dmc.dryRunOutput(), chr(10));
1343+
return dmc.remove(opts);
13511344
case "app":
13521345
if (arrayLen(positional) < 2) {
13531346
throw(message="wheels deploy app requires a verb");
@@ -1367,8 +1360,7 @@ component extends="modules.BaseModule" {
13671360
case "live":
13681361
case "maintenance":
13691362
case "remove":
1370-
appCli[appVerb](opts);
1371-
return arrayToList(appCli.dryRunOutput(), chr(10));
1363+
return invoke(appCli, appVerb, [opts]);
13721364
default:
13731365
throw(message="Unknown wheels deploy app verb: #appVerb#");
13741366
}
@@ -1389,8 +1381,7 @@ component extends="modules.BaseModule" {
13891381
case "details":
13901382
case "logs":
13911383
case "remove":
1392-
invoke(proxyCli, proxyVerb, [opts]);
1393-
return arrayToList(proxyCli.dryRunOutput(), chr(10));
1384+
return invoke(proxyCli, proxyVerb, [opts]);
13941385
default:
13951386
throw(message="Unknown wheels deploy proxy verb: #proxyVerb#");
13961387
}
@@ -1407,8 +1398,7 @@ component extends="modules.BaseModule" {
14071398
case "login":
14081399
case "logout":
14091400
case "remove":
1410-
invoke(registryCli, registryVerb, [opts]);
1411-
return arrayToList(registryCli.dryRunOutput(), chr(10));
1401+
return invoke(registryCli, registryVerb, [opts]);
14121402
default:
14131403
throw(message="Unknown wheels deploy registry verb: #registryVerb#");
14141404
}
@@ -1428,8 +1418,7 @@ component extends="modules.BaseModule" {
14281418
case "remove":
14291419
case "details":
14301420
case "dev":
1431-
invoke(buildCli, buildVerb, [opts]);
1432-
return arrayToList(buildCli.dryRunOutput(), chr(10));
1421+
return invoke(buildCli, buildVerb, [opts]);
14331422
default:
14341423
throw(message="Unknown wheels deploy build verb: #buildVerb#");
14351424
}
@@ -1451,8 +1440,7 @@ component extends="modules.BaseModule" {
14511440
case "details":
14521441
case "logs":
14531442
case "remove":
1454-
invoke(accCli, accVerb, [opts]);
1455-
return arrayToList(accCli.dryRunOutput(), chr(10));
1443+
return invoke(accCli, accVerb, [opts]);
14561444
default:
14571445
throw(message="Unknown wheels deploy accessory verb: #accVerb#");
14581446
}
@@ -1467,8 +1455,7 @@ component extends="modules.BaseModule" {
14671455
var pruneCli = new cli.lucli.services.deploy.cli.DeployPruneCli(
14681456
new cli.lucli.services.deploy.lib.SshPool()
14691457
);
1470-
invoke(pruneCli, pruneVerb, [opts]);
1471-
return arrayToList(pruneCli.dryRunOutput(), chr(10));
1458+
return invoke(pruneCli, pruneVerb, [opts]);
14721459
case "server":
14731460
if (arrayLen(positional) < 2) {
14741461
throw(message="wheels deploy server requires a verb (exec or bootstrap)");
@@ -1490,11 +1477,9 @@ component extends="modules.BaseModule" {
14901477
);
14911478
switch (serverVerb) {
14921479
case "exec":
1493-
serverCli.exec(opts);
1494-
return arrayToList(serverCli.dryRunOutput(), chr(10));
1480+
return serverCli.exec(opts);
14951481
case "bootstrap":
1496-
serverCli.bootstrap(opts);
1497-
return arrayToList(serverCli.dryRunOutput(), chr(10));
1482+
return serverCli.bootstrap(opts);
14981483
default:
14991484
throw(message="Unknown wheels deploy server verb: #serverVerb#");
15001485
}
@@ -1507,8 +1492,7 @@ component extends="modules.BaseModule" {
15071492
var lockCli = new cli.lucli.services.deploy.cli.DeployLockCli(
15081493
new cli.lucli.services.deploy.lib.SshPool()
15091494
);
1510-
invoke(lockCli, lockVerb, [opts]);
1511-
return arrayToList(lockCli.dryRunOutput(), chr(10));
1495+
return invoke(lockCli, lockVerb, [opts]);
15121496
case "secrets":
15131497
if (arrayLen(positional) < 2) {
15141498
throw(message="wheels deploy secrets requires a verb (fetch/extract/print)");

cli/lucli/services/deploy/cli/DeployAccessoryCli.cfc

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,25 @@ component {
2323

2424
public array function dryRunOutput() { return variables.dryRunBuffer; }
2525

26-
public void function boot(required struct opts) { $forEach(arguments.opts, "run"); }
27-
public void function reboot(required struct opts) { $forEach(arguments.opts, "reboot"); }
28-
public void function start(required struct opts) { $forEach(arguments.opts, "start"); }
29-
public void function stop(required struct opts) { $forEach(arguments.opts, "stop"); }
30-
public void function restart(required struct opts) { $forEach(arguments.opts, "restart"); }
31-
public void function details(required struct opts) { $forEach(arguments.opts, "details"); }
32-
public void function remove(required struct opts) { $forEach(arguments.opts, "remove"); }
26+
public string function boot(required struct opts) { return $forEach(arguments.opts, "run", "Booted accessory"); }
27+
public string function reboot(required struct opts) { return $forEach(arguments.opts, "reboot", "Rebooted accessory"); }
28+
public string function start(required struct opts) { return $forEach(arguments.opts, "start", "Started accessory"); }
29+
public string function stop(required struct opts) { return $forEach(arguments.opts, "stop", "Stopped accessory"); }
30+
public string function restart(required struct opts) { return $forEach(arguments.opts, "restart", "Restarted accessory"); }
31+
public string function details(required struct opts) { return $forEach(arguments.opts, "details", "Collected accessory details"); }
32+
public string function remove(required struct opts) { return $forEach(arguments.opts, "remove", "Removed accessory"); }
3333

34-
public void function logs(required struct opts) {
34+
public string function logs(required struct opts) {
3535
var logOpts = {
3636
tail: arguments.opts.tail ?: 100,
3737
follow: arguments.opts.follow ?: false
3838
};
39-
$forEach(arguments.opts, "logs", logOpts);
39+
return $forEach(arguments.opts, "logs", "Tailed accessory logs", logOpts);
4040
}
4141

4242
// ── Private plumbing ───────────────────────────────────────
4343

44-
private void function $forEach(required struct opts, required string method, struct methodOpts = {}) {
44+
private string function $forEach(required struct opts, required string method, required string verbLabel, struct methodOpts = {}) {
4545
arrayClear(variables.dryRunBuffer);
4646
if (!len(arguments.opts.name ?: "")) {
4747
throw(
@@ -58,13 +58,27 @@ component {
5858
var targets = (arguments.opts.name == "all")
5959
? cfg.accessories()
6060
: [cfg.accessory(arguments.opts.name)];
61+
var names = [];
6162

6263
for (var acc in targets) {
6364
var cmd = structIsEmpty(arguments.methodOpts)
6465
? invoke(accCmds, arguments.method, [acc])
6566
: invoke(accCmds, arguments.method, [acc, arguments.methodOpts]);
6667
$dispatch(acc.hosts(), cmd, dryRun);
68+
arrayAppend(names, acc.name());
6769
}
70+
71+
return $renderResult(
72+
arguments.opts,
73+
arguments.verbLabel & " " & arrayToList(names, ", ")
74+
);
75+
}
76+
77+
private string function $renderResult(required struct opts, required string summary) {
78+
if (arguments.opts.dryRun ?: false) {
79+
return arrayToList(variables.dryRunBuffer, chr(10));
80+
}
81+
return arguments.summary;
6882
}
6983

7084
private void function $dispatch(required array hosts, required string cmd, required boolean dryRun) {

cli/lucli/services/deploy/cli/DeployAppCli.cfc

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,74 +22,84 @@ component {
2222

2323
public array function dryRunOutput() { return variables.dryRunBuffer; }
2424

25-
public void function boot(required struct opts) {
26-
$forEachHost(arguments.opts, function(cmds, role, version) {
25+
public string function boot(required struct opts) {
26+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
2727
return cmds.run(role, version);
2828
});
29+
return $renderResult(arguments.opts, "Booted app on " & n & " host(s)");
2930
}
3031

31-
public void function start(required struct opts) {
32-
$forEachHost(arguments.opts, function(cmds, role, version) {
32+
public string function start(required struct opts) {
33+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
3334
return cmds.start(role, version);
3435
});
36+
return $renderResult(arguments.opts, "Started app on " & n & " host(s)");
3537
}
3638

37-
public void function stop(required struct opts) {
38-
$forEachHost(arguments.opts, function(cmds, role, version) {
39+
public string function stop(required struct opts) {
40+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
3941
return cmds.stop(role, version);
4042
});
43+
return $renderResult(arguments.opts, "Stopped app on " & n & " host(s)");
4144
}
4245

43-
public void function details(required struct opts) {
44-
$forEachHost(arguments.opts, function(cmds, role, version) {
46+
public string function details(required struct opts) {
47+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
4548
return cmds.status(role, version);
4649
});
50+
return $renderResult(arguments.opts, "Collected app details on " & n & " host(s)");
4751
}
4852

49-
public void function containers(required struct opts) {
50-
$forEachHost(arguments.opts, function(cmds, role, version) {
53+
public string function containers(required struct opts) {
54+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
5155
return cmds.containers();
5256
}, {versionOptional: true});
57+
return $renderResult(arguments.opts, "Listed app containers on " & n & " host(s)");
5358
}
5459

55-
public void function images(required struct opts) {
56-
$forEachHost(arguments.opts, function(cmds, role, version) {
60+
public string function images(required struct opts) {
61+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
5762
return cmds.images();
5863
}, {versionOptional: true});
64+
return $renderResult(arguments.opts, "Listed app images on " & n & " host(s)");
5965
}
6066

61-
public void function logs(required struct opts) {
67+
public string function logs(required struct opts) {
6268
var logOpts = {
6369
tail: arguments.opts.tail ?: 100,
6470
follow: arguments.opts.follow ?: false,
6571
container: arguments.opts.container ?: ""
6672
};
67-
$forEachHost(arguments.opts, function(cmds, role, version) {
73+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
6874
return cmds.logs(logOpts);
6975
}, {versionOptional: true});
76+
return $renderResult(arguments.opts, "Tailed app logs on " & n & " host(s)");
7077
}
7178

72-
public void function live(required struct opts) {
73-
$forEachHost(arguments.opts, function(cmds, role, version) {
79+
public string function live(required struct opts) {
80+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
7481
return cmds.live(role, version);
7582
});
83+
return $renderResult(arguments.opts, "Marked app live on " & n & " host(s)");
7684
}
7785

78-
public void function maintenance(required struct opts) {
79-
$forEachHost(arguments.opts, function(cmds, role, version) {
86+
public string function maintenance(required struct opts) {
87+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
8088
return cmds.maintenance(role, version);
8189
});
90+
return $renderResult(arguments.opts, "Put app into maintenance mode on " & n & " host(s)");
8291
}
8392

84-
public void function remove(required struct opts) {
85-
$forEachHost(arguments.opts, function(cmds, role, version) {
93+
public string function remove(required struct opts) {
94+
var n = $forEachHost(arguments.opts, function(cmds, role, version) {
8695
return cmds.remove(role, version);
8796
});
97+
return $renderResult(arguments.opts, "Removed app from " & n & " host(s)");
8898
}
8999

90100
// ── Private plumbing ───────────────────────────────────────
91101

92-
private void function $forEachHost(required struct opts, required any cmdFn, struct flags = {}) {
102+
private numeric function $forEachHost(required struct opts, required any cmdFn, struct flags = {}) {
93103
arrayClear(variables.dryRunBuffer);
94104
var cfg = variables.loader.load(
95105
arguments.opts.configPath,
@@ -104,14 +114,24 @@ component {
104114
var dryRun = arguments.opts.dryRun ?: false;
105115
var appCmds = new cli.lucli.services.deploy.commands.AppCommands(cfg);
106116
var roleFilter = arguments.opts.role ?: "";
117+
var hostCount = 0;
107118

108119
for (var role in cfg.roles()) {
109120
if (len(roleFilter) && role.name() != roleFilter) continue;
110121
for (var host in role.hosts()) {
111122
var cmd = arguments.cmdFn(appCmds, role, version);
112123
$dispatch([host], cmd, dryRun);
124+
hostCount++;
113125
}
114126
}
127+
return hostCount;
128+
}
129+
130+
private string function $renderResult(required struct opts, required string summary) {
131+
if (arguments.opts.dryRun ?: false) {
132+
return arrayToList(variables.dryRunBuffer, chr(10));
133+
}
134+
return arguments.summary;
115135
}
116136

117137
private void function $dispatch(required array hosts, required string cmd, required boolean dryRun) {

0 commit comments

Comments
 (0)