@devramps/cli 0.1.2 → 0.1.3
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/dist/index.js +171 -107
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,77 +75,110 @@ var CloudFormationError = class extends DevRampsError {
|
|
|
75
75
|
import chalk from "chalk";
|
|
76
76
|
var verboseMode = false;
|
|
77
77
|
var ProgressBar = class {
|
|
78
|
-
|
|
78
|
+
completed = 0;
|
|
79
79
|
total = 0;
|
|
80
80
|
label;
|
|
81
81
|
barWidth = 30;
|
|
82
82
|
lastLineCount = 0;
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
inProgressResources = /* @__PURE__ */ new Map();
|
|
84
|
+
isTTY;
|
|
85
85
|
constructor(label, total) {
|
|
86
86
|
this.label = label;
|
|
87
87
|
this.total = total;
|
|
88
|
+
this.isTTY = process.stdout.isTTY ?? false;
|
|
88
89
|
this.render();
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
91
|
-
* Update progress
|
|
92
|
+
* Update progress with resource status
|
|
92
93
|
*/
|
|
93
|
-
update(
|
|
94
|
-
this.
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
update(completed, resourceStatus) {
|
|
95
|
+
this.completed = completed;
|
|
96
|
+
if (resourceStatus) {
|
|
97
|
+
if (resourceStatus.status === "in_progress") {
|
|
98
|
+
this.inProgressResources.set(resourceStatus.logicalId, resourceStatus);
|
|
99
|
+
} else {
|
|
100
|
+
this.inProgressResources.delete(resourceStatus.logicalId);
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
this.render();
|
|
102
104
|
}
|
|
103
105
|
/**
|
|
104
|
-
*
|
|
106
|
+
* Set a resource as in-progress
|
|
105
107
|
*/
|
|
106
|
-
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
setInProgress(logicalId, resourceType) {
|
|
109
|
+
this.inProgressResources.set(logicalId, {
|
|
110
|
+
logicalId,
|
|
111
|
+
resourceType,
|
|
112
|
+
status: "in_progress"
|
|
113
|
+
});
|
|
114
|
+
this.render();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Mark a resource as complete (removes from in-progress)
|
|
118
|
+
*/
|
|
119
|
+
setComplete(logicalId) {
|
|
120
|
+
this.inProgressResources.delete(logicalId);
|
|
111
121
|
this.render();
|
|
112
122
|
}
|
|
113
123
|
/**
|
|
114
|
-
* Clear the progress bar from the terminal
|
|
124
|
+
* Clear the progress bar from the terminal (only for TTY)
|
|
115
125
|
*/
|
|
116
126
|
clear() {
|
|
127
|
+
if (!this.isTTY) return;
|
|
117
128
|
for (let i = 0; i < this.lastLineCount; i++) {
|
|
118
129
|
process.stdout.write("\x1B[A\x1B[2K");
|
|
119
130
|
}
|
|
120
131
|
this.lastLineCount = 0;
|
|
121
132
|
}
|
|
122
133
|
/**
|
|
123
|
-
* Finish
|
|
134
|
+
* Finish the progress bar (don't clear - leave final state visible)
|
|
124
135
|
*/
|
|
125
136
|
finish() {
|
|
126
137
|
this.clear();
|
|
138
|
+
this.inProgressResources.clear();
|
|
139
|
+
const percentage = this.total > 0 ? this.completed / this.total : 0;
|
|
140
|
+
const filled = Math.round(this.barWidth * percentage);
|
|
141
|
+
const empty = this.barWidth - filled;
|
|
142
|
+
const bar = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
|
|
143
|
+
const count = chalk.cyan(`${this.completed}/${this.total}`);
|
|
144
|
+
const labelText = chalk.bold(this.label);
|
|
145
|
+
console.log(`${labelText} ${bar} ${count} resources`);
|
|
127
146
|
}
|
|
128
147
|
/**
|
|
129
|
-
* Render the progress bar
|
|
148
|
+
* Render the progress bar with in-progress resources
|
|
130
149
|
*/
|
|
131
150
|
render() {
|
|
132
151
|
this.clear();
|
|
133
152
|
const lines = [];
|
|
134
|
-
|
|
135
|
-
lines.push(event);
|
|
136
|
-
}
|
|
137
|
-
const percentage = this.total > 0 ? this.current / this.total : 0;
|
|
153
|
+
const percentage = this.total > 0 ? this.completed / this.total : 0;
|
|
138
154
|
const filled = Math.round(this.barWidth * percentage);
|
|
139
155
|
const empty = this.barWidth - filled;
|
|
140
156
|
const bar = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
|
|
141
|
-
const count = chalk.cyan(`${this.
|
|
157
|
+
const count = chalk.cyan(`${this.completed}/${this.total}`);
|
|
142
158
|
const labelText = chalk.bold(this.label);
|
|
143
159
|
lines.push(`${labelText} ${bar} ${count} resources`);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
const inProgress = Array.from(this.inProgressResources.values()).slice(0, 8);
|
|
161
|
+
if (inProgress.length > 0) {
|
|
162
|
+
for (const resource of inProgress) {
|
|
163
|
+
const spinner = chalk.yellow("\u22EF");
|
|
164
|
+
const type = chalk.gray(resource.resourceType);
|
|
165
|
+
lines.push(` ${spinner} ${resource.logicalId} ${type}`);
|
|
166
|
+
}
|
|
167
|
+
const remaining = this.inProgressResources.size - inProgress.length;
|
|
168
|
+
if (remaining > 0) {
|
|
169
|
+
lines.push(chalk.gray(` ... and ${remaining} more in progress`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (this.isTTY) {
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
process.stdout.write(line + "\n");
|
|
175
|
+
}
|
|
176
|
+
this.lastLineCount = lines.length;
|
|
177
|
+
} else {
|
|
178
|
+
if (this.completed === 0 || this.completed === this.total) {
|
|
179
|
+
console.log(lines[0]);
|
|
180
|
+
}
|
|
147
181
|
}
|
|
148
|
-
this.lastLineCount = lines.length;
|
|
149
182
|
}
|
|
150
183
|
};
|
|
151
184
|
function setVerbose(enabled) {
|
|
@@ -208,6 +241,18 @@ function table(rows) {
|
|
|
208
241
|
function newline() {
|
|
209
242
|
console.log();
|
|
210
243
|
}
|
|
244
|
+
function resourceProgress(stackName, resourceId, resourceType, status, reason) {
|
|
245
|
+
const stackLabel = chalk.cyan(`[${stackName}]`);
|
|
246
|
+
const typeLabel = chalk.gray(resourceType);
|
|
247
|
+
if (status === "in_progress") {
|
|
248
|
+
console.log(`${stackLabel} ${chalk.yellow("\u22EF")} ${resourceId} ${typeLabel}`);
|
|
249
|
+
} else if (status === "complete") {
|
|
250
|
+
console.log(`${stackLabel} ${chalk.green("\u2714")} ${resourceId} ${typeLabel}`);
|
|
251
|
+
} else if (status === "failed") {
|
|
252
|
+
const reasonText = reason ? ` - ${reason}` : "";
|
|
253
|
+
console.log(`${stackLabel} ${chalk.red("\u2716")} ${resourceId} ${typeLabel}${reasonText}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
211
256
|
|
|
212
257
|
// src/aws/credentials.ts
|
|
213
258
|
var DEFAULT_REGION = "us-east-1";
|
|
@@ -353,7 +398,7 @@ async function previewStackChanges(options) {
|
|
|
353
398
|
const stackStatus = await getStackStatus(stackName, credentials, region);
|
|
354
399
|
const changeSetName = `devramps-preview-${Date.now()}`;
|
|
355
400
|
if (!stackStatus.exists) {
|
|
356
|
-
info(` Stack ${stackName} will be created (new stack)`);
|
|
401
|
+
info(` Stack ${stackName} will be created (new stack) in account ${options.accountId} (${region || "default region"})`);
|
|
357
402
|
return;
|
|
358
403
|
}
|
|
359
404
|
try {
|
|
@@ -376,7 +421,7 @@ async function previewStackChanges(options) {
|
|
|
376
421
|
ChangeSetName: changeSetName
|
|
377
422
|
})
|
|
378
423
|
);
|
|
379
|
-
logStackChanges(stackName, changeSetResponse.Changes || [], stackStatus.exists);
|
|
424
|
+
logStackChanges(stackName, changeSetResponse.Changes || [], stackStatus.exists, options.accountId, region);
|
|
380
425
|
await client.send(
|
|
381
426
|
new DeleteChangeSetCommand({
|
|
382
427
|
StackName: stackName,
|
|
@@ -401,13 +446,13 @@ async function previewStackChanges(options) {
|
|
|
401
446
|
verbose(` Could not preview changes for ${stackName}: ${errorMessage}`);
|
|
402
447
|
}
|
|
403
448
|
}
|
|
404
|
-
function logStackChanges(stackName, changes, isUpdate) {
|
|
449
|
+
function logStackChanges(stackName, changes, isUpdate, accountId, region) {
|
|
405
450
|
if (changes.length === 0) {
|
|
406
451
|
verbose(` Stack ${stackName}: No changes`);
|
|
407
452
|
return;
|
|
408
453
|
}
|
|
409
454
|
const action = isUpdate ? "update" : "create";
|
|
410
|
-
info(` Stack ${stackName} will ${action} ${changes.length} resource(s):`);
|
|
455
|
+
info(` Stack ${stackName} will ${action} ${changes.length} resource(s) in account ${accountId} (${region || "default region"}):`);
|
|
411
456
|
for (const change of changes) {
|
|
412
457
|
const resourceChange = change.ResourceChange;
|
|
413
458
|
if (!resourceChange) continue;
|
|
@@ -450,21 +495,6 @@ var SUCCESS_STATES = /* @__PURE__ */ new Set([
|
|
|
450
495
|
"CREATE_COMPLETE",
|
|
451
496
|
"UPDATE_COMPLETE"
|
|
452
497
|
]);
|
|
453
|
-
function getStatusSymbol(status) {
|
|
454
|
-
if (!status) return "?";
|
|
455
|
-
if (status.includes("COMPLETE") && !status.includes("ROLLBACK")) return "\u2714";
|
|
456
|
-
if (status.includes("FAILED") || status.includes("ROLLBACK")) return "\u2716";
|
|
457
|
-
if (status.includes("IN_PROGRESS")) return "\u22EF";
|
|
458
|
-
return "?";
|
|
459
|
-
}
|
|
460
|
-
function formatStackEvent(event) {
|
|
461
|
-
const symbol = getStatusSymbol(event.ResourceStatus);
|
|
462
|
-
const resourceType = event.ResourceType || "Unknown";
|
|
463
|
-
const logicalId = event.LogicalResourceId || "Unknown";
|
|
464
|
-
const status = event.ResourceStatus || "Unknown";
|
|
465
|
-
const reason = event.ResourceStatusReason ? ` - ${event.ResourceStatusReason}` : "";
|
|
466
|
-
return ` ${symbol} ${resourceType} (${logicalId}): ${status}${reason}`;
|
|
467
|
-
}
|
|
468
498
|
function isResourceComplete(status) {
|
|
469
499
|
if (!status) return false;
|
|
470
500
|
return status.includes("_COMPLETE") && !status.includes("ROLLBACK");
|
|
@@ -472,6 +502,7 @@ function isResourceComplete(status) {
|
|
|
472
502
|
async function waitForStackWithProgress(client, stackName, operationStartTime, totalResources, maxWaitTime = 600, showProgress = true) {
|
|
473
503
|
const seenEventIds = /* @__PURE__ */ new Set();
|
|
474
504
|
const completedResources = /* @__PURE__ */ new Set();
|
|
505
|
+
const inProgressResources = /* @__PURE__ */ new Map();
|
|
475
506
|
const startTime = Date.now();
|
|
476
507
|
const pollInterval = 3e3;
|
|
477
508
|
const progressBar = showProgress ? new ProgressBar(stackName, totalResources) : null;
|
|
@@ -500,11 +531,48 @@ async function waitForStackWithProgress(client, stackName, operationStartTime, t
|
|
|
500
531
|
seenEventIds.add(event.EventId);
|
|
501
532
|
}
|
|
502
533
|
const logicalId = event.LogicalResourceId;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
534
|
+
const resourceType = event.ResourceType || "Unknown";
|
|
535
|
+
const status = event.ResourceStatus || "";
|
|
536
|
+
if (logicalId && logicalId !== stackName) {
|
|
537
|
+
if (status.includes("IN_PROGRESS")) {
|
|
538
|
+
inProgressResources.set(logicalId, { resourceType });
|
|
539
|
+
if (progressBar) {
|
|
540
|
+
const resourceStatus = {
|
|
541
|
+
logicalId,
|
|
542
|
+
resourceType,
|
|
543
|
+
status: "in_progress"
|
|
544
|
+
};
|
|
545
|
+
progressBar.update(completedResources.size, resourceStatus);
|
|
546
|
+
} else {
|
|
547
|
+
resourceProgress(stackName, logicalId, resourceType, "in_progress");
|
|
548
|
+
}
|
|
549
|
+
} else if (isResourceComplete(status)) {
|
|
550
|
+
completedResources.add(logicalId);
|
|
551
|
+
inProgressResources.delete(logicalId);
|
|
552
|
+
if (progressBar) {
|
|
553
|
+
const resourceStatus = {
|
|
554
|
+
logicalId,
|
|
555
|
+
resourceType,
|
|
556
|
+
status: "complete"
|
|
557
|
+
};
|
|
558
|
+
progressBar.update(completedResources.size, resourceStatus);
|
|
559
|
+
} else {
|
|
560
|
+
resourceProgress(stackName, logicalId, resourceType, "complete");
|
|
561
|
+
}
|
|
562
|
+
} else if (status.includes("FAILED") || status.includes("ROLLBACK")) {
|
|
563
|
+
inProgressResources.delete(logicalId);
|
|
564
|
+
if (progressBar) {
|
|
565
|
+
const resourceStatus = {
|
|
566
|
+
logicalId,
|
|
567
|
+
resourceType,
|
|
568
|
+
status: "failed",
|
|
569
|
+
reason: event.ResourceStatusReason
|
|
570
|
+
};
|
|
571
|
+
progressBar.update(completedResources.size, resourceStatus);
|
|
572
|
+
} else {
|
|
573
|
+
resourceProgress(stackName, logicalId, resourceType, "failed", event.ResourceStatusReason);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
508
576
|
}
|
|
509
577
|
}
|
|
510
578
|
const currentStatus = stack.StackStatus || "";
|
|
@@ -2548,30 +2616,39 @@ async function showDryRunPlan(plan) {
|
|
|
2548
2616
|
info(`CI/CD Account: ${plan.cicdAccountId}`);
|
|
2549
2617
|
info(`CI/CD Region: ${plan.cicdRegion}`);
|
|
2550
2618
|
newline();
|
|
2551
|
-
info("
|
|
2619
|
+
info("Org Stack:");
|
|
2552
2620
|
info(` ${plan.orgStack.action}: ${plan.orgStack.stackName}`);
|
|
2553
|
-
info(` Account: ${plan.orgStack.accountId}`);
|
|
2621
|
+
info(` Account: ${plan.orgStack.accountId}, Region: ${plan.orgStack.region}`);
|
|
2554
2622
|
info(` Target accounts with bucket access: ${plan.orgStack.targetAccountIds.length}`);
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2623
|
+
if (plan.pipelineStacks.length > 0) {
|
|
2624
|
+
newline();
|
|
2625
|
+
info("Pipeline Stacks:");
|
|
2626
|
+
for (const stack of plan.pipelineStacks) {
|
|
2627
|
+
info(` ${stack.action}: ${stack.stackName}`);
|
|
2628
|
+
info(` Account: ${stack.accountId}, Region: ${stack.region}`);
|
|
2629
|
+
info(` ECR repos: ${stack.dockerArtifacts.length}, S3 buckets: ${stack.bundleArtifacts.length}`);
|
|
2630
|
+
}
|
|
2560
2631
|
}
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
info(
|
|
2632
|
+
if (plan.accountStacks.length > 0) {
|
|
2633
|
+
newline();
|
|
2634
|
+
info("Account Stacks:");
|
|
2635
|
+
for (const stack of plan.accountStacks) {
|
|
2636
|
+
info(` ${stack.action}: ${stack.stackName}`);
|
|
2637
|
+
info(` Account: ${stack.accountId}, Region: ${stack.region} (OIDC provider)`);
|
|
2638
|
+
}
|
|
2564
2639
|
}
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2640
|
+
if (plan.stageStacks.length > 0) {
|
|
2641
|
+
newline();
|
|
2642
|
+
info("Stage Stacks:");
|
|
2643
|
+
for (const stack of plan.stageStacks) {
|
|
2644
|
+
info(` ${stack.action}: ${stack.stackName}`);
|
|
2645
|
+
info(` Account: ${stack.accountId}, Region: ${stack.region}`);
|
|
2646
|
+
info(` ECR repos: ${stack.dockerArtifacts.length}, S3 buckets: ${stack.bundleArtifacts.length}`);
|
|
2647
|
+
}
|
|
2571
2648
|
}
|
|
2572
2649
|
const totalStacks = 1 + plan.pipelineStacks.length + plan.accountStacks.length + plan.stageStacks.length;
|
|
2573
2650
|
newline();
|
|
2574
|
-
info(`Total stacks to deploy: ${totalStacks}`);
|
|
2651
|
+
info(`Total stacks to deploy (in parallel): ${totalStacks}`);
|
|
2575
2652
|
}
|
|
2576
2653
|
async function confirmDeploymentPlan(plan) {
|
|
2577
2654
|
const totalStacks = 1 + plan.pipelineStacks.length + plan.accountStacks.length + plan.stageStacks.length;
|
|
@@ -2593,22 +2670,23 @@ async function confirmDeploymentPlan(plan) {
|
|
|
2593
2670
|
}
|
|
2594
2671
|
async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, currentAccountId, options) {
|
|
2595
2672
|
const results = { success: 0, failed: 0 };
|
|
2673
|
+
const totalStacks = 1 + plan.pipelineStacks.length + plan.accountStacks.length + plan.stageStacks.length;
|
|
2596
2674
|
newline();
|
|
2597
|
-
header("
|
|
2598
|
-
|
|
2599
|
-
await deployOrgStack(plan, pipelines, authData, currentAccountId, options);
|
|
2600
|
-
results.success++;
|
|
2601
|
-
success("Org stack deployed successfully");
|
|
2602
|
-
} catch (error2) {
|
|
2603
|
-
results.failed++;
|
|
2604
|
-
error(`Org stack failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
2605
|
-
throw error2;
|
|
2606
|
-
}
|
|
2607
|
-
newline();
|
|
2608
|
-
header("Phase 2: Pipeline & Account Stacks (parallel)");
|
|
2609
|
-
const totalPhase2Stacks = plan.pipelineStacks.length + plan.accountStacks.length;
|
|
2610
|
-
info(`Deploying ${totalPhase2Stacks} stack(s) in parallel...`);
|
|
2675
|
+
header("Deploying All Stacks");
|
|
2676
|
+
info(`Deploying ${totalStacks} stack(s) in parallel...`);
|
|
2611
2677
|
newline();
|
|
2678
|
+
const orgPromise = (async () => {
|
|
2679
|
+
try {
|
|
2680
|
+
await deployOrgStack(plan, pipelines, authData, currentAccountId, options);
|
|
2681
|
+
return { stack: plan.orgStack.stackName, success: true };
|
|
2682
|
+
} catch (error2) {
|
|
2683
|
+
return {
|
|
2684
|
+
stack: plan.orgStack.stackName,
|
|
2685
|
+
success: false,
|
|
2686
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
})();
|
|
2612
2690
|
const pipelinePromises = plan.pipelineStacks.map(async (stack) => {
|
|
2613
2691
|
try {
|
|
2614
2692
|
await deployPipelineStack(stack, authData, currentAccountId, options);
|
|
@@ -2633,27 +2711,6 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
|
|
|
2633
2711
|
};
|
|
2634
2712
|
}
|
|
2635
2713
|
});
|
|
2636
|
-
const pipelineResults = await Promise.all(pipelinePromises);
|
|
2637
|
-
const accountResults = await Promise.all(accountPromises);
|
|
2638
|
-
newline();
|
|
2639
|
-
let accountStacksFailed = false;
|
|
2640
|
-
for (const result of [...pipelineResults, ...accountResults]) {
|
|
2641
|
-
if (result.success) {
|
|
2642
|
-
success(`${result.stack} deployed`);
|
|
2643
|
-
results.success++;
|
|
2644
|
-
} else {
|
|
2645
|
-
error(`${result.stack} failed: ${result.error}`);
|
|
2646
|
-
results.failed++;
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
accountStacksFailed = accountResults.some((r) => !r.success);
|
|
2650
|
-
if (accountStacksFailed) {
|
|
2651
|
-
warn("Some Account stacks failed. Stage stacks may fail if their account OIDC provider did not deploy.");
|
|
2652
|
-
}
|
|
2653
|
-
newline();
|
|
2654
|
-
header("Phase 3: Stage Stacks (parallel)");
|
|
2655
|
-
info(`Deploying ${plan.stageStacks.length} stack(s) in parallel...`);
|
|
2656
|
-
newline();
|
|
2657
2714
|
const stagePromises = plan.stageStacks.map(async (stack) => {
|
|
2658
2715
|
try {
|
|
2659
2716
|
await deployStageStack(stack, authData, currentAccountId, options);
|
|
@@ -2666,9 +2723,14 @@ async function executeDeployment(plan, pipelines, pipelineArtifacts, authData, c
|
|
|
2666
2723
|
};
|
|
2667
2724
|
}
|
|
2668
2725
|
});
|
|
2669
|
-
const
|
|
2726
|
+
const allResults = await Promise.all([
|
|
2727
|
+
orgPromise,
|
|
2728
|
+
...pipelinePromises,
|
|
2729
|
+
...accountPromises,
|
|
2730
|
+
...stagePromises
|
|
2731
|
+
]);
|
|
2670
2732
|
newline();
|
|
2671
|
-
for (const result of
|
|
2733
|
+
for (const result of allResults) {
|
|
2672
2734
|
if (result.success) {
|
|
2673
2735
|
success(`${result.stack} deployed`);
|
|
2674
2736
|
results.success++;
|
|
@@ -2722,7 +2784,9 @@ async function deployOrgStack(plan, pipelines, authData, currentAccountId, optio
|
|
|
2722
2784
|
template,
|
|
2723
2785
|
accountId: cicdAccountId,
|
|
2724
2786
|
region: cicdRegion,
|
|
2725
|
-
credentials
|
|
2787
|
+
credentials,
|
|
2788
|
+
showProgress: false
|
|
2789
|
+
// Disable progress bar for parallel deployment
|
|
2726
2790
|
};
|
|
2727
2791
|
await previewStackChanges(deployOptions);
|
|
2728
2792
|
await deployStack(deployOptions);
|