@datadog/datadog-ci 2.18.0 → 2.18.1

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/dist/commands/cloud-run/cli.d.ts +1 -0
  2. package/dist/commands/cloud-run/cli.js +5 -0
  3. package/dist/commands/cloud-run/cli.js.map +1 -0
  4. package/dist/commands/cloud-run/constants.d.ts +1 -0
  5. package/dist/commands/cloud-run/constants.js +22 -0
  6. package/dist/commands/cloud-run/constants.js.map +1 -0
  7. package/dist/commands/cloud-run/flare.d.ts +60 -0
  8. package/dist/commands/cloud-run/flare.js +369 -0
  9. package/dist/commands/cloud-run/flare.js.map +1 -0
  10. package/dist/commands/cloud-run/interfaces.d.ts +27 -0
  11. package/dist/commands/cloud-run/interfaces.js +3 -0
  12. package/dist/commands/cloud-run/interfaces.js.map +1 -0
  13. package/dist/commands/cloud-run/renderer.d.ts +4 -0
  14. package/dist/commands/cloud-run/renderer.js +38 -0
  15. package/dist/commands/cloud-run/renderer.js.map +1 -0
  16. package/dist/commands/lambda/constants.d.ts +10 -8
  17. package/dist/commands/lambda/constants.js +41 -25
  18. package/dist/commands/lambda/constants.js.map +1 -1
  19. package/dist/commands/lambda/flare.d.ts +10 -45
  20. package/dist/commands/lambda/flare.js +121 -215
  21. package/dist/commands/lambda/flare.js.map +1 -1
  22. package/dist/commands/lambda/functions/commons.d.ts +7 -13
  23. package/dist/commands/lambda/functions/commons.js +39 -56
  24. package/dist/commands/lambda/functions/commons.js.map +1 -1
  25. package/dist/commands/lambda/functions/instrument.js +46 -45
  26. package/dist/commands/lambda/functions/instrument.js.map +1 -1
  27. package/dist/commands/lambda/functions/uninstrument.js +29 -28
  28. package/dist/commands/lambda/functions/uninstrument.js.map +1 -1
  29. package/dist/commands/lambda/instrument.js +19 -16
  30. package/dist/commands/lambda/instrument.js.map +1 -1
  31. package/dist/commands/lambda/prompt.d.ts +0 -2
  32. package/dist/commands/lambda/prompt.js +16 -35
  33. package/dist/commands/lambda/prompt.js.map +1 -1
  34. package/dist/commands/lambda/renderers/common-renderer.d.ts +0 -26
  35. package/dist/commands/lambda/renderers/common-renderer.js +4 -32
  36. package/dist/commands/lambda/renderers/common-renderer.js.map +1 -1
  37. package/dist/commands/lambda/renderers/instrument-uninstrument-renderer.js +27 -27
  38. package/dist/commands/lambda/renderers/instrument-uninstrument-renderer.js.map +1 -1
  39. package/dist/commands/lambda/uninstrument.js +9 -7
  40. package/dist/commands/lambda/uninstrument.js.map +1 -1
  41. package/dist/commands/sarif/api.js +2 -18
  42. package/dist/commands/sarif/api.js.map +1 -1
  43. package/dist/commands/synthetics/interfaces.d.ts +2 -1
  44. package/dist/commands/synthetics/interfaces.js.map +1 -1
  45. package/dist/commands/synthetics/reporters/default.js +3 -5
  46. package/dist/commands/synthetics/reporters/default.js.map +1 -1
  47. package/dist/commands/synthetics/utils.js +1 -0
  48. package/dist/commands/synthetics/utils.js.map +1 -1
  49. package/dist/constants.d.ts +10 -0
  50. package/dist/constants.js +14 -1
  51. package/dist/constants.js.map +1 -1
  52. package/dist/helpers/flare.d.ts +19 -0
  53. package/dist/helpers/flare.js +81 -0
  54. package/dist/helpers/flare.js.map +1 -0
  55. package/dist/helpers/fs.d.ts +31 -0
  56. package/dist/helpers/fs.js +117 -0
  57. package/dist/helpers/fs.js.map +1 -0
  58. package/dist/helpers/prompt.d.ts +6 -0
  59. package/dist/helpers/prompt.js +39 -0
  60. package/dist/helpers/prompt.js.map +1 -0
  61. package/dist/helpers/renderer.d.ts +36 -0
  62. package/dist/helpers/renderer.js +47 -0
  63. package/dist/helpers/renderer.js.map +1 -0
  64. package/dist/helpers/utils.d.ts +8 -0
  65. package/dist/helpers/utils.js +40 -1
  66. package/dist/helpers/utils.js.map +1 -1
  67. package/package.json +8 -2
  68. package/dist/commands/lambda/renderers/flare-renderer.d.ts +0 -9
  69. package/dist/commands/lambda/renderers/flare-renderer.js +0 -22
  70. package/dist/commands/lambda/renderers/flare-renderer.js.map +0 -1
  71. package/dist/helpers/file.d.ts +0 -1
  72. package/dist/helpers/file.js +0 -9
  73. package/dist/helpers/file.js.map +0 -1
