@pwrdrvr/microapps-publish 0.0.20 → 0.0.24

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.
Files changed (73) hide show
  1. package/bin/run +5 -0
  2. package/dist/commands/nextjs-docker-auto.d.ts +42 -0
  3. package/dist/commands/nextjs-docker-auto.d.ts.map +1 -0
  4. package/dist/commands/nextjs-docker-auto.js +448 -0
  5. package/dist/commands/nextjs-docker-auto.js.map +1 -0
  6. package/dist/commands/nextjs-version-restore.d.ts +14 -0
  7. package/dist/commands/nextjs-version-restore.d.ts.map +1 -0
  8. package/dist/commands/nextjs-version-restore.js +57 -0
  9. package/dist/commands/nextjs-version-restore.js.map +1 -0
  10. package/dist/commands/nextjs-version.d.ts +16 -0
  11. package/dist/commands/nextjs-version.d.ts.map +1 -0
  12. package/dist/commands/nextjs-version.js +93 -0
  13. package/dist/commands/nextjs-version.js.map +1 -0
  14. package/dist/commands/preflight.d.ts +15 -0
  15. package/dist/commands/preflight.d.ts.map +1 -0
  16. package/dist/commands/preflight.js +111 -0
  17. package/dist/commands/preflight.js.map +1 -0
  18. package/dist/commands/publish-static.d.ts +18 -0
  19. package/dist/commands/publish-static.d.ts.map +1 -0
  20. package/dist/commands/publish-static.js +256 -0
  21. package/dist/commands/publish-static.js.map +1 -0
  22. package/dist/commands/publish.d.ts +25 -0
  23. package/dist/commands/publish.d.ts.map +1 -0
  24. package/dist/commands/publish.js +316 -0
  25. package/dist/commands/publish.js.map +1 -0
  26. package/dist/config/Config.d.ts +0 -2
  27. package/dist/config/Config.d.ts.map +1 -1
  28. package/dist/config/Config.js +0 -5
  29. package/dist/config/Config.js.map +1 -1
  30. package/dist/index.d.ts +1 -64
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +3 -480
  33. package/dist/index.js.map +1 -1
  34. package/dist/{DeployClient.d.ts → lib/DeployClient.d.ts} +14 -6
  35. package/dist/lib/DeployClient.d.ts.map +1 -0
  36. package/dist/{DeployClient.js → lib/DeployClient.js} +17 -6
  37. package/dist/lib/DeployClient.js.map +1 -0
  38. package/dist/{S3TransferUtility.d.ts → lib/S3TransferUtility.d.ts} +0 -0
  39. package/dist/lib/S3TransferUtility.d.ts.map +1 -0
  40. package/dist/{S3TransferUtility.js → lib/S3TransferUtility.js} +0 -0
  41. package/dist/lib/S3TransferUtility.js.map +1 -0
  42. package/dist/{S3Uploader.d.ts → lib/S3Uploader.d.ts} +1 -1
  43. package/dist/lib/S3Uploader.d.ts.map +1 -0
  44. package/dist/{S3Uploader.js → lib/S3Uploader.js} +10 -10
  45. package/dist/lib/S3Uploader.js.map +1 -0
  46. package/dist/lib/Versions.d.ts +27 -0
  47. package/dist/lib/Versions.d.ts.map +1 -0
  48. package/dist/lib/Versions.js +76 -0
  49. package/dist/lib/Versions.js.map +1 -0
  50. package/package.json +11 -3
  51. package/src/commands/nextjs-docker-auto.ts +538 -0
  52. package/src/commands/nextjs-version-restore.ts +74 -0
  53. package/src/commands/nextjs-version.ts +114 -0
  54. package/src/commands/preflight.ts +122 -0
  55. package/src/commands/publish-static.ts +302 -0
  56. package/src/commands/publish.ts +381 -0
  57. package/src/config/Config.ts +0 -4
  58. package/src/index.ts +1 -596
  59. package/src/{DeployClient.ts → lib/DeployClient.ts} +24 -13
  60. package/src/{S3TransferUtility.ts → lib/S3TransferUtility.ts} +0 -0
  61. package/src/{S3Uploader.ts → lib/S3Uploader.ts} +3 -3
  62. package/src/lib/Versions.ts +95 -0
  63. package/dist/DeployClient.d.ts.map +0 -1
  64. package/dist/DeployClient.js.map +0 -1
  65. package/dist/S3TransferUtility.d.ts.map +0 -1
  66. package/dist/S3TransferUtility.js.map +0 -1
  67. package/dist/S3Uploader.d.ts.map +0 -1
  68. package/dist/S3Uploader.js.map +0 -1
  69. package/dist/config/FileStore.d.ts +0 -7
  70. package/dist/config/FileStore.d.ts.map +0 -1
  71. package/dist/config/FileStore.js +0 -17
  72. package/dist/config/FileStore.js.map +0 -1
  73. package/src/config/FileStore.ts +0 -14
