sst 2.0.0-rc.8 → 2.0.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/bootstrap.d.ts +2 -4
- package/bootstrap.js +208 -61
- package/bus.d.ts +3 -3
- package/bus.js +1 -1
- package/cache.js +1 -1
- package/cdk/cloudformation-deployments-wrapper.d.ts +3 -0
- package/cdk/cloudformation-deployments-wrapper.js +117 -0
- package/cdk/cloudformation-deployments.d.ts +19 -4
- package/cdk/deploy-stack.d.ts +17 -2
- package/cdk/deploy-stack.js +6 -1
- package/cli/colors.d.ts +28 -0
- package/cli/colors.js +27 -0
- package/cli/commands/bind.d.ts +4 -0
- package/cli/commands/bind.js +10 -7
- package/cli/commands/bootstrap.d.ts +13 -0
- package/cli/commands/bootstrap.js +11 -0
- package/cli/commands/build.d.ts +6 -2
- package/cli/commands/build.js +13 -8
- package/cli/commands/console.d.ts +5 -1
- package/cli/commands/console.js +8 -6
- package/cli/commands/deploy.d.ts +4 -2
- package/cli/commands/deploy.js +71 -29
- package/cli/commands/dev.d.ts +5 -1
- package/cli/commands/dev.js +157 -79
- package/cli/commands/diff.d.ts +15 -0
- package/cli/commands/diff.js +61 -0
- package/cli/commands/env.d.ts +5 -1
- package/cli/commands/env.js +14 -15
- package/cli/commands/plugins/kysely.js +13 -5
- package/cli/commands/plugins/pothos.js +9 -4
- package/cli/commands/remove.d.ts +4 -0
- package/cli/commands/remove.js +26 -18
- package/cli/commands/secrets/get.d.ts +7 -3
- package/cli/commands/secrets/get.js +11 -24
- package/cli/commands/secrets/list.d.ts +6 -2
- package/cli/commands/secrets/list.js +15 -7
- package/cli/commands/secrets/remove.d.ts +8 -4
- package/cli/commands/secrets/remove.js +18 -16
- package/cli/commands/secrets/secrets.js +3 -3
- package/cli/commands/secrets/set.d.ts +8 -6
- package/cli/commands/secrets/set.js +16 -33
- package/cli/commands/telemetry.d.ts +15 -0
- package/cli/commands/telemetry.js +17 -0
- package/cli/commands/transform.d.ts +15 -0
- package/cli/commands/transform.js +55 -0
- package/cli/commands/update.d.ts +6 -2
- package/cli/commands/update.js +41 -33
- package/cli/commands/version.d.ts +13 -0
- package/cli/commands/version.js +7 -0
- package/cli/local/router.d.ts +6 -6
- package/cli/local/router.js +3 -3
- package/cli/local/server.d.ts +5 -3
- package/cli/local/server.js +21 -14
- package/cli/program.d.ts +5 -1
- package/cli/program.js +29 -5
- package/cli/spinner.js +2 -0
- package/cli/sst.js +35 -23
- package/cli/telemetry/environment.d.ts +1 -1
- package/cli/telemetry/environment.js +1 -1
- package/cli/terminal.d.ts +1 -0
- package/cli/terminal.js +8 -0
- package/cli/ui/deploy.d.ts +3 -2
- package/cli/ui/deploy.js +106 -106
- package/cli/ui/functions.d.ts +2 -0
- package/cli/ui/functions.js +132 -0
- package/cli/ui/header.d.ts +5 -0
- package/cli/ui/header.js +16 -0
- package/cli/ui/stack.d.ts +1 -0
- package/cli/ui/stack.js +6 -0
- package/config.d.ts +1 -1
- package/config.js +8 -8
- package/constructs/Api.d.ts +3 -71
- package/constructs/Api.js +3 -42
- package/constructs/ApiGatewayV1Api.d.ts +3 -3
- package/constructs/ApiGatewayV1Api.js +4 -3
- package/constructs/App.d.ts +2 -22
- package/constructs/App.js +12 -25
- package/constructs/AppSyncApi.d.ts +4 -5
- package/constructs/AppSyncApi.js +8 -5
- package/constructs/AstroSite.d.ts +2 -3
- package/constructs/AstroSite.js +43 -53
- package/constructs/Auth.js +27 -16
- package/constructs/BaseSite.d.ts +10 -10
- package/constructs/BaseSite.js +2 -2
- package/constructs/Cognito.js +28 -28
- package/constructs/EdgeFunction.d.ts +6 -4
- package/constructs/EdgeFunction.js +56 -59
- package/constructs/EventBus.js +1 -1
- package/constructs/Function.d.ts +4 -44
- package/constructs/Function.js +14 -50
- package/constructs/FunctionalStack.d.ts +2 -2
- package/constructs/Job.d.ts +23 -63
- package/constructs/Job.js +11 -32
- package/constructs/Metadata.d.ts +18 -20
- package/constructs/NextjsSite.d.ts +21 -5
- package/constructs/NextjsSite.js +67 -101
- package/constructs/Queue.js +10 -10
- package/constructs/RDS.d.ts +1 -1
- package/constructs/RDS.js +1 -1
- package/constructs/RemixSite.d.ts +0 -2
- package/constructs/RemixSite.js +20 -20
- package/constructs/Script.js +1 -1
- package/constructs/Secret.js +5 -3
- package/constructs/SolidStartSite.d.ts +2 -3
- package/constructs/SolidStartSite.js +42 -52
- package/constructs/SsrFunction.d.ts +22 -0
- package/constructs/SsrFunction.js +113 -0
- package/constructs/SsrSite.d.ts +104 -104
- package/constructs/SsrSite.js +196 -217
- package/constructs/Stack.d.ts +1 -20
- package/constructs/Stack.js +3 -65
- package/constructs/StaticSite.d.ts +33 -53
- package/constructs/StaticSite.js +92 -118
- package/constructs/Table.d.ts +1 -1
- package/constructs/Table.js +4 -4
- package/constructs/Topic.js +1 -1
- package/constructs/cdk/certificate-base.d.ts +18 -0
- package/constructs/cdk/certificate-base.js +26 -0
- package/constructs/cdk/dns-validated-certificate.d.ts +77 -0
- package/constructs/cdk/dns-validated-certificate.js +125 -0
- package/constructs/cdk/website-redirect.d.ts +53 -0
- package/constructs/cdk/website-redirect.js +77 -0
- package/constructs/deferred_task.d.ts +1 -1
- package/constructs/deprecated/NextjsSite.js +8 -6
- package/constructs/index.d.ts +0 -6
- package/constructs/index.js +0 -6
- package/constructs/util/apiGatewayV1AccessLog.d.ts +1 -1
- package/constructs/util/apiGatewayV2AccessLog.js +4 -4
- package/constructs/util/apiGatewayV2Domain.js +9 -2
- package/constructs/util/appSyncApiDomain.d.ts +1 -1
- package/constructs/util/appSyncApiDomain.js +9 -15
- package/constructs/util/duration.d.ts +1 -1
- package/constructs/util/functionBinding.js +1 -1
- package/constructs/util/permission.d.ts +3 -3
- package/constructs/util/permission.js +16 -18
- package/constructs/util/size.d.ts +1 -1
- package/constructs/util/warning.js +2 -2
- package/context/context.d.ts +2 -2
- package/context/context.js +4 -4
- package/context/handler.d.ts +1 -1
- package/credentials.d.ts +1 -1
- package/credentials.js +42 -6
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/iot.js +6 -2
- package/logger.js +3 -1
- package/node/api/index.d.ts +4 -3
- package/node/api/index.js +8 -2
- package/node/auth/adapter/adapter.d.ts +1 -1
- package/node/auth/adapter/facebook.js +1 -1
- package/node/auth/adapter/github.js +4 -4
- package/node/auth/adapter/google.d.ts +1 -1
- package/node/auth/adapter/google.js +2 -2
- package/node/auth/adapter/twitch.js +1 -1
- package/node/auth/session.d.ts +1 -1
- package/node/config/index.d.ts +2 -2
- package/node/config/index.js +3 -8
- package/node/job/index.d.ts +2 -2
- package/node/site/index.js +3 -5
- package/package.json +26 -21
- package/pothos.js +1 -0
- package/project.d.ts +47 -0
- package/{app.js → project.js} +60 -53
- package/runtime/handlers/dotnet.d.ts +1 -1
- package/runtime/handlers/dotnet.js +4 -4
- package/runtime/handlers/go.d.ts +1 -1
- package/runtime/handlers/go.js +3 -3
- package/runtime/handlers/java.d.ts +1 -1
- package/runtime/handlers/java.js +4 -4
- package/runtime/handlers/node.d.ts +1 -1
- package/runtime/handlers/node.js +60 -39
- package/runtime/handlers/python.d.ts +1 -1
- package/runtime/handlers/python.js +3 -3
- package/runtime/handlers.js +15 -12
- package/runtime/iot.js +3 -0
- package/runtime/runtime.d.ts +4 -0
- package/runtime/server.d.ts +3 -3
- package/runtime/server.js +11 -5
- package/runtime/workers.d.ts +4 -2
- package/runtime/workers.js +13 -3
- package/site-env.js +2 -1
- package/sst.mjs +6357 -20846
- package/stacks/app-metadata.d.ts +7 -0
- package/stacks/app-metadata.js +78 -0
- package/stacks/assembly.d.ts +1 -0
- package/stacks/assembly.js +4 -0
- package/stacks/build.d.ts +1 -1
- package/stacks/build.js +41 -48
- package/stacks/deploy.d.ts +1 -0
- package/stacks/deploy.js +128 -7
- package/stacks/diff.d.ts +8 -0
- package/stacks/diff.js +62 -0
- package/stacks/index.d.ts +3 -0
- package/stacks/index.js +3 -0
- package/stacks/metadata.d.ts +2 -0
- package/stacks/metadata.js +14 -12
- package/stacks/monitor.d.ts +8 -3
- package/stacks/monitor.js +38 -4
- package/stacks/remove.js +0 -4
- package/stacks/synth.d.ts +1 -0
- package/stacks/synth.js +27 -13
- package/support/base-site-archiver.mjs +1 -1
- package/support/bootstrap-metadata-function/index.mjs +68740 -0
- package/support/bridge/bridge.mjs +28 -28
- package/support/certificate-requestor/index.js +549 -0
- package/support/custom-resources/index.mjs +3824 -26567
- package/support/dotnet6-bootstrap/release/dotnet-bootstrap.deps.json +1 -1
- package/support/dotnet6-bootstrap/release/dotnet-bootstrap.runtimeconfig.json +1 -1
- package/support/edge-function/edge-lambda.mjs +2 -2
- package/support/job-invoker/index.mjs +26 -0
- package/support/nodejs-runtime/index.mjs +75 -72
- package/support/rds-migrator/index.mjs +22 -23
- package/support/remix-site-function/edge-server.js +1 -1
- package/support/script-function/index.mjs +30485 -0
- package/support/{edge-function-code-replacer → sls-nextjs-site-function-code-replacer}/lambda-code-updater.py +0 -0
- package/support/ssr-site-function-archiver.mjs +96 -0
- package/util/process.d.ts +0 -1
- package/watcher.js +1 -1
- package/app.d.ts +0 -36
- package/constructs/DebugApp.d.ts +0 -49
- package/constructs/DebugApp.js +0 -63
- package/constructs/DebugStack.d.ts +0 -34
- package/constructs/DebugStack.js +0 -132
- package/constructs/GraphQLApi.d.ts +0 -98
- package/constructs/GraphQLApi.js +0 -80
- package/constructs/ReactStaticSite.d.ts +0 -20
- package/constructs/ReactStaticSite.js +0 -54
- package/constructs/Script/cfn-response.d.ts +0 -19
- package/constructs/Script/cfn-response.js +0 -77
- package/constructs/Script/index.d.ts +0 -1
- package/constructs/Script/index.js +0 -78
- package/constructs/Script/outbound.d.ts +0 -10
- package/constructs/Script/outbound.js +0 -42
- package/constructs/Script/util.d.ts +0 -2
- package/constructs/Script/util.js +0 -11
- package/constructs/ViteStaticSite.d.ts +0 -32
- package/constructs/ViteStaticSite.js +0 -66
- package/support/astro-site-html-stub/index.html +0 -99
- package/support/base-site-archiver.cjs +0 -116
- package/support/nextjs-site-html-stub/index.html +0 -99
- package/support/remix-site-html-stub/index.html +0 -99
- package/support/solid-start-site-html-stub/index.html +0 -99
- package/support/static-site-stub/index.html +0 -90
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface Metadata {
|
|
2
|
+
mode: "deploy" | "dev";
|
|
3
|
+
}
|
|
4
|
+
export declare function saveAppMetadata(data: Metadata): Promise<void>;
|
|
5
|
+
export declare function clearAppMetadata(): Promise<void>;
|
|
6
|
+
export declare const useAppMetadata: () => Promise<Metadata | undefined>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useBootstrap } from "../bootstrap.js";
|
|
2
|
+
import { useAWSCredentials } from "../credentials.js";
|
|
3
|
+
import { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, } from "@aws-sdk/client-s3";
|
|
4
|
+
import { json } from "stream/consumers";
|
|
5
|
+
import { Context } from "../context/context.js";
|
|
6
|
+
import { Logger } from "../logger.js";
|
|
7
|
+
import { useProject } from "../project.js";
|
|
8
|
+
async function metadata() {
|
|
9
|
+
Logger.debug("Fetching app metadata");
|
|
10
|
+
const [project, credentials, bootstrap] = await Promise.all([
|
|
11
|
+
useProject(),
|
|
12
|
+
useAWSCredentials(),
|
|
13
|
+
useBootstrap(),
|
|
14
|
+
]);
|
|
15
|
+
const s3 = new S3Client({
|
|
16
|
+
region: project.config.region,
|
|
17
|
+
credentials,
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
const result = await s3
|
|
21
|
+
.send(new GetObjectCommand({
|
|
22
|
+
Key: useS3Key(),
|
|
23
|
+
Bucket: bootstrap.bucket,
|
|
24
|
+
}))
|
|
25
|
+
.then((result) => json(result.Body));
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
catch (ex) {
|
|
29
|
+
Logger.debug("Fetching app metadata: not found");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function saveAppMetadata(data) {
|
|
33
|
+
Logger.debug("Saving app metadata");
|
|
34
|
+
const [project, credentials, bootstrap] = await Promise.all([
|
|
35
|
+
useProject(),
|
|
36
|
+
useAWSCredentials(),
|
|
37
|
+
useBootstrap(),
|
|
38
|
+
]);
|
|
39
|
+
const s3 = new S3Client({
|
|
40
|
+
region: project.config.region,
|
|
41
|
+
credentials,
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
await s3.send(new PutObjectCommand({
|
|
45
|
+
Key: useS3Key(),
|
|
46
|
+
Bucket: bootstrap.bucket,
|
|
47
|
+
Body: JSON.stringify(data),
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
catch (ex) {
|
|
51
|
+
Logger.debug("Saving app metadata: not found");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function clearAppMetadata() {
|
|
55
|
+
Logger.debug("Clearing app metadata");
|
|
56
|
+
const [project, credentials, bootstrap] = await Promise.all([
|
|
57
|
+
useProject(),
|
|
58
|
+
useAWSCredentials(),
|
|
59
|
+
useBootstrap(),
|
|
60
|
+
]);
|
|
61
|
+
const s3 = new S3Client({
|
|
62
|
+
region: project.config.region,
|
|
63
|
+
credentials,
|
|
64
|
+
});
|
|
65
|
+
await s3.send(new DeleteObjectCommand({
|
|
66
|
+
Key: useS3Key(),
|
|
67
|
+
Bucket: bootstrap.bucket,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
function useS3Key() {
|
|
71
|
+
const project = useProject();
|
|
72
|
+
return `appMetadata/app.${project.config.name}/stage.${project.config.stage}.json`;
|
|
73
|
+
}
|
|
74
|
+
const MetadataContext = Context.create(async () => {
|
|
75
|
+
const data = await metadata();
|
|
76
|
+
return data;
|
|
77
|
+
});
|
|
78
|
+
export const useAppMetadata = MetadataContext.use;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadAssembly(from: string): Promise<import("aws-cdk-lib/cx-api").CloudAssembly>;
|
package/stacks/build.d.ts
CHANGED
package/stacks/build.js
CHANGED
|
@@ -1,60 +1,53 @@
|
|
|
1
1
|
import esbuild from "esbuild";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { Logger } from "../logger.js";
|
|
5
|
-
import { useBus } from "../bus.js";
|
|
6
|
-
import { useProject } from "../app.js";
|
|
7
4
|
import { dynamicImport } from "../util/module.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
Logger.debug("Running esbuild on", project.main, "to", outfile);
|
|
16
|
-
const result = await esbuild.build({
|
|
17
|
-
keepNames: true,
|
|
18
|
-
bundle: true,
|
|
19
|
-
sourcemap: "inline",
|
|
20
|
-
platform: "node",
|
|
21
|
-
target: "esnext",
|
|
22
|
-
metafile: true,
|
|
23
|
-
format: "esm",
|
|
24
|
-
external: [
|
|
25
|
-
"aws-cdk-lib",
|
|
26
|
-
"sst",
|
|
27
|
-
...Object.keys({
|
|
28
|
-
...pkg.devDependencies,
|
|
29
|
-
...pkg.dependencies,
|
|
30
|
-
...pkg.peerDependencies,
|
|
31
|
-
}),
|
|
32
|
-
],
|
|
33
|
-
absWorkingDir: project.paths.root,
|
|
34
|
-
outfile,
|
|
35
|
-
banner: {
|
|
36
|
-
js: [
|
|
37
|
-
`import { createRequire as topLevelCreateRequire } from 'module';`,
|
|
38
|
-
`const require = topLevelCreateRequire(import.meta.url);`,
|
|
39
|
-
].join(""),
|
|
40
|
-
},
|
|
41
|
-
// The entry can have any file name (ie. "stacks/anything.ts"). We want the
|
|
42
|
-
// build output to be always named "lib/index.js". This allow us to always
|
|
43
|
-
// import from "buildDir" without needing to pass "anything" around.
|
|
44
|
-
entryPoints: [project.main],
|
|
45
|
-
});
|
|
46
|
-
Logger.debug("Finished esbuild");
|
|
47
|
-
Logger.debug("Sourcing stacks");
|
|
5
|
+
import { findAbove } from "../util/fs.js";
|
|
6
|
+
import { VisibleError } from "../error.js";
|
|
7
|
+
export async function load(input) {
|
|
8
|
+
const parsed = path.parse(input);
|
|
9
|
+
const root = await findAbove(input, "package.json");
|
|
10
|
+
const outfile = path.join(parsed.dir, `.${parsed.name}.${Date.now()}.mjs`);
|
|
11
|
+
const pkg = JSON.parse(await fs.readFile(path.join(root, "package.json")).then((x) => x.toString()));
|
|
48
12
|
try {
|
|
13
|
+
const result = await esbuild.build({
|
|
14
|
+
keepNames: true,
|
|
15
|
+
bundle: true,
|
|
16
|
+
sourcemap: "inline",
|
|
17
|
+
platform: "node",
|
|
18
|
+
target: "esnext",
|
|
19
|
+
metafile: true,
|
|
20
|
+
format: "esm",
|
|
21
|
+
logLevel: "silent",
|
|
22
|
+
external: [
|
|
23
|
+
"aws-cdk-lib",
|
|
24
|
+
"sst",
|
|
25
|
+
...Object.keys({
|
|
26
|
+
...pkg.devDependencies,
|
|
27
|
+
...pkg.dependencies,
|
|
28
|
+
...pkg.peerDependencies,
|
|
29
|
+
}),
|
|
30
|
+
],
|
|
31
|
+
absWorkingDir: root,
|
|
32
|
+
outfile,
|
|
33
|
+
banner: {
|
|
34
|
+
js: [
|
|
35
|
+
`import { createRequire as topLevelCreateRequire } from 'module';`,
|
|
36
|
+
`const require = topLevelCreateRequire(import.meta.url);`,
|
|
37
|
+
].join(""),
|
|
38
|
+
},
|
|
39
|
+
// The entry can have any file name (ie. "stacks/anything.ts"). We want the
|
|
40
|
+
// build output to be always named "lib/index.js". This allow us to always
|
|
41
|
+
// import from "buildDir" without needing to pass "anything" around.
|
|
42
|
+
entryPoints: [input],
|
|
43
|
+
});
|
|
49
44
|
const mod = await dynamicImport(outfile);
|
|
50
|
-
Logger.debug("Finished sourcing stacks");
|
|
51
45
|
await fs.rm(outfile, {
|
|
52
46
|
force: true,
|
|
53
47
|
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return mod.default;
|
|
48
|
+
if (!mod.default?.config)
|
|
49
|
+
throw new VisibleError(`The config file is improperly formatted.`, `Example:`, `export default {`, ` config() {`, ` return {`, ` name: "my-app",`, ` region: "us-east-1"`, ` }`, ` },`, ` stacks(app) {`, ` }`, `}`);
|
|
50
|
+
return [result.metafile, mod.default];
|
|
58
51
|
}
|
|
59
52
|
catch (e) {
|
|
60
53
|
await fs.rm(outfile, {
|
package/stacks/deploy.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CloudFormationStackArtifact } from "aws-cdk-lib/cx-api";
|
|
2
2
|
import { StackDeploymentResult } from "./monitor.js";
|
|
3
|
+
export declare function publishAssets(stacks: CloudFormationStackArtifact[]): Promise<Record<string, any>>;
|
|
3
4
|
export declare function deployMany(stacks: CloudFormationStackArtifact[]): Promise<Record<string, {
|
|
4
5
|
status: string;
|
|
5
6
|
outputs: Record<string, string>;
|
package/stacks/deploy.js
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { useBus } from "../bus.js";
|
|
2
|
-
import { useAWSProvider } from "../credentials.js";
|
|
2
|
+
import { useAWSClient, useAWSProvider } from "../credentials.js";
|
|
3
3
|
import { Logger } from "../logger.js";
|
|
4
|
-
import { isFailed, monitor } from "./monitor.js";
|
|
4
|
+
import { filterOutputs, isFailed, monitor, } from "./monitor.js";
|
|
5
|
+
export async function publishAssets(stacks) {
|
|
6
|
+
Logger.debug("Publishing assets");
|
|
7
|
+
const provider = await useAWSProvider();
|
|
8
|
+
const { publishDeployAssets } = await import("../cdk/cloudformation-deployments-wrapper.js");
|
|
9
|
+
const results = {};
|
|
10
|
+
for (const stack of stacks) {
|
|
11
|
+
const result = await publishDeployAssets(provider, {
|
|
12
|
+
stack: stack,
|
|
13
|
+
quiet: false,
|
|
14
|
+
deploymentMethod: {
|
|
15
|
+
method: "direct",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
results[stack.stackName] = result;
|
|
19
|
+
}
|
|
20
|
+
return results;
|
|
21
|
+
}
|
|
5
22
|
export async function deployMany(stacks) {
|
|
6
23
|
Logger.debug("Deploying stacks", stacks.map((s) => s.stackName));
|
|
7
24
|
const { CloudFormationStackArtifact } = await import("aws-cdk-lib/cx-api");
|
|
@@ -9,11 +26,7 @@ export async function deployMany(stacks) {
|
|
|
9
26
|
const bus = useBus();
|
|
10
27
|
const complete = new Set();
|
|
11
28
|
const todo = new Set(stacks.map((s) => s.id));
|
|
12
|
-
const pending = new Set();
|
|
13
29
|
const results = {};
|
|
14
|
-
bus.subscribe("stack.updated", (evt) => {
|
|
15
|
-
pending.add(evt.properties.stackID);
|
|
16
|
-
});
|
|
17
30
|
return new Promise((resolve) => {
|
|
18
31
|
async function trigger() {
|
|
19
32
|
for (const stack of stacks) {
|
|
@@ -62,6 +75,7 @@ export async function deploy(stack) {
|
|
|
62
75
|
sdkProvider: provider,
|
|
63
76
|
});
|
|
64
77
|
try {
|
|
78
|
+
await addInUseExports(stack);
|
|
65
79
|
const result = await deployment.deployStack({
|
|
66
80
|
stack: stack,
|
|
67
81
|
quiet: true,
|
|
@@ -76,7 +90,7 @@ export async function deploy(stack) {
|
|
|
76
90
|
});
|
|
77
91
|
return {
|
|
78
92
|
errors: {},
|
|
79
|
-
outputs: result.outputs,
|
|
93
|
+
outputs: filterOutputs(result.outputs),
|
|
80
94
|
status: "SKIPPED",
|
|
81
95
|
};
|
|
82
96
|
}
|
|
@@ -103,3 +117,110 @@ export async function deploy(stack) {
|
|
|
103
117
|
};
|
|
104
118
|
}
|
|
105
119
|
}
|
|
120
|
+
async function addInUseExports(stack) {
|
|
121
|
+
// Get old outputs
|
|
122
|
+
const oldOutputs = await getCloudFormationStackOutputs(stack);
|
|
123
|
+
if (!oldOutputs)
|
|
124
|
+
return;
|
|
125
|
+
// Get new exports
|
|
126
|
+
// note: that we only want to handle outputs exported by CDK.
|
|
127
|
+
// ie.
|
|
128
|
+
// "Outputs": {
|
|
129
|
+
// "ExportsOutputRefauthUserPoolA78B038B8D9965B5": {
|
|
130
|
+
// "Value": {
|
|
131
|
+
// "Ref": "authUserPoolA78B038B"
|
|
132
|
+
// },
|
|
133
|
+
// "Export": {
|
|
134
|
+
// "Name": "frank-acme-auth:ExportsOutputRefauthUserPoolA78B038B8D9965B5"
|
|
135
|
+
// }
|
|
136
|
+
// },
|
|
137
|
+
const newTemplate = JSON.parse(await getLocalTemplate(stack));
|
|
138
|
+
const newOutputs = newTemplate.Outputs || {};
|
|
139
|
+
const newExportNames = Object.keys(newOutputs)
|
|
140
|
+
.filter((outputKey) => outputKey.startsWith("ExportsOutput"))
|
|
141
|
+
.filter((outputKey) => newOutputs[outputKey].Export)
|
|
142
|
+
.map((outputKey) => newOutputs[outputKey].Export.Name);
|
|
143
|
+
// Add missing exports
|
|
144
|
+
// ie.
|
|
145
|
+
// Outputs [{
|
|
146
|
+
// OutputKey: (String)
|
|
147
|
+
// OutputValue: (String)
|
|
148
|
+
// Description: (String)
|
|
149
|
+
// ExportName: (String)
|
|
150
|
+
// }]
|
|
151
|
+
let isDirty = false;
|
|
152
|
+
await Promise.all(oldOutputs
|
|
153
|
+
.filter((output) => output.OutputKey?.startsWith("ExportsOutput"))
|
|
154
|
+
.filter((output) => output.ExportName)
|
|
155
|
+
// filter exports not in the new template (ie. CloudFormation will be removing)
|
|
156
|
+
.filter((output) => !newExportNames.includes(output.ExportName))
|
|
157
|
+
// filter the exports still in-use by other stacks
|
|
158
|
+
.map(async (output) => {
|
|
159
|
+
const imports = await listImports(output.ExportName);
|
|
160
|
+
// update template
|
|
161
|
+
if (imports.length > 0) {
|
|
162
|
+
Logger.debug(`deploy stack: addInUseExports: export ${output.ExportName} used in ${imports.join(", ")}`);
|
|
163
|
+
newTemplate.Outputs = newTemplate.Outputs || {};
|
|
164
|
+
newTemplate.Outputs[output.OutputKey] = {
|
|
165
|
+
Description: `Output added by SST b/c exported value still used in ${imports.join(", ")}`,
|
|
166
|
+
Value: output.OutputValue,
|
|
167
|
+
Export: {
|
|
168
|
+
Name: output.ExportName,
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
isDirty = true;
|
|
172
|
+
}
|
|
173
|
+
}));
|
|
174
|
+
// Save new template
|
|
175
|
+
if (isDirty) {
|
|
176
|
+
await saveLocalTemplate(stack, JSON.stringify(newTemplate, null, 2));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function getCloudFormationStackOutputs(stack) {
|
|
180
|
+
const { CloudFormationClient, DescribeStacksCommand } = await import("@aws-sdk/client-cloudformation");
|
|
181
|
+
const client = useAWSClient(CloudFormationClient);
|
|
182
|
+
try {
|
|
183
|
+
const { Stacks: stacks } = await client.send(new DescribeStacksCommand({
|
|
184
|
+
StackName: stack.id,
|
|
185
|
+
}));
|
|
186
|
+
if (!stacks || stacks.length === 0)
|
|
187
|
+
return;
|
|
188
|
+
return stacks[0].Outputs || [];
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
if (e.name === "ValidationError" &&
|
|
192
|
+
e.message.includes("Stack with id") &&
|
|
193
|
+
e.message.includes("does not exist")) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw e;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async function listImports(exportName) {
|
|
202
|
+
const { CloudFormationClient, ListImportsCommand } = await import("@aws-sdk/client-cloudformation");
|
|
203
|
+
const client = useAWSClient(CloudFormationClient);
|
|
204
|
+
try {
|
|
205
|
+
const ret = await client.send(new ListImportsCommand({
|
|
206
|
+
ExportName: exportName,
|
|
207
|
+
}));
|
|
208
|
+
return ret.Imports || [];
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
if (e.name === "ValidationError" &&
|
|
212
|
+
e.message.includes("is not imported by any stack")) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
throw e;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function getLocalTemplate(stack) {
|
|
219
|
+
const fs = await import("fs/promises");
|
|
220
|
+
const fileContent = await fs.readFile(stack.templateFullPath);
|
|
221
|
+
return fileContent.toString();
|
|
222
|
+
}
|
|
223
|
+
async function saveLocalTemplate(stack, content) {
|
|
224
|
+
const fs = await import("fs/promises");
|
|
225
|
+
await fs.writeFile(stack.templateFullPath, content);
|
|
226
|
+
}
|
package/stacks/diff.d.ts
ADDED
package/stacks/diff.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export async function diff(stack, oldTemplate) {
|
|
2
|
+
const { diffTemplate, formatDifferences, TemplateDiff } = await import("@aws-cdk/cloudformation-diff");
|
|
3
|
+
// Generate diff
|
|
4
|
+
const diff = diffTemplate(oldTemplate, stack.template);
|
|
5
|
+
if (diff.isEmpty) {
|
|
6
|
+
return { count: 0 };
|
|
7
|
+
}
|
|
8
|
+
// Only display resource and output changes
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
diff.iamChanges = { hasChanges: false };
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
diff.securityGroupChanges = { hasChanges: false };
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
diff.awsTemplateFormatVersion = false;
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
diff.transform = false;
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
diff.description = false;
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
diff.parameters = { differenceCount: 0 };
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
diff.metadata = { differenceCount: 0 };
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
diff.mappings = { differenceCount: 0 };
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
diff.conditions = { differenceCount: 0 };
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
diff.unknown = { differenceCount: 0 };
|
|
29
|
+
// Filter out SST internal diffs
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
delete diff.outputs.diffs?.["SSTMetadata"];
|
|
32
|
+
// Format diff
|
|
33
|
+
const output = [];
|
|
34
|
+
const stream = {
|
|
35
|
+
write(chunk) {
|
|
36
|
+
output.push(` ${chunk}`);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
const pathMap = await buildLogicalToPathMap(stack);
|
|
40
|
+
formatDifferences(stream, diff, pathMap);
|
|
41
|
+
// Remove trailing newline
|
|
42
|
+
while (true) {
|
|
43
|
+
if (output[output.length - 1].match(/^\s*$/)) {
|
|
44
|
+
output.pop();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
count: diff.outputs.differenceCount + diff.resources.differenceCount,
|
|
52
|
+
diff: output.join(""),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function buildLogicalToPathMap(stack) {
|
|
56
|
+
const { ArtifactMetadataEntryType } = await import("@aws-cdk/cloud-assembly-schema");
|
|
57
|
+
const map = {};
|
|
58
|
+
for (const md of stack.findMetadataByType(ArtifactMetadataEntryType.LOGICAL_ID)) {
|
|
59
|
+
map[md.data] = md.path;
|
|
60
|
+
}
|
|
61
|
+
return map;
|
|
62
|
+
}
|
package/stacks/index.d.ts
CHANGED
package/stacks/index.js
CHANGED
package/stacks/metadata.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { Metadata } from "../constructs/Metadata.js";
|
|
|
2
2
|
declare module "../bus.js" {
|
|
3
3
|
interface Events {
|
|
4
4
|
"stacks.metadata": Awaited<ReturnType<typeof metadata>>;
|
|
5
|
+
"stacks.metadata.updated": {};
|
|
6
|
+
"stacks.metadata.deleted": {};
|
|
5
7
|
}
|
|
6
8
|
}
|
|
7
9
|
export declare function metadata(): Promise<Record<string, Metadata[]>>;
|
package/stacks/metadata.js
CHANGED
|
@@ -5,9 +5,8 @@ import { json } from "stream/consumers";
|
|
|
5
5
|
import { useCache } from "../cache.js";
|
|
6
6
|
import { Context } from "../context/context.js";
|
|
7
7
|
import { useBus } from "../bus.js";
|
|
8
|
-
import { Stacks } from "./index.js";
|
|
9
8
|
import { Logger } from "../logger.js";
|
|
10
|
-
import { useProject } from "../
|
|
9
|
+
import { useProject } from "../project.js";
|
|
11
10
|
export async function metadata() {
|
|
12
11
|
Logger.debug("Fetching all metadata");
|
|
13
12
|
const project = useProject();
|
|
@@ -16,10 +15,10 @@ export async function metadata() {
|
|
|
16
15
|
useBootstrap(),
|
|
17
16
|
]);
|
|
18
17
|
const s3 = new S3Client({
|
|
19
|
-
region: project.region,
|
|
18
|
+
region: project.config.region,
|
|
20
19
|
credentials: credentials,
|
|
21
20
|
});
|
|
22
|
-
const key = `stackMetadata/app.${project.name}/stage.${project.stage}/`;
|
|
21
|
+
const key = `stackMetadata/app.${project.config.name}/stage.${project.config.stage}/`;
|
|
23
22
|
const list = await s3.send(new ListObjectsV2Command({
|
|
24
23
|
Prefix: key,
|
|
25
24
|
Bucket: bootstrap.bucket,
|
|
@@ -44,10 +43,10 @@ export async function metadataForStack(stackID) {
|
|
|
44
43
|
useBootstrap(),
|
|
45
44
|
]);
|
|
46
45
|
const s3 = new S3Client({
|
|
47
|
-
region: project.region,
|
|
46
|
+
region: project.config.region,
|
|
48
47
|
credentials: credentials,
|
|
49
48
|
});
|
|
50
|
-
const key = `stackMetadata/app.${project.name}/stage.${project.stage}/stack.${stackID}.json`;
|
|
49
|
+
const key = `stackMetadata/app.${project.config.name}/stage.${project.config.stage}/stack.${stackID}.json`;
|
|
51
50
|
Logger.debug("Getting metadata", key, "from", bootstrap.bucket);
|
|
52
51
|
try {
|
|
53
52
|
const result = await s3
|
|
@@ -67,12 +66,15 @@ const MetadataContext = Context.create(async () => {
|
|
|
67
66
|
const bus = useBus();
|
|
68
67
|
const cache = await useCache();
|
|
69
68
|
const data = await metadata();
|
|
70
|
-
bus.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
bus.publish("stacks.metadata", data);
|
|
70
|
+
bus.subscribe("stacks.metadata.updated", async () => {
|
|
71
|
+
const data = await metadata();
|
|
72
|
+
await cache.write(`metadata.json`, JSON.stringify(data));
|
|
73
|
+
bus.publish("stacks.metadata", data);
|
|
74
|
+
MetadataContext.provide(Promise.resolve(data));
|
|
75
|
+
});
|
|
76
|
+
bus.subscribe("stacks.metadata.deleted", async () => {
|
|
77
|
+
const data = await metadata();
|
|
76
78
|
await cache.write(`metadata.json`, JSON.stringify(data));
|
|
77
79
|
bus.publish("stacks.metadata", data);
|
|
78
80
|
MetadataContext.provide(Promise.resolve(data));
|
package/stacks/monitor.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DescribeStackResourcesOutput } from "@aws-sdk/client-cloudformation";
|
|
1
|
+
import { DescribeStackResourcesOutput, StackEvent } from "@aws-sdk/client-cloudformation";
|
|
2
2
|
declare module "../bus.js" {
|
|
3
3
|
interface Events {
|
|
4
4
|
"stack.updated": {
|
|
@@ -6,12 +6,16 @@ declare module "../bus.js" {
|
|
|
6
6
|
};
|
|
7
7
|
"stack.status": {
|
|
8
8
|
stackID: string;
|
|
9
|
-
status: typeof STATUSES[number];
|
|
9
|
+
status: (typeof STATUSES)[number];
|
|
10
10
|
};
|
|
11
11
|
"stack.resources": {
|
|
12
12
|
stackID: string;
|
|
13
13
|
resources: DescribeStackResourcesOutput["StackResources"];
|
|
14
14
|
};
|
|
15
|
+
"stack.event": {
|
|
16
|
+
stackID: string;
|
|
17
|
+
event: StackEvent;
|
|
18
|
+
};
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
export declare const STATUSES: readonly ["CREATE_IN_PROGRESS", "DELETE_IN_PROGRESS", "REVIEW_IN_PROGRESS", "ROLLBACK_IN_PROGRESS", "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_IN_PROGRESS", "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_ROLLBACK_IN_PROGRESS", "CREATE_COMPLETE", "UPDATE_COMPLETE", "DELETE_COMPLETE", "SKIPPED", "CREATE_FAILED", "DELETE_FAILED", "ROLLBACK_FAILED", "ROLLBACK_COMPLETE", "UPDATE_FAILED", "UPDATE_ROLLBACK_COMPLETE", "UPDATE_ROLLBACK_FAILED", "DEPENDENCY_FAILED"];
|
|
@@ -24,4 +28,5 @@ export declare function monitor(stack: string): Promise<{
|
|
|
24
28
|
outputs: Record<string, string>;
|
|
25
29
|
errors: Record<string, string>;
|
|
26
30
|
}>;
|
|
27
|
-
export declare
|
|
31
|
+
export declare function filterOutputs(input: Record<string, string>): Record<string, string>;
|
|
32
|
+
export type StackDeploymentResult = Awaited<ReturnType<typeof monitor>>;
|
package/stacks/monitor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CloudFormationClient, DescribeStackResourcesCommand, DescribeStacksCommand, } from "@aws-sdk/client-cloudformation";
|
|
1
|
+
import { CloudFormationClient, DescribeStackResourcesCommand, DescribeStacksCommand, DescribeStackEventsCommand, } from "@aws-sdk/client-cloudformation";
|
|
2
2
|
import { useBus } from "../bus.js";
|
|
3
3
|
import { useAWSClient } from "../credentials.js";
|
|
4
4
|
import { Logger } from "../logger.js";
|
|
@@ -53,23 +53,51 @@ export async function monitor(stack) {
|
|
|
53
53
|
]);
|
|
54
54
|
let lastStatus;
|
|
55
55
|
const errors = {};
|
|
56
|
+
let lastEvent;
|
|
56
57
|
while (true) {
|
|
57
58
|
try {
|
|
58
|
-
const [describe, resources] = await Promise.all([
|
|
59
|
+
const [describe, resources, events] = await Promise.all([
|
|
59
60
|
cfn.send(new DescribeStacksCommand({
|
|
60
61
|
StackName: stack,
|
|
61
62
|
})),
|
|
62
63
|
cfn.send(new DescribeStackResourcesCommand({
|
|
63
64
|
StackName: stack,
|
|
64
65
|
})),
|
|
66
|
+
cfn.send(new DescribeStackEventsCommand({
|
|
67
|
+
StackName: stack,
|
|
68
|
+
})),
|
|
65
69
|
]);
|
|
66
70
|
Logger.debug("Stack description", describe);
|
|
71
|
+
if (lastEvent) {
|
|
72
|
+
const eventsReversed = [...(events.StackEvents ?? [])].reverse();
|
|
73
|
+
for (const event of eventsReversed) {
|
|
74
|
+
if (!event.Timestamp)
|
|
75
|
+
continue;
|
|
76
|
+
if (event.Timestamp.getTime() > lastEvent.getTime()) {
|
|
77
|
+
bus.publish("stack.event", {
|
|
78
|
+
event: event,
|
|
79
|
+
stackID: stack,
|
|
80
|
+
});
|
|
81
|
+
if (event.ResourceStatusReason) {
|
|
82
|
+
if (event.ResourceStatusReason.includes("Resource creation cancelled") ||
|
|
83
|
+
event.ResourceStatusReason.includes("Resource update cancelled") ||
|
|
84
|
+
event.ResourceStatusReason?.includes("Resource creation Initiated"))
|
|
85
|
+
continue;
|
|
86
|
+
errors[event.LogicalResourceId] = event.ResourceStatusReason;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
Logger.debug("Last event set to", lastEvent);
|
|
91
|
+
}
|
|
92
|
+
lastEvent = events.StackEvents?.at(0)?.Timestamp;
|
|
67
93
|
bus.publish("stack.resources", {
|
|
68
94
|
stackID: stack,
|
|
69
95
|
resources: resources.StackResources,
|
|
70
96
|
});
|
|
71
97
|
for (const resource of resources.StackResources || []) {
|
|
72
|
-
if (resource.ResourceStatusReason?.includes("Resource creation cancelled")
|
|
98
|
+
if (resource.ResourceStatusReason?.includes("Resource creation cancelled") ||
|
|
99
|
+
resource.ResourceStatusReason?.includes("Resource update cancelled") ||
|
|
100
|
+
resource.ResourceStatusReason?.includes("Resource creation Initiated"))
|
|
73
101
|
continue;
|
|
74
102
|
if (resource.ResourceStatusReason)
|
|
75
103
|
errors[resource.LogicalResourceId] = resource.ResourceStatusReason;
|
|
@@ -86,7 +114,7 @@ export async function monitor(stack) {
|
|
|
86
114
|
if (isFinal(first.StackStatus)) {
|
|
87
115
|
return {
|
|
88
116
|
status: first.StackStatus,
|
|
89
|
-
outputs:
|
|
117
|
+
outputs: pipe(first.Outputs || [], map((o) => [o.OutputKey, o.OutputValue]), Object.fromEntries, filterOutputs),
|
|
90
118
|
errors: isFailed(first.StackStatus) ? errors : {},
|
|
91
119
|
};
|
|
92
120
|
}
|
|
@@ -109,3 +137,9 @@ export async function monitor(stack) {
|
|
|
109
137
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
110
138
|
}
|
|
111
139
|
}
|
|
140
|
+
import { map, omitBy, pipe } from "remeda";
|
|
141
|
+
export function filterOutputs(input) {
|
|
142
|
+
return pipe(input, omitBy((_, key) => {
|
|
143
|
+
return key.startsWith("Export") || key === "SSTMetadata";
|
|
144
|
+
}));
|
|
145
|
+
}
|
package/stacks/remove.js
CHANGED
|
@@ -8,11 +8,7 @@ export async function removeMany(stacks) {
|
|
|
8
8
|
const bus = useBus();
|
|
9
9
|
const complete = new Set();
|
|
10
10
|
const todo = new Set(stacks.map((s) => s.id));
|
|
11
|
-
const pending = new Set();
|
|
12
11
|
const results = {};
|
|
13
|
-
bus.subscribe("stack.updated", (evt) => {
|
|
14
|
-
pending.add(evt.properties.stackID);
|
|
15
|
-
});
|
|
16
12
|
return new Promise((resolve) => {
|
|
17
13
|
async function trigger() {
|
|
18
14
|
for (const stack of stacks) {
|