@go-to-k/cdkd 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/cli.js +239 -40
- package/dist/cli.js.map +4 -4
- package/dist/go-to-k-cdkd-0.4.0.tgz +0 -0
- package/dist/index.js +165 -9
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.3.6.tgz +0 -0
package/README.md
CHANGED
|
@@ -380,9 +380,15 @@ cdkd deploy Stack1 Stack2
|
|
|
380
380
|
# Deploy all stacks
|
|
381
381
|
cdkd deploy --all
|
|
382
382
|
|
|
383
|
-
# Deploy with wildcard
|
|
383
|
+
# Deploy with wildcard (matched against the physical CloudFormation stack name)
|
|
384
384
|
cdkd deploy 'My*'
|
|
385
385
|
|
|
386
|
+
# Deploy stacks under a CDK Stage using the hierarchical path (CDK CLI parity)
|
|
387
|
+
# Patterns containing '/' are routed to the CDK display path; both forms work:
|
|
388
|
+
cdkd deploy 'MyStage/*' # all stacks under MyStage
|
|
389
|
+
cdkd deploy MyStage/Api # specific stack by display path
|
|
390
|
+
cdkd deploy MyStage-Api # same stack by physical CloudFormation name
|
|
391
|
+
|
|
386
392
|
# Deploy with context values
|
|
387
393
|
cdkd deploy -c env=staging -c featureFlag=true
|
|
388
394
|
|
package/dist/cli.js
CHANGED
|
@@ -527,6 +527,141 @@ var destroyOptions = [
|
|
|
527
527
|
)
|
|
528
528
|
];
|
|
529
529
|
|
|
530
|
+
// src/utils/live-renderer.ts
|
|
531
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
532
|
+
var FRAME_INTERVAL_MS = 80;
|
|
533
|
+
var ESC = "\x1B[";
|
|
534
|
+
var LiveRenderer = class {
|
|
535
|
+
constructor(stream = process.stdout) {
|
|
536
|
+
this.stream = stream;
|
|
537
|
+
}
|
|
538
|
+
tasks = /* @__PURE__ */ new Map();
|
|
539
|
+
active = false;
|
|
540
|
+
spinnerIndex = 0;
|
|
541
|
+
interval = null;
|
|
542
|
+
linesDrawn = 0;
|
|
543
|
+
cursorHidden = false;
|
|
544
|
+
exitListener = null;
|
|
545
|
+
isActive() {
|
|
546
|
+
return this.active;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Enable the live renderer. No-op if stdout is not a TTY or if
|
|
550
|
+
* `CDKD_NO_LIVE=1`. Returns true if successfully enabled.
|
|
551
|
+
*/
|
|
552
|
+
start() {
|
|
553
|
+
if (this.active)
|
|
554
|
+
return true;
|
|
555
|
+
if (!this.stream.isTTY)
|
|
556
|
+
return false;
|
|
557
|
+
if (process.env["CDKD_NO_LIVE"] === "1")
|
|
558
|
+
return false;
|
|
559
|
+
this.active = true;
|
|
560
|
+
this.hideCursor();
|
|
561
|
+
if (!this.exitListener) {
|
|
562
|
+
this.exitListener = () => this.showCursor();
|
|
563
|
+
process.on("exit", this.exitListener);
|
|
564
|
+
}
|
|
565
|
+
this.interval = setInterval(() => this.draw(), FRAME_INTERVAL_MS);
|
|
566
|
+
if (typeof this.interval.unref === "function")
|
|
567
|
+
this.interval.unref();
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
stop() {
|
|
571
|
+
if (!this.active)
|
|
572
|
+
return;
|
|
573
|
+
if (this.interval) {
|
|
574
|
+
clearInterval(this.interval);
|
|
575
|
+
this.interval = null;
|
|
576
|
+
}
|
|
577
|
+
this.clear();
|
|
578
|
+
this.showCursor();
|
|
579
|
+
if (this.exitListener) {
|
|
580
|
+
process.removeListener("exit", this.exitListener);
|
|
581
|
+
this.exitListener = null;
|
|
582
|
+
}
|
|
583
|
+
this.tasks.clear();
|
|
584
|
+
this.active = false;
|
|
585
|
+
}
|
|
586
|
+
addTask(id, label) {
|
|
587
|
+
this.tasks.set(id, { label, startedAt: Date.now() });
|
|
588
|
+
if (this.active)
|
|
589
|
+
this.draw();
|
|
590
|
+
}
|
|
591
|
+
removeTask(id) {
|
|
592
|
+
if (!this.tasks.delete(id))
|
|
593
|
+
return;
|
|
594
|
+
if (this.active)
|
|
595
|
+
this.draw();
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Print content above the live area. Clears the live area, runs the writer,
|
|
599
|
+
* then redraws the live area. When the renderer is inactive, the writer
|
|
600
|
+
* runs directly so callers can use this unconditionally.
|
|
601
|
+
*/
|
|
602
|
+
printAbove(write) {
|
|
603
|
+
if (!this.active) {
|
|
604
|
+
write();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
this.clear();
|
|
608
|
+
write();
|
|
609
|
+
this.draw();
|
|
610
|
+
}
|
|
611
|
+
clear() {
|
|
612
|
+
if (this.linesDrawn === 0)
|
|
613
|
+
return;
|
|
614
|
+
this.stream.write("\r");
|
|
615
|
+
for (let i = 0; i < this.linesDrawn; i++) {
|
|
616
|
+
this.stream.write(`${ESC}1A${ESC}2K`);
|
|
617
|
+
}
|
|
618
|
+
this.linesDrawn = 0;
|
|
619
|
+
}
|
|
620
|
+
draw() {
|
|
621
|
+
if (!this.active)
|
|
622
|
+
return;
|
|
623
|
+
this.clear();
|
|
624
|
+
if (this.tasks.size === 0)
|
|
625
|
+
return;
|
|
626
|
+
const frame = SPINNER_FRAMES[this.spinnerIndex % SPINNER_FRAMES.length];
|
|
627
|
+
this.spinnerIndex++;
|
|
628
|
+
const cols = this.stream.columns ?? 80;
|
|
629
|
+
const lines = [];
|
|
630
|
+
for (const task of this.tasks.values()) {
|
|
631
|
+
const elapsed = ((Date.now() - task.startedAt) / 1e3).toFixed(1);
|
|
632
|
+
const raw = ` ${frame} ${task.label} (${elapsed}s)`;
|
|
633
|
+
lines.push(this.truncate(raw, cols));
|
|
634
|
+
}
|
|
635
|
+
this.stream.write(lines.join("\n") + "\n");
|
|
636
|
+
this.linesDrawn = lines.length;
|
|
637
|
+
}
|
|
638
|
+
truncate(s, maxLen) {
|
|
639
|
+
if (s.length <= maxLen)
|
|
640
|
+
return s;
|
|
641
|
+
if (maxLen <= 1)
|
|
642
|
+
return "\u2026";
|
|
643
|
+
return s.substring(0, maxLen - 1) + "\u2026";
|
|
644
|
+
}
|
|
645
|
+
hideCursor() {
|
|
646
|
+
if (this.cursorHidden)
|
|
647
|
+
return;
|
|
648
|
+
this.stream.write(`${ESC}?25l`);
|
|
649
|
+
this.cursorHidden = true;
|
|
650
|
+
}
|
|
651
|
+
showCursor() {
|
|
652
|
+
if (!this.cursorHidden)
|
|
653
|
+
return;
|
|
654
|
+
this.stream.write(`${ESC}?25h`);
|
|
655
|
+
this.cursorHidden = false;
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
var globalRenderer = null;
|
|
659
|
+
function getLiveRenderer() {
|
|
660
|
+
if (!globalRenderer)
|
|
661
|
+
globalRenderer = new LiveRenderer();
|
|
662
|
+
return globalRenderer;
|
|
663
|
+
}
|
|
664
|
+
|
|
530
665
|
// src/utils/logger.ts
|
|
531
666
|
var colors = {
|
|
532
667
|
reset: "\x1B[0m",
|
|
@@ -583,22 +718,26 @@ var ConsoleLogger = class {
|
|
|
583
718
|
}
|
|
584
719
|
debug(message, ...args) {
|
|
585
720
|
if (this.shouldLog("debug")) {
|
|
586
|
-
|
|
721
|
+
const formatted = this.formatMessage("debug", message, ...args);
|
|
722
|
+
getLiveRenderer().printAbove(() => console.debug(formatted));
|
|
587
723
|
}
|
|
588
724
|
}
|
|
589
725
|
info(message, ...args) {
|
|
590
726
|
if (this.shouldLog("info")) {
|
|
591
|
-
|
|
727
|
+
const formatted = this.formatMessage("info", message, ...args);
|
|
728
|
+
getLiveRenderer().printAbove(() => console.info(formatted));
|
|
592
729
|
}
|
|
593
730
|
}
|
|
594
731
|
warn(message, ...args) {
|
|
595
732
|
if (this.shouldLog("warn")) {
|
|
596
|
-
|
|
733
|
+
const formatted = this.formatMessage("warn", message, ...args);
|
|
734
|
+
getLiveRenderer().printAbove(() => console.warn(formatted));
|
|
597
735
|
}
|
|
598
736
|
}
|
|
599
737
|
error(message, ...args) {
|
|
600
738
|
if (this.shouldLog("error")) {
|
|
601
|
-
|
|
739
|
+
const formatted = this.formatMessage("error", message, ...args);
|
|
740
|
+
getLiveRenderer().printAbove(() => console.error(formatted));
|
|
602
741
|
}
|
|
603
742
|
}
|
|
604
743
|
/**
|
|
@@ -1243,6 +1382,7 @@ var AssemblyReader = class {
|
|
|
1243
1382
|
}
|
|
1244
1383
|
return {
|
|
1245
1384
|
stackName,
|
|
1385
|
+
displayName: artifact.displayName ?? stackName,
|
|
1246
1386
|
artifactId,
|
|
1247
1387
|
template,
|
|
1248
1388
|
assetManifestPath,
|
|
@@ -25874,11 +26014,15 @@ var DeployEngine = class {
|
|
|
25874
26014
|
this.logger.debug(`Starting deployment for stack: ${stackName}`);
|
|
25875
26015
|
setCurrentStackName(stackName);
|
|
25876
26016
|
await this.lockManager.acquireLockWithRetry(stackName, void 0, "deploy");
|
|
26017
|
+
const renderer = getLiveRenderer();
|
|
26018
|
+
renderer.start();
|
|
25877
26019
|
this.interrupted = false;
|
|
25878
26020
|
const sigintHandler = () => {
|
|
25879
|
-
|
|
25880
|
-
|
|
25881
|
-
|
|
26021
|
+
renderer.printAbove(() => {
|
|
26022
|
+
process.stderr.write(
|
|
26023
|
+
"\nInterrupted \u2014 saving partial state after current operations complete...\n"
|
|
26024
|
+
);
|
|
26025
|
+
});
|
|
25882
26026
|
this.interrupted = true;
|
|
25883
26027
|
};
|
|
25884
26028
|
process.on("SIGINT", sigintHandler);
|
|
@@ -25993,6 +26137,7 @@ var DeployEngine = class {
|
|
|
25993
26137
|
durationMs
|
|
25994
26138
|
};
|
|
25995
26139
|
} finally {
|
|
26140
|
+
renderer.stop();
|
|
25996
26141
|
process.removeListener("SIGINT", sigintHandler);
|
|
25997
26142
|
try {
|
|
25998
26143
|
await this.lockManager.releaseLock(stackName);
|
|
@@ -26421,6 +26566,10 @@ var DeployEngine = class {
|
|
|
26421
26566
|
async provisionResource(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
26422
26567
|
const resourceType = change.resourceType;
|
|
26423
26568
|
const provider = this.providerRegistry.getProvider(resourceType);
|
|
26569
|
+
const renderer = getLiveRenderer();
|
|
26570
|
+
const needsReplacement = change.changeType === "UPDATE" && (change.propertyChanges?.some((pc) => pc.requiresReplacement) ?? false);
|
|
26571
|
+
const verb = change.changeType === "CREATE" ? "Creating" : change.changeType === "DELETE" ? "Deleting" : needsReplacement ? "Replacing" : "Updating";
|
|
26572
|
+
renderer.addTask(logicalId, `${verb} ${logicalId} (${resourceType})`);
|
|
26424
26573
|
try {
|
|
26425
26574
|
switch (change.changeType) {
|
|
26426
26575
|
case "CREATE": {
|
|
@@ -26452,6 +26601,7 @@ var DeployEngine = class {
|
|
|
26452
26601
|
if (progress)
|
|
26453
26602
|
progress.current++;
|
|
26454
26603
|
const createPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26604
|
+
renderer.removeTask(logicalId);
|
|
26455
26605
|
this.logger.info(`${createPrefix}\u2705 ${logicalId} (${resourceType}) created`);
|
|
26456
26606
|
break;
|
|
26457
26607
|
}
|
|
@@ -26479,9 +26629,9 @@ var DeployEngine = class {
|
|
|
26479
26629
|
counts.skipped++;
|
|
26480
26630
|
break;
|
|
26481
26631
|
}
|
|
26482
|
-
const
|
|
26632
|
+
const needsReplacement2 = change.propertyChanges?.some((pc) => pc.requiresReplacement);
|
|
26483
26633
|
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
26484
|
-
if (
|
|
26634
|
+
if (needsReplacement2) {
|
|
26485
26635
|
const replacedProps = change.propertyChanges?.filter((pc) => pc.requiresReplacement).map((pc) => pc.path).join(", ");
|
|
26486
26636
|
this.logger.info(
|
|
26487
26637
|
`Replacing ${logicalId} (${resourceType}) - immutable properties changed: ${replacedProps}`
|
|
@@ -26525,6 +26675,7 @@ var DeployEngine = class {
|
|
|
26525
26675
|
if (progress)
|
|
26526
26676
|
progress.current++;
|
|
26527
26677
|
const replacePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26678
|
+
renderer.removeTask(logicalId);
|
|
26528
26679
|
this.logger.info(`${replacePrefix}\u2705 ${logicalId} (${resourceType}) replaced`);
|
|
26529
26680
|
} else {
|
|
26530
26681
|
this.logger.debug(`Updating ${logicalId} (${resourceType})`);
|
|
@@ -26600,6 +26751,7 @@ var DeployEngine = class {
|
|
|
26600
26751
|
if (progress)
|
|
26601
26752
|
progress.current++;
|
|
26602
26753
|
const updatePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26754
|
+
renderer.removeTask(logicalId);
|
|
26603
26755
|
this.logger.info(`${updatePrefix}\u2705 ${logicalId} (${resourceType}) updated`);
|
|
26604
26756
|
}
|
|
26605
26757
|
break;
|
|
@@ -26645,11 +26797,13 @@ var DeployEngine = class {
|
|
|
26645
26797
|
if (progress)
|
|
26646
26798
|
progress.current++;
|
|
26647
26799
|
const deletePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26800
|
+
renderer.removeTask(logicalId);
|
|
26648
26801
|
this.logger.info(`${deletePrefix}\u2705 ${logicalId} (${resourceType}) deleted`);
|
|
26649
26802
|
break;
|
|
26650
26803
|
}
|
|
26651
26804
|
}
|
|
26652
26805
|
} catch (error) {
|
|
26806
|
+
renderer.removeTask(logicalId);
|
|
26653
26807
|
const message = error instanceof Error ? error.message : String(error);
|
|
26654
26808
|
this.logger.error(`Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`);
|
|
26655
26809
|
throw new ProvisioningError(
|
|
@@ -26659,6 +26813,8 @@ var DeployEngine = class {
|
|
|
26659
26813
|
stateResources[logicalId]?.physicalId,
|
|
26660
26814
|
error instanceof Error ? error : void 0
|
|
26661
26815
|
);
|
|
26816
|
+
} finally {
|
|
26817
|
+
renderer.removeTask(logicalId);
|
|
26662
26818
|
}
|
|
26663
26819
|
}
|
|
26664
26820
|
/**
|
|
@@ -26922,10 +27078,43 @@ var DeployEngine = class {
|
|
|
26922
27078
|
|
|
26923
27079
|
// src/cli/commands/deploy.ts
|
|
26924
27080
|
init_aws_clients();
|
|
27081
|
+
|
|
27082
|
+
// src/cli/stack-matcher.ts
|
|
27083
|
+
function matchStacks(stacks, patterns) {
|
|
27084
|
+
if (patterns.length === 0)
|
|
27085
|
+
return [];
|
|
27086
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27087
|
+
const result = [];
|
|
27088
|
+
for (const stack of stacks) {
|
|
27089
|
+
const matched = patterns.some((pattern) => stackMatchesPattern(stack, pattern));
|
|
27090
|
+
if (matched && !seen.has(stack.stackName)) {
|
|
27091
|
+
seen.add(stack.stackName);
|
|
27092
|
+
result.push(stack);
|
|
27093
|
+
}
|
|
27094
|
+
}
|
|
27095
|
+
return result;
|
|
27096
|
+
}
|
|
27097
|
+
function describeStack(stack) {
|
|
27098
|
+
if (stack.displayName && stack.displayName !== stack.stackName) {
|
|
27099
|
+
return `${stack.stackName} (${stack.displayName})`;
|
|
27100
|
+
}
|
|
27101
|
+
return stack.stackName;
|
|
27102
|
+
}
|
|
27103
|
+
function stackMatchesPattern(stack, pattern) {
|
|
27104
|
+
const target = pattern.includes("/") ? stack.displayName ?? stack.stackName : stack.stackName;
|
|
27105
|
+
if (pattern.includes("*")) {
|
|
27106
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
27107
|
+
return regex.test(target);
|
|
27108
|
+
}
|
|
27109
|
+
return target === pattern;
|
|
27110
|
+
}
|
|
27111
|
+
|
|
27112
|
+
// src/cli/commands/deploy.ts
|
|
26925
27113
|
async function deployCommand(stacks, options) {
|
|
26926
27114
|
const logger = getLogger();
|
|
26927
27115
|
if (options.verbose) {
|
|
26928
27116
|
logger.setLevel("debug");
|
|
27117
|
+
process.env["CDKD_NO_LIVE"] = "1";
|
|
26929
27118
|
}
|
|
26930
27119
|
if (!options.wait) {
|
|
26931
27120
|
process.env["CDKD_NO_WAIT"] = "true";
|
|
@@ -26983,21 +27172,17 @@ async function deployCommand(stacks, options) {
|
|
|
26983
27172
|
if (options.all) {
|
|
26984
27173
|
targetStacks = allStacks;
|
|
26985
27174
|
} else if (stackPatterns.length > 0) {
|
|
26986
|
-
targetStacks = allStacks
|
|
26987
|
-
(s) => stackPatterns.some(
|
|
26988
|
-
(pattern) => pattern.includes("*") ? new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(s.stackName) : s.stackName === pattern
|
|
26989
|
-
)
|
|
26990
|
-
);
|
|
27175
|
+
targetStacks = matchStacks(allStacks, stackPatterns);
|
|
26991
27176
|
} else if (allStacks.length === 1) {
|
|
26992
27177
|
targetStacks = allStacks;
|
|
26993
27178
|
} else {
|
|
26994
27179
|
throw new Error(
|
|
26995
|
-
`Multiple stacks found: ${allStacks.map(
|
|
27180
|
+
`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`
|
|
26996
27181
|
);
|
|
26997
27182
|
}
|
|
26998
27183
|
if (targetStacks.length === 0) {
|
|
26999
27184
|
throw new Error(
|
|
27000
|
-
stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly. Available: ${allStacks.map(
|
|
27185
|
+
stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly. Available: ${allStacks.map(describeStack).join(", ")}` : "No stacks found in assembly"
|
|
27001
27186
|
);
|
|
27002
27187
|
}
|
|
27003
27188
|
if (!options.exclusively) {
|
|
@@ -27158,7 +27343,10 @@ Deploying stack: ${stackInfo.stackName}${stackRegion !== baseRegion ? ` (region:
|
|
|
27158
27343
|
}
|
|
27159
27344
|
}
|
|
27160
27345
|
function createDeployCommand() {
|
|
27161
|
-
const cmd = new Command3("deploy").description("Deploy CDK app using SDK/Cloud Control API").argument(
|
|
27346
|
+
const cmd = new Command3("deploy").description("Deploy CDK app using SDK/Cloud Control API").argument(
|
|
27347
|
+
"[stacks...]",
|
|
27348
|
+
"Stack name(s) to deploy. Accepts physical CloudFormation names (e.g. 'MyStage-Api') or CDK display paths (e.g. 'MyStage/Api'). Supports wildcards (e.g. 'MyStage/*')."
|
|
27349
|
+
).option("--all", "Deploy all stacks", false).action(withErrorHandling(deployCommand));
|
|
27162
27350
|
[
|
|
27163
27351
|
...commonOptions,
|
|
27164
27352
|
...appOptions,
|
|
@@ -27269,21 +27457,17 @@ async function diffCommand(stacks, options) {
|
|
|
27269
27457
|
if (options.all) {
|
|
27270
27458
|
targetStacks = allStacks;
|
|
27271
27459
|
} else if (stackPatterns.length > 0) {
|
|
27272
|
-
targetStacks = allStacks
|
|
27273
|
-
(s) => stackPatterns.some(
|
|
27274
|
-
(pattern) => pattern.includes("*") ? new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(s.stackName) : s.stackName === pattern
|
|
27275
|
-
)
|
|
27276
|
-
);
|
|
27460
|
+
targetStacks = matchStacks(allStacks, stackPatterns);
|
|
27277
27461
|
} else if (allStacks.length === 1) {
|
|
27278
27462
|
targetStacks = allStacks;
|
|
27279
27463
|
} else {
|
|
27280
27464
|
throw new Error(
|
|
27281
|
-
`Multiple stacks found: ${allStacks.map(
|
|
27465
|
+
`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`
|
|
27282
27466
|
);
|
|
27283
27467
|
}
|
|
27284
27468
|
if (targetStacks.length === 0) {
|
|
27285
27469
|
throw new Error(
|
|
27286
|
-
stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly` : "No stacks found in assembly"
|
|
27470
|
+
stackPatterns.length > 0 ? `No stacks matching ${stackPatterns.join(", ")} found in assembly. Available: ${allStacks.map(describeStack).join(", ")}` : "No stacks found in assembly"
|
|
27287
27471
|
);
|
|
27288
27472
|
}
|
|
27289
27473
|
const stateConfig = {
|
|
@@ -27373,7 +27557,10 @@ ${createCount} to create, ${updateCount} to update, ${deleteCount} to delete`);
|
|
|
27373
27557
|
}
|
|
27374
27558
|
}
|
|
27375
27559
|
function createDiffCommand() {
|
|
27376
|
-
const cmd = new Command4("diff").description("Show difference between current state and desired state").argument(
|
|
27560
|
+
const cmd = new Command4("diff").description("Show difference between current state and desired state").argument(
|
|
27561
|
+
"[stacks...]",
|
|
27562
|
+
"Stack name(s) to diff. Accepts physical CloudFormation names (e.g. 'MyStage-Api') or CDK display paths (e.g. 'MyStage/Api'). Supports wildcards (e.g. 'MyStage/*')."
|
|
27563
|
+
).option("--all", "Diff all stacks", false).action(withErrorHandling(diffCommand));
|
|
27377
27564
|
[...commonOptions, ...appOptions, ...stateOptions, ...stackOptions, ...contextOptions].forEach(
|
|
27378
27565
|
(opt) => cmd.addOption(opt)
|
|
27379
27566
|
);
|
|
@@ -27388,6 +27575,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27388
27575
|
const logger = getLogger();
|
|
27389
27576
|
if (options.verbose) {
|
|
27390
27577
|
logger.setLevel("debug");
|
|
27578
|
+
process.env["CDKD_NO_LIVE"] = "1";
|
|
27391
27579
|
}
|
|
27392
27580
|
const region = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
27393
27581
|
const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region);
|
|
@@ -27415,7 +27603,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27415
27603
|
registerAllProviders(providerRegistry);
|
|
27416
27604
|
providerRegistry.setCustomResourceResponseBucket(stateBucket);
|
|
27417
27605
|
const appCmd = options.app || resolveApp();
|
|
27418
|
-
let
|
|
27606
|
+
let appStacks = [];
|
|
27419
27607
|
if (appCmd) {
|
|
27420
27608
|
try {
|
|
27421
27609
|
const synthesizer = new Synthesizer();
|
|
@@ -27425,17 +27613,21 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27425
27613
|
output: options.output || "cdk.out",
|
|
27426
27614
|
...Object.keys(context).length > 0 && { context }
|
|
27427
27615
|
});
|
|
27428
|
-
|
|
27616
|
+
appStacks = result.stacks.map((s) => ({
|
|
27617
|
+
stackName: s.stackName,
|
|
27618
|
+
displayName: s.displayName
|
|
27619
|
+
}));
|
|
27429
27620
|
} catch {
|
|
27430
27621
|
logger.debug("Could not synthesize app, falling back to state-based stack list");
|
|
27431
27622
|
}
|
|
27432
27623
|
}
|
|
27433
27624
|
const allStateStacks = await stateBackend.listStacks();
|
|
27434
27625
|
let candidateStacks;
|
|
27435
|
-
if (
|
|
27436
|
-
|
|
27626
|
+
if (appStacks.length > 0) {
|
|
27627
|
+
const stateSet = new Set(allStateStacks);
|
|
27628
|
+
candidateStacks = appStacks.filter((s) => stateSet.has(s.stackName));
|
|
27437
27629
|
} else if (stackArgs.length > 0 || options.stack || options.all) {
|
|
27438
|
-
candidateStacks = allStateStacks;
|
|
27630
|
+
candidateStacks = allStateStacks.map((name) => ({ stackName: name }));
|
|
27439
27631
|
} else {
|
|
27440
27632
|
throw new Error(
|
|
27441
27633
|
"Could not determine which stacks belong to this app. Specify stack names explicitly, use --all, or ensure --app / cdk.json is configured."
|
|
@@ -27444,21 +27636,17 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27444
27636
|
const stackPatterns = stackArgs.length > 0 ? stackArgs : options.stack ? [options.stack] : [];
|
|
27445
27637
|
let stackNames;
|
|
27446
27638
|
if (options.all) {
|
|
27447
|
-
stackNames = candidateStacks;
|
|
27639
|
+
stackNames = candidateStacks.map((s) => s.stackName);
|
|
27448
27640
|
} else if (stackPatterns.length > 0) {
|
|
27449
|
-
stackNames = candidateStacks.
|
|
27450
|
-
(name) => stackPatterns.some(
|
|
27451
|
-
(pattern) => pattern.includes("*") ? new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(name) : name === pattern
|
|
27452
|
-
)
|
|
27453
|
-
);
|
|
27641
|
+
stackNames = matchStacks(candidateStacks, stackPatterns).map((s) => s.stackName);
|
|
27454
27642
|
} else if (candidateStacks.length === 1) {
|
|
27455
|
-
stackNames = candidateStacks;
|
|
27643
|
+
stackNames = candidateStacks.map((s) => s.stackName);
|
|
27456
27644
|
} else if (candidateStacks.length === 0) {
|
|
27457
27645
|
logger.info("No stacks found in state");
|
|
27458
27646
|
return;
|
|
27459
27647
|
} else {
|
|
27460
27648
|
throw new Error(
|
|
27461
|
-
`Multiple stacks found: ${candidateStacks.join(", ")}. Specify stack name(s) or use --all`
|
|
27649
|
+
`Multiple stacks found: ${candidateStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`
|
|
27462
27650
|
);
|
|
27463
27651
|
}
|
|
27464
27652
|
if (stackNames.length === 0) {
|
|
@@ -27522,6 +27710,8 @@ Are you sure you want to destroy stack "${stackName}" and delete all ${resourceC
|
|
|
27522
27710
|
logger.info(`
|
|
27523
27711
|
Acquiring lock for stack ${stackName}...`);
|
|
27524
27712
|
await lockManager.acquireLock(stackName, "destroy");
|
|
27713
|
+
const renderer = getLiveRenderer();
|
|
27714
|
+
renderer.start();
|
|
27525
27715
|
try {
|
|
27526
27716
|
logger.info("Building dependency graph...");
|
|
27527
27717
|
const template = {
|
|
@@ -27585,6 +27775,7 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27585
27775
|
logger.warn(`Resource ${logicalId} not found in state, skipping`);
|
|
27586
27776
|
return;
|
|
27587
27777
|
}
|
|
27778
|
+
renderer.addTask(logicalId, `Deleting ${logicalId} (${resource.resourceType})`);
|
|
27588
27779
|
try {
|
|
27589
27780
|
const provider = destroyProviderRegistry.getProvider(resource.resourceType);
|
|
27590
27781
|
let lastDeleteError;
|
|
@@ -27613,9 +27804,11 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27613
27804
|
}
|
|
27614
27805
|
if (lastDeleteError)
|
|
27615
27806
|
throw lastDeleteError;
|
|
27807
|
+
renderer.removeTask(logicalId);
|
|
27616
27808
|
logger.info(` \u2705 ${logicalId} (${resource.resourceType}) deleted`);
|
|
27617
27809
|
deletedCount++;
|
|
27618
27810
|
} catch (error) {
|
|
27811
|
+
renderer.removeTask(logicalId);
|
|
27619
27812
|
const msg = error instanceof Error ? error.message : String(error);
|
|
27620
27813
|
if (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException")) {
|
|
27621
27814
|
logger.debug(` ${logicalId} already deleted, removing from state`);
|
|
@@ -27624,6 +27817,8 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27624
27817
|
logger.error(` \u2717 Failed to delete ${logicalId}:`, String(error));
|
|
27625
27818
|
errorCount++;
|
|
27626
27819
|
}
|
|
27820
|
+
} finally {
|
|
27821
|
+
renderer.removeTask(logicalId);
|
|
27627
27822
|
}
|
|
27628
27823
|
});
|
|
27629
27824
|
await Promise.all(deletePromises);
|
|
@@ -27639,6 +27834,7 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27639
27834
|
\u2713 Stack ${stackName} destroyed (${deletedCount} deleted, ${errorCount} errors)`
|
|
27640
27835
|
);
|
|
27641
27836
|
} finally {
|
|
27837
|
+
renderer.stop();
|
|
27642
27838
|
logger.debug("Releasing lock...");
|
|
27643
27839
|
await lockManager.releaseLock(stackName);
|
|
27644
27840
|
if (destroyAwsClients) {
|
|
@@ -27654,7 +27850,10 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27654
27850
|
}
|
|
27655
27851
|
}
|
|
27656
27852
|
function createDestroyCommand() {
|
|
27657
|
-
const cmd = new Command5("destroy").description("Destroy all resources in the stack").argument(
|
|
27853
|
+
const cmd = new Command5("destroy").description("Destroy all resources in the stack").argument(
|
|
27854
|
+
"[stacks...]",
|
|
27855
|
+
"Stack name(s) to destroy. Accepts physical CloudFormation names (e.g. 'MyStage-Api') or CDK display paths (e.g. 'MyStage/Api'). Supports wildcards (e.g. 'MyStage/*')."
|
|
27856
|
+
).option("--all", "Destroy all stacks", false).action(withErrorHandling(destroyCommand));
|
|
27658
27857
|
[
|
|
27659
27858
|
...commonOptions,
|
|
27660
27859
|
...appOptions,
|
|
@@ -27768,7 +27967,7 @@ function reorderArgs(argv) {
|
|
|
27768
27967
|
}
|
|
27769
27968
|
async function main() {
|
|
27770
27969
|
const program = new Command8();
|
|
27771
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
27970
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.4.0");
|
|
27772
27971
|
program.addCommand(createBootstrapCommand());
|
|
27773
27972
|
program.addCommand(createSynthCommand());
|
|
27774
27973
|
program.addCommand(createDeployCommand());
|