@herodevs/cli 2.0.0-beta.13 → 2.0.0-beta.15

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 (63) hide show
  1. package/README.md +192 -20
  2. package/dist/api/apollo.client.d.ts +3 -0
  3. package/dist/api/apollo.client.js +53 -0
  4. package/dist/api/ci-token.client.d.ts +26 -0
  5. package/dist/api/ci-token.client.js +95 -0
  6. package/dist/api/errors.d.ts +8 -0
  7. package/dist/api/errors.js +13 -0
  8. package/dist/api/gql-operations.d.ts +3 -0
  9. package/dist/api/gql-operations.js +36 -1
  10. package/dist/api/graphql-errors.d.ts +6 -0
  11. package/dist/api/graphql-errors.js +22 -0
  12. package/dist/api/nes.client.d.ts +1 -2
  13. package/dist/api/nes.client.js +31 -20
  14. package/dist/api/user-setup.client.d.ts +15 -0
  15. package/dist/api/user-setup.client.js +92 -0
  16. package/dist/commands/auth/login.d.ts +14 -0
  17. package/dist/commands/auth/login.js +225 -0
  18. package/dist/commands/auth/logout.d.ts +5 -0
  19. package/dist/commands/auth/logout.js +27 -0
  20. package/dist/commands/auth/provision-ci-token.d.ts +5 -0
  21. package/dist/commands/auth/provision-ci-token.js +62 -0
  22. package/dist/commands/report/committers.d.ts +11 -7
  23. package/dist/commands/report/committers.js +144 -76
  24. package/dist/commands/scan/eol.d.ts +2 -0
  25. package/dist/commands/scan/eol.js +34 -4
  26. package/dist/commands/tracker/init.d.ts +14 -0
  27. package/dist/commands/tracker/init.js +84 -0
  28. package/dist/commands/tracker/run.d.ts +15 -0
  29. package/dist/commands/tracker/run.js +183 -0
  30. package/dist/config/constants.d.ts +14 -0
  31. package/dist/config/constants.js +15 -0
  32. package/dist/config/tracker.config.d.ts +16 -0
  33. package/dist/config/tracker.config.js +16 -0
  34. package/dist/hooks/finally/finally.js +10 -4
  35. package/dist/hooks/init/01_initialize_amplitude.js +20 -9
  36. package/dist/service/analytics.svc.d.ts +10 -3
  37. package/dist/service/analytics.svc.js +180 -18
  38. package/dist/service/auth-config.svc.d.ts +5 -0
  39. package/dist/service/auth-config.svc.js +20 -0
  40. package/dist/service/auth-refresh.svc.d.ts +8 -0
  41. package/dist/service/auth-refresh.svc.js +45 -0
  42. package/dist/service/auth-token.svc.d.ts +11 -0
  43. package/dist/service/auth-token.svc.js +48 -0
  44. package/dist/service/auth.svc.d.ts +27 -0
  45. package/dist/service/auth.svc.js +88 -0
  46. package/dist/service/ci-auth.svc.d.ts +6 -0
  47. package/dist/service/ci-auth.svc.js +32 -0
  48. package/dist/service/ci-token.svc.d.ts +6 -0
  49. package/dist/service/ci-token.svc.js +75 -0
  50. package/dist/service/committers.svc.d.ts +46 -58
  51. package/dist/service/committers.svc.js +55 -173
  52. package/dist/service/jwt.svc.d.ts +1 -0
  53. package/dist/service/jwt.svc.js +19 -0
  54. package/dist/service/tracker.svc.d.ts +58 -0
  55. package/dist/service/tracker.svc.js +101 -0
  56. package/dist/types/auth.d.ts +9 -0
  57. package/dist/types/auth.js +1 -0
  58. package/dist/utils/open-in-browser.d.ts +1 -0
  59. package/dist/utils/open-in-browser.js +21 -0
  60. package/dist/utils/retry.d.ts +11 -0
  61. package/dist/utils/retry.js +29 -0
  62. package/dist/utils/strip-typename.js +2 -1
  63. package/package.json +38 -19
