@pwrdrvr/microapps-publish 0.0.19 → 0.0.23

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