@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.
- package/README.md +192 -20
- package/dist/api/apollo.client.d.ts +3 -0
- package/dist/api/apollo.client.js +53 -0
- package/dist/api/ci-token.client.d.ts +26 -0
- package/dist/api/ci-token.client.js +95 -0
- package/dist/api/errors.d.ts +8 -0
- package/dist/api/errors.js +13 -0
- package/dist/api/gql-operations.d.ts +3 -0
- package/dist/api/gql-operations.js +36 -1
- package/dist/api/graphql-errors.d.ts +6 -0
- package/dist/api/graphql-errors.js +22 -0
- package/dist/api/nes.client.d.ts +1 -2
- package/dist/api/nes.client.js +31 -20
- package/dist/api/user-setup.client.d.ts +15 -0
- package/dist/api/user-setup.client.js +92 -0
- package/dist/commands/auth/login.d.ts +14 -0
- package/dist/commands/auth/login.js +225 -0
- package/dist/commands/auth/logout.d.ts +5 -0
- package/dist/commands/auth/logout.js +27 -0
- package/dist/commands/auth/provision-ci-token.d.ts +5 -0
- package/dist/commands/auth/provision-ci-token.js +62 -0
- package/dist/commands/report/committers.d.ts +11 -7
- package/dist/commands/report/committers.js +144 -76
- package/dist/commands/scan/eol.d.ts +2 -0
- package/dist/commands/scan/eol.js +34 -4
- package/dist/commands/tracker/init.d.ts +14 -0
- package/dist/commands/tracker/init.js +84 -0
- package/dist/commands/tracker/run.d.ts +15 -0
- package/dist/commands/tracker/run.js +183 -0
- package/dist/config/constants.d.ts +14 -0
- package/dist/config/constants.js +15 -0
- package/dist/config/tracker.config.d.ts +16 -0
- package/dist/config/tracker.config.js +16 -0
- package/dist/hooks/finally/finally.js +10 -4
- package/dist/hooks/init/01_initialize_amplitude.js +20 -9
- package/dist/service/analytics.svc.d.ts +10 -3
- package/dist/service/analytics.svc.js +180 -18
- package/dist/service/auth-config.svc.d.ts +5 -0
- package/dist/service/auth-config.svc.js +20 -0
- package/dist/service/auth-refresh.svc.d.ts +8 -0
- package/dist/service/auth-refresh.svc.js +45 -0
- package/dist/service/auth-token.svc.d.ts +11 -0
- package/dist/service/auth-token.svc.js +48 -0
- package/dist/service/auth.svc.d.ts +27 -0
- package/dist/service/auth.svc.js +88 -0
- package/dist/service/ci-auth.svc.d.ts +6 -0
- package/dist/service/ci-auth.svc.js +32 -0
- package/dist/service/ci-token.svc.d.ts +6 -0
- package/dist/service/ci-token.svc.js +75 -0
- package/dist/service/committers.svc.d.ts +46 -58
- package/dist/service/committers.svc.js +55 -173
- package/dist/service/jwt.svc.d.ts +1 -0
- package/dist/service/jwt.svc.js +19 -0
- package/dist/service/tracker.svc.d.ts +58 -0
- package/dist/service/tracker.svc.js +101 -0
- package/dist/types/auth.d.ts +9 -0
- package/dist/types/auth.js +1 -0
- package/dist/utils/open-in-browser.d.ts +1 -0
- package/dist/utils/open-in-browser.js +21 -0
- package/dist/utils/retry.d.ts +11 -0
- package/dist/utils/retry.js +29 -0
- package/dist/utils/strip-typename.js +2 -1
- 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 {
|
|
6
|
-
import {
|
|
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
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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(
|
|
80
|
-
|
|
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 (
|
|
83
|
-
this.error(`Failed to save
|
|
168
|
+
catch (err) {
|
|
169
|
+
this.error(`Failed to save ${reportFormat.toUpperCase()} report: ${getErrorMessage(err)}`);
|
|
84
170
|
}
|
|
85
171
|
}
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
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
|
-
|
|
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: (
|
|
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
|
|
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
|
+
}
|