@go-to-k/cdkd 0.3.5 → 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 +254 -49
- 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.5.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,
|
|
@@ -11432,10 +11572,14 @@ var LambdaFunctionProvider = class {
|
|
|
11432
11572
|
}
|
|
11433
11573
|
/**
|
|
11434
11574
|
* List Lambda-managed ENIs for the given function, paginating through
|
|
11435
|
-
* DescribeNetworkInterfaces and filtering on Description
|
|
11575
|
+
* DescribeNetworkInterfaces and filtering on Description.
|
|
11436
11576
|
*
|
|
11437
|
-
*
|
|
11438
|
-
*
|
|
11577
|
+
* We filter directly on `description=AWS Lambda VPC ENI-*` (the EC2 API
|
|
11578
|
+
* supports `*` wildcards on this filter — same approach as delstack). An
|
|
11579
|
+
* earlier attempt narrowed with `requester-id=*:awslambda_*`, but real
|
|
11580
|
+
* Lambda hyperplane ENIs carry a RequesterId of the form
|
|
11581
|
+
* `AROAXXX...:<account-id>` (no literal "awslambda" substring), so that
|
|
11582
|
+
* filter matched nothing and the cleanup loop quietly listed zero ENIs.
|
|
11439
11583
|
*/
|
|
11440
11584
|
async listLambdaEnis(functionName) {
|
|
11441
11585
|
const enis = [];
|
|
@@ -11444,10 +11588,7 @@ var LambdaFunctionProvider = class {
|
|
|
11444
11588
|
do {
|
|
11445
11589
|
const resp = await this.ec2Client.send(
|
|
11446
11590
|
new DescribeNetworkInterfacesCommand({
|
|
11447
|
-
Filters: [
|
|
11448
|
-
// Lambda hyperplane ENIs are owned by the Lambda service principal.
|
|
11449
|
-
{ Name: "requester-id", Values: ["*:awslambda_*"] }
|
|
11450
|
-
],
|
|
11591
|
+
Filters: [{ Name: "description", Values: [`${descriptionPrefix}*`] }],
|
|
11451
11592
|
NextToken: nextToken
|
|
11452
11593
|
})
|
|
11453
11594
|
);
|
|
@@ -14407,7 +14548,10 @@ var EC2Provider = class {
|
|
|
14407
14548
|
new DescribeNetworkInterfacesCommand2({
|
|
14408
14549
|
Filters: [
|
|
14409
14550
|
{ Name: "subnet-id", Values: [subnetId] },
|
|
14410
|
-
|
|
14551
|
+
// `description` filter is the only reliable way to find Lambda
|
|
14552
|
+
// hyperplane ENIs — `requester-id` does not actually contain the
|
|
14553
|
+
// string "awslambda" (it is an AROA principal id).
|
|
14554
|
+
{ Name: "description", Values: ["AWS Lambda VPC ENI-*"] }
|
|
14411
14555
|
]
|
|
14412
14556
|
})
|
|
14413
14557
|
);
|
|
@@ -14965,7 +15109,9 @@ var EC2Provider = class {
|
|
|
14965
15109
|
new DescribeNetworkInterfacesCommand2({
|
|
14966
15110
|
Filters: [
|
|
14967
15111
|
{ Name: "group-id", Values: [groupId] },
|
|
14968
|
-
|
|
15112
|
+
// See cleanupSubnetLambdaEnis: requester-id does not contain
|
|
15113
|
+
// "awslambda" — filter on description instead.
|
|
15114
|
+
{ Name: "description", Values: ["AWS Lambda VPC ENI-*"] }
|
|
14969
15115
|
]
|
|
14970
15116
|
})
|
|
14971
15117
|
);
|
|
@@ -25868,11 +26014,15 @@ var DeployEngine = class {
|
|
|
25868
26014
|
this.logger.debug(`Starting deployment for stack: ${stackName}`);
|
|
25869
26015
|
setCurrentStackName(stackName);
|
|
25870
26016
|
await this.lockManager.acquireLockWithRetry(stackName, void 0, "deploy");
|
|
26017
|
+
const renderer = getLiveRenderer();
|
|
26018
|
+
renderer.start();
|
|
25871
26019
|
this.interrupted = false;
|
|
25872
26020
|
const sigintHandler = () => {
|
|
25873
|
-
|
|
25874
|
-
|
|
25875
|
-
|
|
26021
|
+
renderer.printAbove(() => {
|
|
26022
|
+
process.stderr.write(
|
|
26023
|
+
"\nInterrupted \u2014 saving partial state after current operations complete...\n"
|
|
26024
|
+
);
|
|
26025
|
+
});
|
|
25876
26026
|
this.interrupted = true;
|
|
25877
26027
|
};
|
|
25878
26028
|
process.on("SIGINT", sigintHandler);
|
|
@@ -25987,6 +26137,7 @@ var DeployEngine = class {
|
|
|
25987
26137
|
durationMs
|
|
25988
26138
|
};
|
|
25989
26139
|
} finally {
|
|
26140
|
+
renderer.stop();
|
|
25990
26141
|
process.removeListener("SIGINT", sigintHandler);
|
|
25991
26142
|
try {
|
|
25992
26143
|
await this.lockManager.releaseLock(stackName);
|
|
@@ -26415,6 +26566,10 @@ var DeployEngine = class {
|
|
|
26415
26566
|
async provisionResource(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
26416
26567
|
const resourceType = change.resourceType;
|
|
26417
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})`);
|
|
26418
26573
|
try {
|
|
26419
26574
|
switch (change.changeType) {
|
|
26420
26575
|
case "CREATE": {
|
|
@@ -26446,6 +26601,7 @@ var DeployEngine = class {
|
|
|
26446
26601
|
if (progress)
|
|
26447
26602
|
progress.current++;
|
|
26448
26603
|
const createPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26604
|
+
renderer.removeTask(logicalId);
|
|
26449
26605
|
this.logger.info(`${createPrefix}\u2705 ${logicalId} (${resourceType}) created`);
|
|
26450
26606
|
break;
|
|
26451
26607
|
}
|
|
@@ -26473,9 +26629,9 @@ var DeployEngine = class {
|
|
|
26473
26629
|
counts.skipped++;
|
|
26474
26630
|
break;
|
|
26475
26631
|
}
|
|
26476
|
-
const
|
|
26632
|
+
const needsReplacement2 = change.propertyChanges?.some((pc) => pc.requiresReplacement);
|
|
26477
26633
|
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
26478
|
-
if (
|
|
26634
|
+
if (needsReplacement2) {
|
|
26479
26635
|
const replacedProps = change.propertyChanges?.filter((pc) => pc.requiresReplacement).map((pc) => pc.path).join(", ");
|
|
26480
26636
|
this.logger.info(
|
|
26481
26637
|
`Replacing ${logicalId} (${resourceType}) - immutable properties changed: ${replacedProps}`
|
|
@@ -26519,6 +26675,7 @@ var DeployEngine = class {
|
|
|
26519
26675
|
if (progress)
|
|
26520
26676
|
progress.current++;
|
|
26521
26677
|
const replacePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26678
|
+
renderer.removeTask(logicalId);
|
|
26522
26679
|
this.logger.info(`${replacePrefix}\u2705 ${logicalId} (${resourceType}) replaced`);
|
|
26523
26680
|
} else {
|
|
26524
26681
|
this.logger.debug(`Updating ${logicalId} (${resourceType})`);
|
|
@@ -26594,6 +26751,7 @@ var DeployEngine = class {
|
|
|
26594
26751
|
if (progress)
|
|
26595
26752
|
progress.current++;
|
|
26596
26753
|
const updatePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26754
|
+
renderer.removeTask(logicalId);
|
|
26597
26755
|
this.logger.info(`${updatePrefix}\u2705 ${logicalId} (${resourceType}) updated`);
|
|
26598
26756
|
}
|
|
26599
26757
|
break;
|
|
@@ -26639,11 +26797,13 @@ var DeployEngine = class {
|
|
|
26639
26797
|
if (progress)
|
|
26640
26798
|
progress.current++;
|
|
26641
26799
|
const deletePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
26800
|
+
renderer.removeTask(logicalId);
|
|
26642
26801
|
this.logger.info(`${deletePrefix}\u2705 ${logicalId} (${resourceType}) deleted`);
|
|
26643
26802
|
break;
|
|
26644
26803
|
}
|
|
26645
26804
|
}
|
|
26646
26805
|
} catch (error) {
|
|
26806
|
+
renderer.removeTask(logicalId);
|
|
26647
26807
|
const message = error instanceof Error ? error.message : String(error);
|
|
26648
26808
|
this.logger.error(`Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`);
|
|
26649
26809
|
throw new ProvisioningError(
|
|
@@ -26653,6 +26813,8 @@ var DeployEngine = class {
|
|
|
26653
26813
|
stateResources[logicalId]?.physicalId,
|
|
26654
26814
|
error instanceof Error ? error : void 0
|
|
26655
26815
|
);
|
|
26816
|
+
} finally {
|
|
26817
|
+
renderer.removeTask(logicalId);
|
|
26656
26818
|
}
|
|
26657
26819
|
}
|
|
26658
26820
|
/**
|
|
@@ -26916,10 +27078,43 @@ var DeployEngine = class {
|
|
|
26916
27078
|
|
|
26917
27079
|
// src/cli/commands/deploy.ts
|
|
26918
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
|
|
26919
27113
|
async function deployCommand(stacks, options) {
|
|
26920
27114
|
const logger = getLogger();
|
|
26921
27115
|
if (options.verbose) {
|
|
26922
27116
|
logger.setLevel("debug");
|
|
27117
|
+
process.env["CDKD_NO_LIVE"] = "1";
|
|
26923
27118
|
}
|
|
26924
27119
|
if (!options.wait) {
|
|
26925
27120
|
process.env["CDKD_NO_WAIT"] = "true";
|
|
@@ -26977,21 +27172,17 @@ async function deployCommand(stacks, options) {
|
|
|
26977
27172
|
if (options.all) {
|
|
26978
27173
|
targetStacks = allStacks;
|
|
26979
27174
|
} else if (stackPatterns.length > 0) {
|
|
26980
|
-
targetStacks = allStacks
|
|
26981
|
-
(s) => stackPatterns.some(
|
|
26982
|
-
(pattern) => pattern.includes("*") ? new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(s.stackName) : s.stackName === pattern
|
|
26983
|
-
)
|
|
26984
|
-
);
|
|
27175
|
+
targetStacks = matchStacks(allStacks, stackPatterns);
|
|
26985
27176
|
} else if (allStacks.length === 1) {
|
|
26986
27177
|
targetStacks = allStacks;
|
|
26987
27178
|
} else {
|
|
26988
27179
|
throw new Error(
|
|
26989
|
-
`Multiple stacks found: ${allStacks.map(
|
|
27180
|
+
`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`
|
|
26990
27181
|
);
|
|
26991
27182
|
}
|
|
26992
27183
|
if (targetStacks.length === 0) {
|
|
26993
27184
|
throw new Error(
|
|
26994
|
-
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"
|
|
26995
27186
|
);
|
|
26996
27187
|
}
|
|
26997
27188
|
if (!options.exclusively) {
|
|
@@ -27152,7 +27343,10 @@ Deploying stack: ${stackInfo.stackName}${stackRegion !== baseRegion ? ` (region:
|
|
|
27152
27343
|
}
|
|
27153
27344
|
}
|
|
27154
27345
|
function createDeployCommand() {
|
|
27155
|
-
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));
|
|
27156
27350
|
[
|
|
27157
27351
|
...commonOptions,
|
|
27158
27352
|
...appOptions,
|
|
@@ -27263,21 +27457,17 @@ async function diffCommand(stacks, options) {
|
|
|
27263
27457
|
if (options.all) {
|
|
27264
27458
|
targetStacks = allStacks;
|
|
27265
27459
|
} else if (stackPatterns.length > 0) {
|
|
27266
|
-
targetStacks = allStacks
|
|
27267
|
-
(s) => stackPatterns.some(
|
|
27268
|
-
(pattern) => pattern.includes("*") ? new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(s.stackName) : s.stackName === pattern
|
|
27269
|
-
)
|
|
27270
|
-
);
|
|
27460
|
+
targetStacks = matchStacks(allStacks, stackPatterns);
|
|
27271
27461
|
} else if (allStacks.length === 1) {
|
|
27272
27462
|
targetStacks = allStacks;
|
|
27273
27463
|
} else {
|
|
27274
27464
|
throw new Error(
|
|
27275
|
-
`Multiple stacks found: ${allStacks.map(
|
|
27465
|
+
`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`
|
|
27276
27466
|
);
|
|
27277
27467
|
}
|
|
27278
27468
|
if (targetStacks.length === 0) {
|
|
27279
27469
|
throw new Error(
|
|
27280
|
-
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"
|
|
27281
27471
|
);
|
|
27282
27472
|
}
|
|
27283
27473
|
const stateConfig = {
|
|
@@ -27367,7 +27557,10 @@ ${createCount} to create, ${updateCount} to update, ${deleteCount} to delete`);
|
|
|
27367
27557
|
}
|
|
27368
27558
|
}
|
|
27369
27559
|
function createDiffCommand() {
|
|
27370
|
-
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));
|
|
27371
27564
|
[...commonOptions, ...appOptions, ...stateOptions, ...stackOptions, ...contextOptions].forEach(
|
|
27372
27565
|
(opt) => cmd.addOption(opt)
|
|
27373
27566
|
);
|
|
@@ -27382,6 +27575,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27382
27575
|
const logger = getLogger();
|
|
27383
27576
|
if (options.verbose) {
|
|
27384
27577
|
logger.setLevel("debug");
|
|
27578
|
+
process.env["CDKD_NO_LIVE"] = "1";
|
|
27385
27579
|
}
|
|
27386
27580
|
const region = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
27387
27581
|
const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region);
|
|
@@ -27409,7 +27603,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27409
27603
|
registerAllProviders(providerRegistry);
|
|
27410
27604
|
providerRegistry.setCustomResourceResponseBucket(stateBucket);
|
|
27411
27605
|
const appCmd = options.app || resolveApp();
|
|
27412
|
-
let
|
|
27606
|
+
let appStacks = [];
|
|
27413
27607
|
if (appCmd) {
|
|
27414
27608
|
try {
|
|
27415
27609
|
const synthesizer = new Synthesizer();
|
|
@@ -27419,17 +27613,21 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27419
27613
|
output: options.output || "cdk.out",
|
|
27420
27614
|
...Object.keys(context).length > 0 && { context }
|
|
27421
27615
|
});
|
|
27422
|
-
|
|
27616
|
+
appStacks = result.stacks.map((s) => ({
|
|
27617
|
+
stackName: s.stackName,
|
|
27618
|
+
displayName: s.displayName
|
|
27619
|
+
}));
|
|
27423
27620
|
} catch {
|
|
27424
27621
|
logger.debug("Could not synthesize app, falling back to state-based stack list");
|
|
27425
27622
|
}
|
|
27426
27623
|
}
|
|
27427
27624
|
const allStateStacks = await stateBackend.listStacks();
|
|
27428
27625
|
let candidateStacks;
|
|
27429
|
-
if (
|
|
27430
|
-
|
|
27626
|
+
if (appStacks.length > 0) {
|
|
27627
|
+
const stateSet = new Set(allStateStacks);
|
|
27628
|
+
candidateStacks = appStacks.filter((s) => stateSet.has(s.stackName));
|
|
27431
27629
|
} else if (stackArgs.length > 0 || options.stack || options.all) {
|
|
27432
|
-
candidateStacks = allStateStacks;
|
|
27630
|
+
candidateStacks = allStateStacks.map((name) => ({ stackName: name }));
|
|
27433
27631
|
} else {
|
|
27434
27632
|
throw new Error(
|
|
27435
27633
|
"Could not determine which stacks belong to this app. Specify stack names explicitly, use --all, or ensure --app / cdk.json is configured."
|
|
@@ -27438,21 +27636,17 @@ async function destroyCommand(stackArgs, options) {
|
|
|
27438
27636
|
const stackPatterns = stackArgs.length > 0 ? stackArgs : options.stack ? [options.stack] : [];
|
|
27439
27637
|
let stackNames;
|
|
27440
27638
|
if (options.all) {
|
|
27441
|
-
stackNames = candidateStacks;
|
|
27639
|
+
stackNames = candidateStacks.map((s) => s.stackName);
|
|
27442
27640
|
} else if (stackPatterns.length > 0) {
|
|
27443
|
-
stackNames = candidateStacks.
|
|
27444
|
-
(name) => stackPatterns.some(
|
|
27445
|
-
(pattern) => pattern.includes("*") ? new RegExp("^" + pattern.replace(/\*/g, ".*") + "$").test(name) : name === pattern
|
|
27446
|
-
)
|
|
27447
|
-
);
|
|
27641
|
+
stackNames = matchStacks(candidateStacks, stackPatterns).map((s) => s.stackName);
|
|
27448
27642
|
} else if (candidateStacks.length === 1) {
|
|
27449
|
-
stackNames = candidateStacks;
|
|
27643
|
+
stackNames = candidateStacks.map((s) => s.stackName);
|
|
27450
27644
|
} else if (candidateStacks.length === 0) {
|
|
27451
27645
|
logger.info("No stacks found in state");
|
|
27452
27646
|
return;
|
|
27453
27647
|
} else {
|
|
27454
27648
|
throw new Error(
|
|
27455
|
-
`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`
|
|
27456
27650
|
);
|
|
27457
27651
|
}
|
|
27458
27652
|
if (stackNames.length === 0) {
|
|
@@ -27516,6 +27710,8 @@ Are you sure you want to destroy stack "${stackName}" and delete all ${resourceC
|
|
|
27516
27710
|
logger.info(`
|
|
27517
27711
|
Acquiring lock for stack ${stackName}...`);
|
|
27518
27712
|
await lockManager.acquireLock(stackName, "destroy");
|
|
27713
|
+
const renderer = getLiveRenderer();
|
|
27714
|
+
renderer.start();
|
|
27519
27715
|
try {
|
|
27520
27716
|
logger.info("Building dependency graph...");
|
|
27521
27717
|
const template = {
|
|
@@ -27579,6 +27775,7 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27579
27775
|
logger.warn(`Resource ${logicalId} not found in state, skipping`);
|
|
27580
27776
|
return;
|
|
27581
27777
|
}
|
|
27778
|
+
renderer.addTask(logicalId, `Deleting ${logicalId} (${resource.resourceType})`);
|
|
27582
27779
|
try {
|
|
27583
27780
|
const provider = destroyProviderRegistry.getProvider(resource.resourceType);
|
|
27584
27781
|
let lastDeleteError;
|
|
@@ -27607,9 +27804,11 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27607
27804
|
}
|
|
27608
27805
|
if (lastDeleteError)
|
|
27609
27806
|
throw lastDeleteError;
|
|
27807
|
+
renderer.removeTask(logicalId);
|
|
27610
27808
|
logger.info(` \u2705 ${logicalId} (${resource.resourceType}) deleted`);
|
|
27611
27809
|
deletedCount++;
|
|
27612
27810
|
} catch (error) {
|
|
27811
|
+
renderer.removeTask(logicalId);
|
|
27613
27812
|
const msg = error instanceof Error ? error.message : String(error);
|
|
27614
27813
|
if (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException")) {
|
|
27615
27814
|
logger.debug(` ${logicalId} already deleted, removing from state`);
|
|
@@ -27618,6 +27817,8 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27618
27817
|
logger.error(` \u2717 Failed to delete ${logicalId}:`, String(error));
|
|
27619
27818
|
errorCount++;
|
|
27620
27819
|
}
|
|
27820
|
+
} finally {
|
|
27821
|
+
renderer.removeTask(logicalId);
|
|
27621
27822
|
}
|
|
27622
27823
|
});
|
|
27623
27824
|
await Promise.all(deletePromises);
|
|
@@ -27633,6 +27834,7 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27633
27834
|
\u2713 Stack ${stackName} destroyed (${deletedCount} deleted, ${errorCount} errors)`
|
|
27634
27835
|
);
|
|
27635
27836
|
} finally {
|
|
27837
|
+
renderer.stop();
|
|
27636
27838
|
logger.debug("Releasing lock...");
|
|
27637
27839
|
await lockManager.releaseLock(stackName);
|
|
27638
27840
|
if (destroyAwsClients) {
|
|
@@ -27648,7 +27850,10 @@ Acquiring lock for stack ${stackName}...`);
|
|
|
27648
27850
|
}
|
|
27649
27851
|
}
|
|
27650
27852
|
function createDestroyCommand() {
|
|
27651
|
-
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));
|
|
27652
27857
|
[
|
|
27653
27858
|
...commonOptions,
|
|
27654
27859
|
...appOptions,
|
|
@@ -27762,7 +27967,7 @@ function reorderArgs(argv) {
|
|
|
27762
27967
|
}
|
|
27763
27968
|
async function main() {
|
|
27764
27969
|
const program = new Command8();
|
|
27765
|
-
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");
|
|
27766
27971
|
program.addCommand(createBootstrapCommand());
|
|
27767
27972
|
program.addCommand(createSynthCommand());
|
|
27768
27973
|
program.addCommand(createDeployCommand());
|