@projectdochelp/s3te 3.2.3 → 3.3.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 +19 -19
- package/package.json +1 -1
- package/packages/aws-adapter/src/deploy.mjs +118 -8
- package/packages/aws-adapter/src/index.mjs +1 -1
- package/packages/aws-adapter/src/package.mjs +8 -1
- package/packages/aws-adapter/src/template.mjs +70 -52
- package/packages/cli/bin/s3te.mjs +25 -16
- package/packages/cli/src/project.mjs +103 -100
- package/packages/core/src/config.mjs +4 -0
- package/packages/core/src/index.mjs +1 -0
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ That source sync is not limited to Lambda code. It includes your `.html`, `.part
|
|
|
66
66
|
|
|
67
67
|
The persistent environment stack contains the long-lived AWS resources such as buckets, Lambda functions, DynamoDB tables, CloudFront distributions and the runtime manifest parameter. The temporary deploy stack exists only so CloudFormation can consume the packaged Lambda artifacts cleanly.
|
|
68
68
|
|
|
69
|
-
If optional runtime features
|
|
69
|
+
If optional runtime features are enabled in `s3te.config.json`, S3TE extends the deployed AWS runtime accordingly. `sitemap` is added to the main environment stack. Webiny is deployed as a separate option stack so retrofitting CMS support does not require CloudFront resources to move with it.
|
|
70
70
|
|
|
71
71
|
</details>
|
|
72
72
|
|
|
@@ -484,7 +484,7 @@ Once Webiny is installed and the stack is deployed with Webiny enabled, CMS cont
|
|
|
484
484
|
| `s3te sync --env <name>` | Uploads current project sources into the configured code buckets. |
|
|
485
485
|
| `s3te doctor --env <name>` | Checks local machine and AWS access before deploy. |
|
|
486
486
|
| `s3te deploy --env <name>` | Deploys or updates the AWS environment and syncs source files. |
|
|
487
|
-
| `s3te
|
|
487
|
+
| `s3te option <webiny|sitemap>` | Writes or updates optional feature configuration such as Webiny or sitemap support in an existing S3TE project. |
|
|
488
488
|
|
|
489
489
|
</details>
|
|
490
490
|
|
|
@@ -929,11 +929,11 @@ Add this block to `s3te.config.json`:
|
|
|
929
929
|
|
|
930
930
|
The top-level `enabled` acts as the default. `integrations.sitemap.environments.<env>.enabled` can override that for a single environment.
|
|
931
931
|
|
|
932
|
-
If you prefer the CLI path, this does the same
|
|
932
|
+
If you prefer the CLI path, this does the same option update:
|
|
933
933
|
|
|
934
934
|
```bash
|
|
935
|
-
npx s3te
|
|
936
|
-
npx s3te
|
|
935
|
+
npx s3te option sitemap --enable --write
|
|
936
|
+
npx s3te option sitemap --env test --enable --write
|
|
937
937
|
```
|
|
938
938
|
|
|
939
939
|
After enabling or disabling `sitemap`, redeploy the affected environment once:
|
|
@@ -989,23 +989,23 @@ This section assumes that S3TE is already installed and deployed. The S3TE-speci
|
|
|
989
989
|
3. Manually enable DynamoDB Streams on that Webiny table before the first S3TE deploy with Webiny enabled.
|
|
990
990
|
Use `NEW_AND_OLD_IMAGES`.
|
|
991
991
|
Without that stream, `s3te deploy --env <name>` cannot wire the Webiny trigger and fails because the table has no `LatestStreamArn`.
|
|
992
|
-
4.
|
|
992
|
+
4. Write the Webiny option into your existing S3TE config:
|
|
993
993
|
|
|
994
994
|
```bash
|
|
995
|
-
npx s3te
|
|
995
|
+
npx s3te option webiny --enable --source-table webiny-1234567 --tenant root --model article --write
|
|
996
996
|
```
|
|
997
997
|
|
|
998
|
-
`staticContent` and `staticCodeContent` are kept automatically. Add `--
|
|
998
|
+
`staticContent` and `staticCodeContent` are kept automatically. Add `--model` once per custom model you want S3TE to mirror.
|
|
999
999
|
|
|
1000
|
-
`--
|
|
1000
|
+
`--model article` means:
|
|
1001
1001
|
|
|
1002
1002
|
- `article` is the technical Webiny model ID, not the human-readable label shown in the CMS UI.
|
|
1003
1003
|
- S3TE adds that model ID to `integrations.webiny.relevantModels` in `s3te.config.json`.
|
|
1004
1004
|
- Only Webiny stream records whose model is listed in `relevantModels` are mirrored into the S3TE content table and can trigger rerendering.
|
|
1005
|
-
- If you omit `--
|
|
1006
|
-
- You can pass the flag multiple times for multiple models, for example `--
|
|
1005
|
+
- If you omit `--model`, only the built-in defaults `staticContent` and `staticCodeContent` are mirrored.
|
|
1006
|
+
- You can pass the flag multiple times for multiple models, for example `--model article --model news --model event`.
|
|
1007
1007
|
|
|
1008
|
-
That makes the
|
|
1008
|
+
That makes the option example above equivalent to a config that contains:
|
|
1009
1009
|
|
|
1010
1010
|
```json
|
|
1011
1011
|
"relevantModels": ["article", "staticContent", "staticCodeContent"]
|
|
@@ -1013,11 +1013,11 @@ That makes the migration example above equivalent to a config that contains:
|
|
|
1013
1013
|
|
|
1014
1014
|
Use this for every Webiny model whose entries should be available to S3TE template commands like `dbitem`, `dbmulti`, `dbmultifile`, `dbmultifileitem`, or `dbpart`.
|
|
1015
1015
|
|
|
1016
|
-
If different environments should read from different Webiny installations or tenants, run the
|
|
1016
|
+
If different environments should read from different Webiny installations or tenants, run the option command per environment:
|
|
1017
1017
|
|
|
1018
1018
|
```bash
|
|
1019
|
-
npx s3te
|
|
1020
|
-
npx s3te
|
|
1019
|
+
npx s3te option webiny --env test --enable --source-table webiny-test-1234567 --tenant preview --write
|
|
1020
|
+
npx s3te option webiny --env prod --enable --source-table webiny-live-1234567 --tenant root --write
|
|
1021
1021
|
```
|
|
1022
1022
|
|
|
1023
1023
|
5. Verify again that DynamoDB Streams are enabled on the Webiny source table with `NEW_AND_OLD_IMAGES`.
|
|
@@ -1037,19 +1037,19 @@ npx s3te doctor --env prod
|
|
|
1037
1037
|
npx s3te deploy --env prod
|
|
1038
1038
|
```
|
|
1039
1039
|
|
|
1040
|
-
That deploy updates the existing environment stack and
|
|
1040
|
+
That deploy updates the existing environment stack and, when Webiny is enabled, also deploys the separate Webiny option stack for `content-mirror` and its DynamoDB stream mapping. You do not need a fresh S3TE installation. After that, Webiny content changes flow through the deployed AWS resources automatically; only template or asset changes still need `s3te sync --env <name>`.
|
|
1041
1041
|
|
|
1042
1042
|
Manual versus automatic responsibilities in this step:
|
|
1043
1043
|
|
|
1044
1044
|
- Manual: enable DynamoDB Streams on the Webiny source table
|
|
1045
|
-
- Automatic during `s3te deploy`: read `LatestStreamArn`, create the Lambda event source mapping, and
|
|
1045
|
+
- Automatic during `s3te deploy`: read `LatestStreamArn`, create the Lambda event source mapping, and deploy the S3TE Webiny mirror Lambda in the separate Webiny option stack
|
|
1046
1046
|
|
|
1047
1047
|
</details>
|
|
1048
1048
|
|
|
1049
1049
|
<details>
|
|
1050
|
-
<summary>What the
|
|
1050
|
+
<summary>What the option command changes</summary>
|
|
1051
1051
|
|
|
1052
|
-
The
|
|
1052
|
+
The option command writes or updates the `integrations.webiny` block in `s3te.config.json`. A typical result looks like this:
|
|
1053
1053
|
|
|
1054
1054
|
Example config block:
|
|
1055
1055
|
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
buildEnvironmentRuntimeConfig,
|
|
6
|
+
resolveOptionStackName,
|
|
6
7
|
resolveStackName,
|
|
7
8
|
S3teError
|
|
8
9
|
} from "../../core/src/index.mjs";
|
|
@@ -34,6 +35,15 @@ function temporaryStackName(stackName) {
|
|
|
34
35
|
return `${stackName}-deploy-temp`;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
function stackDoesNotExist(error) {
|
|
39
|
+
const detailsText = [
|
|
40
|
+
error?.message,
|
|
41
|
+
error?.details?.stderr,
|
|
42
|
+
error?.details?.stdout
|
|
43
|
+
].filter(Boolean).join("\n");
|
|
44
|
+
return /does not exist/i.test(detailsText) || /Stack with id .* does not exist/i.test(detailsText);
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
async function uploadArtifact({ bucketName, key, bodyPath, region, profile, cwd }) {
|
|
38
48
|
await runAwsCli(["s3api", "put-object", "--bucket", bucketName, "--key", key, "--body", bodyPath], {
|
|
39
49
|
region,
|
|
@@ -53,6 +63,18 @@ async function describeStack({ stackName, region, profile, cwd }) {
|
|
|
53
63
|
return JSON.parse(describedStack.stdout).Stacks?.[0];
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
async function stackExists({ stackName, region, profile, cwd }) {
|
|
67
|
+
try {
|
|
68
|
+
await describeStack({ stackName, region, profile, cwd });
|
|
69
|
+
return true;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (stackDoesNotExist(error)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
56
78
|
async function describeStackEvents({ stackName, region, profile, cwd }) {
|
|
57
79
|
const describedEvents = await runAwsCli(["cloudformation", "describe-stack-events", "--stack-name", stackName, "--output", "json"], {
|
|
58
80
|
region,
|
|
@@ -331,11 +353,37 @@ async function cleanupTemporaryArtifactsStack({
|
|
|
331
353
|
});
|
|
332
354
|
}
|
|
333
355
|
|
|
356
|
+
async function deleteCloudFormationStackIfExists({
|
|
357
|
+
stackName,
|
|
358
|
+
region,
|
|
359
|
+
profile,
|
|
360
|
+
cwd
|
|
361
|
+
}) {
|
|
362
|
+
if (!(await stackExists({ stackName, region, profile, cwd }))) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
await runAwsCli(["cloudformation", "delete-stack", "--stack-name", stackName], {
|
|
367
|
+
region,
|
|
368
|
+
profile,
|
|
369
|
+
cwd,
|
|
370
|
+
errorCode: "ADAPTER_ERROR"
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
await runAwsCli(["cloudformation", "wait", "stack-delete-complete", "--stack-name", stackName], {
|
|
374
|
+
region,
|
|
375
|
+
profile,
|
|
376
|
+
cwd,
|
|
377
|
+
errorCode: "ADAPTER_ERROR"
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
|
|
334
383
|
function buildEnvironmentStackParameters({
|
|
335
384
|
artifactBucket,
|
|
336
385
|
uploadedArtifacts,
|
|
337
|
-
runtimeManifestValue
|
|
338
|
-
webinyStreamArn = ""
|
|
386
|
+
runtimeManifestValue
|
|
339
387
|
}) {
|
|
340
388
|
return [
|
|
341
389
|
`ArtifactBucket=${artifactBucket}`,
|
|
@@ -343,9 +391,19 @@ function buildEnvironmentStackParameters({
|
|
|
343
391
|
`RenderWorkerArtifactKey=${uploadedArtifacts.renderWorker}`,
|
|
344
392
|
`InvalidationSchedulerArtifactKey=${uploadedArtifacts.invalidationScheduler}`,
|
|
345
393
|
`InvalidationExecutorArtifactKey=${uploadedArtifacts.invalidationExecutor}`,
|
|
346
|
-
`ContentMirrorArtifactKey=${uploadedArtifacts.contentMirror}`,
|
|
347
394
|
`SitemapUpdaterArtifactKey=${uploadedArtifacts.sitemapUpdater}`,
|
|
348
|
-
`RuntimeManifestValue=${runtimeManifestValue}
|
|
395
|
+
`RuntimeManifestValue=${runtimeManifestValue}`
|
|
396
|
+
];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function buildWebinyStackParameters({
|
|
400
|
+
artifactBucket,
|
|
401
|
+
uploadedArtifacts,
|
|
402
|
+
webinyStreamArn
|
|
403
|
+
}) {
|
|
404
|
+
return [
|
|
405
|
+
`ArtifactBucket=${artifactBucket}`,
|
|
406
|
+
`ContentMirrorArtifactKey=${uploadedArtifacts.contentMirror}`,
|
|
349
407
|
`WebinySourceTableStreamArn=${webinyStreamArn}`
|
|
350
408
|
];
|
|
351
409
|
}
|
|
@@ -365,6 +423,7 @@ export async function deployAwsProject({
|
|
|
365
423
|
const requestedFeatureSet = new Set(features);
|
|
366
424
|
const featureSet = new Set(resolveRequestedFeatures(config, features, environment));
|
|
367
425
|
const stackName = resolveStackName(config, environment);
|
|
426
|
+
const webinyStackName = resolveOptionStackName(config, environment, "webiny");
|
|
368
427
|
const tempStackName = temporaryStackName(stackName);
|
|
369
428
|
const runtimeManifestPath = path.join(projectDir, packageDir ?? path.join("offline", "IAAS", "package", environment), "runtime-manifest.json");
|
|
370
429
|
|
|
@@ -439,16 +498,36 @@ export async function deployAwsProject({
|
|
|
439
498
|
parameterOverrides: buildEnvironmentStackParameters({
|
|
440
499
|
artifactBucket: tempStack.artifactBucket,
|
|
441
500
|
uploadedArtifacts,
|
|
442
|
-
runtimeManifestValue: "{}"
|
|
443
|
-
webinyStreamArn
|
|
501
|
+
runtimeManifestValue: "{}"
|
|
444
502
|
}),
|
|
445
503
|
noExecute: plan,
|
|
446
504
|
stdio
|
|
447
505
|
});
|
|
448
506
|
|
|
449
507
|
if (plan) {
|
|
508
|
+
if (featureSet.has("webiny")) {
|
|
509
|
+
if (!packaged.manifest.webinyCloudFormationTemplate) {
|
|
510
|
+
throw new S3teError("ADAPTER_ERROR", "Package manifest is missing the Webiny option template.");
|
|
511
|
+
}
|
|
512
|
+
await deployCloudFormationStack({
|
|
513
|
+
stackName: webinyStackName,
|
|
514
|
+
templatePath: path.join(projectDir, packaged.manifest.webinyCloudFormationTemplate),
|
|
515
|
+
region: runtimeConfig.awsRegion,
|
|
516
|
+
profile,
|
|
517
|
+
cwd: projectDir,
|
|
518
|
+
capabilities: ["CAPABILITY_NAMED_IAM"],
|
|
519
|
+
parameterOverrides: buildWebinyStackParameters({
|
|
520
|
+
artifactBucket: tempStack.artifactBucket,
|
|
521
|
+
uploadedArtifacts,
|
|
522
|
+
webinyStreamArn
|
|
523
|
+
}),
|
|
524
|
+
noExecute: true,
|
|
525
|
+
stdio
|
|
526
|
+
});
|
|
527
|
+
}
|
|
450
528
|
return {
|
|
451
529
|
stackName,
|
|
530
|
+
optionalStacks: featureSet.has("webiny") ? [webinyStackName] : [],
|
|
452
531
|
packageDir: packaged.manifest.packageDir,
|
|
453
532
|
runtimeManifestPath: normalizeRelative(projectDir, runtimeManifestPath),
|
|
454
533
|
syncedCodeBuckets: [],
|
|
@@ -482,12 +561,41 @@ export async function deployAwsProject({
|
|
|
482
561
|
parameterOverrides: buildEnvironmentStackParameters({
|
|
483
562
|
artifactBucket: tempStack.artifactBucket,
|
|
484
563
|
uploadedArtifacts,
|
|
485
|
-
runtimeManifestValue: JSON.stringify(runtimeManifest)
|
|
486
|
-
webinyStreamArn
|
|
564
|
+
runtimeManifestValue: JSON.stringify(runtimeManifest)
|
|
487
565
|
}),
|
|
488
566
|
stdio
|
|
489
567
|
});
|
|
490
568
|
|
|
569
|
+
let deployedOptionalStacks = [];
|
|
570
|
+
let removedOptionalStacks = [];
|
|
571
|
+
if (featureSet.has("webiny")) {
|
|
572
|
+
if (!packaged.manifest.webinyCloudFormationTemplate) {
|
|
573
|
+
throw new S3teError("ADAPTER_ERROR", "Package manifest is missing the Webiny option template.");
|
|
574
|
+
}
|
|
575
|
+
await deployCloudFormationStack({
|
|
576
|
+
stackName: webinyStackName,
|
|
577
|
+
templatePath: path.join(projectDir, packaged.manifest.webinyCloudFormationTemplate),
|
|
578
|
+
region: runtimeConfig.awsRegion,
|
|
579
|
+
profile,
|
|
580
|
+
cwd: projectDir,
|
|
581
|
+
capabilities: ["CAPABILITY_NAMED_IAM"],
|
|
582
|
+
parameterOverrides: buildWebinyStackParameters({
|
|
583
|
+
artifactBucket: tempStack.artifactBucket,
|
|
584
|
+
uploadedArtifacts,
|
|
585
|
+
webinyStreamArn
|
|
586
|
+
}),
|
|
587
|
+
stdio
|
|
588
|
+
});
|
|
589
|
+
deployedOptionalStacks = [webinyStackName];
|
|
590
|
+
} else if (await deleteCloudFormationStackIfExists({
|
|
591
|
+
stackName: webinyStackName,
|
|
592
|
+
region: runtimeConfig.awsRegion,
|
|
593
|
+
profile,
|
|
594
|
+
cwd: projectDir
|
|
595
|
+
})) {
|
|
596
|
+
removedOptionalStacks = [webinyStackName];
|
|
597
|
+
}
|
|
598
|
+
|
|
491
599
|
const syncedCodeBuckets = noSync
|
|
492
600
|
? []
|
|
493
601
|
: (await syncPreparedSources({
|
|
@@ -511,6 +619,8 @@ export async function deployAwsProject({
|
|
|
511
619
|
|
|
512
620
|
return {
|
|
513
621
|
stackName,
|
|
622
|
+
optionalStacks: deployedOptionalStacks,
|
|
623
|
+
removedOptionalStacks,
|
|
514
624
|
packageDir: packaged.manifest.packageDir,
|
|
515
625
|
runtimeManifestPath: normalizeRelative(projectDir, runtimeManifestPath),
|
|
516
626
|
syncedCodeBuckets,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { writeZipArchive } from "./zip.mjs";
|
|
2
|
-
export { buildCloudFormationTemplate, buildTemporaryDeployStackTemplate } from "./template.mjs";
|
|
2
|
+
export { buildCloudFormationTemplate, buildTemporaryDeployStackTemplate, buildWebinyCloudFormationTemplate } from "./template.mjs";
|
|
3
3
|
export { buildAwsRuntimeManifest, extractStackOutputsMap } from "./manifest.mjs";
|
|
4
4
|
export { ensureAwsCliAvailable, ensureAwsCredentials, runAwsCli } from "./aws-cli.mjs";
|
|
5
5
|
export { getConfiguredFeatures, resolveRequestedFeatures } from "./features.mjs";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
|
|
12
12
|
import { buildAwsRuntimeManifest } from "./manifest.mjs";
|
|
13
13
|
import { resolveRequestedFeatures } from "./features.mjs";
|
|
14
|
-
import { buildCloudFormationTemplate } from "./template.mjs";
|
|
14
|
+
import { buildCloudFormationTemplate, buildWebinyCloudFormationTemplate } from "./template.mjs";
|
|
15
15
|
import { writeZipArchive } from "./zip.mjs";
|
|
16
16
|
|
|
17
17
|
const ZIP_DATE = new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -242,6 +242,7 @@ export async function packageAwsProject({
|
|
|
242
242
|
|
|
243
243
|
const lambdaDir = path.join(packageDir, "lambda");
|
|
244
244
|
const templatePath = path.join(packageDir, "cloudformation.template.json");
|
|
245
|
+
const webinyTemplatePath = path.join(packageDir, "cloudformation.webiny.template.json");
|
|
245
246
|
const packagingManifestPath = path.join(packageDir, "manifest.json");
|
|
246
247
|
const runtimeManifestSeedPath = path.join(packageDir, "runtime-manifest.base.json");
|
|
247
248
|
const lambdaEntries = await collectLambdaArchiveEntries();
|
|
@@ -292,6 +293,9 @@ export async function packageAwsProject({
|
|
|
292
293
|
const runtimeManifestSeed = buildAwsRuntimeManifest({ config, environment });
|
|
293
294
|
|
|
294
295
|
await writeJsonFile(templatePath, template);
|
|
296
|
+
if (resolvedFeatures.includes("webiny") && runtimeConfig.integrations.webiny.enabled) {
|
|
297
|
+
await writeJsonFile(webinyTemplatePath, buildWebinyCloudFormationTemplate({ config, environment }));
|
|
298
|
+
}
|
|
295
299
|
await writeJsonFile(runtimeManifestSeedPath, runtimeManifestSeed);
|
|
296
300
|
|
|
297
301
|
const manifest = {
|
|
@@ -302,6 +306,9 @@ export async function packageAwsProject({
|
|
|
302
306
|
runtimeParameterName: runtimeConfig.runtimeParameterName,
|
|
303
307
|
packageDir: normalizeRelative(projectDir, packageDir),
|
|
304
308
|
cloudFormationTemplate: normalizeRelative(projectDir, templatePath),
|
|
309
|
+
...(resolvedFeatures.includes("webiny") && runtimeConfig.integrations.webiny.enabled
|
|
310
|
+
? { webinyCloudFormationTemplate: normalizeRelative(projectDir, webinyTemplatePath) }
|
|
311
|
+
: {}),
|
|
305
312
|
runtimeManifestSeed: normalizeRelative(projectDir, runtimeManifestSeedPath),
|
|
306
313
|
lambdaArtifacts: Object.fromEntries(Object.entries(lambdaArtifacts).map(([name, artifact]) => [
|
|
307
314
|
name,
|
|
@@ -70,6 +70,17 @@ function lambdaRuntimeProperties(runtimeConfig, roleRef, name, keyParameter, han
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
function buildFunctionNames(runtimeConfig) {
|
|
74
|
+
return {
|
|
75
|
+
sourceDispatcher: `${runtimeConfig.stackPrefix}_s3te_source_dispatcher`,
|
|
76
|
+
renderWorker: `${runtimeConfig.stackPrefix}_s3te_render_worker`,
|
|
77
|
+
invalidationScheduler: `${runtimeConfig.stackPrefix}_s3te_invalidation_scheduler`,
|
|
78
|
+
invalidationExecutor: `${runtimeConfig.stackPrefix}_s3te_invalidation_executor`,
|
|
79
|
+
contentMirror: `${runtimeConfig.stackPrefix}_s3te_content_mirror`,
|
|
80
|
+
sitemapUpdater: `${runtimeConfig.stackPrefix}_s3te_sitemap_updater`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
73
84
|
function createExecutionRole(roleName) {
|
|
74
85
|
return {
|
|
75
86
|
Type: "AWS::IAM::Role",
|
|
@@ -132,14 +143,7 @@ export function buildCloudFormationTemplate({ config, environment, features = []
|
|
|
132
143
|
const outputs = {};
|
|
133
144
|
const featureSet = new Set(features);
|
|
134
145
|
|
|
135
|
-
const functionNames =
|
|
136
|
-
sourceDispatcher: `${runtimeConfig.stackPrefix}_s3te_source_dispatcher`,
|
|
137
|
-
renderWorker: `${runtimeConfig.stackPrefix}_s3te_render_worker`,
|
|
138
|
-
invalidationScheduler: `${runtimeConfig.stackPrefix}_s3te_invalidation_scheduler`,
|
|
139
|
-
invalidationExecutor: `${runtimeConfig.stackPrefix}_s3te_invalidation_executor`,
|
|
140
|
-
contentMirror: `${runtimeConfig.stackPrefix}_s3te_content_mirror`,
|
|
141
|
-
sitemapUpdater: `${runtimeConfig.stackPrefix}_s3te_sitemap_updater`
|
|
142
|
-
};
|
|
146
|
+
const functionNames = buildFunctionNames(runtimeConfig);
|
|
143
147
|
|
|
144
148
|
const parameters = {
|
|
145
149
|
ArtifactBucket: {
|
|
@@ -157,10 +161,6 @@ export function buildCloudFormationTemplate({ config, environment, features = []
|
|
|
157
161
|
InvalidationExecutorArtifactKey: {
|
|
158
162
|
Type: "String"
|
|
159
163
|
},
|
|
160
|
-
ContentMirrorArtifactKey: {
|
|
161
|
-
Type: "String",
|
|
162
|
-
Default: ""
|
|
163
|
-
},
|
|
164
164
|
SitemapUpdaterArtifactKey: {
|
|
165
165
|
Type: "String",
|
|
166
166
|
Default: ""
|
|
@@ -168,10 +168,6 @@ export function buildCloudFormationTemplate({ config, environment, features = []
|
|
|
168
168
|
RuntimeManifestValue: {
|
|
169
169
|
Type: "String",
|
|
170
170
|
Default: "{}"
|
|
171
|
-
},
|
|
172
|
-
WebinySourceTableStreamArn: {
|
|
173
|
-
Type: "String",
|
|
174
|
-
Default: ""
|
|
175
171
|
}
|
|
176
172
|
};
|
|
177
173
|
|
|
@@ -361,39 +357,6 @@ export function buildCloudFormationTemplate({ config, environment, features = []
|
|
|
361
357
|
}
|
|
362
358
|
);
|
|
363
359
|
|
|
364
|
-
if (featureSet.has("webiny") && runtimeConfig.integrations.webiny.enabled) {
|
|
365
|
-
resources.ContentMirror = lambdaRuntimeProperties(
|
|
366
|
-
runtimeConfig,
|
|
367
|
-
"ExecutionRole",
|
|
368
|
-
functionNames.contentMirror,
|
|
369
|
-
"ContentMirrorArtifactKey",
|
|
370
|
-
"content-mirror",
|
|
371
|
-
{
|
|
372
|
-
Timeout: 300,
|
|
373
|
-
MemorySize: 512,
|
|
374
|
-
Environment: {
|
|
375
|
-
Variables: {
|
|
376
|
-
S3TE_ENVIRONMENT: environment,
|
|
377
|
-
S3TE_CONTENT_TABLE: runtimeConfig.tables.content,
|
|
378
|
-
S3TE_RELEVANT_MODELS: runtimeConfig.integrations.webiny.relevantModels.join(","),
|
|
379
|
-
S3TE_WEBINY_TENANT: runtimeConfig.integrations.webiny.tenant ?? "",
|
|
380
|
-
S3TE_RENDER_WORKER_NAME: functionNames.renderWorker
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
|
|
386
|
-
resources.ContentMirrorEventSourceMapping = {
|
|
387
|
-
Type: "AWS::Lambda::EventSourceMapping",
|
|
388
|
-
Properties: {
|
|
389
|
-
BatchSize: 10,
|
|
390
|
-
StartingPosition: "LATEST",
|
|
391
|
-
EventSourceArn: { Ref: "WebinySourceTableStreamArn" },
|
|
392
|
-
FunctionName: { Ref: "ContentMirror" }
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
|
|
397
360
|
if (featureSet.has("sitemap") && runtimeConfig.integrations.sitemap.enabled) {
|
|
398
361
|
resources.SitemapUpdater = lambdaRuntimeProperties(
|
|
399
362
|
runtimeConfig,
|
|
@@ -426,9 +389,6 @@ export function buildCloudFormationTemplate({ config, environment, features = []
|
|
|
426
389
|
outputs.InvalidationSchedulerFunctionName = { Value: functionNames.invalidationScheduler };
|
|
427
390
|
outputs.InvalidationExecutorFunctionName = { Value: functionNames.invalidationExecutor };
|
|
428
391
|
|
|
429
|
-
if (resources.ContentMirror) {
|
|
430
|
-
outputs.ContentMirrorFunctionName = { Value: functionNames.contentMirror };
|
|
431
|
-
}
|
|
432
392
|
if (resources.SitemapUpdater) {
|
|
433
393
|
outputs.SitemapUpdaterFunctionName = { Value: functionNames.sitemapUpdater };
|
|
434
394
|
}
|
|
@@ -620,6 +580,64 @@ export function buildCloudFormationTemplate({ config, environment, features = []
|
|
|
620
580
|
};
|
|
621
581
|
}
|
|
622
582
|
|
|
583
|
+
export function buildWebinyCloudFormationTemplate({ config, environment }) {
|
|
584
|
+
const runtimeConfig = buildEnvironmentRuntimeConfig(config, environment);
|
|
585
|
+
const functionNames = buildFunctionNames(runtimeConfig);
|
|
586
|
+
|
|
587
|
+
return {
|
|
588
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
589
|
+
Description: `S3TE Webiny option stack for ${config.project.name} (${environment})`,
|
|
590
|
+
Parameters: {
|
|
591
|
+
ArtifactBucket: {
|
|
592
|
+
Type: "String"
|
|
593
|
+
},
|
|
594
|
+
ContentMirrorArtifactKey: {
|
|
595
|
+
Type: "String"
|
|
596
|
+
},
|
|
597
|
+
WebinySourceTableStreamArn: {
|
|
598
|
+
Type: "String"
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
Resources: {
|
|
602
|
+
ExecutionRole: createExecutionRole(`${runtimeConfig.stackPrefix}_s3te_webiny_lambda_runtime`),
|
|
603
|
+
ContentMirror: lambdaRuntimeProperties(
|
|
604
|
+
runtimeConfig,
|
|
605
|
+
"ExecutionRole",
|
|
606
|
+
functionNames.contentMirror,
|
|
607
|
+
"ContentMirrorArtifactKey",
|
|
608
|
+
"content-mirror",
|
|
609
|
+
{
|
|
610
|
+
Timeout: 300,
|
|
611
|
+
MemorySize: 512,
|
|
612
|
+
Environment: {
|
|
613
|
+
Variables: {
|
|
614
|
+
S3TE_ENVIRONMENT: environment,
|
|
615
|
+
S3TE_CONTENT_TABLE: runtimeConfig.tables.content,
|
|
616
|
+
S3TE_RELEVANT_MODELS: runtimeConfig.integrations.webiny.relevantModels.join(","),
|
|
617
|
+
S3TE_WEBINY_TENANT: runtimeConfig.integrations.webiny.tenant ?? "",
|
|
618
|
+
S3TE_RENDER_WORKER_NAME: functionNames.renderWorker
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
),
|
|
623
|
+
ContentMirrorEventSourceMapping: {
|
|
624
|
+
Type: "AWS::Lambda::EventSourceMapping",
|
|
625
|
+
Properties: {
|
|
626
|
+
BatchSize: 10,
|
|
627
|
+
StartingPosition: "LATEST",
|
|
628
|
+
EventSourceArn: { Ref: "WebinySourceTableStreamArn" },
|
|
629
|
+
FunctionName: { Ref: "ContentMirror" }
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
Outputs: {
|
|
634
|
+
ContentMirrorFunctionName: {
|
|
635
|
+
Value: functionNames.contentMirror
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
623
641
|
export function buildTemporaryDeployStackTemplate() {
|
|
624
642
|
return {
|
|
625
643
|
AWSTemplateFormatVersion: "2010-09-09",
|
|
@@ -3,10 +3,10 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
configureProjectOption,
|
|
6
7
|
deployProject,
|
|
7
8
|
doctorProject,
|
|
8
9
|
loadResolvedConfig,
|
|
9
|
-
migrateProject,
|
|
10
10
|
packageProject,
|
|
11
11
|
renderProject,
|
|
12
12
|
runProjectTests,
|
|
@@ -86,7 +86,7 @@ function printHelp() {
|
|
|
86
86
|
" sync\n" +
|
|
87
87
|
" deploy\n" +
|
|
88
88
|
" doctor\n" +
|
|
89
|
-
"
|
|
89
|
+
" option <webiny|sitemap>\n"
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -377,30 +377,39 @@ async function main() {
|
|
|
377
377
|
return;
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
-
if (command === "
|
|
380
|
+
if (command === "option") {
|
|
381
|
+
const optionName = String(options._[0] ?? "").trim().toLowerCase();
|
|
382
|
+
if (!optionName) {
|
|
383
|
+
process.stderr.write("option requires a name such as webiny or sitemap\n");
|
|
384
|
+
process.exitCode = 1;
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
381
387
|
const configPath = path.resolve(cwd, options.config ?? "s3te.config.json");
|
|
382
388
|
const rawConfig = JSON.parse(await fs.readFile(configPath, "utf8"));
|
|
383
|
-
const
|
|
389
|
+
const optionResult = await configureProjectOption(configPath, rawConfig, {
|
|
390
|
+
optionName,
|
|
384
391
|
writeChanges: Boolean(options.write) && !Boolean(options["dry-run"]),
|
|
385
392
|
environment: asArray(options.env)[0],
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
webinyTenant: options["webiny-tenant"],
|
|
392
|
-
webinyModels: asArray(options["webiny-model"])
|
|
393
|
+
enable: Boolean(options.enable),
|
|
394
|
+
disable: Boolean(options.disable),
|
|
395
|
+
sourceTable: options["source-table"],
|
|
396
|
+
tenant: options.tenant,
|
|
397
|
+
models: asArray(options.model)
|
|
393
398
|
});
|
|
394
399
|
if (wantsJson) {
|
|
395
|
-
printJson(
|
|
396
|
-
configVersion:
|
|
400
|
+
printJson(`option ${optionName}`, true, [], [], startedAt, {
|
|
401
|
+
configVersion: optionResult.config.configVersion,
|
|
397
402
|
wrote: Boolean(options.write) && !Boolean(options["dry-run"]),
|
|
398
|
-
changes:
|
|
403
|
+
changes: optionResult.changes
|
|
399
404
|
});
|
|
400
405
|
return;
|
|
401
406
|
}
|
|
402
|
-
process.stdout.write(
|
|
403
|
-
|
|
407
|
+
process.stdout.write(
|
|
408
|
+
options.write
|
|
409
|
+
? `Updated option ${optionName} in ${configPath}\n`
|
|
410
|
+
: `Option preview for ${optionName} in ${configPath}: configVersion=${optionResult.config.configVersion}\n`
|
|
411
|
+
);
|
|
412
|
+
for (const change of optionResult.changes) {
|
|
404
413
|
process.stdout.write(`- ${change}\n`);
|
|
405
414
|
}
|
|
406
415
|
return;
|
|
@@ -991,128 +991,129 @@ export async function doctorProject(projectDir, configPath, options = {}) {
|
|
|
991
991
|
return checks;
|
|
992
992
|
}
|
|
993
993
|
|
|
994
|
-
export async function
|
|
995
|
-
const options = typeof
|
|
996
|
-
?
|
|
997
|
-
: { writeChanges };
|
|
994
|
+
export async function configureProjectOption(configPath, rawConfig, optionConfiguration) {
|
|
995
|
+
const options = typeof optionConfiguration === "object" && optionConfiguration !== null
|
|
996
|
+
? optionConfiguration
|
|
997
|
+
: { writeChanges: optionConfiguration };
|
|
998
|
+
const optionName = String(options.optionName ?? "").trim().toLowerCase();
|
|
999
|
+
const targetEnvironment = options.environment ? String(options.environment).trim() : "";
|
|
998
1000
|
const nextConfig = {
|
|
999
1001
|
...rawConfig,
|
|
1000
1002
|
configVersion: rawConfig.configVersion ?? 1
|
|
1001
1003
|
};
|
|
1002
1004
|
const changes = [];
|
|
1003
1005
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
const enableSitemap = Boolean(options.enableSitemap);
|
|
1007
|
-
const disableSitemap = Boolean(options.disableSitemap);
|
|
1008
|
-
const targetEnvironment = options.environment ? String(options.environment).trim() : "";
|
|
1009
|
-
const webinySourceTable = options.webinySourceTable ? String(options.webinySourceTable).trim() : "";
|
|
1010
|
-
const webinyTenant = options.webinyTenant ? String(options.webinyTenant).trim() : "";
|
|
1011
|
-
const webinyModels = normalizeStringList(options.webinyModels);
|
|
1012
|
-
|
|
1013
|
-
if (enableWebiny && disableWebiny) {
|
|
1014
|
-
throw new S3teError("CONFIG_CONFLICT_ERROR", "migrate does not allow --enable-webiny and --disable-webiny at the same time.");
|
|
1006
|
+
if (!optionName) {
|
|
1007
|
+
throw new S3teError("CONFIG_CONFLICT_ERROR", "option requires an optionName such as webiny or sitemap.");
|
|
1015
1008
|
}
|
|
1016
|
-
if (
|
|
1017
|
-
throw new S3teError("CONFIG_CONFLICT_ERROR",
|
|
1009
|
+
if (!["webiny", "sitemap"].includes(optionName)) {
|
|
1010
|
+
throw new S3teError("CONFIG_CONFLICT_ERROR", `Unknown option ${optionName}. Supported options: webiny, sitemap.`);
|
|
1011
|
+
}
|
|
1012
|
+
if (targetEnvironment && !nextConfig.environments?.[targetEnvironment]) {
|
|
1013
|
+
throw new S3teError("CONFIG_CONFLICT_ERROR", `Unknown environment for option ${optionName}: ${targetEnvironment}.`);
|
|
1018
1014
|
}
|
|
1019
1015
|
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1016
|
+
const enable = Boolean(options.enable);
|
|
1017
|
+
const disable = Boolean(options.disable);
|
|
1018
|
+
if (enable && disable) {
|
|
1019
|
+
throw new S3teError("CONFIG_CONFLICT_ERROR", `option ${optionName} does not allow --enable and --disable at the same time.`);
|
|
1020
|
+
}
|
|
1025
1021
|
|
|
1026
|
-
|
|
1027
|
-
const
|
|
1028
|
-
const
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
??
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
: existingWebiny.enabled));
|
|
1044
|
-
const nextSourceTableName = webinySourceTable
|
|
1045
|
-
|| existingTargetWebiny.sourceTableName
|
|
1046
|
-
|| (targetEnvironment ? existingWebiny.sourceTableName : "")
|
|
1047
|
-
|| "";
|
|
1048
|
-
|
|
1049
|
-
if (shouldEnableWebiny && !nextSourceTableName) {
|
|
1050
|
-
throw new S3teError(
|
|
1051
|
-
"CONFIG_CONFLICT_ERROR",
|
|
1052
|
-
targetEnvironment
|
|
1053
|
-
? `Enabling Webiny for environment ${targetEnvironment} requires --webiny-source-table <table> or an existing sourceTableName.`
|
|
1054
|
-
: "Enabling Webiny requires --webiny-source-table <table> or an existing integrations.webiny.sourceTableName."
|
|
1022
|
+
if (optionName === "webiny") {
|
|
1023
|
+
const webinySourceTable = options.sourceTable ? String(options.sourceTable).trim() : "";
|
|
1024
|
+
const webinyTenant = options.tenant ? String(options.tenant).trim() : "";
|
|
1025
|
+
const webinyModels = normalizeStringList(options.models);
|
|
1026
|
+
const touchesWebiny = enable || disable || Boolean(webinySourceTable) || Boolean(webinyTenant) || webinyModels.length > 0;
|
|
1027
|
+
|
|
1028
|
+
if (touchesWebiny) {
|
|
1029
|
+
const existingIntegrations = nextConfig.integrations ?? {};
|
|
1030
|
+
const existingWebiny = existingIntegrations.webiny ?? {};
|
|
1031
|
+
const existingEnvironmentOverrides = existingWebiny.environments ?? {};
|
|
1032
|
+
const existingTargetWebiny = targetEnvironment
|
|
1033
|
+
? (existingEnvironmentOverrides[targetEnvironment] ?? {})
|
|
1034
|
+
: existingWebiny;
|
|
1035
|
+
const inheritedModels = normalizeStringList(
|
|
1036
|
+
existingTargetWebiny.relevantModels
|
|
1037
|
+
?? (targetEnvironment ? existingWebiny.relevantModels : undefined)
|
|
1038
|
+
?? ["staticContent", "staticCodeContent"]
|
|
1055
1039
|
);
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1040
|
+
const shouldEnableWebiny = disable
|
|
1041
|
+
? false
|
|
1042
|
+
: (enable || Boolean(webinySourceTable) || webinyModels.length > 0
|
|
1043
|
+
? true
|
|
1044
|
+
: Boolean(targetEnvironment
|
|
1045
|
+
? (existingTargetWebiny.enabled ?? existingWebiny.enabled)
|
|
1046
|
+
: existingWebiny.enabled));
|
|
1047
|
+
const nextSourceTableName = webinySourceTable
|
|
1048
|
+
|| existingTargetWebiny.sourceTableName
|
|
1049
|
+
|| (targetEnvironment ? existingWebiny.sourceTableName : "")
|
|
1050
|
+
|| "";
|
|
1051
|
+
|
|
1052
|
+
if (shouldEnableWebiny && !nextSourceTableName) {
|
|
1053
|
+
throw new S3teError(
|
|
1054
|
+
"CONFIG_CONFLICT_ERROR",
|
|
1055
|
+
targetEnvironment
|
|
1056
|
+
? `Enabling Webiny for environment ${targetEnvironment} requires --source-table <table> or an existing sourceTableName.`
|
|
1057
|
+
: "Enabling Webiny requires --source-table <table> or an existing integrations.webiny.sourceTableName."
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1070
1060
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1061
|
+
const nextWebinyConfig = {
|
|
1062
|
+
enabled: shouldEnableWebiny,
|
|
1063
|
+
sourceTableName: nextSourceTableName || undefined,
|
|
1064
|
+
mirrorTableName: existingTargetWebiny.mirrorTableName
|
|
1065
|
+
?? (targetEnvironment ? existingWebiny.mirrorTableName : undefined)
|
|
1066
|
+
?? "{stackPrefix}_s3te_content_{project}",
|
|
1067
|
+
tenant: webinyTenant || existingTargetWebiny.tenant || (targetEnvironment ? existingWebiny.tenant : undefined) || undefined,
|
|
1068
|
+
relevantModels: normalizeStringList([
|
|
1069
|
+
...(inheritedModels.length > 0 ? inheritedModels : ["staticContent", "staticCodeContent"]),
|
|
1070
|
+
...webinyModels
|
|
1071
|
+
])
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
nextConfig.integrations = {
|
|
1075
|
+
...existingIntegrations,
|
|
1076
|
+
webiny: targetEnvironment
|
|
1077
|
+
? {
|
|
1078
|
+
...existingWebiny,
|
|
1079
|
+
environments: {
|
|
1080
|
+
...existingEnvironmentOverrides,
|
|
1081
|
+
[targetEnvironment]: nextWebinyConfig
|
|
1082
|
+
}
|
|
1079
1083
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1084
|
+
: {
|
|
1085
|
+
...existingWebiny,
|
|
1086
|
+
...nextWebinyConfig,
|
|
1087
|
+
...(Object.keys(existingEnvironmentOverrides).length > 0
|
|
1088
|
+
? { environments: existingEnvironmentOverrides }
|
|
1089
|
+
: {})
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1087
1092
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1093
|
+
const scopeLabel = targetEnvironment ? ` for environment ${targetEnvironment}` : "";
|
|
1094
|
+
changes.push(shouldEnableWebiny ? `Enabled Webiny option${scopeLabel}.` : `Disabled Webiny option${scopeLabel}.`);
|
|
1095
|
+
if (webinySourceTable) {
|
|
1096
|
+
changes.push(`Set Webiny source table${scopeLabel} to ${webinySourceTable}.`);
|
|
1097
|
+
}
|
|
1098
|
+
if (webinyTenant) {
|
|
1099
|
+
changes.push(`Set Webiny tenant${scopeLabel} to ${webinyTenant}.`);
|
|
1100
|
+
}
|
|
1101
|
+
if (webinyModels.length > 0) {
|
|
1102
|
+
changes.push(`Added Webiny models${scopeLabel}: ${webinyModels.join(", ")}.`);
|
|
1103
|
+
}
|
|
1098
1104
|
}
|
|
1099
1105
|
}
|
|
1100
1106
|
|
|
1101
|
-
|
|
1102
|
-
if (touchesSitemap) {
|
|
1103
|
-
if (targetEnvironment && !nextConfig.environments?.[targetEnvironment]) {
|
|
1104
|
-
throw new S3teError("CONFIG_CONFLICT_ERROR", `Unknown environment for migrate: ${targetEnvironment}.`);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
+
if (optionName === "sitemap") {
|
|
1107
1108
|
const existingIntegrations = nextConfig.integrations ?? {};
|
|
1108
1109
|
const existingSitemap = existingIntegrations.sitemap ?? {};
|
|
1109
1110
|
const existingEnvironmentOverrides = existingSitemap.environments ?? {};
|
|
1110
1111
|
const existingTargetSitemap = targetEnvironment
|
|
1111
1112
|
? (existingEnvironmentOverrides[targetEnvironment] ?? {})
|
|
1112
1113
|
: existingSitemap;
|
|
1113
|
-
const nextEnabled =
|
|
1114
|
+
const nextEnabled = disable
|
|
1114
1115
|
? false
|
|
1115
|
-
: (
|
|
1116
|
+
: (enable || Boolean(targetEnvironment
|
|
1116
1117
|
? (existingTargetSitemap.enabled ?? existingSitemap.enabled)
|
|
1117
1118
|
: existingSitemap.enabled));
|
|
1118
1119
|
|
|
@@ -1132,12 +1133,14 @@ export async function migrateProject(configPath, rawConfig, writeChanges) {
|
|
|
1132
1133
|
: {
|
|
1133
1134
|
...existingSitemap,
|
|
1134
1135
|
enabled: nextEnabled,
|
|
1135
|
-
|
|
1136
|
+
...(Object.keys(existingEnvironmentOverrides).length > 0
|
|
1137
|
+
? { environments: existingEnvironmentOverrides }
|
|
1138
|
+
: {})
|
|
1136
1139
|
}
|
|
1137
1140
|
};
|
|
1138
1141
|
|
|
1139
1142
|
const scopeLabel = targetEnvironment ? ` for environment ${targetEnvironment}` : "";
|
|
1140
|
-
changes.push(nextEnabled ? `Enabled sitemap
|
|
1143
|
+
changes.push(nextEnabled ? `Enabled sitemap option${scopeLabel}.` : `Disabled sitemap option${scopeLabel}.`);
|
|
1141
1144
|
}
|
|
1142
1145
|
|
|
1143
1146
|
if (options.writeChanges) {
|
|
@@ -383,6 +383,10 @@ export function resolveStackName(config, environmentName) {
|
|
|
383
383
|
return `${config.environments[environmentName].stackPrefix}-s3te-${config.project.name}`;
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
+
export function resolveOptionStackName(config, environmentName, optionName) {
|
|
387
|
+
return `${resolveStackName(config, environmentName)}-${String(optionName).trim().toLowerCase()}`;
|
|
388
|
+
}
|
|
389
|
+
|
|
386
390
|
export function buildEnvironmentRuntimeConfig(config, environmentName, stackOutputs = {}) {
|
|
387
391
|
const environmentConfig = config.environments[environmentName];
|
|
388
392
|
const webinyConfig = resolveEnvironmentWebinyIntegration(config, environmentName);
|