@@ -1,9 +1,10 @@
1
1
  import { spawnSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
- import path from 'node:path';
4
3
  import { Command, Flags } from '@oclif/core';
5
- import { filenamePrefix } from "../../config/constants.js";
6
- import { calculateOverallStats, formatAsCsv, formatAsText, groupCommitsByMonth, parseGitLogOutput, } from "../../service/committers.svc.js";
4
+ import { makeTable } from '@oclif/table';
5
+ import { endOfDay, formatDate, formatISO, parse, subMonths } from 'date-fns';
6
+ import { DEFAULT_DATE_COMMIT_FORMAT, DEFAULT_DATE_FORMAT, filenamePrefix, GIT_OUTPUT_FORMAT, } from "../../config/constants.js";
7
+ import { generateCommittersReport, generateMonthlyReport, parseGitLogOutput, } from "../../service/committers.svc.js";
7
8
  import { getErrorMessage, isErrnoException } from "../../service/error.svc.js";
8
9
  export default class Committers extends Command {
9
10
  static description = 'Generate report of committers to a git repository';
@@ -15,10 +16,39 @@ export default class Committers extends Command {
15
16
  '<%= config.bin %> <%= command.id %> --csv',
16
17
  ];
17
18
  static flags = {
19
+ beforeDate: Flags.string({
20
+ char: 's',
21
+ default: formatDate(new Date(), DEFAULT_DATE_FORMAT),
22
+ description: `End date (format: ${DEFAULT_DATE_FORMAT})`,
23
+ }),
24
+ afterDate: Flags.string({
25
+ char: 'e',
26
+ default: formatDate(subMonths(new Date(), 12), DEFAULT_DATE_FORMAT),
27
+ description: `Start date (format: ${DEFAULT_DATE_FORMAT})`,
28
+ }),
29
+ exclude: Flags.string({
30
+ char: 'x',
31
+ description: 'Path Exclusions (eg -x="./src/bin" -x="./dist")',
32
+ multiple: true,
33
+ multipleNonGreedy: true,
34
+ }),
35
+ json: Flags.boolean({
36
+ description: 'Output to JSON format',
37
+ default: false,
38
+ }),
39
+ directory: Flags.string({
40
+ char: 'd',
41
+ description: 'Directory to search',
42
+ }),
43
+ monthly: Flags.boolean({
44
+ description: 'Break down by calendar month.',
45
+ default: false,
46
+ }),
18
47
  months: Flags.integer({
19
48
  char: 'm',
20
- description: 'The number of months of git history to review',
49
+ description: 'The number of months of git history to review. Cannot be used along beforeDate and afterDate',
21
50
  default: 12,
51
+ exclusive: ['beforeDate', 'afterDate', 's', 'e'],
22
52
  }),
23
53
  csv: Flags.boolean({
24
54
  char: 'c',
@@ -33,100 +63,138 @@ export default class Committers extends Command {
33
63
  };
34
64
  async run() {
35
65
  const { flags } = await this.parse(Committers);
36
- const { months, csv, save } = flags;
66
+ const { afterDate, beforeDate, exclude, directory: cwd, monthly, months, csv, save } = flags;
37
67
  const isJson = this.jsonEnabled();
38
- const sinceDate = `${months} months ago`;
39
- this.log('Starting committers report with flags: %O', flags);
68
+ const reportFormat = isJson ? 'json' : csv ? 'csv' : 'txt';
69
+ const afterDateStartOfDay = months
70
+ ? `${subMonths(new Date(), months)}`
71
+ : `${parse(afterDate, DEFAULT_DATE_FORMAT, new Date())}`;
72
+ const beforeDateEndOfDay = formatISO(endOfDay(parse(beforeDate, DEFAULT_DATE_FORMAT, new Date())));
73
+ const ignores = exclude && exclude.length > 0 ? `. "!(${exclude.join('|')})"` : undefined;
40
74
  try {
41
- // Generate structured report data
42
- const entries = this.fetchGitCommitData(sinceDate);
43
- this.log('Fetched %d commit entries', entries.length);
44
- const reportData = this.generateReportData(entries);
45
- // Handle different output scenarios
46
- if (isJson) {
47
- // JSON mode
48
- if (save) {
49
- try {
50
- fs.writeFileSync(path.resolve(`${filenamePrefix}.committers.json`), JSON.stringify(reportData, null, 2));
51
- this.log('Report written to json');
52
- }
53
- catch (error) {
54
- this.error(`Failed to save JSON report: ${getErrorMessage(error)}`);
55
- }
56
- }
57
- return reportData;
75
+ const entries = this.fetchGitCommitData(afterDateStartOfDay, beforeDateEndOfDay, ignores, cwd);
76
+ if (entries.length === 0) {
77
+ return `No commits found between ${afterDate} and ${beforeDate}`;
58
78
  }
59
- const textOutput = formatAsText(reportData);
60
- if (csv) {
61
- // CSV mode
62
- const csvOutput = formatAsCsv(reportData);
63
- if (save) {
64
- try {
65
- fs.writeFileSync(path.resolve(`${filenamePrefix}.committers.csv`), csvOutput);
66
- this.log('Report written to csv');
79
+ this.log('\nFetched %d commit entries\n', entries.length);
80
+ const reportData = monthly ? generateMonthlyReport(entries) : generateCommittersReport(entries);
81
+ let finalReport;
82
+ switch (reportFormat) {
83
+ case 'json':
84
+ finalReport = JSON.stringify(reportData.map((row) => 'month' in row
85
+ ? {
86
+ month: row.month,
87
+ start: row.start,
88
+ end: row.end,
89
+ committers: row.committers,
90
+ }
91
+ : {
92
+ name: row.author,
93
+ count: row.commits.length,
94
+ lastCommitDate: formatDate(row.lastCommitOn, DEFAULT_DATE_COMMIT_FORMAT),
95
+ }), null, 2);
96
+ break;
97
+ case 'csv':
98
+ finalReport = reportData
99
+ .map((row, index) => 'month' in row
100
+ ? `${index},${row.month},${row.start},${row.end},${row.totalCommits}`
101
+ : `${index},${row.author},${row.commits.length},${formatDate(row.lastCommitOn, DEFAULT_DATE_COMMIT_FORMAT).replace(',', '')}`)
102
+ .join('\n')
103
+ .replace(/^/, monthly ? `(index),month,start,end,totalCommits\n` : `(index),Committer,Commits,Last Commit Date\n`);
104
+ break;
105
+ default:
106
+ if (monthly) {
107
+ finalReport = makeTable({
108
+ title: 'Monthly Report',
109
+ data: reportData
110
+ .filter((row) => 'month' in row)
111
+ .map((row, index) => ({
112
+ index,
113
+ month: row.month,
114
+ start: row.start,
115
+ end: row.end,
116
+ totalCommits: row.totalCommits,
117
+ })),
118
+ headerOptions: {
119
+ color: undefined,
120
+ bold: false,
121
+ },
122
+ });
67
123
  }
68
- catch (error) {
69
- this.error(`Failed to save CSV report: ${getErrorMessage(error)}`);
124
+ else {
125
+ finalReport = makeTable({
126
+ title: 'Committers Report',
127
+ data: reportData
128
+ .filter((row) => 'author' in row)
129
+ .map((row, index) => ({
130
+ index,
131
+ author: row.author,
132
+ commits: row.commits.length,
133
+ lastCommitOn: formatDate(row.lastCommitOn, DEFAULT_DATE_COMMIT_FORMAT),
134
+ })),
135
+ columns: [
136
+ {
137
+ key: 'index',
138
+ name: '(index)',
139
+ },
140
+ {
141
+ key: 'author',
142
+ name: 'Committer',
143
+ },
144
+ {
145
+ key: 'commits',
146
+ name: 'Commits',
147
+ },
148
+ {
149
+ key: 'lastCommitOn',
150
+ name: 'Last Commit Date',
151
+ },
152
+ ],
153
+ headerOptions: {
154
+ color: undefined,
155
+ bold: false,
156
+ },
157
+ });
70
158
  }
71
- }
72
- else {
73
- this.log(textOutput);
74
- }
75
- return csvOutput;
159
+ break;
76
160
  }
77
161
  if (save) {
78
162
  try {
79
- fs.writeFileSync(path.resolve(`${filenamePrefix}.committers.txt`), textOutput);
80
- this.log('Report written to txt');
163
+ fs.writeFileSync(`${filenamePrefix}.${monthly ? 'monthly' : 'committers'}.${reportFormat}`, finalReport, {
164
+ encoding: 'utf-8',
165
+ });
166
+ this.log(`Report written to ${reportFormat.toUpperCase()}`);
81
167
  }
82
- catch (error) {
83
- this.error(`Failed to save txt report: ${getErrorMessage(error)}`);
168
+ catch (err) {
169
+ this.error(`Failed to save ${reportFormat.toUpperCase()} report: ${getErrorMessage(err)}`);
84
170
  }
85
171
  }
86
- else {
87
- this.log(textOutput);
88
- }
89
- return textOutput;
172
+ this.log(finalReport);
173
+ return finalReport;
90
174
  }
91
175
  catch (error) {
92
176
  this.error(`Failed to generate report: ${getErrorMessage(error)}`);
93
177
  }
94
178
  }
95
- /**
96
- * Generates structured report data
97
- * @param entries - parsed git log output for commits
98
- */
99
- generateReportData(entries) {
100
- if (entries.length === 0) {
101
- return { monthly: {}, overall: { total: 0 } };
102
- }
103
- const monthlyData = groupCommitsByMonth(entries);
104
- const overallStats = calculateOverallStats(entries);
105
- const grandTotal = entries.length;
106
- // Format into a structured report data object
107
- const report = {
108
- monthly: {},
109
- overall: { ...overallStats, total: grandTotal },
110
- };
111
- // Add monthly totals
112
- for (const [month, authors] of Object.entries(monthlyData)) {
113
- const monthTotal = Object.values(authors).reduce((sum, count) => sum + count, 0);
114
- report.monthly[month] = { ...authors, total: monthTotal };
115
- }
116
- return report;
117
- }
118
179
  /**
119
180
  * Fetches git commit data with month and author information
120
181
  * @param sinceDate - Date range for git log
182
+ * @param beforeDateEndOfDay - End date for git log
183
+ * @param ignores - indicate elements to exclude for git log
184
+ * @param cwd - directory to use for git log
121
185
  */
122
- fetchGitCommitData(sinceDate) {
123
- const logProcess = spawnSync('git', [
186
+ fetchGitCommitData(sinceDate, beforeDateEndOfDay, ignores, cwd) {
187
+ const logParameters = [
124
188
  'log',
125
- '--all', // Include committers on all branches in the repo
126
- '--format="%ad|%an"', // Format: date|author
127
- '--date=format:%Y-%m', // Format date as YYYY-MM
128
189
  `--since="${sinceDate}"`,
129
- ], { encoding: 'utf-8' });
190
+ `--until="${beforeDateEndOfDay}"`,
191
+ `--format=${GIT_OUTPUT_FORMAT}`,
192
+ ...(cwd ? ['--', cwd] : []),
193
+ ...(ignores ? ['--', ignores] : []),
194
+ ];
195
+ const logProcess = spawnSync('git', logParameters, {
196
+ encoding: 'utf-8',
197
+ });
130
198
  if (logProcess.error) {
131
199
  if (isErrnoException(logProcess.error)) {
132
200
  if (logProcess.error.code === 'ENOENT') {
@@ -16,11 +16,13 @@ export default class ScanEol extends Command {
16
16
  sbomOutput: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
17
  saveTrimmedSbom: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
18
  hideReportUrl: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ automated: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
20
  version: import("@oclif/core/interfaces").BooleanFlag<void>;
20
21
  };
21
22
  run(): Promise<EolReport | undefined>;
22
23
  private loadSbom;
23
24
  private scanSbom;
25
+ private getScanLoadTime;
24
26
  private saveReport;
25
27
  private saveSbom;
26
28
  private saveTrimmedSbom;
@@ -1,9 +1,11 @@
1
1
  import { trimCdxBom } from '@herodevs/eol-shared';
2
2
  import { Command, Flags } from '@oclif/core';
3
3
  import ora from 'ora';
4
+ import { ApiError } from "../../api/errors.js";
4
5
  import { submitScan } from "../../api/nes.client.js";
5
- import { config, filenamePrefix } from "../../config/constants.js";
6
+ import { config, filenamePrefix, SCAN_ORIGIN_AUTOMATED, SCAN_ORIGIN_CLI } from "../../config/constants.js";
6
7
  import { track } from "../../service/analytics.svc.js";
8
+ import { AUTH_ERROR_MESSAGES, getTokenForScanWithSource } from "../../service/auth.svc.js";
7
9
  import { createSbom } from "../../service/cdx.svc.js";
8
10
  import { countComponentsByStatus, formatDataPrivacyLink, formatReportSaveHint, formatScanResults, formatWebReportUrl, } from "../../service/display.svc.js";
9
11
  import { readSbomFromFile, saveArtifactToFile, validateDirectory } from "../../service/file.svc.js";
@@ -68,10 +70,19 @@ export default class ScanEol extends Command {
68
70
  default: false,
69
71
  description: 'Hide the generated web report URL for this scan',
70
72
  }),
73
+ automated: Flags.boolean({
74
+ default: false,
75
+ description: 'Mark scan as automated (for CI/CD pipelines)',
76
+ }),
71
77
  version: Flags.version(),
72
78
  };
73
79
  async run() {
74
80
  const { flags } = await this.parse(ScanEol);
81
+ const { source } = await getTokenForScanWithSource();
82
+ if (source === 'ci') {
83
+ this.log('CI credentials found');
84
+ this.log('Using CI credentials');
85
+ }
75
86
  track('CLI EOL Scan Started', (context) => ({
76
87
  command: context.command,
77
88
  command_flags: context.command_flags,
@@ -116,7 +127,6 @@ export default class ScanEol extends Command {
116
127
  }
117
128
  const scanStartTime = performance.now();
118
129
  const scan = await this.scanSbom(sbom);
119
- const scanEndTime = performance.now();
120
130
  const componentCounts = countComponentsByStatus(scan);
121
131
  track('CLI EOL Scan Completed', (context) => ({
122
132
  command: context.command,
@@ -126,7 +136,7 @@ export default class ScanEol extends Command {
126
136
  nes_available_count: componentCounts.NES_AVAILABLE,
127
137
  number_of_packages: componentCounts.TOTAL,
128
138
  sbom_created: !flags.file,
129
- scan_load_time: (scanEndTime - scanStartTime) / 1000,
139
+ scan_load_time: this.getScanLoadTime(scanStartTime),
130
140
  scanned_ecosystems: componentCounts.ECOSYSTEMS,
131
141
  web_report_link: !flags.hideReportUrl && scan.id ? `${config.eolReportUrl}/${scan.id}` : undefined,
132
142
  web_report_hidden: flags.hideReportUrl,
@@ -159,6 +169,8 @@ export default class ScanEol extends Command {
159
169
  return sbom;
160
170
  }
161
171
  async scanSbom(sbom) {
172
+ const scanStartTime = performance.now();
173
+ const numberOfPackages = sbom.components?.length ?? 0;
162
174
  const { flags } = await this.parse(ScanEol);
163
175
  const spinner = ora().start('Trimming SBOM');
164
176
  const trimmedSbom = trimCdxBom(sbom);
@@ -173,21 +185,39 @@ export default class ScanEol extends Command {
173
185
  }
174
186
  spinner.start('Scanning for EOL packages');
175
187
  try {
176
- const scan = await submitScan({ sbom: trimmedSbom });
188
+ const scanOrigin = flags.automated ? SCAN_ORIGIN_AUTOMATED : SCAN_ORIGIN_CLI;
189
+ const scan = await submitScan({ sbom: trimmedSbom, scanOrigin });
177
190
  spinner.succeed('Scan completed');
178
191
  return scan;
179
192
  }
180
193
  catch (error) {
181
194
  spinner.fail('Scanning failed');
195
+ const scanLoadTime = this.getScanLoadTime(scanStartTime);
196
+ if (error instanceof ApiError) {
197
+ track('CLI EOL Scan Failed', (context) => ({
198
+ command: context.command,
199
+ command_flags: context.command_flags,
200
+ scan_failure_reason: error.code,
201
+ scan_load_time: scanLoadTime,
202
+ number_of_packages: numberOfPackages,
203
+ }));
204
+ const message = AUTH_ERROR_MESSAGES[error.code] ?? error.message?.trim();
205
+ this.error(message);
206
+ }
182
207
  const errorMessage = getErrorMessage(error);
183
208
  track('CLI EOL Scan Failed', (context) => ({
184
209
  command: context.command,
185
210
  command_flags: context.command_flags,
186
211
  scan_failure_reason: errorMessage,
212
+ scan_load_time: scanLoadTime,
213
+ number_of_packages: numberOfPackages,
187
214
  }));
188
215
  this.error(`Failed to submit scan to NES. ${errorMessage}`);
189
216
  }
190
217
  }
218
+ getScanLoadTime(scanStartTime) {
219
+ return (performance.now() - scanStartTime) / 1000;
220
+ }
191
221
  saveReport(report, dir, outputPath) {
192
222
  try {
193
223
  return saveArtifactToFile(dir, { kind: 'report', payload: report, outputPath });
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Init extends Command {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ overwrite: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ outputDir: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ configFile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ ignorePatterns: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,84 @@
1
+ import { confirm } from '@inquirer/prompts';
2
+ import { Command, Flags } from '@oclif/core';
3
+ import { TRACKER_DEFAULT_CONFIG } from '../../config/tracker.config.js';
4
+ import { createTrackerConfig, getRootDir } from '../../service/tracker.svc.js';
5
+ export default class Init extends Command {
6
+ static description = 'Initialize the tracker configuration';
7
+ static enableJsonFlag = false;
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %>',
10
+ '<%= config.bin %> <%= command.id %> -d trackerDir',
11
+ '<%= config.bin %> <%= command.id %> -d trackerDir -f configFileName',
12
+ '<%= config.bin %> <%= command.id %> -i node_modules',
13
+ '<%= config.bin %> <%= command.id %> -i node_modules -i custom_modules',
14
+ '<%= config.bin %> <%= command.id %> -o',
15
+ ];
16
+ static flags = {
17
+ overwrite: Flags.boolean({
18
+ char: 'o',
19
+ description: 'Overwrites the tracker configuration file if it exists',
20
+ }),
21
+ force: Flags.boolean({
22
+ description: 'Force tracker configuration file creation. Use with --overwrite flag',
23
+ dependsOn: ['overwrite'],
24
+ }),
25
+ outputDir: Flags.string({
26
+ char: 'd',
27
+ description: 'Output directory for the tracker configuration file',
28
+ default: 'hd-tracker',
29
+ }),
30
+ configFile: Flags.string({
31
+ char: 'f',
32
+ description: 'Filename for the tracker configuration file',
33
+ default: 'config.json',
34
+ }),
35
+ ignorePatterns: Flags.string({
36
+ char: 'i',
37
+ description: 'Ignore patterns to use for the tracker configuration file',
38
+ multiple: true,
39
+ multipleNonGreedy: true,
40
+ default: ['node_modules'],
41
+ }),
42
+ };
43
+ async run() {
44
+ const { flags } = await this.parse(Init);
45
+ const { overwrite, outputDir, configFile, ignorePatterns, force } = flags;
46
+ this.log('Starting tracker init command');
47
+ if (overwrite) {
48
+ if (force) {
49
+ this.warn(`You're using the --force flag along the --overwrite flag.`);
50
+ }
51
+ else {
52
+ const response = await confirm({
53
+ message: `You're using the overwrite flag. If a previous configuration file exists, it will be replaced. Do you want to continue?`,
54
+ default: false,
55
+ });
56
+ this.log(response ? 'Yes' : 'No');
57
+ if (!response) {
58
+ return;
59
+ }
60
+ }
61
+ }
62
+ try {
63
+ const rootDir = getRootDir(global.process.cwd());
64
+ const outputConfig = {
65
+ ...TRACKER_DEFAULT_CONFIG,
66
+ outputDir,
67
+ configFile,
68
+ ignorePatterns,
69
+ };
70
+ await createTrackerConfig(rootDir, outputConfig, overwrite);
71
+ this.log(`Tracker init command completed successfully.`);
72
+ }
73
+ catch (err) {
74
+ if (err instanceof Error) {
75
+ this.error(err, {
76
+ message: err.message,
77
+ });
78
+ }
79
+ else {
80
+ this.error('An unknown error occurred while running the tracker init command');
81
+ }
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Run extends Command {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ configDir: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ configFile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<void>;
11
+ /**
12
+ * Fetches Git last commit
13
+ */
14
+ private fetchGitLastCommit;
15
+ }