@hot-updater/aws 0.35.8 → 0.35.10
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/iac/index.cjs +563 -233
- package/dist/iac/index.d.cts +4 -5
- package/dist/iac/index.d.mts +4 -5
- package/dist/iac/index.mjs +564 -234
- package/dist/init/index.cjs +149 -0
- package/dist/init/index.d.cts +102 -0
- package/dist/init/index.d.mts +102 -0
- package/dist/init/index.mjs +144 -0
- package/dist/lambda/index.cjs +1 -1
- package/package.json +13 -8
package/dist/iac/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import {
|
|
3
|
-
import { ConfigBuilder, colors, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, createZip, ensureInstallPackages, getCwd, link, makeEnv, p, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
|
|
2
|
+
import { ConfigBuilder, MissingInitInputsError, assertInitProviderInputs, colors, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, createZip, ensureInstallPackages, getCwd, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, readHotUpdaterInitEnv, resolveInitProviderInput, transformEnv, transformTemplate, writeHotUpdaterConfig } from "@hot-updater/cli-tools";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
6
5
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -17,6 +16,7 @@ import { appendFileSync, createReadStream, createWriteStream, readFileSync, stat
|
|
|
17
16
|
import { finished } from "node:stream/promises";
|
|
18
17
|
import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
|
|
19
18
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
19
|
+
import { fromIni, fromNodeProviderChain, fromSSO } from "@aws-sdk/credential-providers";
|
|
20
20
|
import crypto from "crypto";
|
|
21
21
|
import { CloudFront } from "@aws-sdk/client-cloudfront";
|
|
22
22
|
import { IAM } from "@aws-sdk/client-iam";
|
|
@@ -6209,6 +6209,380 @@ createExeca(mapNode);
|
|
|
6209
6209
|
createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
6210
6210
|
const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6211
6211
|
//#endregion
|
|
6212
|
+
//#region iac/init/index.ts
|
|
6213
|
+
const AWS_AUTH_MODES = [
|
|
6214
|
+
"local-session",
|
|
6215
|
+
"shared-profile",
|
|
6216
|
+
"sso",
|
|
6217
|
+
"account"
|
|
6218
|
+
];
|
|
6219
|
+
const isAwsAuthMode = (value) => value !== void 0 && AWS_AUTH_MODES.some((mode) => mode === value);
|
|
6220
|
+
const AWS_REGION_VALUES = [
|
|
6221
|
+
"af-south-1",
|
|
6222
|
+
"ap-east-1",
|
|
6223
|
+
"ap-east-2",
|
|
6224
|
+
"ap-northeast-1",
|
|
6225
|
+
"ap-northeast-2",
|
|
6226
|
+
"ap-northeast-3",
|
|
6227
|
+
"ap-south-1",
|
|
6228
|
+
"ap-south-2",
|
|
6229
|
+
"ap-southeast-1",
|
|
6230
|
+
"ap-southeast-2",
|
|
6231
|
+
"ap-southeast-3",
|
|
6232
|
+
"ap-southeast-4",
|
|
6233
|
+
"ap-southeast-5",
|
|
6234
|
+
"ap-southeast-6",
|
|
6235
|
+
"ap-southeast-7",
|
|
6236
|
+
"ca-central-1",
|
|
6237
|
+
"ca-west-1",
|
|
6238
|
+
"cn-north-1",
|
|
6239
|
+
"cn-northwest-1",
|
|
6240
|
+
"eu-central-1",
|
|
6241
|
+
"eu-central-2",
|
|
6242
|
+
"eu-north-1",
|
|
6243
|
+
"eu-south-1",
|
|
6244
|
+
"eu-south-2",
|
|
6245
|
+
"eu-west-1",
|
|
6246
|
+
"eu-west-2",
|
|
6247
|
+
"eu-west-3",
|
|
6248
|
+
"il-central-1",
|
|
6249
|
+
"me-central-1",
|
|
6250
|
+
"me-south-1",
|
|
6251
|
+
"mx-central-1",
|
|
6252
|
+
"sa-east-1",
|
|
6253
|
+
"us-east-1",
|
|
6254
|
+
"us-east-2",
|
|
6255
|
+
"us-gov-east-1",
|
|
6256
|
+
"us-gov-west-1",
|
|
6257
|
+
"us-west-1",
|
|
6258
|
+
"us-west-2"
|
|
6259
|
+
];
|
|
6260
|
+
const isAwsRegionValue = (value) => value !== void 0 && AWS_REGION_VALUES.some((region) => region === value);
|
|
6261
|
+
const initProvider = {
|
|
6262
|
+
label: "AWS S3 + Lambda@Edge",
|
|
6263
|
+
inputs: {
|
|
6264
|
+
authMode: {
|
|
6265
|
+
envKey: "HOT_UPDATER_AWS_AUTH_MODE",
|
|
6266
|
+
help: "AWS authentication mode: local-session, shared-profile, sso, or account",
|
|
6267
|
+
prompt: {
|
|
6268
|
+
message: "Select the mode to login to AWS",
|
|
6269
|
+
type: "select"
|
|
6270
|
+
},
|
|
6271
|
+
validate: isAwsAuthMode
|
|
6272
|
+
},
|
|
6273
|
+
profile: {
|
|
6274
|
+
envKey: "HOT_UPDATER_AWS_PROFILE",
|
|
6275
|
+
help: "AWS profile name",
|
|
6276
|
+
requiredWhen: (inputs) => inputs.authMode === "shared-profile" || inputs.authMode === "sso",
|
|
6277
|
+
requirementHint: "required for shared-profile and sso auth",
|
|
6278
|
+
prompt: {
|
|
6279
|
+
defaultValue: "default",
|
|
6280
|
+
message: "Enter the AWS profile name",
|
|
6281
|
+
placeholder: "default",
|
|
6282
|
+
type: "text"
|
|
6283
|
+
}
|
|
6284
|
+
},
|
|
6285
|
+
accessKeyId: {
|
|
6286
|
+
envKey: "HOT_UPDATER_S3_ACCESS_KEY_ID",
|
|
6287
|
+
help: "AWS access key ID",
|
|
6288
|
+
persistence: "with-consent",
|
|
6289
|
+
requiredWhen: (inputs) => inputs.authMode === "account",
|
|
6290
|
+
requirementHint: "required for account auth",
|
|
6291
|
+
prompt: {
|
|
6292
|
+
message: "Enter your AWS Access Key ID",
|
|
6293
|
+
placeholder: "AKIA...",
|
|
6294
|
+
type: "text"
|
|
6295
|
+
}
|
|
6296
|
+
},
|
|
6297
|
+
secretAccessKey: {
|
|
6298
|
+
envKey: "HOT_UPDATER_S3_SECRET_ACCESS_KEY",
|
|
6299
|
+
help: "AWS secret access key",
|
|
6300
|
+
persistence: "with-consent",
|
|
6301
|
+
requiredWhen: (inputs) => inputs.authMode === "account",
|
|
6302
|
+
requirementHint: "required for account auth",
|
|
6303
|
+
prompt: {
|
|
6304
|
+
message: "Enter your AWS Secret Access Key",
|
|
6305
|
+
type: "password"
|
|
6306
|
+
}
|
|
6307
|
+
},
|
|
6308
|
+
bucketName: {
|
|
6309
|
+
envKey: "HOT_UPDATER_S3_BUCKET_NAME",
|
|
6310
|
+
help: "S3 bucket name",
|
|
6311
|
+
prompt: {
|
|
6312
|
+
defaultValue: "hot-updater-storage",
|
|
6313
|
+
message: "Enter the name of the new S3 Bucket",
|
|
6314
|
+
placeholder: "hot-updater-storage",
|
|
6315
|
+
type: "text"
|
|
6316
|
+
}
|
|
6317
|
+
},
|
|
6318
|
+
bucketRegion: {
|
|
6319
|
+
envKey: "HOT_UPDATER_S3_REGION",
|
|
6320
|
+
help: "S3 bucket region",
|
|
6321
|
+
prompt: {
|
|
6322
|
+
message: "Enter AWS region for the S3 bucket",
|
|
6323
|
+
type: "select"
|
|
6324
|
+
},
|
|
6325
|
+
validate: isAwsRegionValue
|
|
6326
|
+
},
|
|
6327
|
+
lambdaName: {
|
|
6328
|
+
envKey: "HOT_UPDATER_AWS_LAMBDA_NAME",
|
|
6329
|
+
help: "Lambda@Edge function name",
|
|
6330
|
+
prompt: {
|
|
6331
|
+
defaultValue: "hot-updater-edge",
|
|
6332
|
+
message: "Enter the name of the Lambda@Edge function",
|
|
6333
|
+
placeholder: "hot-updater-edge",
|
|
6334
|
+
type: "text"
|
|
6335
|
+
}
|
|
6336
|
+
},
|
|
6337
|
+
distributionId: {
|
|
6338
|
+
envKey: "HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID",
|
|
6339
|
+
help: "Existing CloudFront distribution ID",
|
|
6340
|
+
optional: true
|
|
6341
|
+
},
|
|
6342
|
+
migrationApproved: {
|
|
6343
|
+
envKey: "HOT_UPDATER_AWS_MIGRATION_APPROVED",
|
|
6344
|
+
help: "Allow pending Hot Updater S3 migrations (true)",
|
|
6345
|
+
optional: true,
|
|
6346
|
+
prompt: {
|
|
6347
|
+
message: "Apply pending Hot Updater S3 migrations during init and future infrastructure updates?",
|
|
6348
|
+
type: "confirm"
|
|
6349
|
+
},
|
|
6350
|
+
validate: (value) => value === "true"
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
//#endregion
|
|
6355
|
+
//#region iac/regionLocationMap.ts
|
|
6356
|
+
const regionLocationMap = {
|
|
6357
|
+
"af-south-1": "Cape Town, South Africa",
|
|
6358
|
+
"ap-east-1": "Hong Kong",
|
|
6359
|
+
"ap-east-2": "Taipei, Taiwan",
|
|
6360
|
+
"ap-northeast-1": "Tokyo, Japan",
|
|
6361
|
+
"ap-northeast-2": "Seoul, South Korea",
|
|
6362
|
+
"ap-northeast-3": "Osaka, Japan",
|
|
6363
|
+
"ap-south-1": "Mumbai, India",
|
|
6364
|
+
"ap-south-2": "Hyderabad, India",
|
|
6365
|
+
"ap-southeast-1": "Singapore",
|
|
6366
|
+
"ap-southeast-2": "Sydney, Australia",
|
|
6367
|
+
"ap-southeast-3": "Jakarta, Indonesia",
|
|
6368
|
+
"ap-southeast-4": "Melbourne, Australia",
|
|
6369
|
+
"ap-southeast-5": "Auckland, New Zealand",
|
|
6370
|
+
"ap-southeast-6": "New Zealand",
|
|
6371
|
+
"ap-southeast-7": "Thailand",
|
|
6372
|
+
"ca-central-1": "Montreal, Canada",
|
|
6373
|
+
"ca-west-1": "Calgary, Canada",
|
|
6374
|
+
"cn-north-1": "Beijing, China",
|
|
6375
|
+
"cn-northwest-1": "Ningxia, China",
|
|
6376
|
+
"eu-central-1": "Frankfurt, Germany",
|
|
6377
|
+
"eu-north-1": "Stockholm, Sweden",
|
|
6378
|
+
"eu-south-1": "Milan, Italy",
|
|
6379
|
+
"eu-south-2": "Spain",
|
|
6380
|
+
"eu-west-1": "Ireland",
|
|
6381
|
+
"eu-west-2": "London, UK",
|
|
6382
|
+
"eu-west-3": "Paris, France",
|
|
6383
|
+
"il-central-1": "Tel Aviv, Israel",
|
|
6384
|
+
"me-central-1": "UAE",
|
|
6385
|
+
"me-south-1": "Bahrain",
|
|
6386
|
+
"mx-central-1": "Mexico",
|
|
6387
|
+
"sa-east-1": "São Paulo, Brazil",
|
|
6388
|
+
"us-east-1": "Virginia, USA",
|
|
6389
|
+
"us-east-2": "Ohio, USA",
|
|
6390
|
+
"us-gov-east-1": "US Gov East (Virginia, USA)",
|
|
6391
|
+
"us-gov-west-1": "US Gov West (Oregon, USA)",
|
|
6392
|
+
"us-west-1": "California, USA",
|
|
6393
|
+
"us-west-2": "Oregon, USA",
|
|
6394
|
+
"eu-central-2": "Zurich, Switzerland"
|
|
6395
|
+
};
|
|
6396
|
+
//#endregion
|
|
6397
|
+
//#region iac/awsInitInputs.ts
|
|
6398
|
+
const resolveAwsInitInputs = (existingEnv) => {
|
|
6399
|
+
const { inputs } = initProvider;
|
|
6400
|
+
const savedAuthMode = resolveInitProviderInput(existingEnv, inputs.authMode);
|
|
6401
|
+
return {
|
|
6402
|
+
accessKeyId: resolveInitProviderInput(existingEnv, inputs.accessKeyId),
|
|
6403
|
+
authMode: isAwsAuthMode(savedAuthMode) ? savedAuthMode : void 0,
|
|
6404
|
+
bucketName: resolveInitProviderInput(existingEnv, inputs.bucketName),
|
|
6405
|
+
bucketRegion: resolveInitProviderInput(existingEnv, inputs.bucketRegion),
|
|
6406
|
+
distributionId: resolveInitProviderInput(existingEnv, inputs.distributionId),
|
|
6407
|
+
lambdaName: resolveInitProviderInput(existingEnv, inputs.lambdaName),
|
|
6408
|
+
migrationApproved: resolveInitProviderInput(existingEnv, inputs.migrationApproved),
|
|
6409
|
+
profile: resolveInitProviderInput(existingEnv, inputs.profile),
|
|
6410
|
+
secretAccessKey: resolveInitProviderInput(existingEnv, inputs.secretAccessKey)
|
|
6411
|
+
};
|
|
6412
|
+
};
|
|
6413
|
+
const assertAwsNonInteractiveInputs = (inputs, nonInteractive) => {
|
|
6414
|
+
assertInitProviderInputs({
|
|
6415
|
+
inputs: {
|
|
6416
|
+
...inputs,
|
|
6417
|
+
bucketRegion: inputs.bucketRegion && Object.hasOwn(regionLocationMap, inputs.bucketRegion) ? inputs.bucketRegion : void 0
|
|
6418
|
+
},
|
|
6419
|
+
provider: initProvider,
|
|
6420
|
+
strict: nonInteractive
|
|
6421
|
+
});
|
|
6422
|
+
};
|
|
6423
|
+
//#endregion
|
|
6424
|
+
//#region iac/awsAuth.ts
|
|
6425
|
+
const exitWithCredentialError = (error) => {
|
|
6426
|
+
if (error instanceof ExecaError) p.log.error(error.stdout || error.stderr || error.message);
|
|
6427
|
+
else p.log.error(error.message);
|
|
6428
|
+
process.exit(1);
|
|
6429
|
+
};
|
|
6430
|
+
const resolveAwsAuth = async (existingEnv, nonInteractive = false) => {
|
|
6431
|
+
const inputDefinitions = initProvider.inputs;
|
|
6432
|
+
const savedMode = resolveInitProviderInput(existingEnv, inputDefinitions.authMode);
|
|
6433
|
+
const mode = isAwsAuthMode(savedMode) ? savedMode : void 0;
|
|
6434
|
+
const savedProfile = resolveInitProviderInput(existingEnv, inputDefinitions.profile);
|
|
6435
|
+
const savedAccessKeyId = resolveInitProviderInput(existingEnv, inputDefinitions.accessKeyId);
|
|
6436
|
+
const savedSecretAccessKey = resolveInitProviderInput(existingEnv, inputDefinitions.secretAccessKey);
|
|
6437
|
+
if (nonInteractive && mode === "account" && savedAccessKeyId && savedSecretAccessKey) return {
|
|
6438
|
+
awsProfile: null,
|
|
6439
|
+
configAuthMode: { mode: "account" },
|
|
6440
|
+
credentials: {
|
|
6441
|
+
accessKeyId: savedAccessKeyId,
|
|
6442
|
+
secretAccessKey: savedSecretAccessKey
|
|
6443
|
+
},
|
|
6444
|
+
mode
|
|
6445
|
+
};
|
|
6446
|
+
const inputs = await p.group({
|
|
6447
|
+
mode: () => nonInteractive && mode ? Promise.resolve(mode) : p.select({
|
|
6448
|
+
initialValue: mode,
|
|
6449
|
+
message: inputDefinitions.authMode.prompt.message,
|
|
6450
|
+
options: [
|
|
6451
|
+
{
|
|
6452
|
+
label: "Current AWS CLI Session / Default Credential Chain",
|
|
6453
|
+
value: "local-session"
|
|
6454
|
+
},
|
|
6455
|
+
{
|
|
6456
|
+
label: "Shared AWS Profile",
|
|
6457
|
+
value: "shared-profile"
|
|
6458
|
+
},
|
|
6459
|
+
{
|
|
6460
|
+
label: "AWS SSO Login",
|
|
6461
|
+
value: "sso"
|
|
6462
|
+
},
|
|
6463
|
+
{
|
|
6464
|
+
label: "AWS Access Key ID & Secret Access Key",
|
|
6465
|
+
value: "account"
|
|
6466
|
+
}
|
|
6467
|
+
]
|
|
6468
|
+
}),
|
|
6469
|
+
profile: ({ results }) => {
|
|
6470
|
+
if (results.mode !== "shared-profile" && results.mode !== "sso") return Promise.resolve(void 0);
|
|
6471
|
+
if (nonInteractive && savedProfile) return Promise.resolve(savedProfile);
|
|
6472
|
+
const prompt = inputDefinitions.profile.prompt;
|
|
6473
|
+
return p.text({
|
|
6474
|
+
...getInitProviderTextPromptValues(prompt, savedProfile ?? process.env.AWS_PROFILE),
|
|
6475
|
+
message: prompt.message,
|
|
6476
|
+
validate: (value) => (value ?? "").trim() ? void 0 : "AWS profile name is required"
|
|
6477
|
+
});
|
|
6478
|
+
},
|
|
6479
|
+
accessKeyId: ({ results }) => {
|
|
6480
|
+
if (results.mode !== "account") return Promise.resolve(void 0);
|
|
6481
|
+
if (nonInteractive && savedAccessKeyId) return Promise.resolve(savedAccessKeyId);
|
|
6482
|
+
const prompt = inputDefinitions.accessKeyId.prompt;
|
|
6483
|
+
return p.text({
|
|
6484
|
+
...getInitProviderTextPromptValues(prompt, savedAccessKeyId),
|
|
6485
|
+
message: prompt.message,
|
|
6486
|
+
validate: (value) => value ? void 0 : "Access Key ID is required"
|
|
6487
|
+
});
|
|
6488
|
+
},
|
|
6489
|
+
secretAccessKey: ({ results }) => {
|
|
6490
|
+
if (results.mode !== "account") return Promise.resolve(void 0);
|
|
6491
|
+
if (nonInteractive && savedSecretAccessKey) return Promise.resolve(savedSecretAccessKey);
|
|
6492
|
+
return p.password({
|
|
6493
|
+
message: inputDefinitions.secretAccessKey.prompt.message,
|
|
6494
|
+
validate: (value) => value ? void 0 : "Secret Access Key is required"
|
|
6495
|
+
});
|
|
6496
|
+
}
|
|
6497
|
+
}, { onCancel: () => process.exit(1) });
|
|
6498
|
+
switch (inputs.mode) {
|
|
6499
|
+
case "account":
|
|
6500
|
+
if (!inputs.accessKeyId || !inputs.secretAccessKey) {
|
|
6501
|
+
p.log.error("AWS access key credentials are required.");
|
|
6502
|
+
process.exit(1);
|
|
6503
|
+
}
|
|
6504
|
+
return {
|
|
6505
|
+
awsProfile: null,
|
|
6506
|
+
configAuthMode: { mode: "account" },
|
|
6507
|
+
credentials: {
|
|
6508
|
+
accessKeyId: inputs.accessKeyId,
|
|
6509
|
+
secretAccessKey: inputs.secretAccessKey
|
|
6510
|
+
},
|
|
6511
|
+
mode: inputs.mode
|
|
6512
|
+
};
|
|
6513
|
+
case "local-session": try {
|
|
6514
|
+
return {
|
|
6515
|
+
awsProfile: null,
|
|
6516
|
+
configAuthMode: {
|
|
6517
|
+
mode: "local",
|
|
6518
|
+
profile: null
|
|
6519
|
+
},
|
|
6520
|
+
credentials: await fromNodeProviderChain()(),
|
|
6521
|
+
mode: inputs.mode
|
|
6522
|
+
};
|
|
6523
|
+
} catch (error) {
|
|
6524
|
+
if (error instanceof Error) return exitWithCredentialError(error);
|
|
6525
|
+
throw error;
|
|
6526
|
+
}
|
|
6527
|
+
case "shared-profile": {
|
|
6528
|
+
if (!inputs.profile) {
|
|
6529
|
+
p.log.error("AWS profile is required.");
|
|
6530
|
+
process.exit(1);
|
|
6531
|
+
}
|
|
6532
|
+
const profile = inputs.profile.trim();
|
|
6533
|
+
try {
|
|
6534
|
+
return {
|
|
6535
|
+
awsProfile: profile,
|
|
6536
|
+
configAuthMode: {
|
|
6537
|
+
mode: "local",
|
|
6538
|
+
profile
|
|
6539
|
+
},
|
|
6540
|
+
credentials: await fromIni({ profile })(),
|
|
6541
|
+
mode: inputs.mode
|
|
6542
|
+
};
|
|
6543
|
+
} catch (error) {
|
|
6544
|
+
if (error instanceof Error) return exitWithCredentialError(error);
|
|
6545
|
+
throw error;
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
case "sso": {
|
|
6549
|
+
if (!inputs.profile) {
|
|
6550
|
+
p.log.error("AWS SSO profile is required.");
|
|
6551
|
+
process.exit(1);
|
|
6552
|
+
}
|
|
6553
|
+
const profile = inputs.profile.trim();
|
|
6554
|
+
try {
|
|
6555
|
+
let credentials;
|
|
6556
|
+
try {
|
|
6557
|
+
credentials = await fromSSO({ profile })();
|
|
6558
|
+
} catch {
|
|
6559
|
+
if (nonInteractive) throw new MissingInitInputsError(["active AWS SSO session (`aws sso login`)"]);
|
|
6560
|
+
await execa("aws", [
|
|
6561
|
+
"sso",
|
|
6562
|
+
"login",
|
|
6563
|
+
"--profile",
|
|
6564
|
+
profile
|
|
6565
|
+
], { stdio: "inherit" });
|
|
6566
|
+
credentials = await fromSSO({ profile })();
|
|
6567
|
+
}
|
|
6568
|
+
return {
|
|
6569
|
+
awsProfile: profile,
|
|
6570
|
+
configAuthMode: {
|
|
6571
|
+
mode: "sso",
|
|
6572
|
+
profile
|
|
6573
|
+
},
|
|
6574
|
+
credentials,
|
|
6575
|
+
mode: inputs.mode
|
|
6576
|
+
};
|
|
6577
|
+
} catch (error) {
|
|
6578
|
+
if (error instanceof MissingInitInputsError) throw error;
|
|
6579
|
+
if (error instanceof Error) return exitWithCredentialError(error);
|
|
6580
|
+
throw error;
|
|
6581
|
+
}
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
};
|
|
6585
|
+
//#endregion
|
|
6212
6586
|
//#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/globalThis.mjs
|
|
6213
6587
|
const globalThis_ = typeof globalThis === "object" && globalThis || typeof window === "object" && window || typeof self === "object" && self || typeof global === "object" && global || (function() {
|
|
6214
6588
|
return this;
|
|
@@ -6591,6 +6965,16 @@ const buildDistributionConfig = (options) => sanitizeDistributionConfig({
|
|
|
6591
6965
|
});
|
|
6592
6966
|
//#endregion
|
|
6593
6967
|
//#region iac/cloudfrontPagination.ts
|
|
6968
|
+
const collectPaginatedCloudFrontList = async ({ listPage }) => {
|
|
6969
|
+
const items = [];
|
|
6970
|
+
let marker;
|
|
6971
|
+
do {
|
|
6972
|
+
const page = await listPage(marker);
|
|
6973
|
+
items.push(...page.items);
|
|
6974
|
+
marker = page.nextMarker;
|
|
6975
|
+
} while (marker);
|
|
6976
|
+
return items;
|
|
6977
|
+
};
|
|
6594
6978
|
const findInPaginatedCloudFrontList = async ({ listPage, matches }) => {
|
|
6595
6979
|
let marker;
|
|
6596
6980
|
do {
|
|
@@ -6670,6 +7054,11 @@ var CloudFrontManager = class {
|
|
|
6670
7054
|
region: this.region,
|
|
6671
7055
|
credentials: this.credentials
|
|
6672
7056
|
});
|
|
7057
|
+
const selectedDistribution = options.distribution === void 0 ? await this.selectDistribution({
|
|
7058
|
+
bucketName: options.bucketName,
|
|
7059
|
+
distributionId: options.distributionId,
|
|
7060
|
+
nonInteractive: options.nonInteractive
|
|
7061
|
+
}) : options.distribution;
|
|
6673
7062
|
let oacId;
|
|
6674
7063
|
if (!options.functionArn.split(":")[4]) throw new Error("Failed to get AWS account ID");
|
|
6675
7064
|
try {
|
|
@@ -6696,29 +7085,6 @@ var CloudFrontManager = class {
|
|
|
6696
7085
|
} catch (error) {
|
|
6697
7086
|
throw new Error(`Failed to get or create shared cache policy: ${error instanceof Error ? error.message : String(error)}`);
|
|
6698
7087
|
}
|
|
6699
|
-
const matchingDistributions = [];
|
|
6700
|
-
try {
|
|
6701
|
-
const items = (await cloudfrontClient.listDistributions({})).DistributionList?.Items || [];
|
|
6702
|
-
for (const dist of items) if ((dist.Origins?.Items || []).some((origin) => origin.DomainName === bucketDomain)) matchingDistributions.push({
|
|
6703
|
-
Id: dist.Id,
|
|
6704
|
-
DomainName: dist.DomainName
|
|
6705
|
-
});
|
|
6706
|
-
} catch (error) {
|
|
6707
|
-
console.error("Error listing CloudFront distributions:", error);
|
|
6708
|
-
}
|
|
6709
|
-
let selectedDistribution = null;
|
|
6710
|
-
if (matchingDistributions.length === 1) selectedDistribution = matchingDistributions[0];
|
|
6711
|
-
else if (matchingDistributions.length > 1) {
|
|
6712
|
-
const selectedDistributionStr = await p.select({
|
|
6713
|
-
message: "Multiple CloudFront distributions found. Please select one to use:",
|
|
6714
|
-
options: matchingDistributions.map((dist) => ({
|
|
6715
|
-
value: JSON.stringify(dist),
|
|
6716
|
-
label: `${dist.Id} (${dist.DomainName})`
|
|
6717
|
-
}))
|
|
6718
|
-
});
|
|
6719
|
-
if (p.isCancel(selectedDistributionStr)) process.exit(0);
|
|
6720
|
-
selectedDistribution = JSON.parse(selectedDistributionStr);
|
|
6721
|
-
}
|
|
6722
7088
|
const newOverrides = buildDistributionConfigOverrides({
|
|
6723
7089
|
bucketName: options.bucketName,
|
|
6724
7090
|
bucketDomain,
|
|
@@ -6728,6 +7094,7 @@ var CloudFrontManager = class {
|
|
|
6728
7094
|
sharedCachePolicyId
|
|
6729
7095
|
});
|
|
6730
7096
|
if (selectedDistribution) {
|
|
7097
|
+
await makeEnv({ HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID: selectedDistribution.Id });
|
|
6731
7098
|
p.log.success(`Existing CloudFront distribution selected. Distribution ID: ${selectedDistribution.Id}.`);
|
|
6732
7099
|
try {
|
|
6733
7100
|
const { DistributionConfig, ETag } = await cloudfrontClient.getDistributionConfig({ Id: selectedDistribution.Id });
|
|
@@ -6772,6 +7139,7 @@ var CloudFrontManager = class {
|
|
|
6772
7139
|
if (!distResp.Distribution?.Id || !distResp.Distribution?.DomainName) throw new Error("Failed to create CloudFront distribution: No ID or DomainName returned");
|
|
6773
7140
|
const distributionId = distResp.Distribution.Id;
|
|
6774
7141
|
const distributionDomain = distResp.Distribution.DomainName;
|
|
7142
|
+
await makeEnv({ HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID: distributionId });
|
|
6775
7143
|
p.log.success(`Created new CloudFront distribution. Distribution ID: ${distributionId}`);
|
|
6776
7144
|
let retryCount = 0;
|
|
6777
7145
|
await p.tasks([{
|
|
@@ -6797,6 +7165,53 @@ var CloudFrontManager = class {
|
|
|
6797
7165
|
throw error;
|
|
6798
7166
|
}
|
|
6799
7167
|
}
|
|
7168
|
+
async selectDistribution({ bucketName, distributionId, nonInteractive }) {
|
|
7169
|
+
const bucketDomain = `${bucketName}.s3.${this.region}.amazonaws.com`;
|
|
7170
|
+
const cloudfrontClient = new CloudFront({
|
|
7171
|
+
region: this.region,
|
|
7172
|
+
credentials: this.credentials
|
|
7173
|
+
});
|
|
7174
|
+
const distributions = await collectPaginatedCloudFrontList({ listPage: async (marker) => {
|
|
7175
|
+
const options = marker ? { Marker: marker } : {};
|
|
7176
|
+
const response = await cloudfrontClient.listDistributions(options);
|
|
7177
|
+
return {
|
|
7178
|
+
items: response.DistributionList?.Items ?? [],
|
|
7179
|
+
nextMarker: response.DistributionList?.NextMarker
|
|
7180
|
+
};
|
|
7181
|
+
} });
|
|
7182
|
+
const matchingDistributions = distributions.flatMap((distribution) => {
|
|
7183
|
+
return (distribution.Origins?.Items ?? []).some((origin) => origin.DomainName === bucketDomain) && distribution.Id && distribution.DomainName ? [{
|
|
7184
|
+
Id: distribution.Id,
|
|
7185
|
+
DomainName: distribution.DomainName
|
|
7186
|
+
}] : [];
|
|
7187
|
+
});
|
|
7188
|
+
const savedDistribution = matchingDistributions.find((distribution) => distribution.Id === distributionId);
|
|
7189
|
+
const savedDistributionExists = distributions.some((distribution) => distribution.Id === distributionId);
|
|
7190
|
+
if (nonInteractive && savedDistribution) return savedDistribution;
|
|
7191
|
+
if (distributionId && !savedDistribution) {
|
|
7192
|
+
if (matchingDistributions.length === 0 && !savedDistributionExists) {
|
|
7193
|
+
p.log.warn("Saved CloudFront distribution was not found. A new distribution will be created.");
|
|
7194
|
+
return null;
|
|
7195
|
+
}
|
|
7196
|
+
if (nonInteractive) throw new MissingInitInputsError(["HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID"]);
|
|
7197
|
+
p.log.warn("Saved CloudFront distribution was not found. Select a distribution again.");
|
|
7198
|
+
}
|
|
7199
|
+
if (matchingDistributions.length === 0) return null;
|
|
7200
|
+
if (nonInteractive) {
|
|
7201
|
+
if (!distributionId && matchingDistributions.length === 1) return matchingDistributions[0] ?? null;
|
|
7202
|
+
throw new MissingInitInputsError(["HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID"]);
|
|
7203
|
+
}
|
|
7204
|
+
const selectedDistributionId = await p.select({
|
|
7205
|
+
initialValue: savedDistribution?.Id ?? matchingDistributions[0]?.Id,
|
|
7206
|
+
message: "Select a CloudFront distribution:",
|
|
7207
|
+
options: matchingDistributions.map((distribution) => ({
|
|
7208
|
+
value: distribution.Id,
|
|
7209
|
+
label: `${distribution.Id} (${distribution.DomainName})`
|
|
7210
|
+
}))
|
|
7211
|
+
});
|
|
7212
|
+
if (p.isCancel(selectedDistributionId)) process.exit(0);
|
|
7213
|
+
return matchingDistributions.find((distribution) => distribution.Id === selectedDistributionId) ?? null;
|
|
7214
|
+
}
|
|
6800
7215
|
};
|
|
6801
7216
|
//#endregion
|
|
6802
7217
|
//#region iac/iam.ts
|
|
@@ -6893,14 +7308,8 @@ var LambdaEdgeDeployer = class {
|
|
|
6893
7308
|
constructor(credentials) {
|
|
6894
7309
|
this.credentials = credentials;
|
|
6895
7310
|
}
|
|
6896
|
-
async deploy(lambdaRoleArn, config) {
|
|
7311
|
+
async deploy(lambdaRoleArn, lambdaName, config) {
|
|
6897
7312
|
const cwd = getCwd();
|
|
6898
|
-
const lambdaName = await p.text({
|
|
6899
|
-
message: "Enter the name of the Lambda@Edge function",
|
|
6900
|
-
defaultValue: "hot-updater-edge",
|
|
6901
|
-
placeholder: "hot-updater-edge"
|
|
6902
|
-
});
|
|
6903
|
-
if (p.isCancel(lambdaName)) process.exit(1);
|
|
6904
7313
|
const lambdaPath = __require.resolve("@hot-updater/aws/lambda");
|
|
6905
7314
|
const { tmpDir, removeTmpDir } = await copyDirToTmp(path$1.dirname(lambdaPath));
|
|
6906
7315
|
const indexPath = path$1.join(tmpDir, "index.cjs");
|
|
@@ -7297,48 +7706,6 @@ var Migration0001HotUpdater0_18_0 = class extends S3Migration {
|
|
|
7297
7706
|
}
|
|
7298
7707
|
};
|
|
7299
7708
|
//#endregion
|
|
7300
|
-
//#region iac/regionLocationMap.ts
|
|
7301
|
-
const regionLocationMap = {
|
|
7302
|
-
"af-south-1": "Cape Town, South Africa",
|
|
7303
|
-
"ap-east-1": "Hong Kong",
|
|
7304
|
-
"ap-east-2": "Taipei, Taiwan",
|
|
7305
|
-
"ap-northeast-1": "Tokyo, Japan",
|
|
7306
|
-
"ap-northeast-2": "Seoul, South Korea",
|
|
7307
|
-
"ap-northeast-3": "Osaka, Japan",
|
|
7308
|
-
"ap-south-1": "Mumbai, India",
|
|
7309
|
-
"ap-south-2": "Hyderabad, India",
|
|
7310
|
-
"ap-southeast-1": "Singapore",
|
|
7311
|
-
"ap-southeast-2": "Sydney, Australia",
|
|
7312
|
-
"ap-southeast-3": "Jakarta, Indonesia",
|
|
7313
|
-
"ap-southeast-4": "Melbourne, Australia",
|
|
7314
|
-
"ap-southeast-5": "Auckland, New Zealand",
|
|
7315
|
-
"ap-southeast-6": "New Zealand",
|
|
7316
|
-
"ap-southeast-7": "Thailand",
|
|
7317
|
-
"ca-central-1": "Montreal, Canada",
|
|
7318
|
-
"ca-west-1": "Calgary, Canada",
|
|
7319
|
-
"cn-north-1": "Beijing, China",
|
|
7320
|
-
"cn-northwest-1": "Ningxia, China",
|
|
7321
|
-
"eu-central-1": "Frankfurt, Germany",
|
|
7322
|
-
"eu-north-1": "Stockholm, Sweden",
|
|
7323
|
-
"eu-south-1": "Milan, Italy",
|
|
7324
|
-
"eu-south-2": "Spain",
|
|
7325
|
-
"eu-west-1": "Ireland",
|
|
7326
|
-
"eu-west-2": "London, UK",
|
|
7327
|
-
"eu-west-3": "Paris, France",
|
|
7328
|
-
"il-central-1": "Tel Aviv, Israel",
|
|
7329
|
-
"me-central-1": "UAE",
|
|
7330
|
-
"me-south-1": "Bahrain",
|
|
7331
|
-
"mx-central-1": "Mexico",
|
|
7332
|
-
"sa-east-1": "São Paulo, Brazil",
|
|
7333
|
-
"us-east-1": "Virginia, USA",
|
|
7334
|
-
"us-east-2": "Ohio, USA",
|
|
7335
|
-
"us-gov-east-1": "US Gov East (Virginia, USA)",
|
|
7336
|
-
"us-gov-west-1": "US Gov West (Oregon, USA)",
|
|
7337
|
-
"us-west-1": "California, USA",
|
|
7338
|
-
"us-west-2": "Oregon, USA",
|
|
7339
|
-
"eu-central-2": "Zurich, Switzerland"
|
|
7340
|
-
};
|
|
7341
|
-
//#endregion
|
|
7342
7709
|
//#region iac/s3.ts
|
|
7343
7710
|
function normalizeBucketRegion(region) {
|
|
7344
7711
|
if (region == null) return "us-east-1";
|
|
@@ -7356,7 +7723,7 @@ var S3Manager = class {
|
|
|
7356
7723
|
credentials: this.credentials
|
|
7357
7724
|
});
|
|
7358
7725
|
const buckets = (await s3Client.listBuckets({})).Buckets ?? [];
|
|
7359
|
-
return await Promise.all(buckets.filter((bucket) => bucket.Name).map(async (bucket) => {
|
|
7726
|
+
return await Promise.all(buckets.filter((bucket) => typeof bucket.Name === "string").map(async (bucket) => {
|
|
7360
7727
|
const { LocationConstraint } = await s3Client.getBucketLocation({ Bucket: bucket.Name });
|
|
7361
7728
|
return {
|
|
7362
7729
|
name: bucket.Name,
|
|
@@ -7374,7 +7741,7 @@ var S3Manager = class {
|
|
|
7374
7741
|
});
|
|
7375
7742
|
p.log.info(`Created S3 bucket: ${bucketName}`);
|
|
7376
7743
|
}
|
|
7377
|
-
async runMigrations({ bucketName, region, migrations }) {
|
|
7744
|
+
async runMigrations({ approved, bucketName, nonInteractive, region, migrations }) {
|
|
7378
7745
|
const migrator = new S3Migrator({
|
|
7379
7746
|
s3: new S3({
|
|
7380
7747
|
region,
|
|
@@ -7385,16 +7752,22 @@ var S3Manager = class {
|
|
|
7385
7752
|
});
|
|
7386
7753
|
const { pending } = await migrator.list();
|
|
7387
7754
|
await migrator.migrate({ dryRun: true });
|
|
7388
|
-
if (pending.length
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7755
|
+
if (pending.length === 0) return false;
|
|
7756
|
+
p.log.step("Pending migrations:");
|
|
7757
|
+
for (const migration of pending) p.log.step(`- ${migration.name}`);
|
|
7758
|
+
if (!nonInteractive || !approved) {
|
|
7759
|
+
if (nonInteractive) throw new MissingInitInputsError(["HOT_UPDATER_AWS_MIGRATION_APPROVED"]);
|
|
7760
|
+
const confirm = await p.confirm({
|
|
7761
|
+
initialValue: approved,
|
|
7762
|
+
message: "Do you want to continue?"
|
|
7763
|
+
});
|
|
7764
|
+
if (p.isCancel(confirm) || !confirm) {
|
|
7765
|
+
p.log.info("Migration cancelled.");
|
|
7766
|
+
process.exit(1);
|
|
7767
|
+
}
|
|
7396
7768
|
}
|
|
7397
7769
|
await migrator.migrate({ dryRun: false });
|
|
7770
|
+
return true;
|
|
7398
7771
|
}
|
|
7399
7772
|
async updateBucketPolicy({ bucketName, region, distributionId, accountId }) {
|
|
7400
7773
|
const s3Client = new S3({
|
|
@@ -7590,118 +7963,34 @@ const checkIfAwsCliInstalled = async () => {
|
|
|
7590
7963
|
return false;
|
|
7591
7964
|
}
|
|
7592
7965
|
};
|
|
7593
|
-
const
|
|
7966
|
+
const isAwsRegion = (value) => {
|
|
7967
|
+
return value !== void 0 && Object.hasOwn(regionLocationMap, value);
|
|
7968
|
+
};
|
|
7969
|
+
const runInit = async ({ build, envFile }) => {
|
|
7970
|
+
const nonInteractive = envFile !== void 0;
|
|
7971
|
+
const initEnvSources = await readHotUpdaterInitEnv(process.cwd(), envFile);
|
|
7972
|
+
const { managedEnv } = initEnvSources;
|
|
7973
|
+
const providerEnv = getHotUpdaterInitInputEnv(initEnvSources, nonInteractive);
|
|
7974
|
+
const savedInputs = resolveAwsInitInputs(providerEnv);
|
|
7975
|
+
assertAwsNonInteractiveInputs(savedInputs, nonInteractive);
|
|
7594
7976
|
if (!await checkIfAwsCliInstalled()) {
|
|
7595
7977
|
p.log.error(`AWS CLI is not installed. Please visit ${link("https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html")} for installation instructions`);
|
|
7596
7978
|
process.exit(1);
|
|
7597
7979
|
}
|
|
7598
|
-
let credentials;
|
|
7599
|
-
let configAuthMode = { mode: "account" };
|
|
7600
|
-
let awsProfile = null;
|
|
7601
|
-
const mode = await p.select({
|
|
7602
|
-
message: "Select the mode to login to AWS",
|
|
7603
|
-
options: [
|
|
7604
|
-
{
|
|
7605
|
-
label: "Current AWS CLI Session / Default Credential Chain",
|
|
7606
|
-
value: "local-session"
|
|
7607
|
-
},
|
|
7608
|
-
{
|
|
7609
|
-
label: "Shared AWS Profile",
|
|
7610
|
-
value: "shared-profile"
|
|
7611
|
-
},
|
|
7612
|
-
{
|
|
7613
|
-
label: "AWS SSO Login",
|
|
7614
|
-
value: "sso"
|
|
7615
|
-
},
|
|
7616
|
-
{
|
|
7617
|
-
label: "AWS Access Key ID & Secret Access Key",
|
|
7618
|
-
value: "account"
|
|
7619
|
-
}
|
|
7620
|
-
]
|
|
7621
|
-
});
|
|
7622
|
-
if (p.isCancel(mode)) process.exit(1);
|
|
7623
7980
|
p.log.message(colors.blue("The following permissions are required:"));
|
|
7624
7981
|
p.log.message(`${colors.blue("AmazonS3FullAccess")}: Create and read S3 buckets`);
|
|
7625
7982
|
p.log.message(`${colors.blue("AWSLambda_FullAccess")}: Create and update Lambda functions`);
|
|
7626
7983
|
p.log.message(`${colors.blue("CloudFrontFullAccess")}: Create and update CloudFront distributions`);
|
|
7627
7984
|
p.log.message(`${colors.blue("IAMFullAccess")}: Get or create IAM roles for Lambda@Edge`);
|
|
7628
7985
|
p.log.message(`${colors.blue("AmazonSSMFullAccess")}: Access to SSM Parameters for storing CloudFront key pairs`);
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
configAuthMode = {
|
|
7638
|
-
mode: "sso",
|
|
7639
|
-
profile
|
|
7640
|
-
};
|
|
7641
|
-
await execa("aws", [
|
|
7642
|
-
"sso",
|
|
7643
|
-
"login",
|
|
7644
|
-
"--profile",
|
|
7645
|
-
profile
|
|
7646
|
-
], {
|
|
7647
|
-
stdio: "inherit",
|
|
7648
|
-
shell: true
|
|
7649
|
-
});
|
|
7650
|
-
credentials = await fromSSO({ profile })();
|
|
7651
|
-
} catch (error) {
|
|
7652
|
-
if (error instanceof ExecaError) p.log.error(error.stdout || error.stderr || error.message);
|
|
7653
|
-
process.exit(1);
|
|
7654
|
-
}
|
|
7655
|
-
else if (mode === "local-session") try {
|
|
7656
|
-
configAuthMode = {
|
|
7657
|
-
mode: "local",
|
|
7658
|
-
profile: null
|
|
7659
|
-
};
|
|
7660
|
-
credentials = await fromNodeProviderChain()();
|
|
7661
|
-
} catch (error) {
|
|
7662
|
-
if (error instanceof Error) p.log.error(error.message);
|
|
7663
|
-
process.exit(1);
|
|
7664
|
-
}
|
|
7665
|
-
else if (mode === "shared-profile") try {
|
|
7666
|
-
const profile = await p.text({
|
|
7667
|
-
message: "Enter the AWS profile name",
|
|
7668
|
-
defaultValue: process.env.AWS_PROFILE ?? "default",
|
|
7669
|
-
placeholder: process.env.AWS_PROFILE ?? "default",
|
|
7670
|
-
validate: (value) => (value ?? "").trim() ? void 0 : "AWS profile name is required"
|
|
7671
|
-
});
|
|
7672
|
-
if (p.isCancel(profile)) process.exit(1);
|
|
7673
|
-
awsProfile = profile.trim();
|
|
7674
|
-
configAuthMode = {
|
|
7675
|
-
mode: "local",
|
|
7676
|
-
profile: awsProfile
|
|
7677
|
-
};
|
|
7678
|
-
credentials = await fromIni({ profile: awsProfile })();
|
|
7679
|
-
} catch (error) {
|
|
7680
|
-
if (error instanceof Error) p.log.error(error.message);
|
|
7681
|
-
process.exit(1);
|
|
7682
|
-
}
|
|
7683
|
-
else {
|
|
7684
|
-
const creds = await p.group({
|
|
7685
|
-
accessKeyId: () => p.text({
|
|
7686
|
-
message: "Enter your AWS Access Key ID",
|
|
7687
|
-
validate: (value) => value ? void 0 : "Access Key ID is required"
|
|
7688
|
-
}),
|
|
7689
|
-
secretAccessKey: () => p.password({
|
|
7690
|
-
message: "Enter your AWS Secret Access Key",
|
|
7691
|
-
validate: (value) => value ? void 0 : "Secret Access Key is required"
|
|
7692
|
-
})
|
|
7693
|
-
});
|
|
7694
|
-
if (p.isCancel(creds)) process.exit(1);
|
|
7695
|
-
credentials = {
|
|
7696
|
-
accessKeyId: creds.accessKeyId,
|
|
7697
|
-
secretAccessKey: creds.secretAccessKey
|
|
7698
|
-
};
|
|
7699
|
-
configAuthMode = { mode: "account" };
|
|
7700
|
-
}
|
|
7701
|
-
if (!credentials) {
|
|
7702
|
-
p.log.error("Couldn't fetch the credentials.");
|
|
7703
|
-
process.exit(1);
|
|
7704
|
-
}
|
|
7986
|
+
const { awsProfile, configAuthMode, credentials, mode } = await resolveAwsAuth(providerEnv, nonInteractive);
|
|
7987
|
+
const resolvedAuthInputs = {
|
|
7988
|
+
...savedInputs,
|
|
7989
|
+
accessKeyId: mode === "account" ? credentials.accessKeyId : savedInputs.accessKeyId,
|
|
7990
|
+
authMode: mode,
|
|
7991
|
+
profile: awsProfile ?? void 0,
|
|
7992
|
+
secretAccessKey: mode === "account" ? credentials.secretAccessKey : savedInputs.secretAccessKey
|
|
7993
|
+
};
|
|
7705
7994
|
const s3Manager = new S3Manager(credentials);
|
|
7706
7995
|
let availableBuckets = [];
|
|
7707
7996
|
try {
|
|
@@ -7716,54 +8005,109 @@ const runInit = async ({ build }) => {
|
|
|
7716
8005
|
throw e;
|
|
7717
8006
|
}
|
|
7718
8007
|
const createKey = `create/${Math.random().toString(36).substring(2, 15)}`;
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
8008
|
+
const savedBucketName = savedInputs.bucketName;
|
|
8009
|
+
const savedBucketRegion = savedInputs.bucketRegion;
|
|
8010
|
+
const existingBucket = availableBuckets.find((bucket) => bucket.name === savedBucketName);
|
|
8011
|
+
const createSavedBucket = nonInteractive && savedBucketName !== void 0 && isAwsRegion(savedBucketRegion) && !existingBucket;
|
|
8012
|
+
if (createSavedBucket && savedInputs.migrationApproved !== "true") throw new MissingInitInputsError(["HOT_UPDATER_AWS_MIGRATION_APPROVED"]);
|
|
8013
|
+
if (savedBucketName && !existingBucket && !createSavedBucket) p.log.warn("Saved S3 bucket was not found. Select a bucket again.");
|
|
8014
|
+
const savedLambdaName = savedInputs.lambdaName;
|
|
8015
|
+
const resourceInputs = await p.group({
|
|
8016
|
+
bucketSelection: () => {
|
|
8017
|
+
if (nonInteractive && existingBucket) return Promise.resolve(existingBucket.name);
|
|
8018
|
+
if (createSavedBucket) return Promise.resolve(createKey);
|
|
8019
|
+
return p.select({
|
|
8020
|
+
initialValue: existingBucket?.name ?? availableBuckets[0]?.name,
|
|
8021
|
+
message: "S3 Bucket List",
|
|
8022
|
+
options: [...availableBuckets.map((bucket) => ({
|
|
8023
|
+
value: bucket.name,
|
|
8024
|
+
label: `${bucket.name} (${bucket.region})`
|
|
8025
|
+
})), {
|
|
8026
|
+
value: createKey,
|
|
8027
|
+
label: "Create New S3 Bucket"
|
|
8028
|
+
}]
|
|
8029
|
+
});
|
|
8030
|
+
},
|
|
8031
|
+
bucketName: ({ results }) => results.bucketSelection === createKey ? createSavedBucket ? Promise.resolve(savedBucketName) : p.text({
|
|
8032
|
+
...getInitProviderTextPromptValues(initProvider.inputs.bucketName.prompt, savedBucketName),
|
|
8033
|
+
message: initProvider.inputs.bucketName.prompt.message,
|
|
8034
|
+
validate: (value) => value ? void 0 : "S3 bucket name is required"
|
|
8035
|
+
}) : Promise.resolve(results.bucketSelection),
|
|
8036
|
+
bucketRegion: ({ results }) => results.bucketSelection === createKey ? createSavedBucket ? Promise.resolve(savedBucketRegion) : p.select({
|
|
8037
|
+
initialValue: isAwsRegion(savedBucketRegion) ? savedBucketRegion : void 0,
|
|
8038
|
+
message: initProvider.inputs.bucketRegion.prompt.message,
|
|
7741
8039
|
options: Object.entries(regionLocationMap).map(([region, location]) => ({
|
|
7742
8040
|
label: `${region} (${location})`,
|
|
7743
8041
|
value: region
|
|
7744
8042
|
}))
|
|
7745
|
-
})
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
8043
|
+
}) : Promise.resolve(availableBuckets.find((bucket) => bucket.name === results.bucketSelection)?.region),
|
|
8044
|
+
lambdaName: () => nonInteractive && savedLambdaName ? Promise.resolve(savedLambdaName) : p.text({
|
|
8045
|
+
...getInitProviderTextPromptValues(initProvider.inputs.lambdaName.prompt, savedLambdaName),
|
|
8046
|
+
message: initProvider.inputs.lambdaName.prompt.message,
|
|
8047
|
+
validate: (value) => value ? void 0 : "Lambda function name is required"
|
|
8048
|
+
})
|
|
8049
|
+
}, { onCancel: () => process.exit(1) });
|
|
8050
|
+
const { bucketName, bucketRegion, lambdaName } = resourceInputs;
|
|
8051
|
+
if (!bucketName || !lambdaName) {
|
|
8052
|
+
p.log.error("AWS resource names are required.");
|
|
8053
|
+
process.exit(1);
|
|
7749
8054
|
}
|
|
7750
|
-
if (!bucketRegion) {
|
|
7751
|
-
p.log.error("
|
|
8055
|
+
if (!isAwsRegion(bucketRegion)) {
|
|
8056
|
+
p.log.error("AWS bucket region is required.");
|
|
7752
8057
|
process.exit(1);
|
|
7753
8058
|
}
|
|
8059
|
+
const cloudFrontManager = new CloudFrontManager(bucketRegion, credentials);
|
|
8060
|
+
const selectedDistribution = await cloudFrontManager.selectDistribution({
|
|
8061
|
+
bucketName,
|
|
8062
|
+
distributionId: savedInputs.distributionId,
|
|
8063
|
+
nonInteractive
|
|
8064
|
+
});
|
|
8065
|
+
const resolvedInputs = {
|
|
8066
|
+
...resolvedAuthInputs,
|
|
8067
|
+
bucketName,
|
|
8068
|
+
bucketRegion,
|
|
8069
|
+
distributionId: selectedDistribution?.Id,
|
|
8070
|
+
lambdaName,
|
|
8071
|
+
migrationApproved: savedInputs.migrationApproved
|
|
8072
|
+
};
|
|
8073
|
+
const initEnv = getInitProviderEnvVars({
|
|
8074
|
+
includeConsentInputs: await confirmInitInputPersistence({
|
|
8075
|
+
existingEnv: managedEnv,
|
|
8076
|
+
inputs: resolvedInputs,
|
|
8077
|
+
nonInteractive,
|
|
8078
|
+
provider: initProvider
|
|
8079
|
+
}),
|
|
8080
|
+
inputs: resolvedInputs,
|
|
8081
|
+
provider: initProvider
|
|
8082
|
+
});
|
|
8083
|
+
const accessKeyEnvKey = initProvider.inputs.accessKeyId.envKey;
|
|
8084
|
+
const secretAccessKeyEnvKey = initProvider.inputs.secretAccessKey.envKey;
|
|
8085
|
+
await makeEnv({
|
|
8086
|
+
...initEnv,
|
|
8087
|
+
...initEnv[accessKeyEnvKey] ? { [accessKeyEnvKey]: {
|
|
8088
|
+
comment: "The current key may have excessive permissions. Update it with an S3FullAccess and CloudFrontFullAccess key.",
|
|
8089
|
+
value: initEnv[accessKeyEnvKey]
|
|
8090
|
+
} } : {},
|
|
8091
|
+
...initEnv[secretAccessKeyEnvKey] ? { [secretAccessKeyEnvKey]: {
|
|
8092
|
+
comment: "The current key may have excessive permissions. Update it with an S3FullAccess and CloudFrontFullAccess key.",
|
|
8093
|
+
value: initEnv[secretAccessKeyEnvKey]
|
|
8094
|
+
} } : {}
|
|
8095
|
+
});
|
|
8096
|
+
if (resourceInputs.bucketSelection === createKey) await s3Manager.createBucket(bucketName, bucketRegion);
|
|
7754
8097
|
p.log.info(`Selected S3 Bucket: ${bucketName} (${bucketRegion})`);
|
|
7755
|
-
await s3Manager.runMigrations({
|
|
8098
|
+
if (await s3Manager.runMigrations({
|
|
8099
|
+
approved: savedInputs.migrationApproved === "true",
|
|
7756
8100
|
bucketName,
|
|
8101
|
+
nonInteractive,
|
|
7757
8102
|
region: bucketRegion,
|
|
7758
8103
|
migrations: [new Migration0001HotUpdater0_13_0(), new Migration0001HotUpdater0_18_0()]
|
|
7759
|
-
});
|
|
8104
|
+
}) && savedInputs.migrationApproved !== "true") await makeEnv({ [initProvider.inputs.migrationApproved.envKey]: "true" });
|
|
7760
8105
|
const lambdaRoleArn = await new IAMManager(bucketRegion, credentials).createOrSelectRole();
|
|
7761
8106
|
const keyPair = await new SSMKeyPairManager(bucketRegion, credentials).getOrCreateKeyPair(`/hot-updater/${bucketName}/keypair`);
|
|
7762
|
-
const cloudFrontManager = new CloudFrontManager(bucketRegion, credentials);
|
|
7763
8107
|
const { publicKeyId, keyGroupId } = await cloudFrontManager.getOrCreateKeyGroup(keyPair.publicKey);
|
|
7764
8108
|
const lambdaEdgeDeployer = new LambdaEdgeDeployer(credentials);
|
|
7765
8109
|
const ssmParameterName = `/hot-updater/${bucketName}/keypair`;
|
|
7766
|
-
const { functionArn } = await lambdaEdgeDeployer.deploy(lambdaRoleArn, {
|
|
8110
|
+
const { functionArn } = await lambdaEdgeDeployer.deploy(lambdaRoleArn, lambdaName, {
|
|
7767
8111
|
bucketName,
|
|
7768
8112
|
publicKeyId,
|
|
7769
8113
|
ssmParameterName,
|
|
@@ -7772,6 +8116,7 @@ const runInit = async ({ build }) => {
|
|
|
7772
8116
|
const { distributionDomain, distributionId } = await cloudFrontManager.createOrUpdateDistribution({
|
|
7773
8117
|
keyGroupId,
|
|
7774
8118
|
bucketName,
|
|
8119
|
+
distribution: selectedDistribution,
|
|
7775
8120
|
functionArn
|
|
7776
8121
|
});
|
|
7777
8122
|
const accountId = functionArn.split(":")[4];
|
|
@@ -7782,22 +8127,7 @@ const runInit = async ({ build }) => {
|
|
|
7782
8127
|
accountId
|
|
7783
8128
|
});
|
|
7784
8129
|
const configWriteResult = await writeHotUpdaterConfig(getConfigScaffold(build, configAuthMode));
|
|
7785
|
-
await makeEnv({
|
|
7786
|
-
HOT_UPDATER_S3_BUCKET_NAME: bucketName,
|
|
7787
|
-
HOT_UPDATER_S3_REGION: bucketRegion,
|
|
7788
|
-
...mode === "account" ? {
|
|
7789
|
-
HOT_UPDATER_S3_ACCESS_KEY_ID: {
|
|
7790
|
-
comment: "The current key may have excessive permissions. Update it with an S3FullAccess and CloudFrontFullAccess key.",
|
|
7791
|
-
value: credentials.accessKeyId
|
|
7792
|
-
},
|
|
7793
|
-
HOT_UPDATER_S3_SECRET_ACCESS_KEY: {
|
|
7794
|
-
comment: "The current key may have excessive permissions. Update it with an S3FullAccess and CloudFrontFullAccess key.",
|
|
7795
|
-
value: credentials.secretAccessKey
|
|
7796
|
-
}
|
|
7797
|
-
} : {},
|
|
7798
|
-
...awsProfile !== null ? { HOT_UPDATER_AWS_PROFILE: awsProfile } : {},
|
|
7799
|
-
HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID: distributionId
|
|
7800
|
-
});
|
|
8130
|
+
await makeEnv({ [initProvider.inputs.distributionId.envKey]: distributionId });
|
|
7801
8131
|
if (mode === "sso") await ensureInstallPackages({ devDependencies: ["@aws-sdk/credential-provider-sso"] });
|
|
7802
8132
|
else if (mode === "local-session" || mode === "shared-profile") await ensureInstallPackages({ devDependencies: ["@aws-sdk/credential-providers"] });
|
|
7803
8133
|
p.log.success("Generated '.env.hotupdater' file with AWS settings.");
|