@@ -0,0 +1,381 @@
1
+ import 'reflect-metadata';
2
+ import * as util from 'util';
3
+ import * as lambda from '@aws-sdk/client-lambda';
4
+ import * as s3 from '@aws-sdk/client-s3';
5
+ import * as sts from '@aws-sdk/client-sts';
6
+ import { Command, flags as flagsParser } from '@oclif/command';
7
+ import * as chalk from 'chalk';
8
+ import * as path from 'path';
9
+ import { pathExists, createReadStream } from 'fs-extra';
10
+ import { Listr, ListrTask } from 'listr2';
11
+ import { Config, IConfig } from '../config/Config';
12
+ import DeployClient, { IDeployVersionPreflightResult } from '../lib/DeployClient';
13
+ import S3Uploader from '../lib/S3Uploader';
14
+ import S3TransferUtility from '../lib/S3TransferUtility';
15
+ import { Upload } from '@aws-sdk/lib-storage';
16
+ import { contentType } from 'mime-types';
17
+ import { TaskWrapper } from 'listr2/dist/lib/task-wrapper';
18
+ import { DefaultRenderer } from 'listr2/dist/renderer/default.renderer';
19
+ import { createVersions, IVersions } from '../lib/Versions';
20
+ const asyncSetTimeout = util.promisify(setTimeout);
21
+
22
+ const lambdaClient = new lambda.LambdaClient({
23
+ maxAttempts: 8,
24
+ });
25
+
26
+ interface IContext {
27
+ preflightResult: IDeployVersionPreflightResult;
28
+ files: string[];
29
+ }
30
+
31
+ export class PublishCommand extends Command {
32
+ static description =
33
+ 'Publish arbitrary framework app - deploy static assets to S3, alias the $LATEST Lambda function, and add route to API Gateway.';
34
+
35
+ static examples = [
36
+ `$ microapps-publish publish -d microapps-deployer-dev -n 0.0.21 -l microapps-app-release-dev -a release
37
+ ✔ Get S3 Temp Credentials [1s]
38
+ ✔ Deploy to Lambda [0.6s]
39
+ ✔ Confirm Static Assets Folder Exists [0.0s]
40
+ ✔ Copy Static Files to Local Upload Dir [0.0s]
41
+ ✔ Enumerate Files to Upload to S3 [0.0s]
42
+ ✔ Upload Static Files to S3 [1s]
43
+ ✔ Creating MicroApp Application: release [0.0s]
44
+ ✔ Creating MicroApp Version: 0.0.21 [1s]
45
+ `,
46
+ ];
47
+
48
+ static flags = {
49
+ version: flagsParser.version({
50
+ char: 'v',
51
+ }),
52
+ help: flagsParser.help(),
53
+ deployerLambdaName: flagsParser.string({
54
+ char: 'd',
55
+ multiple: false,
56
+ required: true,
57
+ description: 'Name of the deployer lambda function',
58
+ }),
59
+ newVersion: flagsParser.string({
60
+ char: 'n',
61
+ multiple: false,
62
+ required: true,
63
+ description: 'New semantic version to apply',
64
+ }),
65
+ appLambdaName: flagsParser.string({
66
+ char: 'l',
67
+ multiple: false,
68
+ required: false,
69
+ description: 'Name of the application lambda function',
70
+ }),
71
+ appName: flagsParser.string({
72
+ char: 'a',
73
+ multiple: false,
74
+ required: false,
75
+ description: 'MicroApps app name',
76
+ }),
77
+ staticAssetsPath: flagsParser.string({
78
+ char: 's',
79
+ multiple: false,
80
+ required: false,
81
+ description:
82
+ 'Path to files to be uploaded to S3 static bucket at app/version/ path. Do include app/version/ in path if files are already "rooted" under that path locally.',
83
+ }),
84
+ defaultFile: flagsParser.string({
85
+ char: 'i',
86
+ multiple: false,
87
+ required: false,
88
+ description:
89
+ 'Default file to return when the app is loaded via the router without a version (e.g. when app/ is requested).',
90
+ }),
91
+ };
92
+
93
+ private VersionAndAlias: IVersions;
94
+
95
+ async run(): Promise<void> {
96
+ const config = Config.instance;
97
+
98
+ // const RUNNING_TEXT = ' RUNS ';
99
+ // const RUNNING = chalk.reset.inverse.yellow.bold(RUNNING_TEXT) + ' ';
100
+ const RUNNING = ''; //chalk.reset.inverse.yellow.bold(RUNNING_TEXT) + ' ';
101
+
102
+ const { flags: parsedFlags } = this.parse(PublishCommand);
103
+ const appLambdaName = parsedFlags.appLambdaName ?? config.app.lambdaName;
104
+ const appName = parsedFlags.appName ?? config.app.name;
105
+ const deployerLambdaName = parsedFlags.deployerLambdaName ?? config.deployer.lambdaName;
106
+ const semVer = parsedFlags.newVersion ?? config.app.semVer;
107
+ const staticAssetsPath = parsedFlags.staticAssetsPath ?? config.app.staticAssetsPath;
108
+ const defaultFile = parsedFlags.defaultFile ?? config.app.defaultFile;
109
+
110
+ // Override the config value
111
+ config.deployer.lambdaName = deployerLambdaName;
112
+ config.app.lambdaName = appLambdaName;
113
+ config.app.name = appName;
114
+ config.app.semVer = semVer;
115
+ config.app.staticAssetsPath = staticAssetsPath;
116
+ config.app.defaultFile = defaultFile;
117
+
118
+ // Get the account ID and region from STS
119
+ // TODO: Move this to the right place
120
+ if (config.app.awsAccountID === 0 || config.app.awsRegion === '') {
121
+ const stsClient = new sts.STSClient({
122
+ maxAttempts: 8,
123
+ });
124
+ const stsResponse = await stsClient.send(new sts.GetCallerIdentityCommand({}));
125
+ if (config.app.awsAccountID === 0) {
126
+ config.app.awsAccountID = parseInt(stsResponse.Account, 10);
127
+ }
128
+ if (config.app.awsRegion === '') {
129
+ config.app.awsRegion = stsClient.config.region as string;
130
+ }
131
+ }
132
+
133
+ this.VersionAndAlias = createVersions(semVer);
134
+
135
+ if (config === undefined) {
136
+ this.error('Failed to load the config file');
137
+ }
138
+ if (config.app.staticAssetsPath === undefined) {
139
+ this.error('StaticAssetsPath must be specified in the config file');
140
+ }
141
+
142
+ //
143
+ // Setup Tasks
144
+ //
145
+
146
+ const tasks = new Listr<IContext>(
147
+ [
148
+ {
149
+ // TODO: Disable this task if no static assets path
150
+ title: 'Get S3 Temp Credentials',
151
+ task: async (ctx, task) => {
152
+ const origTitle = task.title;
153
+ task.title = RUNNING + origTitle;
154
+
155
+ // Confirm the Version Does Not Exist in Published State
156
+ task.output = `Checking if deployed app/version already exists for ${config.app.name}/${semVer}`;
157
+ ctx.preflightResult = await DeployClient.DeployVersionPreflight({
158
+ config,
159
+ output: (message: string) => (task.output = message),
160
+ });
161
+ if (ctx.preflightResult.exists) {
162
+ task.output = `Warning: App/Version already exists: ${config.app.name}/${config.app.semVer}`;
163
+ }
164
+
165
+ task.title = origTitle;
166
+ },
167
+ },
168
+ {
169
+ // TODO: Disable this task if no Lambda function
170
+ title: 'Deploy to Lambda',
171
+ task: async (ctx, task) => {
172
+ const origTitle = task.title;
173
+ task.title = RUNNING + origTitle;
174
+
175
+ // Update the Lambda function
176
+ await this.deployToLambda(config, this.VersionAndAlias, task);
177
+
178
+ task.title = origTitle;
179
+ },
180
+ },
181
+ {
182
+ // TODO: Disable this task if no static assets path
183
+ title: 'Confirm Static Assets Folder Exists',
184
+ task: async (ctx, task) => {
185
+ const origTitle = task.title;
186
+ task.title = RUNNING + origTitle;
187
+
188
+ // Check that Static Assets Folder exists
189
+ if (!(await pathExists(config.app.staticAssetsPath))) {
190
+ this.error(`Static asset path does not exist: ${config.app.staticAssetsPath}`);
191
+ }
192
+
193
+ task.title = origTitle;
194
+ },
195
+ },
196
+ {
197
+ // TODO: Disable this task if no static assets path
198
+ title: 'Copy Static Files to Local Upload Dir',
199
+ task: async (ctx, task) => {
200
+ const origTitle = task.title;
201
+ task.title = RUNNING + origTitle;
202
+
203
+ // Copy files to local dir to be uploaded
204
+ await S3Uploader.CopyToUploadDir(config, ctx.preflightResult.response.s3UploadUrl);
205
+
206
+ task.title = origTitle;
207
+ },
208
+ },
209
+ {
210
+ // TODO: Disable this task if no static assets path
211
+ title: 'Enumerate Files to Upload to S3',
212
+ task: async (ctx, task) => {
213
+ const origTitle = task.title;
214
+ task.title = RUNNING + origTitle;
215
+
216
+ ctx.files = (await S3TransferUtility.GetFiles(S3Uploader.TempDir)) as string[];
217
+
218
+ task.title = origTitle;
219
+ },
220
+ },
221
+ {
222
+ // TODO: Disable this task if no static assets path
223
+ title: 'Upload Static Files to S3',
224
+ task: (ctx, task) => {
225
+ const origTitle = task.title;
226
+ task.title = RUNNING + origTitle;
227
+
228
+ const { bucketName, destinationPrefix } = S3Uploader.ParseUploadPath(
229
+ ctx.preflightResult.response.s3UploadUrl,
230
+ );
231
+
232
+ // Use temp credentials for S3
233
+ const s3Client = new s3.S3Client({
234
+ maxAttempts: 16,
235
+ credentials: {
236
+ accessKeyId: ctx.preflightResult.response.awsCredentials.accessKeyId,
237
+ secretAccessKey: ctx.preflightResult.response.awsCredentials.secretAccessKey,
238
+ sessionToken: ctx.preflightResult.response.awsCredentials.sessionToken,
239
+ },
240
+ });
241
+
242
+ const pathWithoutAppAndVer = path.join(S3Uploader.TempDir, destinationPrefix);
243
+
244
+ const tasks: ListrTask<IContext>[] = ctx.files.map((filePath) => ({
245
+ task: async (ctx: IContext, subtask) => {
246
+ const relFilePath = path.relative(pathWithoutAppAndVer, filePath);
247
+
248
+ const origTitle = relFilePath;
249
+ subtask.title = RUNNING + origTitle;
250
+
251
+ const upload = new Upload({
252
+ client: s3Client,
253
+ leavePartsOnError: false,
254
+ params: {
255
+ Bucket: bucketName,
256
+ Key: path.relative(S3Uploader.TempDir, filePath),
257
+ Body: createReadStream(filePath),
258
+ ContentType: contentType(path.basename(filePath)) || 'application/octet-stream',
259
+ CacheControl: 'max-age=86400; public',
260
+ },
261
+ });
262
+ await upload.done();
263
+
264
+ subtask.title = origTitle;
265
+ },
266
+ }));
267
+
268
+ task.title = origTitle;
269
+
270
+ return task.newListr(tasks, {
271
+ concurrent: 8,
272
+ rendererOptions: {
273
+ clearOutput: false,
274
+ showErrorMessage: true,
275
+ showTimer: true,
276
+ },
277
+ });
278
+ },
279
+ },
280
+ {
281
+ title: `Creating MicroApp Application: ${config.app.name}`,
282
+ task: async (ctx, task) => {
283
+ const origTitle = task.title;
284
+ task.title = RUNNING + origTitle;
285
+
286
+ // Call Deployer to Create App if Not Exists
287
+ await DeployClient.CreateApp(config);
288
+
289
+ task.title = origTitle;
290
+ },
291
+ },
292
+ {
293
+ title: `Creating MicroApp Version: ${config.app.semVer}`,
294
+ task: async (ctx, task) => {
295
+ const origTitle = task.title;
296
+ task.title = RUNNING + origTitle;
297
+
298
+ // Call Deployer to Deploy AppName/Version
299
+ await DeployClient.DeployVersion(
300
+ config,
301
+ 'lambda',
302
+ (message: string) => (task.output = message),
303
+ );
304
+
305
+ task.title = origTitle;
306
+ },
307
+ },
308
+ ],
309
+ {
310
+ rendererOptions: {
311
+ showTimer: true,
312
+ },
313
+ },
314
+ );
315
+
316
+ try {
317
+ await tasks.run();
318
+ // this.log(`Published: ${config.app.name}/${config.app.semVer}`);
319
+ } catch (error) {
320
+ this.log(`Caught exception: ${error.message}`);
321
+ } finally {
322
+ await S3Uploader.removeTempDirIfExists();
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Publish an app version to Lambda
328
+ * @param config
329
+ * @param versions
330
+ */
331
+ private async deployToLambda(
332
+ config: IConfig,
333
+ versions: IVersions,
334
+ task: TaskWrapper<IContext, typeof DefaultRenderer>,
335
+ ): Promise<void> {
336
+ // Create Lambda version
337
+ task.output = 'Creating version for Lambda $LATEST';
338
+ const resultUpdate = await lambdaClient.send(
339
+ new lambda.PublishVersionCommand({
340
+ FunctionName: config.app.lambdaName,
341
+ }),
342
+ );
343
+ const lambdaVersion = resultUpdate.Version;
344
+ task.output = `Lambda version created: ${resultUpdate.Version}`;
345
+
346
+ let lastUpdateStatus = resultUpdate.LastUpdateStatus;
347
+ for (let i = 0; i < 5; i++) {
348
+ // When the function is created the status will be "Pending"
349
+ // and we have to wait until it's done creating
350
+ // before we can point an alias to it
351
+ if (lastUpdateStatus === 'Successful') {
352
+ task.output = `Lambda function updated, version: ${lambdaVersion}`;
353
+ break;
354
+ }
355
+
356
+ // If it didn't work, wait and try again
357
+ await asyncSetTimeout(1000 * i);
358
+
359
+ const resultGet = await lambdaClient.send(
360
+ new lambda.GetFunctionCommand({
361
+ FunctionName: config.app.lambdaName,
362
+ Qualifier: lambdaVersion,
363
+ }),
364
+ );
365
+
366
+ // Save the last update status so we can check on re-loop
367
+ lastUpdateStatus = resultGet?.Configuration?.LastUpdateStatus;
368
+ }
369
+
370
+ // Create Lambda alias point
371
+ task.output = `Creating the lambda alias for the new version: ${lambdaVersion}`;
372
+ const resultLambdaAlias = await lambdaClient.send(
373
+ new lambda.CreateAliasCommand({
374
+ FunctionName: config.app.lambdaName,
375
+ Name: versions.alias,
376
+ FunctionVersion: lambdaVersion,
377
+ }),
378
+ );
379
+ task.output = `Lambda alias created, name: ${resultLambdaAlias.Name}`;
380
+ }
381
+ }
@@ -5,7 +5,6 @@ import { TSConvict } from 'ts-convict';
5
5
  import { FilesExist } from '../lib/FilesExist';
6
6
  import { ApplicationConfig, IApplicationConfig } from './Application';
7
7
  import { DeployerConfig, IDeployerConfig } from './Deployer';
8
- import { FileStoreConfig, IFileStoreRename } from './FileStore';
9
8
 
10
9
  export interface IConfig {
11
10
  deployer: IDeployerConfig;
@@ -76,7 +75,4 @@ export class Config implements IConfig {
76
75
 
77
76
  @convict.Property(DeployerConfig)
78
77
  public deployer!: IDeployerConfig;
79
-
80
- @convict.Property(FileStoreConfig)
81
- public filestore!: IFileStoreRename;
82
78
  }