@@ -31,33 +31,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
32
32
  };
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
- exports.sendToDatadog = exports.getEndpointUrl = exports.zipContents = exports.sleep = exports.convertToCSV = exports.getUniqueFileNames = exports.writeFile = exports.getTags = exports.getAllLogs = exports.getLogEvents = exports.getLogStreamNames = exports.validateFilePath = exports.getProjectFiles = exports.createDirectories = exports.deleteFolder = exports.maskConfig = exports.validateStartEndFlags = exports.LambdaFlareCommand = void 0;
34
+ exports.generateInsightsFile = exports.getFramework = exports.sleep = exports.convertToCSV = exports.getUniqueFileNames = exports.getTags = exports.getAllLogs = exports.getLogEvents = exports.getLogStreamNames = exports.validateFilePath = exports.getProjectFiles = exports.validateStartEndFlags = exports.LambdaFlareCommand = void 0;
35
35
  const fs = __importStar(require("fs"));
36
36
  const path = __importStar(require("path"));
37
37
  const util_1 = __importDefault(require("util"));
38
38
  const client_cloudwatch_logs_1 = require("@aws-sdk/client-cloudwatch-logs");
39
39
  const client_lambda_1 = require("@aws-sdk/client-lambda");
40
- const axios_1 = __importDefault(require("axios"));
41
40
  const chalk_1 = __importDefault(require("chalk"));
42
41
  const clipanion_1 = require("clipanion");
43
- const form_data_1 = __importDefault(require("form-data"));
44
- const jszip_1 = __importDefault(require("jszip"));
45
42
  const constants_1 = require("../../constants");
46
- const validation_1 = require("../../helpers/validation");
43
+ const flare_1 = require("../../helpers/flare");
44
+ const fs_1 = require("../../helpers/fs");
45
+ const prompt_1 = require("../../helpers/prompt");
46
+ const helpersRenderer = __importStar(require("../../helpers/renderer"));
47
+ const utils_1 = require("../../helpers/utils");
47
48
  const constants_2 = require("./constants");
48
49
  const commons_1 = require("./functions/commons");
49
- const prompt_1 = require("./prompt");
50
+ const prompt_2 = require("./prompt");
50
51
  const commonRenderer = __importStar(require("./renderers/common-renderer"));
51
- const flareRenderer = __importStar(require("./renderers/flare-renderer"));
52
- const { version } = require('../../../package.json');
53
- const ENDPOINT_PATH = '/api/ui/support/serverless/flare';
54
- const FLARE_OUTPUT_DIRECTORY = '.datadog-ci';
55
- const LOGS_DIRECTORY = 'logs';
52
+ const version = require('../../../package.json').version;
56
53
  const PROJECT_FILES_DIRECTORY = 'project_files';
57
54
  const ADDITIONAL_FILES_DIRECTORY = 'additional_files';
58
55
  const FUNCTION_CONFIG_FILE_NAME = 'function_config.json';
59
56
  const TAGS_FILE_NAME = 'tags.json';
60
- const ZIP_FILE_NAME = 'lambda-flare-output.zip';
57
+ const INSIGHTS_FILE_NAME = 'INSIGHTS.md';
58
+ const FLARE_ZIP_FILE_NAME = 'lambda-flare-output.zip';
61
59
  const MAX_LOG_STREAMS = 50;
62
60
  const DEFAULT_LOG_STREAMS = 3;
63
61
  const MAX_LOG_EVENTS_PER_STREAM = 1000;
@@ -69,16 +67,17 @@ class LambdaFlareCommand extends clipanion_1.Command {
69
67
  }
70
68
  /**
71
69
  * Entry point for the `lambda flare` command.
72
- * Gathers lambda function configuration and sends it to Datadog.
70
+ * Gathers config, logs, tags, project files, and more from a
71
+ * Lambda function and sends them to Datadog support.
73
72
  * @returns 0 if the command ran successfully, 1 otherwise.
74
73
  */
