sst 2.24.15 → 2.24.16

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.js CHANGED
@@ -291,6 +291,7 @@ async function bootstrapCDK() {
291
291
  AWS_SESSION_TOKEN: credentials.sessionToken,
292
292
  AWS_REGION: region,
293
293
  AWS_PROFILE: profile,
294
+ JSII_SILENCE_WARNING_DEPRECATED_NODE_VERSION: "1",
294
295
  },
295
296
  stdio: "pipe",
296
297
  shell: true,
@@ -17,6 +17,7 @@ export const bind = (program) => program
17
17
  const { Token } = await import("aws-cdk-lib");
18
18
  const { spawn } = await import("child_process");
19
19
  const kill = await import("tree-kill");
20
+ const { exit, exitWithError } = await import("../program.js");
20
21
  const { useProject } = await import("../../project.js");
21
22
  const { Stacks } = await import("../../stacks/index.js");
22
23
  const { useBus } = await import("../../bus.js");
@@ -31,321 +32,329 @@ export const bind = (program) => program
31
32
  import("../../constructs/Config.js"),
32
33
  import("../../constructs/util/functionBinding.js"),
33
34
  ]);
34
- // Handle deprecated "env" command
35
- if (args._[0] === "env") {
36
- Colors.line(Colors.warning(`Warning: ${Colors.bold(`sst env`)} has been renamed to ${Colors.bold(`sst bind`)}`));
37
- }
38
- // Handle missing command
39
- const command = args.command?.join(" ");
40
- if (!command) {
41
- throw new VisibleError("Command is required, e.g. sst bind npm run script");
42
- }
43
- await useIOT();
44
- const bus = useBus();
45
- const project = useProject();
46
- let p;
47
- let timer;
48
- let siteConfigCache;
49
- await buildApp();
50
- const ssrSite = isInSsrSite();
51
- const staticSite = isInStaticSite();
52
- const service = isInService();
53
- // Run the script
54
- if (args.script || (!ssrSite && !staticSite && !service)) {
55
- return await runScript();
56
- }
57
- // Run the app
58
35
  try {
59
- await runSite("init");
60
- }
61
- catch (e) {
62
- if (!(e instanceof MetadataOutdatedError) &&
63
- !(e instanceof MetadataNotFoundError)) {
64
- return;
36
+ // Handle deprecated "env" command
37
+ if (args._[0] === "env") {
38
+ Colors.line(Colors.warning(`Warning: ${Colors.bold(`sst env`)} has been renamed to ${Colors.bold(`sst bind`)}`));
65
39
  }
66
- Colors.line(Colors.warning(e instanceof MetadataOutdatedError
67
- ? "Warning: This was deployed with an old version of SST. Run `sst dev` or `sst deploy` to update."
68
- : "Warning: The site has not been deployed. Some resources might not be available."));
69
- return await runSiteUndeployed();
70
- }
71
- bus.subscribe("stacks.metadata.updated", () => runSite("metadata_updated"));
72
- bus.subscribe("stacks.metadata.deleted", () => runSite("metadata_updated"));
73
- bus.subscribe("config.secret.updated", (payload) => {
74
- const secretName = payload.properties.name;
75
- if (!siteConfigCache.secrets.includes(secretName))
76
- return;
77
- Colors.line(`\n`, `SST secrets have been updated. Restarting \`${command}\`...`);
78
- runSite("secrets_updated");
79
- });
80
- async function buildApp() {
81
- const [_metafile, sstConfig] = await Stacks.load(project.paths.config);
82
- const cwd = process.cwd();
83
- process.chdir(project.paths.root);
84
- await Stacks.synth({
85
- fn: sstConfig.stacks,
86
- mode: "remove",
87
- });
88
- process.chdir(cwd);
89
- }
90
- function isInSsrSite() {
91
- const cwd = process.cwd();
92
- return useSsrSites().all.find(({ props }) => {
93
- return path.resolve(project.paths.root, props.path) === cwd;
94
- });
95
- }
96
- function isInStaticSite() {
97
- const cwd = process.cwd();
98
- return (useStaticSites().all.find(({ props }) => {
99
- return path.resolve(project.paths.root, props.path) === cwd;
100
- }) ||
101
- useSlsNextjsSites().all.find(({ props }) => {
102
- return path.resolve(project.paths.root, props.path) === cwd;
103
- }));
104
- }
105
- function isInService() {
106
- const cwd = process.cwd();
107
- return useServices().all.find(({ props }) => {
108
- return path.resolve(project.paths.root, props.path) === cwd;
109
- });
110
- }
111
- async function runSite(reason) {
112
- const siteConfig = ssrSite
113
- ? await getSsrSiteMetadata()
114
- : staticSite
115
- ? await getStaticSiteMetadata()
116
- : await getServiceMetadata();
117
- // Handle rebind due to metadata updated
118
- if (reason === "metadata_updated") {
119
- if (areEnvsSame(siteConfig.envs, siteConfigCache?.envs || {}))
120
- return;
121
- Colors.line(`\n`, `SST resources have been updated. Restarting \`${command}\`...`);
40
+ // Handle missing command
41
+ const command = args.command?.join(" ");
42
+ if (!command) {
43
+ throw new VisibleError("Command is required, e.g. sst bind npm run script");
122
44
  }
123
- siteConfigCache = siteConfig;
124
- // Assume function's role credentials
125
- // Fallback to use local IAM credentials
126
- const credentials = (siteConfig.role &&
127
- (await getLiveIamCredentials(siteConfig.role))) ||
128
- (await getLocalIamCredentials());
129
- await runCommand({
130
- ...siteConfig.envs,
131
- ...credentials,
132
- });
133
- }
134
- async function runSiteUndeployed() {
135
- // Note: when the site is undeployed:
136
- // - bind all resources
137
- // - bind resources that are constant (ie. Config.Parameter)
138
- // - set environment variables that are constant
139
- const constructEnvs = {};
140
- Object.entries((ssrSite || staticSite || service)?.props.environment || {})
141
- .filter(([key, value]) => !Token.isUnresolved(value))
142
- .forEach(([key, value]) => (constructEnvs[key] = value));
143
- ((ssrSite || service)?.props.bind || []).forEach((b) => {
144
- if (b instanceof Parameter && !Token.isUnresolved(b.value)) {
145
- constructEnvs[getEnvironmentKey(b, "name")] = b.value;
146
- }
147
- });
148
- const { Config } = await import("../../config.js");
149
- await runCommand({
150
- ...constructEnvs,
151
- ...(await Config.env()),
152
- ...(await getLocalIamCredentials()),
153
- });
154
- }
155
- async function runScript() {
156
- const { Config } = await import("../../config.js");
157
- await runCommand({
158
- ...(await Config.env()),
159
- ...(await getLocalIamCredentials()),
160
- });
161
- }
162
- async function getSsrSiteMetadata() {
163
- const [{ metadataForStack }, { LambdaClient, GetFunctionCommand }, { useAWSClient },] = await Promise.all([
164
- import("../../stacks/metadata.js"),
165
- import("@aws-sdk/client-lambda"),
166
- import("../../credentials.js"),
167
- ]);
168
- const metadataData = await metadataForStack(ssrSite.stack);
169
- const metadata = metadataData
170
- ?.filter((c) => [
171
- "NextjsSite",
172
- "AstroSite",
173
- "RemixSite",
174
- "SolidStartSite",
175
- "SvelteKitSite",
176
- ].includes(c.type))
177
- .find((c) => {
178
- // metadata prior to SST v2.3.0 doesn't have path
179
- if (!c.data.path || !c.data.server) {
180
- throw new MetadataOutdatedError();
45
+ await useIOT();
46
+ const bus = useBus();
47
+ const project = useProject();
48
+ let p;
49
+ let timer;
50
+ let siteConfigCache;
51
+ await buildApp();
52
+ const ssrSite = isInSsrSite();
53
+ const staticSite = isInStaticSite();
54
+ const service = isInService();
55
+ // Run the script
56
+ if (args.script || (!ssrSite && !staticSite && !service)) {
57
+ return await runScript();
58
+ }
59
+ // Run the app
60
+ try {
61
+ await runSite("init");
62
+ }
63
+ catch (e) {
64
+ if (!(e instanceof MetadataOutdatedError) &&
65
+ !(e instanceof MetadataNotFoundError)) {
66
+ return;
181
67
  }
182
- return (path.resolve(project.paths.root, c.data.path) === process.cwd());
68
+ Colors.line(Colors.warning(e instanceof MetadataOutdatedError
69
+ ? "Warning: This was deployed with an old version of SST. Run `sst dev` or `sst deploy` to update."
70
+ : "Warning: The site has not been deployed. Some resources might not be available."));
71
+ return await runSiteUndeployed();
72
+ }
73
+ bus.subscribe("stacks.metadata.updated", () => runSite("metadata_updated"));
74
+ bus.subscribe("stacks.metadata.deleted", () => runSite("metadata_updated"));
75
+ bus.subscribe("config.secret.updated", (payload) => {
76
+ const secretName = payload.properties.name;
77
+ if (!siteConfigCache.secrets.includes(secretName))
78
+ return;
79
+ Colors.line(`\n`, `SST secrets have been updated. Restarting \`${command}\`...`);
80
+ runSite("secrets_updated");
183
81
  });
184
- if (!metadata)
185
- throw new MetadataNotFoundError();
186
- // Parse metadata
187
- const lambda = useAWSClient(LambdaClient);
188
- const { Configuration: functionConfig } = await lambda.send(new GetFunctionCommand({
189
- FunctionName: metadata.data.server,
190
- }));
191
- return {
192
- role: functionConfig?.Role,
193
- envs: functionConfig?.Environment?.Variables || {},
194
- secrets: metadata.data.secrets,
195
- };
196
- }
197
- async function getStaticSiteMetadata() {
198
- const { metadataForStack } = await import("../../stacks/metadata.js");
199
- const metadataData = await metadataForStack(staticSite.stack);
200
- const metadata = metadataData
201
- ?.filter((c) => ["StaticSite", "SlsNextjsSite"].includes(c.type))
202
- .find((c) => {
203
- // metadata prior to SST v2.3.0 doesn't have path
204
- if (!c.data.path || !c.data.environment) {
205
- throw new MetadataOutdatedError();
82
+ async function buildApp() {
83
+ const [_metafile, sstConfig] = await Stacks.load(project.paths.config);
84
+ const cwd = process.cwd();
85
+ process.chdir(project.paths.root);
86
+ await Stacks.synth({
87
+ fn: sstConfig.stacks,
88
+ mode: "remove",
89
+ });
90
+ process.chdir(cwd);
91
+ }
92
+ function isInSsrSite() {
93
+ const cwd = process.cwd();
94
+ return useSsrSites().all.find(({ props }) => {
95
+ return path.resolve(project.paths.root, props.path) === cwd;
96
+ });
97
+ }
98
+ function isInStaticSite() {
99
+ const cwd = process.cwd();
100
+ return (useStaticSites().all.find(({ props }) => {
101
+ return path.resolve(project.paths.root, props.path) === cwd;
102
+ }) ||
103
+ useSlsNextjsSites().all.find(({ props }) => {
104
+ return path.resolve(project.paths.root, props.path) === cwd;
105
+ }));
106
+ }
107
+ function isInService() {
108
+ const cwd = process.cwd();
109
+ return useServices().all.find(({ props }) => {
110
+ return path.resolve(project.paths.root, props.path) === cwd;
111
+ });
112
+ }
113
+ async function runSite(reason) {
114
+ const siteConfig = ssrSite
115
+ ? await getSsrSiteMetadata()
116
+ : staticSite
117
+ ? await getStaticSiteMetadata()
118
+ : await getServiceMetadata();
119
+ // Handle rebind due to metadata updated
120
+ if (reason === "metadata_updated") {
121
+ if (areEnvsSame(siteConfig.envs, siteConfigCache?.envs || {}))
122
+ return;
123
+ Colors.line(`\n`, `SST resources have been updated. Restarting \`${command}\`...`);
206
124
  }
207
- return (path.resolve(project.paths.root, c.data.path) === process.cwd());
208
- });
209
- if (!metadata)
210
- throw new MetadataNotFoundError();
211
- return {
212
- envs: metadata.data.environment,
213
- role: undefined,
214
- secrets: [],
215
- };
216
- }
217
- async function getServiceMetadata() {
218
- const [{ metadataForStack }, { LambdaClient, GetFunctionCommand }, { ECSClient, DescribeTaskDefinitionCommand }, { useAWSClient },] = await Promise.all([
219
- import("../../stacks/metadata.js"),
220
- import("@aws-sdk/client-lambda"),
221
- import("@aws-sdk/client-ecs"),
222
- import("../../credentials.js"),
223
- ]);
224
- // Get metadata
225
- const metadataData = await metadataForStack(service.stack);
226
- const metadata = metadataData
227
- ?.filter((c) => ["Service"].includes(c.type))
228
- .find((c) => {
229
- return (path.resolve(project.paths.root, c.data.path) === process.cwd());
230
- });
231
- if (!metadata)
232
- throw new MetadataNotFoundError();
233
- // Parse metadata for "sst deploy"
234
- if (metadata.data.mode === "deployed") {
235
- const ecs = useAWSClient(ECSClient);
236
- const task = await ecs.send(new DescribeTaskDefinitionCommand({
237
- taskDefinition: metadata.data.task,
125
+ siteConfigCache = siteConfig;
126
+ // Assume function's role credentials
127
+ // Fallback to use local IAM credentials
128
+ const credentials = (siteConfig.role &&
129
+ (await getLiveIamCredentials(siteConfig.role))) ||
130
+ (await getLocalIamCredentials());
131
+ await runCommand({
132
+ ...siteConfig.envs,
133
+ ...credentials,
134
+ });
135
+ }
136
+ async function runSiteUndeployed() {
137
+ // Note: when the site is undeployed:
138
+ // - bind all resources
139
+ // - bind resources that are constant (ie. Config.Parameter)
140
+ // - set environment variables that are constant
141
+ const constructEnvs = {};
142
+ Object.entries((ssrSite || staticSite || service)?.props.environment || {})
143
+ .filter(([key, value]) => !Token.isUnresolved(value))
144
+ .forEach(([key, value]) => (constructEnvs[key] = value));
145
+ ((ssrSite || service)?.props.bind || []).forEach((b) => {
146
+ if (b instanceof Parameter && !Token.isUnresolved(b.value)) {
147
+ constructEnvs[getEnvironmentKey(b, "name")] = b.value;
148
+ }
149
+ });
150
+ const { Config } = await import("../../config.js");
151
+ await runCommand({
152
+ ...constructEnvs,
153
+ ...(await Config.env()),
154
+ ...(await getLocalIamCredentials()),
155
+ });
156
+ }
157
+ async function runScript() {
158
+ const { Config } = await import("../../config.js");
159
+ await runCommand({
160
+ ...(await Config.env()),
161
+ ...(await getLocalIamCredentials()),
162
+ });
163
+ }
164
+ async function getSsrSiteMetadata() {
165
+ const [{ metadataForStack }, { LambdaClient, GetFunctionCommand }, { useAWSClient },] = await Promise.all([
166
+ import("../../stacks/metadata.js"),
167
+ import("@aws-sdk/client-lambda"),
168
+ import("../../credentials.js"),
169
+ ]);
170
+ const metadataData = await metadataForStack(ssrSite.stack);
171
+ const metadata = metadataData
172
+ ?.filter((c) => [
173
+ "NextjsSite",
174
+ "AstroSite",
175
+ "RemixSite",
176
+ "SolidStartSite",
177
+ "SvelteKitSite",
178
+ ].includes(c.type))
179
+ .find((c) => {
180
+ // metadata prior to SST v2.3.0 doesn't have path
181
+ if (!c.data.path || !c.data.server) {
182
+ throw new MetadataOutdatedError();
183
+ }
184
+ return (path.resolve(project.paths.root, c.data.path) ===
185
+ process.cwd());
186
+ });
187
+ if (!metadata)
188
+ throw new MetadataNotFoundError();
189
+ // Parse metadata
190
+ const lambda = useAWSClient(LambdaClient);
191
+ const { Configuration: functionConfig } = await lambda.send(new GetFunctionCommand({
192
+ FunctionName: metadata.data.server,
238
193
  }));
239
- const envs = {};
240
- (task?.taskDefinition?.containerDefinitions[0].environment || []).forEach(({ name, value }) => (envs[name] = value));
241
194
  return {
242
- role: task?.taskDefinition?.taskRoleArn,
243
- envs,
195
+ role: functionConfig?.Role,
196
+ envs: functionConfig?.Environment?.Variables || {},
244
197
  secrets: metadata.data.secrets,
245
198
  };
246
199
  }
247
- // Parse metadata for "sst dev"
248
- const lambda = useAWSClient(LambdaClient);
249
- const { Configuration: functionConfig } = await lambda.send(new GetFunctionCommand({
250
- FunctionName: metadata.data.devFunction,
251
- }));
252
- return {
253
- role: functionConfig?.Role,
254
- envs: functionConfig?.Environment?.Variables || {},
255
- secrets: metadata.data.secrets,
256
- };
257
- }
258
- async function getLiveIamCredentials(roleArn) {
259
- const credentials = await assumeSsrRole(roleArn);
260
- if (!credentials)
261
- return;
262
- // refresh crecentials 1 minute before expiration
263
- const expireAt = credentials.Expiration.getTime() - 60000;
264
- clearTimeout(timer);
265
- timer = setTimeout(() => {
266
- Colors.line(`\n`, `Your AWS session is about to expire. Creating a new session and restarting \`${command}\`...`);
267
- runSite("iam_expired");
268
- }, expireAt - Date.now());
269
- return {
270
- AWS_ACCESS_KEY_ID: credentials.AccessKeyId,
271
- AWS_SECRET_ACCESS_KEY: credentials.SecretAccessKey,
272
- AWS_SESSION_TOKEN: credentials.SessionToken,
273
- };
274
- }
275
- async function getLocalIamCredentials() {
276
- const { useAWSCredentials } = await import("../../credentials.js");
277
- const credentials = await useAWSCredentials();
278
- return {
279
- AWS_ACCESS_KEY_ID: credentials.accessKeyId,
280
- AWS_SECRET_ACCESS_KEY: credentials.secretAccessKey,
281
- AWS_SESSION_TOKEN: credentials.sessionToken,
282
- };
283
- }
284
- async function runCommand(envs) {
285
- Colors.gap();
286
- if (p) {
287
- p.removeAllListeners("exit");
288
- // Note: calling p.kill() does not kill child processes. And in the
289
- // cases of Next.js and CRA, servers are child processes. Need to
290
- // kill the entire process tree to free up port ie. 3000.
291
- await new Promise((resolve, reject) => {
292
- kill.default(p?.pid, (error) => {
293
- if (error) {
294
- return reject(error);
295
- }
296
- resolve(true);
297
- });
200
+ async function getStaticSiteMetadata() {
201
+ const { metadataForStack } = await import("../../stacks/metadata.js");
202
+ const metadataData = await metadataForStack(staticSite.stack);
203
+ const metadata = metadataData
204
+ ?.filter((c) => ["StaticSite", "SlsNextjsSite"].includes(c.type))
205
+ .find((c) => {
206
+ // metadata prior to SST v2.3.0 doesn't have path
207
+ if (!c.data.path || !c.data.environment) {
208
+ throw new MetadataOutdatedError();
209
+ }
210
+ return (path.resolve(project.paths.root, c.data.path) ===
211
+ process.cwd());
298
212
  });
213
+ if (!metadata)
214
+ throw new MetadataNotFoundError();
215
+ return {
216
+ envs: metadata.data.environment,
217
+ role: undefined,
218
+ secrets: [],
219
+ };
299
220
  }
300
- p = spawn(command, {
301
- env: {
302
- ...process.env,
303
- ...envs,
304
- AWS_REGION: project.config.region,
305
- },
306
- stdio: "inherit",
307
- shell: true,
308
- });
309
- p.on("exit", (code) => {
310
- process.exit(code || 0);
311
- });
312
- }
313
- function areEnvsSame(envs1, envs2) {
314
- return (Object.keys(envs1).length === Object.keys(envs2).length &&
315
- Object.keys(envs1).every((key) => envs1[key] === envs2[key]));
316
- }
317
- async function assumeSsrRole(roleArn) {
318
- const { STSClient, AssumeRoleCommand } = await import("@aws-sdk/client-sts");
319
- const { useAWSClient } = await import("../../credentials.js");
320
- const sts = useAWSClient(STSClient);
321
- const assumeRole = async (duration) => {
322
- const { Credentials: credentials } = await sts.send(new AssumeRoleCommand({
323
- RoleArn: roleArn,
324
- RoleSessionName: "dev-session",
325
- DurationSeconds: duration,
221
+ async function getServiceMetadata() {
222
+ const [{ metadataForStack }, { LambdaClient, GetFunctionCommand }, { ECSClient, DescribeTaskDefinitionCommand }, { useAWSClient },] = await Promise.all([
223
+ import("../../stacks/metadata.js"),
224
+ import("@aws-sdk/client-lambda"),
225
+ import("@aws-sdk/client-ecs"),
226
+ import("../../credentials.js"),
227
+ ]);
228
+ // Get metadata
229
+ const metadataData = await metadataForStack(service.stack);
230
+ const metadata = metadataData
231
+ ?.filter((c) => ["Service"].includes(c.type))
232
+ .find((c) => {
233
+ return (path.resolve(project.paths.root, c.data.path) ===
234
+ process.cwd());
235
+ });
236
+ if (!metadata)
237
+ throw new MetadataNotFoundError();
238
+ // Parse metadata for "sst deploy"
239
+ if (metadata.data.mode === "deployed") {
240
+ const ecs = useAWSClient(ECSClient);
241
+ const task = await ecs.send(new DescribeTaskDefinitionCommand({
242
+ taskDefinition: metadata.data.task,
243
+ }));
244
+ const envs = {};
245
+ (task?.taskDefinition?.containerDefinitions[0].environment || []).forEach(({ name, value }) => (envs[name] = value));
246
+ return {
247
+ role: task?.taskDefinition?.taskRoleArn,
248
+ envs,
249
+ secrets: metadata.data.secrets,
250
+ };
251
+ }
252
+ // Parse metadata for "sst dev"
253
+ const lambda = useAWSClient(LambdaClient);
254
+ const { Configuration: functionConfig } = await lambda.send(new GetFunctionCommand({
255
+ FunctionName: metadata.data.devFunction,
326
256
  }));
327
- return credentials;
328
- };
329
- // Assume role with max duration first. This can fail if chaining roles, or if
330
- // the role has a max duration set. If it fails, assume role with 1 hour duration.
331
- let err;
332
- try {
333
- return await assumeRole(43200);
257
+ return {
258
+ role: functionConfig?.Role,
259
+ envs: functionConfig?.Environment?.Variables || {},
260
+ secrets: metadata.data.secrets,
261
+ };
334
262
  }
335
- catch (e) {
336
- err = e;
263
+ async function getLiveIamCredentials(roleArn) {
264
+ const credentials = await assumeSsrRole(roleArn);
265
+ if (!credentials)
266
+ return;
267
+ // refresh crecentials 1 minute before expiration
268
+ const expireAt = credentials.Expiration.getTime() - 60000;
269
+ clearTimeout(timer);
270
+ timer = setTimeout(() => {
271
+ Colors.line(`\n`, `Your AWS session is about to expire. Creating a new session and restarting \`${command}\`...`);
272
+ runSite("iam_expired");
273
+ }, expireAt - Date.now());
274
+ return {
275
+ AWS_ACCESS_KEY_ID: credentials.AccessKeyId,
276
+ AWS_SECRET_ACCESS_KEY: credentials.SecretAccessKey,
277
+ AWS_SESSION_TOKEN: credentials.SessionToken,
278
+ };
279
+ }
280
+ async function getLocalIamCredentials() {
281
+ const { useAWSCredentials } = await import("../../credentials.js");
282
+ const credentials = await useAWSCredentials();
283
+ return {
284
+ AWS_ACCESS_KEY_ID: credentials.accessKeyId,
285
+ AWS_SECRET_ACCESS_KEY: credentials.secretAccessKey,
286
+ AWS_SESSION_TOKEN: credentials.sessionToken,
287
+ };
337
288
  }
338
- if (err.name === "ValidationError" &&
339
- err.message.startsWith("The requested DurationSeconds exceeds")) {
289
+ async function runCommand(envs) {
290
+ Colors.gap();
291
+ if (p) {
292
+ p.removeAllListeners("exit");
293
+ // Note: calling p.kill() does not kill child processes. And in the
294
+ // cases of Next.js and CRA, servers are child processes. Need to
295
+ // kill the entire process tree to free up port ie. 3000.
296
+ await new Promise((resolve, reject) => {
297
+ kill.default(p?.pid, (error) => {
298
+ if (error) {
299
+ return reject(error);
300
+ }
301
+ resolve(true);
302
+ });
303
+ });
304
+ }
305
+ p = spawn(command, {
306
+ env: {
307
+ ...process.env,
308
+ ...envs,
309
+ AWS_REGION: project.config.region,
310
+ },
311
+ stdio: "inherit",
312
+ shell: true,
313
+ });
314
+ p.on("exit", (code) => {
315
+ exit(code || 0);
316
+ });
317
+ }
318
+ function areEnvsSame(envs1, envs2) {
319
+ return (Object.keys(envs1).length === Object.keys(envs2).length &&
320
+ Object.keys(envs1).every((key) => envs1[key] === envs2[key]));
321
+ }
322
+ async function assumeSsrRole(roleArn) {
323
+ const { STSClient, AssumeRoleCommand } = await import("@aws-sdk/client-sts");
324
+ const { useAWSClient } = await import("../../credentials.js");
325
+ const sts = useAWSClient(STSClient);
326
+ const assumeRole = async (duration) => {
327
+ const { Credentials: credentials } = await sts.send(new AssumeRoleCommand({
328
+ RoleArn: roleArn,
329
+ RoleSessionName: "dev-session",
330
+ DurationSeconds: duration,
331
+ }));
332
+ return credentials;
333
+ };
334
+ // Assume role with max duration first. This can fail if chaining roles, or if
335
+ // the role has a max duration set. If it fails, assume role with 1 hour duration.
336
+ let err;
340
337
  try {
341
- return await assumeRole(3600);
338
+ return await assumeRole(43200);
342
339
  }
343
340
  catch (e) {
344
341
  err = e;
345
342
  }
343
+ if (err.name === "ValidationError" &&
344
+ err.message.startsWith("The requested DurationSeconds exceeds")) {
345
+ try {
346
+ return await assumeRole(3600);
347
+ }
348
+ catch (e) {
349
+ err = e;
350
+ }
351
+ }
352
+ Colors.line("Using local IAM credentials since `sst dev` is not running.");
353
+ Logger.debug(`Failed to assume ${roleArn}.`, err);
346
354
  }
347
- Colors.line("Using local IAM credentials since `sst dev` is not running.");
348
- Logger.debug(`Failed to assume ${roleArn}.`, err);
355
+ }
356
+ catch (e) {
357
+ await exitWithError(e);
349
358
  }
350
359
  })
351
360
  .strict(false);
@@ -1,5 +1,11 @@
1
1
  export const bootstrap = (program) => program.command("bootstrap", "Create the SST bootstrap stack", (yargs) => yargs, async () => {
2
+ const { exit, exitWithError } = await import("../program.js");
2
3
  const { useBootstrap } = await import("../../bootstrap.js");
3
- await useBootstrap();
4
- process.exit(0);
4
+ try {
5
+ await useBootstrap();
6
+ await exit();
7
+ }
8
+ catch (e) {
9
+ await exitWithError(e);
10
+ }
5
11
  });