@wraps.dev/cli 1.4.1 → 1.5.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/dist/cli.js +407 -28
- package/dist/cli.js.map +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -146,7 +146,7 @@ var require_package = __commonJS({
|
|
|
146
146
|
"package.json"(exports, module) {
|
|
147
147
|
module.exports = {
|
|
148
148
|
name: "@wraps.dev/cli",
|
|
149
|
-
version: "1.
|
|
149
|
+
version: "1.5.0",
|
|
150
150
|
description: "CLI for deploying Wraps email infrastructure to your AWS account",
|
|
151
151
|
type: "module",
|
|
152
152
|
main: "./dist/cli.js",
|
|
@@ -3198,6 +3198,35 @@ ${pc2.bold("Dashboard:")} ${pc2.blue("https://app.wraps.dev")}`);
|
|
|
3198
3198
|
console.log(`${pc2.bold("Docs:")} ${pc2.blue("https://wraps.dev/docs")}
|
|
3199
3199
|
`);
|
|
3200
3200
|
}
|
|
3201
|
+
function displayPreview(outputs) {
|
|
3202
|
+
console.log(pc2.yellow("\n--- PREVIEW MODE (no changes will be made) ---\n"));
|
|
3203
|
+
const changes = outputs.changeSummary;
|
|
3204
|
+
const summaryLines = [];
|
|
3205
|
+
if (changes.create && changes.create > 0) {
|
|
3206
|
+
summaryLines.push(` ${pc2.green("+")} ${changes.create} to create`);
|
|
3207
|
+
}
|
|
3208
|
+
if (changes.update && changes.update > 0) {
|
|
3209
|
+
summaryLines.push(` ${pc2.yellow("~")} ${changes.update} to update`);
|
|
3210
|
+
}
|
|
3211
|
+
if (changes.delete && changes.delete > 0) {
|
|
3212
|
+
summaryLines.push(` ${pc2.red("-")} ${changes.delete} to destroy`);
|
|
3213
|
+
}
|
|
3214
|
+
if (changes.same && changes.same > 0) {
|
|
3215
|
+
summaryLines.push(` ${pc2.dim("=")} ${changes.same} unchanged`);
|
|
3216
|
+
}
|
|
3217
|
+
if (changes.replace && changes.replace > 0) {
|
|
3218
|
+
summaryLines.push(` ${pc2.magenta("+-")} ${changes.replace} to replace`);
|
|
3219
|
+
}
|
|
3220
|
+
if (summaryLines.length > 0) {
|
|
3221
|
+
clack2.note(summaryLines.join("\n"), "Resource Changes");
|
|
3222
|
+
} else {
|
|
3223
|
+
clack2.note("No changes detected", "Resource Changes");
|
|
3224
|
+
}
|
|
3225
|
+
if (outputs.costEstimate) {
|
|
3226
|
+
clack2.note(outputs.costEstimate, "Estimated Monthly Cost");
|
|
3227
|
+
}
|
|
3228
|
+
console.log(pc2.yellow("\n--- END PREVIEW (no changes were made) ---\n"));
|
|
3229
|
+
}
|
|
3201
3230
|
|
|
3202
3231
|
// src/commands/dashboard/update-role.ts
|
|
3203
3232
|
async function updateRole(options) {
|
|
@@ -3965,19 +3994,14 @@ async function configurationSetExists(configSetName, region) {
|
|
|
3965
3994
|
}
|
|
3966
3995
|
async function eventDestinationExists(configSetName, eventDestName, region) {
|
|
3967
3996
|
try {
|
|
3968
|
-
const {
|
|
3969
|
-
SESv2Client: SESv2Client5,
|
|
3970
|
-
GetConfigurationSetEventDestinationsCommand
|
|
3971
|
-
} = await import("@aws-sdk/client-sesv2");
|
|
3997
|
+
const { SESv2Client: SESv2Client5, GetConfigurationSetEventDestinationsCommand } = await import("@aws-sdk/client-sesv2");
|
|
3972
3998
|
const ses = new SESv2Client5({ region });
|
|
3973
3999
|
const response = await ses.send(
|
|
3974
4000
|
new GetConfigurationSetEventDestinationsCommand({
|
|
3975
4001
|
ConfigurationSetName: configSetName
|
|
3976
4002
|
})
|
|
3977
4003
|
);
|
|
3978
|
-
return response.EventDestinations?.some(
|
|
3979
|
-
(dest) => dest.Name === eventDestName
|
|
3980
|
-
) ?? false;
|
|
4004
|
+
return response.EventDestinations?.some((dest) => dest.Name === eventDestName) ?? false;
|
|
3981
4005
|
} catch (error) {
|
|
3982
4006
|
if (error.name === "NotFoundException") {
|
|
3983
4007
|
return false;
|
|
@@ -4394,7 +4418,11 @@ async function ensurePulumiInstalled() {
|
|
|
4394
4418
|
|
|
4395
4419
|
// src/commands/email/config.ts
|
|
4396
4420
|
async function config(options) {
|
|
4397
|
-
clack3.intro(
|
|
4421
|
+
clack3.intro(
|
|
4422
|
+
pc4.bold(
|
|
4423
|
+
options.preview ? "Wraps Config Preview" : "Wraps Config - Apply CLI Updates to Infrastructure"
|
|
4424
|
+
)
|
|
4425
|
+
);
|
|
4398
4426
|
const progress = new DeploymentProgress();
|
|
4399
4427
|
const wasAutoInstalled = await progress.execute(
|
|
4400
4428
|
"Checking Pulumi CLI installation",
|
|
@@ -4471,7 +4499,7 @@ ${pc4.bold("Current Configuration:")}
|
|
|
4471
4499
|
"Your current configuration will be preserved - no features will be added or removed"
|
|
4472
4500
|
);
|
|
4473
4501
|
console.log("");
|
|
4474
|
-
if (!options.yes) {
|
|
4502
|
+
if (!(options.yes || options.preview)) {
|
|
4475
4503
|
const confirmed = await clack3.confirm({
|
|
4476
4504
|
message: "Proceed with update?",
|
|
4477
4505
|
initialValue: true
|
|
@@ -4491,6 +4519,61 @@ ${pc4.bold("Current Configuration:")}
|
|
|
4491
4519
|
vercel: vercelConfig,
|
|
4492
4520
|
emailConfig: config2
|
|
4493
4521
|
};
|
|
4522
|
+
if (options.preview) {
|
|
4523
|
+
try {
|
|
4524
|
+
const previewResult = await progress.execute(
|
|
4525
|
+
"Generating update preview",
|
|
4526
|
+
async () => {
|
|
4527
|
+
await ensurePulumiWorkDir();
|
|
4528
|
+
const stack = await pulumi5.automation.LocalWorkspace.createOrSelectStack(
|
|
4529
|
+
{
|
|
4530
|
+
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
4531
|
+
projectName: "wraps-email",
|
|
4532
|
+
program: async () => {
|
|
4533
|
+
const result2 = await deployEmailStack(stackConfig);
|
|
4534
|
+
return {
|
|
4535
|
+
roleArn: result2.roleArn,
|
|
4536
|
+
configSetName: result2.configSetName,
|
|
4537
|
+
tableName: result2.tableName,
|
|
4538
|
+
region: result2.region,
|
|
4539
|
+
lambdaFunctions: result2.lambdaFunctions,
|
|
4540
|
+
domain: result2.domain,
|
|
4541
|
+
dkimTokens: result2.dkimTokens,
|
|
4542
|
+
customTrackingDomain: result2.customTrackingDomain
|
|
4543
|
+
};
|
|
4544
|
+
}
|
|
4545
|
+
},
|
|
4546
|
+
{
|
|
4547
|
+
workDir: getPulumiWorkDir(),
|
|
4548
|
+
envVars: {
|
|
4549
|
+
PULUMI_CONFIG_PASSPHRASE: "",
|
|
4550
|
+
AWS_REGION: region
|
|
4551
|
+
},
|
|
4552
|
+
secretsProvider: "passphrase"
|
|
4553
|
+
}
|
|
4554
|
+
);
|
|
4555
|
+
await stack.setConfig("aws:region", { value: region });
|
|
4556
|
+
await stack.refresh({ onOutput: () => {
|
|
4557
|
+
} });
|
|
4558
|
+
const result = await stack.preview({ diff: true });
|
|
4559
|
+
return result;
|
|
4560
|
+
}
|
|
4561
|
+
);
|
|
4562
|
+
displayPreview({
|
|
4563
|
+
changeSummary: previewResult.changeSummary,
|
|
4564
|
+
commandName: "wraps email config"
|
|
4565
|
+
});
|
|
4566
|
+
clack3.outro(
|
|
4567
|
+
pc4.green("Preview complete. Run without --preview to update.")
|
|
4568
|
+
);
|
|
4569
|
+
return;
|
|
4570
|
+
} catch (error) {
|
|
4571
|
+
if (error.message?.includes("stack is currently locked")) {
|
|
4572
|
+
throw errors.stackLocked();
|
|
4573
|
+
}
|
|
4574
|
+
throw new Error(`Preview failed: ${error.message}`);
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4494
4577
|
let outputs;
|
|
4495
4578
|
try {
|
|
4496
4579
|
outputs = await progress.execute(
|
|
@@ -4819,7 +4902,11 @@ async function scanAWSResources(region) {
|
|
|
4819
4902
|
|
|
4820
4903
|
// src/commands/email/connect.ts
|
|
4821
4904
|
async function connect(options) {
|
|
4822
|
-
clack5.intro(
|
|
4905
|
+
clack5.intro(
|
|
4906
|
+
pc6.bold(
|
|
4907
|
+
options.preview ? "Wraps Connect Preview" : "Wraps Connect - Link Existing Infrastructure"
|
|
4908
|
+
)
|
|
4909
|
+
);
|
|
4823
4910
|
const progress = new DeploymentProgress();
|
|
4824
4911
|
const wasAutoInstalled = await progress.execute(
|
|
4825
4912
|
"Checking Pulumi CLI installation",
|
|
@@ -4897,7 +4984,7 @@ async function connect(options) {
|
|
|
4897
4984
|
if (domainIdentities.length > 0) {
|
|
4898
4985
|
emailConfig.domain = domainIdentities[0];
|
|
4899
4986
|
}
|
|
4900
|
-
if (!options.yes) {
|
|
4987
|
+
if (!(options.yes || options.preview)) {
|
|
4901
4988
|
const confirmed = await confirmConnect();
|
|
4902
4989
|
if (!confirmed) {
|
|
4903
4990
|
clack5.cancel("Connection cancelled.");
|
|
@@ -4910,6 +4997,59 @@ async function connect(options) {
|
|
|
4910
4997
|
vercel: vercelConfig,
|
|
4911
4998
|
emailConfig
|
|
4912
4999
|
};
|
|
5000
|
+
if (options.preview) {
|
|
5001
|
+
try {
|
|
5002
|
+
const previewResult = await progress.execute(
|
|
5003
|
+
"Generating infrastructure preview",
|
|
5004
|
+
async () => {
|
|
5005
|
+
await ensurePulumiWorkDir();
|
|
5006
|
+
const stack = await pulumi6.automation.LocalWorkspace.createOrSelectStack(
|
|
5007
|
+
{
|
|
5008
|
+
stackName: `wraps-${identity.accountId}-${region}`,
|
|
5009
|
+
projectName: "wraps-email",
|
|
5010
|
+
program: async () => {
|
|
5011
|
+
const result2 = await deployEmailStack(stackConfig);
|
|
5012
|
+
return {
|
|
5013
|
+
roleArn: result2.roleArn,
|
|
5014
|
+
configSetName: result2.configSetName,
|
|
5015
|
+
tableName: result2.tableName,
|
|
5016
|
+
region: result2.region,
|
|
5017
|
+
lambdaFunctions: result2.lambdaFunctions,
|
|
5018
|
+
domain: result2.domain,
|
|
5019
|
+
dkimTokens: result2.dkimTokens,
|
|
5020
|
+
customTrackingDomain: result2.customTrackingDomain
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
},
|
|
5024
|
+
{
|
|
5025
|
+
workDir: getPulumiWorkDir(),
|
|
5026
|
+
envVars: {
|
|
5027
|
+
PULUMI_CONFIG_PASSPHRASE: "",
|
|
5028
|
+
AWS_REGION: region
|
|
5029
|
+
},
|
|
5030
|
+
secretsProvider: "passphrase"
|
|
5031
|
+
}
|
|
5032
|
+
);
|
|
5033
|
+
await stack.setConfig("aws:region", { value: region });
|
|
5034
|
+
const result = await stack.preview({ diff: true });
|
|
5035
|
+
return result;
|
|
5036
|
+
}
|
|
5037
|
+
);
|
|
5038
|
+
displayPreview({
|
|
5039
|
+
changeSummary: previewResult.changeSummary,
|
|
5040
|
+
commandName: "wraps email connect"
|
|
5041
|
+
});
|
|
5042
|
+
clack5.outro(
|
|
5043
|
+
pc6.green("Preview complete. Run without --preview to connect.")
|
|
5044
|
+
);
|
|
5045
|
+
return;
|
|
5046
|
+
} catch (error) {
|
|
5047
|
+
if (error.message?.includes("stack is currently locked")) {
|
|
5048
|
+
throw errors.stackLocked();
|
|
5049
|
+
}
|
|
5050
|
+
throw new Error(`Preview failed: ${error.message}`);
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
4913
5053
|
let outputs;
|
|
4914
5054
|
try {
|
|
4915
5055
|
outputs = await progress.execute(
|
|
@@ -5465,7 +5605,11 @@ init_aws();
|
|
|
5465
5605
|
init_errors();
|
|
5466
5606
|
init_prompts();
|
|
5467
5607
|
async function init(options) {
|
|
5468
|
-
clack7.intro(
|
|
5608
|
+
clack7.intro(
|
|
5609
|
+
pc8.bold(
|
|
5610
|
+
options.preview ? "Wraps Email Infrastructure Preview" : "Wraps Email Infrastructure Setup"
|
|
5611
|
+
)
|
|
5612
|
+
);
|
|
5469
5613
|
const progress = new DeploymentProgress();
|
|
5470
5614
|
const wasAutoInstalled = await progress.execute(
|
|
5471
5615
|
"Checking Pulumi CLI installation",
|
|
@@ -5548,7 +5692,7 @@ ${pc8.yellow(pc8.bold("Configuration Warnings:"))}`);
|
|
|
5548
5692
|
if (vercelConfig) {
|
|
5549
5693
|
metadata.vercel = vercelConfig;
|
|
5550
5694
|
}
|
|
5551
|
-
if (!options.yes) {
|
|
5695
|
+
if (!(options.yes || options.preview)) {
|
|
5552
5696
|
const confirmed = await confirmDeploy();
|
|
5553
5697
|
if (!confirmed) {
|
|
5554
5698
|
clack7.cancel("Deployment cancelled.");
|
|
@@ -5561,6 +5705,64 @@ ${pc8.yellow(pc8.bold("Configuration Warnings:"))}`);
|
|
|
5561
5705
|
vercel: vercelConfig,
|
|
5562
5706
|
emailConfig
|
|
5563
5707
|
};
|
|
5708
|
+
if (options.preview) {
|
|
5709
|
+
try {
|
|
5710
|
+
const previewResult = await progress.execute(
|
|
5711
|
+
"Generating infrastructure preview",
|
|
5712
|
+
async () => {
|
|
5713
|
+
await ensurePulumiWorkDir();
|
|
5714
|
+
const stack = await pulumi7.automation.LocalWorkspace.createOrSelectStack(
|
|
5715
|
+
{
|
|
5716
|
+
stackName: `wraps-${identity.accountId}-${region}`,
|
|
5717
|
+
projectName: "wraps-email",
|
|
5718
|
+
program: async () => {
|
|
5719
|
+
const result2 = await deployEmailStack(stackConfig);
|
|
5720
|
+
return {
|
|
5721
|
+
roleArn: result2.roleArn,
|
|
5722
|
+
configSetName: result2.configSetName,
|
|
5723
|
+
tableName: result2.tableName,
|
|
5724
|
+
region: result2.region,
|
|
5725
|
+
lambdaFunctions: result2.lambdaFunctions,
|
|
5726
|
+
domain: result2.domain,
|
|
5727
|
+
dkimTokens: result2.dkimTokens,
|
|
5728
|
+
customTrackingDomain: result2.customTrackingDomain,
|
|
5729
|
+
mailFromDomain: result2.mailFromDomain,
|
|
5730
|
+
archiveArn: result2.archiveArn,
|
|
5731
|
+
archivingEnabled: result2.archivingEnabled,
|
|
5732
|
+
archiveRetention: result2.archiveRetention
|
|
5733
|
+
};
|
|
5734
|
+
}
|
|
5735
|
+
},
|
|
5736
|
+
{
|
|
5737
|
+
workDir: getPulumiWorkDir(),
|
|
5738
|
+
envVars: {
|
|
5739
|
+
PULUMI_CONFIG_PASSPHRASE: "",
|
|
5740
|
+
AWS_REGION: region
|
|
5741
|
+
},
|
|
5742
|
+
secretsProvider: "passphrase"
|
|
5743
|
+
}
|
|
5744
|
+
);
|
|
5745
|
+
await stack.setConfig("aws:region", { value: region });
|
|
5746
|
+
const result = await stack.preview({ diff: true });
|
|
5747
|
+
return result;
|
|
5748
|
+
}
|
|
5749
|
+
);
|
|
5750
|
+
displayPreview({
|
|
5751
|
+
changeSummary: previewResult.changeSummary,
|
|
5752
|
+
costEstimate: costSummary,
|
|
5753
|
+
commandName: "wraps email init"
|
|
5754
|
+
});
|
|
5755
|
+
clack7.outro(
|
|
5756
|
+
pc8.green("Preview complete. Run without --preview to deploy.")
|
|
5757
|
+
);
|
|
5758
|
+
return;
|
|
5759
|
+
} catch (error) {
|
|
5760
|
+
if (error.message?.includes("stack is currently locked")) {
|
|
5761
|
+
throw errors.stackLocked();
|
|
5762
|
+
}
|
|
5763
|
+
throw new Error(`Preview failed: ${error.message}`);
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5564
5766
|
let outputs;
|
|
5565
5767
|
try {
|
|
5566
5768
|
outputs = await progress.execute(
|
|
@@ -5686,7 +5888,11 @@ import * as clack8 from "@clack/prompts";
|
|
|
5686
5888
|
import * as pulumi8 from "@pulumi/pulumi";
|
|
5687
5889
|
import pc9 from "picocolors";
|
|
5688
5890
|
async function restore(options) {
|
|
5689
|
-
clack8.intro(
|
|
5891
|
+
clack8.intro(
|
|
5892
|
+
pc9.bold(
|
|
5893
|
+
options.preview ? "Wraps Restore Preview" : "Wraps Restore - Remove Wraps Infrastructure"
|
|
5894
|
+
)
|
|
5895
|
+
);
|
|
5690
5896
|
clack8.log.info(
|
|
5691
5897
|
`${pc9.yellow("Note:")} This will remove all Wraps-managed infrastructure.`
|
|
5692
5898
|
);
|
|
@@ -5733,7 +5939,7 @@ ${pc9.bold("The following Wraps resources will be removed:")}
|
|
|
5733
5939
|
}
|
|
5734
5940
|
console.log(` ${pc9.cyan("\u2713")} IAM Role (wraps-email-role)`);
|
|
5735
5941
|
console.log("");
|
|
5736
|
-
if (!options.force) {
|
|
5942
|
+
if (!(options.force || options.preview)) {
|
|
5737
5943
|
const confirmed = await clack8.confirm({
|
|
5738
5944
|
message: "Proceed with removal? This cannot be undone.",
|
|
5739
5945
|
initialValue: false
|
|
@@ -5743,6 +5949,50 @@ ${pc9.bold("The following Wraps resources will be removed:")}
|
|
|
5743
5949
|
process.exit(0);
|
|
5744
5950
|
}
|
|
5745
5951
|
}
|
|
5952
|
+
if (options.preview) {
|
|
5953
|
+
if (metadata.services.email?.pulumiStackName) {
|
|
5954
|
+
try {
|
|
5955
|
+
const previewResult = await progress.execute(
|
|
5956
|
+
"Generating removal preview",
|
|
5957
|
+
async () => {
|
|
5958
|
+
const stack = await pulumi8.automation.LocalWorkspace.selectStack(
|
|
5959
|
+
{
|
|
5960
|
+
stackName: metadata.services.email.pulumiStackName,
|
|
5961
|
+
projectName: "wraps-email",
|
|
5962
|
+
program: async () => {
|
|
5963
|
+
}
|
|
5964
|
+
// Empty program for destroy
|
|
5965
|
+
},
|
|
5966
|
+
{
|
|
5967
|
+
workDir: getPulumiWorkDir(),
|
|
5968
|
+
envVars: {
|
|
5969
|
+
PULUMI_CONFIG_PASSPHRASE: "",
|
|
5970
|
+
AWS_REGION: region
|
|
5971
|
+
},
|
|
5972
|
+
secretsProvider: "passphrase"
|
|
5973
|
+
}
|
|
5974
|
+
);
|
|
5975
|
+
const result = await stack.preview({ diff: true });
|
|
5976
|
+
return result;
|
|
5977
|
+
}
|
|
5978
|
+
);
|
|
5979
|
+
displayPreview({
|
|
5980
|
+
changeSummary: previewResult.changeSummary,
|
|
5981
|
+
costEstimate: "Monthly cost after removal: $0.00",
|
|
5982
|
+
commandName: "wraps email restore"
|
|
5983
|
+
});
|
|
5984
|
+
clack8.outro(
|
|
5985
|
+
pc9.green(
|
|
5986
|
+
"Preview complete. Run without --preview to remove infrastructure."
|
|
5987
|
+
)
|
|
5988
|
+
);
|
|
5989
|
+
return;
|
|
5990
|
+
} catch (error) {
|
|
5991
|
+
throw new Error(`Preview failed: ${error.message}`);
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
return;
|
|
5995
|
+
}
|
|
5746
5996
|
if (metadata.services.email?.pulumiStackName) {
|
|
5747
5997
|
await progress.execute("Removing Wraps infrastructure", async () => {
|
|
5748
5998
|
try {
|
|
@@ -5801,7 +6051,11 @@ init_aws();
|
|
|
5801
6051
|
init_errors();
|
|
5802
6052
|
init_prompts();
|
|
5803
6053
|
async function upgrade(options) {
|
|
5804
|
-
clack9.intro(
|
|
6054
|
+
clack9.intro(
|
|
6055
|
+
pc10.bold(
|
|
6056
|
+
options.preview ? "Wraps Upgrade Preview" : "Wraps Upgrade - Enhance Your Email Infrastructure"
|
|
6057
|
+
)
|
|
6058
|
+
);
|
|
5805
6059
|
const progress = new DeploymentProgress();
|
|
5806
6060
|
const wasAutoInstalled = await progress.execute(
|
|
5807
6061
|
"Checking Pulumi CLI installation",
|
|
@@ -6431,7 +6685,7 @@ ${pc10.bold("Cost Impact:")}`);
|
|
|
6431
6685
|
);
|
|
6432
6686
|
}
|
|
6433
6687
|
console.log("");
|
|
6434
|
-
if (!options.yes) {
|
|
6688
|
+
if (!(options.yes || options.preview)) {
|
|
6435
6689
|
const confirmed = await clack9.confirm({
|
|
6436
6690
|
message: "Proceed with upgrade?",
|
|
6437
6691
|
initialValue: true
|
|
@@ -6453,6 +6707,73 @@ ${pc10.bold("Cost Impact:")}`);
|
|
|
6453
6707
|
vercel: vercelConfig,
|
|
6454
6708
|
emailConfig: updatedConfig
|
|
6455
6709
|
};
|
|
6710
|
+
if (options.preview) {
|
|
6711
|
+
try {
|
|
6712
|
+
const previewResult = await progress.execute(
|
|
6713
|
+
"Generating upgrade preview",
|
|
6714
|
+
async () => {
|
|
6715
|
+
await ensurePulumiWorkDir();
|
|
6716
|
+
const stack = await pulumi9.automation.LocalWorkspace.createOrSelectStack(
|
|
6717
|
+
{
|
|
6718
|
+
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
6719
|
+
projectName: "wraps-email",
|
|
6720
|
+
program: async () => {
|
|
6721
|
+
const result2 = await deployEmailStack(stackConfig);
|
|
6722
|
+
return {
|
|
6723
|
+
roleArn: result2.roleArn,
|
|
6724
|
+
configSetName: result2.configSetName,
|
|
6725
|
+
tableName: result2.tableName,
|
|
6726
|
+
region: result2.region,
|
|
6727
|
+
lambdaFunctions: result2.lambdaFunctions,
|
|
6728
|
+
domain: result2.domain,
|
|
6729
|
+
dkimTokens: result2.dkimTokens,
|
|
6730
|
+
customTrackingDomain: result2.customTrackingDomain,
|
|
6731
|
+
httpsTrackingEnabled: result2.httpsTrackingEnabled,
|
|
6732
|
+
cloudFrontDomain: result2.cloudFrontDomain,
|
|
6733
|
+
acmCertificateValidationRecords: result2.acmCertificateValidationRecords,
|
|
6734
|
+
archiveArn: result2.archiveArn,
|
|
6735
|
+
archivingEnabled: result2.archivingEnabled,
|
|
6736
|
+
archiveRetention: result2.archiveRetention
|
|
6737
|
+
};
|
|
6738
|
+
}
|
|
6739
|
+
},
|
|
6740
|
+
{
|
|
6741
|
+
workDir: getPulumiWorkDir(),
|
|
6742
|
+
envVars: {
|
|
6743
|
+
PULUMI_CONFIG_PASSPHRASE: "",
|
|
6744
|
+
AWS_REGION: region
|
|
6745
|
+
},
|
|
6746
|
+
secretsProvider: "passphrase"
|
|
6747
|
+
}
|
|
6748
|
+
);
|
|
6749
|
+
await stack.setConfig("aws:region", { value: region });
|
|
6750
|
+
await stack.refresh({ onOutput: () => {
|
|
6751
|
+
} });
|
|
6752
|
+
const result = await stack.preview({ diff: true });
|
|
6753
|
+
return result;
|
|
6754
|
+
}
|
|
6755
|
+
);
|
|
6756
|
+
const costComparison = [
|
|
6757
|
+
`Current: ${formatCost(currentCostData.total.monthly)}/mo`,
|
|
6758
|
+
`After upgrade: ${formatCost(newCostData.total.monthly)}/mo`,
|
|
6759
|
+
costDiff > 0 ? `Change: +${formatCost(costDiff)}/mo` : costDiff < 0 ? `Change: -${formatCost(Math.abs(costDiff))}/mo` : "Change: No cost difference"
|
|
6760
|
+
].join("\n");
|
|
6761
|
+
displayPreview({
|
|
6762
|
+
changeSummary: previewResult.changeSummary,
|
|
6763
|
+
costEstimate: costComparison,
|
|
6764
|
+
commandName: "wraps email upgrade"
|
|
6765
|
+
});
|
|
6766
|
+
clack9.outro(
|
|
6767
|
+
pc10.green("Preview complete. Run without --preview to upgrade.")
|
|
6768
|
+
);
|
|
6769
|
+
return;
|
|
6770
|
+
} catch (error) {
|
|
6771
|
+
if (error.message?.includes("stack is currently locked")) {
|
|
6772
|
+
throw errors.stackLocked();
|
|
6773
|
+
}
|
|
6774
|
+
throw new Error(`Preview failed: ${error.message}`);
|
|
6775
|
+
}
|
|
6776
|
+
}
|
|
6456
6777
|
let outputs;
|
|
6457
6778
|
try {
|
|
6458
6779
|
outputs = await progress.execute(
|
|
@@ -7918,14 +8239,18 @@ import * as clack11 from "@clack/prompts";
|
|
|
7918
8239
|
import * as pulumi11 from "@pulumi/pulumi";
|
|
7919
8240
|
import pc12 from "picocolors";
|
|
7920
8241
|
async function destroy(options) {
|
|
7921
|
-
clack11.intro(
|
|
8242
|
+
clack11.intro(
|
|
8243
|
+
pc12.bold(
|
|
8244
|
+
options.preview ? "Wraps Destruction Preview" : "Wraps Email Infrastructure Teardown"
|
|
8245
|
+
)
|
|
8246
|
+
);
|
|
7922
8247
|
const progress = new DeploymentProgress();
|
|
7923
8248
|
const identity = await progress.execute(
|
|
7924
8249
|
"Validating AWS credentials",
|
|
7925
8250
|
async () => validateAWSCredentials()
|
|
7926
8251
|
);
|
|
7927
8252
|
const region = await getAWSRegion();
|
|
7928
|
-
if (!options.force) {
|
|
8253
|
+
if (!(options.force || options.preview)) {
|
|
7929
8254
|
const confirmed = await clack11.confirm({
|
|
7930
8255
|
message: pc12.red(
|
|
7931
8256
|
"Are you sure you want to destroy all Wraps infrastructure?"
|
|
@@ -7937,6 +8262,44 @@ async function destroy(options) {
|
|
|
7937
8262
|
process.exit(0);
|
|
7938
8263
|
}
|
|
7939
8264
|
}
|
|
8265
|
+
if (options.preview) {
|
|
8266
|
+
try {
|
|
8267
|
+
const previewResult = await progress.execute(
|
|
8268
|
+
"Generating destruction preview",
|
|
8269
|
+
async () => {
|
|
8270
|
+
await ensurePulumiWorkDir();
|
|
8271
|
+
const stackName = `wraps-${identity.accountId}-${region}`;
|
|
8272
|
+
let stack;
|
|
8273
|
+
try {
|
|
8274
|
+
stack = await pulumi11.automation.LocalWorkspace.selectStack({
|
|
8275
|
+
stackName,
|
|
8276
|
+
workDir: getPulumiWorkDir()
|
|
8277
|
+
});
|
|
8278
|
+
} catch (_error) {
|
|
8279
|
+
throw new Error("No Wraps infrastructure found to preview");
|
|
8280
|
+
}
|
|
8281
|
+
const result = await stack.preview({ diff: true });
|
|
8282
|
+
return result;
|
|
8283
|
+
}
|
|
8284
|
+
);
|
|
8285
|
+
displayPreview({
|
|
8286
|
+
changeSummary: previewResult.changeSummary,
|
|
8287
|
+
costEstimate: "Monthly cost after destruction: $0.00",
|
|
8288
|
+
commandName: "wraps destroy"
|
|
8289
|
+
});
|
|
8290
|
+
clack11.outro(
|
|
8291
|
+
pc12.green("Preview complete. Run without --preview to destroy.")
|
|
8292
|
+
);
|
|
8293
|
+
return;
|
|
8294
|
+
} catch (error) {
|
|
8295
|
+
progress.stop();
|
|
8296
|
+
if (error.message.includes("No Wraps infrastructure found")) {
|
|
8297
|
+
clack11.log.warn("No Wraps infrastructure found to preview");
|
|
8298
|
+
process.exit(0);
|
|
8299
|
+
}
|
|
8300
|
+
throw new Error(`Preview failed: ${error.message}`);
|
|
8301
|
+
}
|
|
8302
|
+
}
|
|
7940
8303
|
try {
|
|
7941
8304
|
await progress.execute(
|
|
7942
8305
|
"Destroying infrastructure (this may take 2-3 minutes)",
|
|
@@ -8207,6 +8570,9 @@ function showHelp() {
|
|
|
8207
8570
|
console.log(` ${pc15.dim("--preset")} Configuration preset`);
|
|
8208
8571
|
console.log(` ${pc15.dim("-y, --yes")} Skip confirmation prompts`);
|
|
8209
8572
|
console.log(` ${pc15.dim("-f, --force")} Force destructive operations`);
|
|
8573
|
+
console.log(
|
|
8574
|
+
` ${pc15.dim("--preview")} Preview changes without deploying`
|
|
8575
|
+
);
|
|
8210
8576
|
console.log(` ${pc15.dim("-v, --version")} Show version number
|
|
8211
8577
|
`);
|
|
8212
8578
|
console.log(
|
|
@@ -8266,6 +8632,11 @@ args.options([
|
|
|
8266
8632
|
name: "noOpen",
|
|
8267
8633
|
description: "Don't open browser automatically",
|
|
8268
8634
|
defaultValue: false
|
|
8635
|
+
},
|
|
8636
|
+
{
|
|
8637
|
+
name: "preview",
|
|
8638
|
+
description: "Preview changes without deploying",
|
|
8639
|
+
defaultValue: false
|
|
8269
8640
|
}
|
|
8270
8641
|
]);
|
|
8271
8642
|
var flags = args.parse(process.argv);
|
|
@@ -8327,13 +8698,15 @@ Check back soon or follow our progress at ${pc15.cyan("https://github.com/wraps-
|
|
|
8327
8698
|
region: flags.region,
|
|
8328
8699
|
domain: flags.domain,
|
|
8329
8700
|
preset: flags.preset,
|
|
8330
|
-
yes: flags.yes
|
|
8701
|
+
yes: flags.yes,
|
|
8702
|
+
preview: flags.preview
|
|
8331
8703
|
});
|
|
8332
8704
|
} else {
|
|
8333
8705
|
await connect({
|
|
8334
8706
|
provider: flags.provider,
|
|
8335
8707
|
region: flags.region,
|
|
8336
|
-
yes: flags.yes
|
|
8708
|
+
yes: flags.yes,
|
|
8709
|
+
preview: flags.preview
|
|
8337
8710
|
});
|
|
8338
8711
|
}
|
|
8339
8712
|
}
|
|
@@ -8371,33 +8744,38 @@ async function run() {
|
|
|
8371
8744
|
region: flags.region,
|
|
8372
8745
|
domain: flags.domain,
|
|
8373
8746
|
preset: flags.preset,
|
|
8374
|
-
yes: flags.yes
|
|
8747
|
+
yes: flags.yes,
|
|
8748
|
+
preview: flags.preview
|
|
8375
8749
|
});
|
|
8376
8750
|
break;
|
|
8377
8751
|
case "connect":
|
|
8378
8752
|
await connect({
|
|
8379
8753
|
provider: flags.provider,
|
|
8380
8754
|
region: flags.region,
|
|
8381
|
-
yes: flags.yes
|
|
8755
|
+
yes: flags.yes,
|
|
8756
|
+
preview: flags.preview
|
|
8382
8757
|
});
|
|
8383
8758
|
break;
|
|
8384
8759
|
case "config":
|
|
8385
8760
|
case "sync":
|
|
8386
8761
|
await config({
|
|
8387
8762
|
region: flags.region,
|
|
8388
|
-
yes: flags.yes
|
|
8763
|
+
yes: flags.yes,
|
|
8764
|
+
preview: flags.preview
|
|
8389
8765
|
});
|
|
8390
8766
|
break;
|
|
8391
8767
|
case "upgrade":
|
|
8392
8768
|
await upgrade({
|
|
8393
8769
|
region: flags.region,
|
|
8394
|
-
yes: flags.yes
|
|
8770
|
+
yes: flags.yes,
|
|
8771
|
+
preview: flags.preview
|
|
8395
8772
|
});
|
|
8396
8773
|
break;
|
|
8397
8774
|
case "restore":
|
|
8398
8775
|
await restore({
|
|
8399
8776
|
region: flags.region,
|
|
8400
|
-
force: flags.force
|
|
8777
|
+
force: flags.force,
|
|
8778
|
+
preview: flags.preview
|
|
8401
8779
|
});
|
|
8402
8780
|
break;
|
|
8403
8781
|
case "domains": {
|
|
@@ -8539,7 +8917,8 @@ Check back soon or follow our progress at ${pc15.cyan("https://github.com/wraps-
|
|
|
8539
8917
|
break;
|
|
8540
8918
|
case "destroy":
|
|
8541
8919
|
await destroy({
|
|
8542
|
-
force: flags.force
|
|
8920
|
+
force: flags.force,
|
|
8921
|
+
preview: flags.preview
|
|
8543
8922
|
});
|
|
8544
8923
|
break;
|
|
8545
8924
|
case "completion":
|