75
74
  execute() {
76
75
  var _a, _b, _c;
77
76
  return __awaiter(this, void 0, void 0, function* () {
78
- this.context.stdout.write(flareRenderer.renderLambdaFlareHeader(this.isDryRun));
77
+ this.context.stdout.write(helpersRenderer.renderFlareHeader('Lambda', this.isDryRun));
79
78
  // Validate function name
80
79
  if (this.functionName === undefined) {
81
- this.context.stderr.write(commonRenderer.renderError('No function name specified. [-f,--function]'));
80
+ this.context.stderr.write(helpersRenderer.renderError('No function name specified. [-f,--function]'));
82
81
  return 1;
83
82
  }
84
83
  const errorMessages = [];
@@ -88,18 +87,18 @@ class LambdaFlareCommand extends clipanion_1.Command {
88
87
  errorMessages.push(commonRenderer.renderNoDefaultRegionSpecifiedError());
89
88
  }
90
89
  // Validate Datadog API key
91
- this.apiKey = (_c = process.env[constants_2.CI_API_KEY_ENV_VAR]) !== null && _c !== void 0 ? _c : process.env[constants_2.API_KEY_ENV_VAR];
90
+ this.apiKey = (_c = process.env[constants_1.CI_API_KEY_ENV_VAR]) !== null && _c !== void 0 ? _c : process.env[constants_1.API_KEY_ENV_VAR];
92
91
  if (this.apiKey === undefined) {
93
- errorMessages.push(commonRenderer.renderError('No Datadog API key specified. Set an API key with the DATADOG_API_KEY environment variable.'));
92
+ errorMessages.push(helpersRenderer.renderError('No Datadog API key specified. Set an API key with the DATADOG_API_KEY environment variable.'));
94
93
  }
95
94
  if (!this.isDryRun) {
96
95
  // Validate case ID
97
96
  if (this.caseId === undefined) {
98
- errorMessages.push(commonRenderer.renderError('No case ID specified. [-c,--case-id]'));
97
+ errorMessages.push(helpersRenderer.renderError('No case ID specified. [-c,--case-id]'));
99
98
  }
100
99
  // Validate email
101
100
  if (this.email === undefined) {
102
- errorMessages.push(commonRenderer.renderError('No email specified. [-e,--email]'));
101
+ errorMessages.push(helpersRenderer.renderError('No email specified. [-e,--email]'));
103
102
  }
104
103
  }
105
104
  // Validate start/end flags if both are specified
@@ -111,7 +110,7 @@ class LambdaFlareCommand extends clipanion_1.Command {
111
110
  }
112
111
  catch (err) {
113
112
  if (err instanceof Error) {
114
- errorMessages.push(commonRenderer.renderError(err.message));
113
+ errorMessages.push(helpersRenderer.renderError(err.message));
115
114
  }
116
115
  }
117
116
  if (errorMessages.length > 0) {
@@ -127,18 +126,18 @@ class LambdaFlareCommand extends clipanion_1.Command {
127
126
  }
128
127
  catch (err) {
129
128
  if (err instanceof Error) {
130
- this.context.stderr.write(commonRenderer.renderError(err.message));
129
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
131
130
  }
132
131
  return 1;
133
132
  }
134
133
  if (this.credentials === undefined) {
135
134
  this.context.stdout.write('\n' + commonRenderer.renderNoAWSCredentialsFound());
136
135
  try {
137
- yield prompt_1.requestAWSCredentials();
136
+ yield prompt_2.requestAWSCredentials();
138
137
  }
139
138
  catch (err) {
140
139
  if (err instanceof Error) {
141
- this.context.stderr.write(commonRenderer.renderError(err.message));
140
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
142
141
  }
143
142
  return 1;
144
143
  }
@@ -156,11 +155,11 @@ class LambdaFlareCommand extends clipanion_1.Command {
156
155
  }
157
156
  catch (err) {
158
157
  if (err instanceof Error) {
159
- this.context.stderr.write(commonRenderer.renderError(`Unable to get Lambda function configuration: ${err.message}`));
158
+ this.context.stderr.write(helpersRenderer.renderError(`Unable to get Lambda function configuration: ${err.message}`));
160
159
  }
161
160
  return 1;
162
161
  }
163
- config = exports.maskConfig(config);
162
+ config = commons_1.maskConfig(config);
164
163
  const configStr = util_1.default.inspect(config, false, undefined, true);
165
164
  this.context.stdout.write(`\n${configStr}\n`);
166
165
  // Get project files
@@ -168,7 +167,7 @@ class LambdaFlareCommand extends clipanion_1.Command {
168
167
  const projectFilePaths = yield exports.getProjectFiles();
169
168
  let projectFilesMessage = chalk_1.default.bold(`\n✅ Found project file(s) in ${process.cwd()}:\n`);
170
169
  if (projectFilePaths.size === 0) {
171
- projectFilesMessage = commonRenderer.renderSoftWarning('No project files found.');
170
+ projectFilesMessage = helpersRenderer.renderSoftWarning('No project files found.');
172
171
  }
173
172
  this.context.stdout.write(projectFilesMessage);
174
173
  for (const filePath of projectFilePaths) {
@@ -184,7 +183,7 @@ class LambdaFlareCommand extends clipanion_1.Command {
184
183
  }
185
184
  catch (err) {
186
185
  if (err instanceof Error) {
187
- this.context.stderr.write(commonRenderer.renderError(err.message));
186
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
188
187
  }
189
188
  return 1;
190
189
  }
@@ -192,11 +191,11 @@ class LambdaFlareCommand extends clipanion_1.Command {
192
191
  this.context.stdout.write('\n');
193
192
  let filePath;
194
193
  try {
195
- filePath = yield prompt_1.requestFilePath();
194
+ filePath = yield prompt_2.requestFilePath();
196
195
  }
197
196
  catch (err) {
198
197
  if (err instanceof Error) {
199
- this.context.stderr.write(commonRenderer.renderError(err.message));
198
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
200
199
  }
201
200
  return 1;
202
201
  }
@@ -228,13 +227,13 @@ class LambdaFlareCommand extends clipanion_1.Command {
228
227
  }
229
228
  catch (err) {
230
229
  if (err instanceof Error) {
231
- this.context.stderr.write(commonRenderer.renderError(err.message));
230
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
232
231
  }
233
232
  return 1;
234
233
  }
235
234
  const tagsLength = Object.keys(tags).length;
236
235
  if (tagsLength === 0) {
237
- this.context.stdout.write(commonRenderer.renderSoftWarning(`No resource tags were found.`));
236
+ this.context.stdout.write(helpersRenderer.renderSoftWarning(`No resource tags were found.`));
238
237
  }
239
238
  else {
240
239
  this.context.stdout.write(`Found ${tagsLength} resource tag(s).\n`);
@@ -248,7 +247,7 @@ class LambdaFlareCommand extends clipanion_1.Command {
248
247
  }
249
248
  catch (err) {
250
249
  if (err instanceof Error) {
251
- this.context.stderr.write(commonRenderer.renderError(err.message));
250
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
252
251
  }
253
252
  return 1;
254
253
  }
@@ -258,25 +257,25 @@ class LambdaFlareCommand extends clipanion_1.Command {
258
257
  if (this.withLogs) {
259
258
  let message = chalk_1.default.bold('\n✅ Found log streams:\n');
260
259
  if (logs.size === 0) {
261
- message = commonRenderer.renderSoftWarning('No CloudWatch log streams were found. Logs will not be retrieved or sent.');
260
+ message = helpersRenderer.renderSoftWarning('No CloudWatch log streams were found. Logs will not be retrieved or sent.');
262
261
  }
263
262
  this.context.stdout.write(message);
264
263
  for (const [logStreamName, logEvents] of logs) {
265
264
  let warningMessage = '\n';
266
265
  if (logEvents.length === 0) {
267
- warningMessage = ' - ' + commonRenderer.renderSoftWarning('No log events found in this stream');
266
+ warningMessage = ` - ${helpersRenderer.renderSoftWarning('No log events found in this stream')}`;
268
267
  }
269
268
  this.context.stdout.write(`• ${logStreamName}${warningMessage}`);
270
269
  }
271
270
  }
272
271
  // Create folders
273
- const rootFolderPath = path.join(process.cwd(), FLARE_OUTPUT_DIRECTORY);
274
- const logsFolderPath = path.join(rootFolderPath, LOGS_DIRECTORY);
272
+ const rootFolderPath = path.join(process.cwd(), constants_1.FLARE_OUTPUT_DIRECTORY);
273
+ const logsFolderPath = path.join(rootFolderPath, constants_1.LOGS_DIRECTORY);
275
274
  const projectFilesFolderPath = path.join(rootFolderPath, PROJECT_FILES_DIRECTORY);
276
275
  const additionalFilesFolderPath = path.join(rootFolderPath, ADDITIONAL_FILES_DIRECTORY);
277
276
  this.context.stdout.write(chalk_1.default.bold(`\n💾 Saving files to ${rootFolderPath}...\n`));
278
277
  if (fs.existsSync(rootFolderPath)) {
279
- exports.deleteFolder(rootFolderPath);
278
+ fs_1.deleteFolder(rootFolderPath);
280
279
  }
281
280
  const subFolders = [];
282
281
  if (logs.size > 0) {
@@ -288,15 +287,15 @@ class LambdaFlareCommand extends clipanion_1.Command {
288
287
  if (additionalFilePaths.size > 0) {
289
288
  subFolders.push(additionalFilesFolderPath);
290
289
  }
291
- exports.createDirectories(rootFolderPath, subFolders);
290
+ fs_1.createDirectories(rootFolderPath, subFolders);
292
291
  // Write config file
293
292
  const configFilePath = path.join(rootFolderPath, FUNCTION_CONFIG_FILE_NAME);
294
- exports.writeFile(configFilePath, JSON.stringify(config, undefined, 2));
293
+ fs_1.writeFile(configFilePath, JSON.stringify(config, undefined, 2));
295
294
  this.context.stdout.write(`• Saved function config to ./${FUNCTION_CONFIG_FILE_NAME}\n`);
296
295
  // Write tags file
297
296
  if (tagsLength > 0) {
298
297
  const tagsFilePath = path.join(rootFolderPath, TAGS_FILE_NAME);
299
- exports.writeFile(tagsFilePath, JSON.stringify(tags, undefined, 2));
298
+ fs_1.writeFile(tagsFilePath, JSON.stringify(tags, undefined, 2));
300
299
  this.context.stdout.write(`• Saved tags to ./${TAGS_FILE_NAME}\n`);
301
300
  }
302
301
  // Write log files
@@ -306,8 +305,8 @@ class LambdaFlareCommand extends clipanion_1.Command {
306
305
  }
307
306
  const logFilePath = path.join(logsFolderPath, `${logStreamName.split('/').join('-')}.csv`);
308
307
  const data = exports.convertToCSV(logEvents);
309
- exports.writeFile(logFilePath, data);
310
- this.context.stdout.write(`• Saved logs to ./${LOGS_DIRECTORY}/${logStreamName}\n`);
308
+ fs_1.writeFile(logFilePath, data);
309
+ this.context.stdout.write(`• Saved logs to ./${constants_1.LOGS_DIRECTORY}/${logStreamName}\n`);
311
310
  // Sleep for 1 millisecond so creation times are different
312
311
  // This allows the logs to be sorted by creation time by the support team
313
312
  yield exports.sleep(1);
@@ -327,6 +326,10 @@ class LambdaFlareCommand extends clipanion_1.Command {
327
326
  fs.copyFileSync(originalFilePath, newFilePath);
328
327
  this.context.stdout.write(`• Copied ${originalFileName} to ./${ADDITIONAL_FILES_DIRECTORY}/${newFileName}\n`);
329
328
  }
329
+ // Write insights file
330
+ const insightsFilePath = path.join(rootFolderPath, INSIGHTS_FILE_NAME);
331
+ exports.generateInsightsFile(insightsFilePath, this.isDryRun, config);
332
+ this.context.stdout.write(`• Saved insights file to ./${INSIGHTS_FILE_NAME}\n`);
330
333
  // Exit if dry run
331
334
  const outputMsg = `\nℹ️ Your output files are located at: ${rootFolderPath}\n\n`;
332
335
  if (this.isDryRun) {
@@ -336,34 +339,25 @@ class LambdaFlareCommand extends clipanion_1.Command {
336
339
  }
337
340
  // Confirm before sending
338
341
  this.context.stdout.write('\n');
339
- let confirmSendFiles;
340
- try {
341
- confirmSendFiles = yield prompt_1.requestConfirmation('Are you sure you want to send the flare file to Datadog Support?', false);
342
- }
343
- catch (err) {
344
- if (err instanceof Error) {
345
- this.context.stderr.write(commonRenderer.renderError(err.message));
346
- }
347
- return 1;
348
- }
342
+ const confirmSendFiles = yield prompt_1.requestConfirmation('Are you sure you want to send the flare file to Datadog Support?', false);
349
343
  if (!confirmSendFiles) {
350
344
  this.context.stdout.write('\n🚫 The flare files were not sent based on your selection.');
351
345
  this.context.stdout.write(outputMsg);
352
346
  return 0;
353
347
  }
354
348
  // Zip folder
355
- const zipPath = path.join(rootFolderPath, ZIP_FILE_NAME);
356
- yield exports.zipContents(rootFolderPath, zipPath);
349
+ const zipPath = path.join(rootFolderPath, FLARE_ZIP_FILE_NAME);
350
+ yield fs_1.zipContents(rootFolderPath, zipPath);
357
351
  // Send to Datadog
358
352
  this.context.stdout.write(chalk_1.default.bold('\n🚀 Sending to Datadog Support...\n'));
359
- yield exports.sendToDatadog(zipPath, this.caseId, this.email, this.apiKey, rootFolderPath);
353
+ yield flare_1.sendToDatadog(zipPath, this.caseId, this.email, this.apiKey, rootFolderPath);
360
354
  this.context.stdout.write(chalk_1.default.bold('\n✅ Successfully sent flare file to Datadog Support!\n'));
361
355
  // Delete contents
362
- exports.deleteFolder(rootFolderPath);
356
+ fs_1.deleteFolder(rootFolderPath);
363
357
  }
364
358
  catch (err) {
365
359
  if (err instanceof Error) {
366
- this.context.stderr.write(commonRenderer.renderError(err.message));
360
+ this.context.stderr.write(helpersRenderer.renderError(err.message));
367
361
  }
368
362
  return 1;
369
363
  }
@@ -405,57 +399,6 @@ const validateStartEndFlags = (start, end) => {
405
399
  return [startMillis, endMillis];
406
400
  };
407
401
  exports.validateStartEndFlags = validateStartEndFlags;
408
- /**
409
- * Mask the environment variables in a Lambda function configuration
410
- * @param config
411
- */
412
- const maskConfig = (config) => {
413
- var _a;
414
- const environmentVariables = (_a = config.Environment) === null || _a === void 0 ? void 0 : _a.Variables;
415
- if (!environmentVariables) {
416
- return config;
417
- }
418
- const replacer = commons_1.maskStringifiedEnvVar(environmentVariables);
419
- const stringifiedConfig = JSON.stringify(config, replacer);
420
- return JSON.parse(stringifiedConfig);
421
- };
422
- exports.maskConfig = maskConfig;
423
- /**
424
- * Delete a folder and all its contents
425
- * @param folderPath the folder to delete
426
- * @throws Error if the deletion fails
427
- */
428
- const deleteFolder = (folderPath) => {
429
- try {
430
- fs.rmSync(folderPath, { recursive: true, force: true });
431
- }
432
- catch (err) {
433
- if (err instanceof Error) {
434
- throw Error(`Failed to delete files located at ${folderPath}: ${err.message}`);
435
- }
436
- }
437
- };
438
- exports.deleteFolder = deleteFolder;
439
- /**
440
- * Creates the root folder and any subfolders
441
- * @param rootFolderPath path to the root folder
442
- * @param subFolders paths to any subfolders to be created
443
- * @throws Error if the root folder cannot be deleted or folders cannot be created
444
- */
445
- const createDirectories = (rootFolderPath, subFolders) => {
446
- try {
447
- fs.mkdirSync(rootFolderPath);
448
- for (const subFolder of subFolders) {
449
- fs.mkdirSync(subFolder);
450
- }
451
- }
452
- catch (err) {
453
- if (err instanceof Error) {
454
- throw Error(`Unable to create directories: ${err.message}`);
455
- }
456
- }
457
- };
458
- exports.createDirectories = createDirectories;
459
402
  /**
460
403
  * Searches current directory for project files
461
404
  * @returns a set of file paths of project files
@@ -484,11 +427,11 @@ const validateFilePath = (filePath, projectFilePaths, additionalFiles) => {
484
427
  const originalPath = filePath;
485
428
  filePath = fs.existsSync(filePath) ? filePath : path.join(process.cwd(), filePath);
486
429
  if (!fs.existsSync(filePath)) {
487
- throw Error(commonRenderer.renderError(`File path '${originalPath}' not found. Please try again.`));
430
+ throw Error(helpersRenderer.renderError(`File path '${originalPath}' not found. Please try again.`));
488
431
  }
489
432
  filePath = path.resolve(filePath);
490
433
  if (projectFilePaths.has(filePath) || additionalFiles.has(filePath)) {
491
- throw Error(commonRenderer.renderSoftWarning(`File '${filePath}' has already been added.`));
434
+ throw Error(helpersRenderer.renderSoftWarning(`File '${filePath}' has already been added.`));
492
435
  }
493
436
  return filePath;
494
437
  };
@@ -636,23 +579,6 @@ const getTags = (lambdaClient, region, arn) => __awaiter(void 0, void 0, void 0,
636
579
  }
637
580
  });
638
581
  exports.getTags = getTags;
639
- /**
640
- * Write the function config to a file
641
- * @param filePath path to the file
642
- * @param data the data to write
643
- * @throws Error if the file cannot be written
644
- */
645
- const writeFile = (filePath, data) => {
646
- try {
647
- fs.writeFileSync(filePath, data);
648
- }
649
- catch (err) {
650
- if (err instanceof Error) {
651
- throw Error(`Unable to create function configuration file: ${err.message}`);
652
- }
653
- }
654
- };
655
- exports.writeFile = writeFile;
656
582
  /**
657
583
  * Generate unique file names
658
584
  * If the original file name is unique, keep it as is
@@ -716,101 +642,81 @@ const sleep = (ms) => __awaiter(void 0, void 0, void 0, function* () {
716
642
  });
717
643
  exports.sleep = sleep;
718
644
  /**
719
- * Zip the contents of the flare folder
720
- * @param rootFolderPath path to the root folder to zip
721
- * @param zipPath path to save the zip file
722
- * @throws Error if the zip fails
645
+ * Get the framework used based on the files in the directory
646
+ * @returns the framework used or undefined if no framework is found
723
647
  */
724
- const zipContents = (rootFolderPath, zipPath) => __awaiter(void 0, void 0, void 0, function* () {
725
- const zip = new jszip_1.default();
726
- const addFolderToZip = (folderPath) => {
727
- if (!fs.existsSync(folderPath)) {
728
- throw Error(`Folder does not exist: ${folderPath}`);
648
+ const getFramework = () => {
649
+ const frameworks = new Set();
650
+ const files = fs.readdirSync(process.cwd());
651
+ files.forEach((file) => {
652
+ if (constants_2.FRAMEWORK_FILES_MAPPING.has(file)) {
653
+ frameworks.add(constants_2.FRAMEWORK_FILES_MAPPING.get(file));
729
654
  }
730
- const folder = fs.statSync(folderPath);
731
- if (!folder.isDirectory()) {
732
- throw Error(`Path is not a directory: ${folderPath}`);
733
- }
734
- const contents = fs.readdirSync(folderPath);
735
- for (const item of contents) {
736
- const fullPath = path.join(folderPath, item);
737
- const file = fs.statSync(fullPath);
738
- if (file.isDirectory()) {
739
- addFolderToZip(fullPath);
740
- }
741
- else {
742
- const data = fs.readFileSync(fullPath);
743
- zip.file(path.relative(rootFolderPath, fullPath), data);
744
- }
745
- }
746
- };
747
- try {
748
- addFolderToZip(rootFolderPath);
749
- const zipContent = yield zip.generateAsync({ type: 'nodebuffer' });
750
- fs.writeFileSync(zipPath, zipContent);
751
- }
752
- catch (err) {
753
- if (err instanceof Error) {
754
- throw Error(`Unable to zip the flare files: ${err.message}`);
755
- }
756
- }
757
- });
758
- exports.zipContents = zipContents;
759
- /**
760
- * Calculates the full endpoint URL
761
- * @throws Error if the site is invalid
762
- * @returns the full endpoint URL
763
- */
764
- const getEndpointUrl = () => {
765
- var _a, _b;
766
- const baseUrl = (_b = (_a = process.env[constants_2.CI_SITE_ENV_VAR]) !== null && _a !== void 0 ? _a : process.env[constants_2.SITE_ENV_VAR]) !== null && _b !== void 0 ? _b : constants_1.DATADOG_SITE_US1;
767
- // The DNS doesn't redirect to the proper endpoint when a subdomain is not present in the baseUrl.
768
- // There is a DNS inconsistency
769
- let endpointUrl = baseUrl;
770
- if ([constants_1.DATADOG_SITE_US1, constants_1.DATADOG_SITE_EU1, constants_1.DATADOG_SITE_GOV].includes(baseUrl)) {
771
- endpointUrl = 'app.' + baseUrl;
772
- }
773
- if (!validation_1.isValidDatadogSite(baseUrl)) {
774
- throw Error(`Invalid site: ${baseUrl}. Must be one of: ${constants_1.DATADOG_SITES.join(', ')}`);
655
+ });
656
+ if (frameworks.size > 0) {
657
+ return Array.from(frameworks).join(', ');
775
658
  }
776
- return 'https://' + endpointUrl + ENDPOINT_PATH;
659
+ return constants_2.DeploymentFrameworks.Unknown;
777
660
  };
778
- exports.getEndpointUrl = getEndpointUrl;
661
+ exports.getFramework = getFramework;
779
662
  /**
780
- * Send the zip file to Datadog support
781
- * @param zipPath
782
- * @param caseId
783
- * @param email
784
- * @param apiKey
785
- * @param rootFolderPath
786
- * @throws Error if the request fails
663
+ * Generate the insights file
664
+ * @param insightsFilePath path to the insights file
665
+ * @param isDryRun whether or not this is a dry run
666
+ * @param config Lambda function configuration
787
667
  */
788
- const sendToDatadog = (zipPath, caseId, email, apiKey, rootFolderPath) => __awaiter(void 0, void 0, void 0, function* () {
789
- var _b, _c, _d;
790
- const endpointUrl = exports.getEndpointUrl();
791
- const form = new form_data_1.default();
792
- form.append('case_id', caseId);
793
- form.append('flare_file', fs.createReadStream(zipPath));
794
- form.append('datadog_ci_version', version);
795
- form.append('email', email);
796
- const headerConfig = {
797
- headers: Object.assign(Object.assign({}, form.getHeaders()), { 'DD-API-KEY': apiKey }),
798
- };
799
- try {
800
- yield axios_1.default.post(endpointUrl, form, headerConfig);
801
- }
802
- catch (err) {
803
- // Ensure the root folder is deleted if the request fails
804
- exports.deleteFolder(rootFolderPath);
805
- if (axios_1.default.isAxiosError(err)) {
806
- const errResponse = (_c = (_b = err.response) === null || _b === void 0 ? void 0 : _b.data.error) !== null && _c !== void 0 ? _c : '';
807
- const errorMessage = (_d = err.message) !== null && _d !== void 0 ? _d : '';
808
- throw Error(`Failed to send flare file to Datadog Support: ${errorMessage}. ${errResponse}\n`);
668
+ const generateInsightsFile = (insightsFilePath, isDryRun, config) => {
669
+ var _a, _b, _c, _d, _e;
670
+ const lines = [];
671
+ // Header
672
+ lines.push('# Flare Insights');
673
+ lines.push('\n_Autogenerated file from `lambda flare`_ ');
674
+ if (isDryRun) {
675
+ lines.push('_This command was run in dry mode._');
676
+ }
677
+ // AWS Lambda Configuration
678
+ lines.push('\n## AWS Lambda Configuration');
679
+ lines.push(`**Function Name**: \`${config.FunctionName}\` `);
680
+ lines.push(`**Function ARN**: \`${config.FunctionArn}\` `);
681
+ lines.push(`**Runtime**: \`${config.Runtime}\` `);
682
+ lines.push(`**Handler**: \`${config.Handler}\` `);
683
+ lines.push(`**Timeout**: \`${config.Timeout}\` `);
684
+ lines.push(`**Memory Size**: \`${config.MemorySize}\` `);
685
+ const architectures = (_a = config.Architectures) !== null && _a !== void 0 ? _a : ['Unknown'];
686
+ lines.push(`**Architecture**: \`${architectures.join(', ')}\` `);
687
+ lines.push('**Environment Variables**:');
688
+ const envVars = Object.entries((_c = (_b = config.Environment) === null || _b === void 0 ? void 0 : _b.Variables) !== null && _c !== void 0 ? _c : {});
689
+ if (envVars.length === 0) {
690
+ lines.push('- No environment variables found.');
691
+ }
692
+ for (const [key, value] of envVars) {
693
+ lines.push(`- \`${key}\`: \`${value}\``);
694
+ }
695
+ lines.push('\n**Layers**:');
696
+ const layers = (_d = config.Layers) !== null && _d !== void 0 ? _d : [];
697
+ if (layers.length === 0) {
698
+ lines.push(' - No layers found.');
699
+ }
700
+ let codeSize = (_e = config.CodeSize) !== null && _e !== void 0 ? _e : 0;
701
+ layers.forEach((layer) => {
702
+ var _a, _b;
703
+ const nameAndVersion = commons_1.getLayerNameWithVersion((_a = layer.Arn) !== null && _a !== void 0 ? _a : '');
704
+ if (nameAndVersion) {
705
+ lines.push(`- \`${nameAndVersion}\``);
809
706
  }
810
- throw err;
811
- }
812
- });
813
- exports.sendToDatadog = sendToDatadog;
707
+ codeSize += (_b = layer.CodeSize) !== null && _b !== void 0 ? _b : 0;
708
+ });
709
+ lines.push(`\n**Package Size**: \`${utils_1.formatBytes(codeSize)}\``);
710
+ // CLI Insights
711
+ lines.push('\n ## CLI');
712
+ lines.push(`**Run Location**: \`${process.cwd()}\` `);
713
+ lines.push(`**CLI Version**: \`${version}\` `);
714
+ const timeString = new Date().toISOString().replace('T', ' ').replace('Z', '') + ' UTC';
715
+ lines.push(`**Timestamp**: \`${timeString}\` `);
716
+ lines.push(`**Framework**: \`${exports.getFramework()}\``);
717
+ fs_1.writeFile(insightsFilePath, lines.join('\n'));
718
+ };
719
+ exports.generateInsightsFile = generateInsightsFile;
814
720
  LambdaFlareCommand.addPath('lambda', 'flare');
815
721
  LambdaFlareCommand.addOption('isDryRun', clipanion_1.Command.Boolean('-d,--dry'));
816
722
  LambdaFlareCommand.addOption('withLogs', clipanion_1.Command.Boolean('--with-logs'));