@herodevs/cli 1.0.0-beta.2 → 1.2.0-beta.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.
- package/README.md +30 -325
- package/bin/dev.js +6 -6
- package/bin/run.js +3 -2
- package/dist/api/client.d.ts +0 -2
- package/dist/api/client.js +19 -18
- package/dist/api/nes/nes.client.d.ts +4 -0
- package/dist/api/nes/nes.client.js +11 -0
- package/dist/api/queries/nes/sbom.js +5 -0
- package/dist/api/types/nes.types.d.ts +17 -3
- package/dist/api/types/nes.types.js +11 -1
- package/dist/commands/report/committers.d.ts +3 -2
- package/dist/commands/report/committers.js +75 -33
- package/dist/commands/report/purls.d.ts +4 -2
- package/dist/commands/report/purls.js +51 -31
- package/dist/commands/scan/eol.d.ts +13 -4
- package/dist/commands/scan/eol.js +112 -37
- package/dist/commands/scan/sbom.d.ts +4 -1
- package/dist/commands/scan/sbom.js +86 -33
- package/dist/hooks/prerun.js +8 -0
- package/dist/service/committers.svc.js +24 -3
- package/dist/service/eol/cdx.svc.d.ts +52 -0
- package/dist/service/eol/cdx.svc.js +58 -62
- package/dist/service/eol/eol.svc.d.ts +0 -21
- package/dist/service/eol/eol.svc.js +2 -62
- package/dist/service/eol/sbom.worker.d.ts +1 -0
- package/dist/service/eol/sbom.worker.js +26 -0
- package/dist/service/error.svc.d.ts +8 -0
- package/dist/service/error.svc.js +28 -0
- package/dist/service/log.svc.d.ts +5 -8
- package/dist/service/log.svc.js +5 -18
- package/dist/service/nes/nes.svc.js +4 -3
- package/dist/service/purls.svc.js +1 -1
- package/dist/ui/date.ui.d.ts +1 -0
- package/dist/ui/date.ui.js +15 -0
- package/dist/ui/eol.ui.d.ts +4 -3
- package/dist/ui/eol.ui.js +56 -15
- package/dist/ui/shared.us.d.ts +3 -0
- package/dist/ui/shared.us.js +13 -0
- package/package.json +13 -14
- package/dist/hooks/init/update.d.ts +0 -2
- package/dist/hooks/init/update.js +0 -5
- package/dist/hooks/prerun/CommandContextHook.js +0 -8
- package/dist/service/line.svc.d.ts +0 -24
- package/dist/service/line.svc.js +0 -61
- /package/dist/hooks/{prerun/CommandContextHook.d.ts → prerun.d.ts} +0 -0
|
@@ -2,15 +2,16 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { calculateOverallStats,
|
|
5
|
+
import { calculateOverallStats, formatAsCsv, formatAsText, groupCommitsByMonth, parseGitLogOutput, } from "../../service/committers.svc.js";
|
|
6
|
+
import { getErrorMessage, isErrnoException } from "../../service/error.svc.js";
|
|
6
7
|
export default class Committers extends Command {
|
|
7
8
|
static description = 'Generate report of committers to a git repository';
|
|
8
9
|
static enableJsonFlag = true;
|
|
9
10
|
static examples = [
|
|
10
11
|
'<%= config.bin %> <%= command.id %>',
|
|
11
|
-
'<%= config.bin %> <%= command.id %>
|
|
12
|
-
'<%= config.bin %> <%= command.id %> --
|
|
13
|
-
'<%= config.bin %> <%= command.id %> --
|
|
12
|
+
'<%= config.bin %> <%= command.id %> --csv -s',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> --csv',
|
|
14
15
|
];
|
|
15
16
|
static flags = {
|
|
16
17
|
months: Flags.integer({
|
|
@@ -18,11 +19,10 @@ export default class Committers extends Command {
|
|
|
18
19
|
description: 'The number of months of git history to review',
|
|
19
20
|
default: 12,
|
|
20
21
|
}),
|
|
21
|
-
|
|
22
|
-
char: '
|
|
23
|
-
description: 'Output
|
|
24
|
-
|
|
25
|
-
default: 'text',
|
|
22
|
+
csv: Flags.boolean({
|
|
23
|
+
char: 'c',
|
|
24
|
+
description: 'Output in CSV format',
|
|
25
|
+
default: false,
|
|
26
26
|
}),
|
|
27
27
|
save: Flags.boolean({
|
|
28
28
|
char: 's',
|
|
@@ -32,24 +32,63 @@ export default class Committers extends Command {
|
|
|
32
32
|
};
|
|
33
33
|
async run() {
|
|
34
34
|
const { flags } = await this.parse(Committers);
|
|
35
|
-
const { months,
|
|
35
|
+
const { months, csv, save } = flags;
|
|
36
|
+
const isJson = this.jsonEnabled();
|
|
36
37
|
const sinceDate = `${months} months ago`;
|
|
38
|
+
this.log('Starting committers report with flags: %O', flags);
|
|
37
39
|
try {
|
|
38
40
|
// Generate structured report data
|
|
39
41
|
const entries = this.fetchGitCommitData(sinceDate);
|
|
42
|
+
this.log('Fetched %d commit entries', entries.length);
|
|
40
43
|
const reportData = this.generateReportData(entries);
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
// Handle different output scenarios
|
|
45
|
+
if (isJson) {
|
|
46
|
+
// JSON mode
|
|
47
|
+
if (save) {
|
|
48
|
+
try {
|
|
49
|
+
fs.writeFileSync(path.resolve('nes.committers.json'), JSON.stringify(reportData, null, 2));
|
|
50
|
+
this.log('Report written to json');
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this.error(`Failed to save JSON report: ${getErrorMessage(error)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return reportData;
|
|
57
|
+
}
|
|
58
|
+
const textOutput = formatAsText(reportData);
|
|
59
|
+
if (csv) {
|
|
60
|
+
// CSV mode
|
|
61
|
+
const csvOutput = formatAsCsv(reportData);
|
|
62
|
+
if (save) {
|
|
63
|
+
try {
|
|
64
|
+
fs.writeFileSync(path.resolve('nes.committers.csv'), csvOutput);
|
|
65
|
+
this.log('Report written to csv');
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
this.error(`Failed to save CSV report: ${getErrorMessage(error)}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.log(textOutput);
|
|
73
|
+
}
|
|
74
|
+
return csvOutput;
|
|
75
|
+
}
|
|
43
76
|
if (save) {
|
|
44
|
-
|
|
45
|
-
|
|
77
|
+
try {
|
|
78
|
+
fs.writeFileSync(path.resolve('nes.committers.txt'), textOutput);
|
|
79
|
+
this.log('Report written to txt');
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.error(`Failed to save txt report: ${getErrorMessage(error)}`);
|
|
83
|
+
}
|
|
46
84
|
}
|
|
47
85
|
else {
|
|
48
|
-
this.log(
|
|
86
|
+
this.log(textOutput);
|
|
49
87
|
}
|
|
88
|
+
return textOutput;
|
|
50
89
|
}
|
|
51
90
|
catch (error) {
|
|
52
|
-
this.error(`Failed to generate report: ${error
|
|
91
|
+
this.error(`Failed to generate report: ${getErrorMessage(error)}`);
|
|
53
92
|
}
|
|
54
93
|
}
|
|
55
94
|
/**
|
|
@@ -80,25 +119,28 @@ export default class Committers extends Command {
|
|
|
80
119
|
* @param sinceDate - Date range for git log
|
|
81
120
|
*/
|
|
82
121
|
fetchGitCommitData(sinceDate) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (logProcess.error) {
|
|
92
|
-
|
|
122
|
+
const logProcess = spawnSync('git', [
|
|
123
|
+
'log',
|
|
124
|
+
'--all', // Include committers on all branches in the repo
|
|
125
|
+
'--format="%ad|%an"', // Format: date|author
|
|
126
|
+
'--date=format:%Y-%m', // Format date as YYYY-MM
|
|
127
|
+
`--since="${sinceDate}"`,
|
|
128
|
+
], { encoding: 'utf-8' });
|
|
129
|
+
if (logProcess.error) {
|
|
130
|
+
if (isErrnoException(logProcess.error)) {
|
|
131
|
+
if (logProcess.error.code === 'ENOENT') {
|
|
132
|
+
this.error('Git command not found. Please ensure git is installed and available in your PATH.');
|
|
133
|
+
}
|
|
134
|
+
this.error(`Git command failed: ${getErrorMessage(logProcess.error)}`);
|
|
93
135
|
}
|
|
94
|
-
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
return parseGitLogOutput(logProcess.stdout);
|
|
136
|
+
this.error(`Git command failed: ${getErrorMessage(logProcess.error)}`);
|
|
98
137
|
}
|
|
99
|
-
|
|
100
|
-
this.error(`
|
|
101
|
-
|
|
138
|
+
if (logProcess.status !== 0) {
|
|
139
|
+
this.error(`Git command failed with status ${logProcess.status}: ${logProcess.stderr}`);
|
|
140
|
+
}
|
|
141
|
+
if (!logProcess.stdout) {
|
|
142
|
+
return [];
|
|
102
143
|
}
|
|
144
|
+
return parseGitLogOutput(logProcess.stdout);
|
|
103
145
|
}
|
|
104
146
|
}
|
|
@@ -7,7 +7,9 @@ export default class ReportPurls extends Command {
|
|
|
7
7
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
|
|
10
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
};
|
|
12
|
-
run(): Promise<
|
|
12
|
+
run(): Promise<{
|
|
13
|
+
purls: string[];
|
|
14
|
+
}>;
|
|
13
15
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Command, Flags, ux } from '@oclif/core';
|
|
4
|
+
import { getErrorMessage, isErrnoException } from "../../service/error.svc.js";
|
|
4
5
|
import { extractPurls, getPurlOutput } from "../../service/purls.svc.js";
|
|
5
|
-
import
|
|
6
|
+
import ScanSbom from "../scan/sbom.js";
|
|
6
7
|
export default class ReportPurls extends Command {
|
|
7
8
|
static description = 'Generate a list of purls from a sbom';
|
|
8
9
|
static enableJsonFlag = true;
|
|
9
10
|
static examples = [
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --json -s',
|
|
10
12
|
'<%= config.bin %> <%= command.id %> --dir=./my-project',
|
|
11
13
|
'<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
|
|
12
14
|
'<%= config.bin %> <%= command.id %> --dir=./my-project --save',
|
|
13
|
-
'<%= config.bin %> <%= command.id %> --save --
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --save --csv',
|
|
14
16
|
];
|
|
15
17
|
static flags = {
|
|
16
18
|
file: Flags.string({
|
|
@@ -26,41 +28,59 @@ export default class ReportPurls extends Command {
|
|
|
26
28
|
default: false,
|
|
27
29
|
description: 'Save the list of purls as nes.purls.<output>',
|
|
28
30
|
}),
|
|
29
|
-
|
|
30
|
-
char: '
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
description: 'The format of the saved file (when using --save)',
|
|
31
|
+
csv: Flags.boolean({
|
|
32
|
+
char: 'c',
|
|
33
|
+
default: false,
|
|
34
|
+
description: 'Save output in CSV format (only applies when using --save)',
|
|
34
35
|
}),
|
|
35
36
|
};
|
|
36
37
|
async run() {
|
|
37
38
|
const { flags } = await this.parse(ReportPurls);
|
|
38
|
-
const { dir: _dirFlag, file: _fileFlag, save,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.log('Found purls:');
|
|
48
|
-
for (const purl of purls) {
|
|
49
|
-
this.log(purl);
|
|
50
|
-
}
|
|
51
|
-
// Save if requested
|
|
52
|
-
if (save) {
|
|
53
|
-
try {
|
|
54
|
-
const outputPath = path.join(_dirFlag || process.cwd(), `nes.purls.${output}`);
|
|
55
|
-
const purlOutput = getPurlOutput(purls, output);
|
|
56
|
-
fs.writeFileSync(outputPath, purlOutput);
|
|
57
|
-
this.log(`\nPurls saved to ${outputPath}`);
|
|
39
|
+
const { dir: _dirFlag, file: _fileFlag, save, csv } = flags;
|
|
40
|
+
try {
|
|
41
|
+
const sbom = await ScanSbom.loadSbom(flags, this.config);
|
|
42
|
+
const purls = await extractPurls(sbom);
|
|
43
|
+
this.log('Extracted %d purls from SBOM', purls.length);
|
|
44
|
+
ux.action.stop('Scan completed');
|
|
45
|
+
// Print the purls
|
|
46
|
+
for (const purl of purls) {
|
|
47
|
+
this.log(purl);
|
|
58
48
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
// Save if requested
|
|
50
|
+
if (save) {
|
|
51
|
+
try {
|
|
52
|
+
const outputFile = csv && !this.jsonEnabled() ? 'csv' : 'json';
|
|
53
|
+
const outputPath = path.join(_dirFlag || process.cwd(), `nes.purls.${outputFile}`);
|
|
54
|
+
const purlOutput = getPurlOutput(purls, outputFile);
|
|
55
|
+
fs.writeFileSync(outputPath, purlOutput);
|
|
56
|
+
this.log('Purls saved to %s', outputPath);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (isErrnoException(error)) {
|
|
60
|
+
switch (error.code) {
|
|
61
|
+
case 'EACCES':
|
|
62
|
+
this.error('Permission denied: Cannot write to output file');
|
|
63
|
+
break;
|
|
64
|
+
case 'ENOSPC':
|
|
65
|
+
this.error('No space left on device');
|
|
66
|
+
break;
|
|
67
|
+
case 'EISDIR':
|
|
68
|
+
this.error('Cannot write to output file: Is a directory');
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
this.error(`Failed to save purls: ${getErrorMessage(error)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.error(`Failed to save purls: ${getErrorMessage(error)}`);
|
|
75
|
+
}
|
|
62
76
|
}
|
|
77
|
+
// Return wrapped object with metadata
|
|
78
|
+
return {
|
|
79
|
+
purls,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
this.error(`Failed to generate PURLs: ${getErrorMessage(error)}`);
|
|
63
84
|
}
|
|
64
|
-
return purls;
|
|
65
85
|
}
|
|
66
86
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ScanResultComponent } from '../../api/types/nes.types.ts';
|
|
3
3
|
export default class ScanEol extends Command {
|
|
4
4
|
static description: string;
|
|
5
5
|
static enableJsonFlag: boolean;
|
|
@@ -8,9 +8,18 @@ export default class ScanEol extends Command {
|
|
|
8
8
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
getCustomerSupport: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
13
|
};
|
|
12
|
-
run(): Promise<
|
|
13
|
-
components: [];
|
|
14
|
+
run(): Promise<{
|
|
15
|
+
components: ScanResultComponent[];
|
|
14
16
|
}>;
|
|
15
|
-
private
|
|
17
|
+
private scanSbom;
|
|
18
|
+
private getFilteredComponents;
|
|
19
|
+
private saveReport;
|
|
20
|
+
private displayResults;
|
|
21
|
+
private displayNoComponentsMessage;
|
|
22
|
+
private logLine;
|
|
23
|
+
private displayStatusSection;
|
|
24
|
+
private logLegend;
|
|
16
25
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import { Command, Flags, ux } from '@oclif/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { submitScan } from "../../api/nes/nes.client.js";
|
|
4
|
+
import { getErrorMessage, isErrnoException } from "../../service/error.svc.js";
|
|
5
|
+
import { extractPurls } from "../../service/purls.svc.js";
|
|
6
|
+
import { createStatusDisplay } from "../../ui/eol.ui.js";
|
|
7
|
+
import { INDICATORS, STATUS_COLORS } from "../../ui/shared.us.js";
|
|
8
|
+
import ScanSbom from "./sbom.js";
|
|
5
9
|
export default class ScanEol extends Command {
|
|
6
10
|
static description = 'Scan a given sbom for EOL data';
|
|
7
11
|
static enableJsonFlag = true;
|
|
8
12
|
static examples = [
|
|
9
13
|
'<%= config.bin %> <%= command.id %> --dir=./my-project',
|
|
10
14
|
'<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> -a --dir=./my-project',
|
|
11
16
|
];
|
|
12
17
|
static flags = {
|
|
13
18
|
file: Flags.string({
|
|
@@ -23,49 +28,119 @@ export default class ScanEol extends Command {
|
|
|
23
28
|
default: false,
|
|
24
29
|
description: 'Save the generated SBOM as nes.sbom.json in the scanned directory',
|
|
25
30
|
}),
|
|
31
|
+
all: Flags.boolean({
|
|
32
|
+
char: 'a',
|
|
33
|
+
description: 'Show all components (default is EOL and LTS only)',
|
|
34
|
+
default: false,
|
|
35
|
+
}),
|
|
36
|
+
getCustomerSupport: Flags.boolean({
|
|
37
|
+
char: 'c',
|
|
38
|
+
description: 'Get Never-Ending Support for End-of-Life components',
|
|
39
|
+
default: false,
|
|
40
|
+
}),
|
|
26
41
|
};
|
|
27
42
|
async run() {
|
|
28
|
-
this.checkEolScanDisabled();
|
|
29
43
|
const { flags } = await this.parse(ScanEol);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
if (flags.getCustomerSupport) {
|
|
45
|
+
this.log(ux.colorize('yellow', 'Never-Ending Support is on the way. Please stay tuned for this feature.'));
|
|
46
|
+
}
|
|
47
|
+
const sbom = await ScanSbom.loadSbom(flags, this.config);
|
|
48
|
+
const scan = await this.scanSbom(sbom);
|
|
49
|
+
ux.action.stop('\nScan completed');
|
|
50
|
+
const filteredComponents = this.getFilteredComponents(scan, flags.all);
|
|
51
|
+
if (flags.save) {
|
|
52
|
+
await this.saveReport(filteredComponents);
|
|
53
|
+
}
|
|
54
|
+
if (this.jsonEnabled()) {
|
|
55
|
+
return { components: filteredComponents };
|
|
56
|
+
}
|
|
57
|
+
await this.displayResults(scan, flags.all);
|
|
58
|
+
return { components: filteredComponents };
|
|
59
|
+
}
|
|
60
|
+
async scanSbom(sbom) {
|
|
61
|
+
let scan;
|
|
62
|
+
let purls;
|
|
63
|
+
try {
|
|
64
|
+
purls = await extractPurls(sbom);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
this.error(`Failed to extract purls from sbom. ${getErrorMessage(error)}`);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
scan = await submitScan(purls);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
this.error(`Failed to submit scan to NES from sbom. ${getErrorMessage(error)}`);
|
|
74
|
+
}
|
|
75
|
+
if (scan.components.size === 0) {
|
|
76
|
+
this.warn('No components found in scan');
|
|
77
|
+
}
|
|
78
|
+
return scan;
|
|
79
|
+
}
|
|
80
|
+
getFilteredComponents(scan, all) {
|
|
81
|
+
return Array.from(scan.components.entries())
|
|
82
|
+
.filter(([_, component]) => all || ['EOL', 'LTS'].includes(component.info.status))
|
|
83
|
+
.map(([_, component]) => component);
|
|
84
|
+
}
|
|
85
|
+
async saveReport(components) {
|
|
86
|
+
try {
|
|
87
|
+
fs.writeFileSync('nes.eol.json', JSON.stringify({ components }, null, 2));
|
|
88
|
+
this.log('Report saved to nes.eol.json');
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (isErrnoException(error)) {
|
|
92
|
+
switch (error.code) {
|
|
93
|
+
case 'EACCES':
|
|
94
|
+
this.error('Permission denied. Unable to save report to nes.eol.json');
|
|
95
|
+
break;
|
|
96
|
+
case 'ENOSPC':
|
|
97
|
+
this.error('No space left on device. Unable to save report to nes.eol.json');
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
this.error(`Failed to save report: ${getErrorMessage(error)}`);
|
|
101
|
+
}
|
|
41
102
|
}
|
|
42
|
-
|
|
43
|
-
|
|
103
|
+
else {
|
|
104
|
+
this.error(`Failed to save report: ${getErrorMessage(error)}`);
|
|
44
105
|
}
|
|
45
|
-
throw new Error('Scan failed to generate components.');
|
|
46
106
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
107
|
+
}
|
|
108
|
+
async displayResults(scan, all) {
|
|
109
|
+
const { UNKNOWN, OK, LTS, EOL } = createStatusDisplay(scan.components, all);
|
|
110
|
+
if (!UNKNOWN.length && !OK.length && !LTS.length && !EOL.length) {
|
|
111
|
+
this.displayNoComponentsMessage(all);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.log(ux.colorize('bold', 'Here are the results of the scan:'));
|
|
115
|
+
this.logLine();
|
|
116
|
+
// Display sections in order of increasing severity
|
|
117
|
+
for (const components of [UNKNOWN, OK, LTS, EOL]) {
|
|
118
|
+
this.displayStatusSection(components);
|
|
51
119
|
}
|
|
52
|
-
|
|
53
|
-
this.log('What now %o', r);
|
|
54
|
-
return scan;
|
|
120
|
+
this.logLegend();
|
|
55
121
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
122
|
+
displayNoComponentsMessage(all) {
|
|
123
|
+
if (!all) {
|
|
124
|
+
this.log(ux.colorize('yellow', 'No End-of-Life or Long Term Support components found in scan.'));
|
|
125
|
+
this.log(ux.colorize('yellow', 'Use --all flag to view all components.'));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.log(ux.colorize('yellow', 'No components found in scan.'));
|
|
63
129
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
130
|
+
}
|
|
131
|
+
logLine() {
|
|
132
|
+
this.log(ux.colorize('bold', '-'.repeat(50)));
|
|
133
|
+
}
|
|
134
|
+
displayStatusSection(components) {
|
|
135
|
+
if (components.length > 0) {
|
|
136
|
+
this.log(components.join('\n'));
|
|
137
|
+
this.logLine();
|
|
69
138
|
}
|
|
70
139
|
}
|
|
140
|
+
logLegend() {
|
|
141
|
+
this.log(ux.colorize(`${STATUS_COLORS.UNKNOWN}`, `${INDICATORS.UNKNOWN} = No Known Issues`));
|
|
142
|
+
this.log(ux.colorize(`${STATUS_COLORS.OK}`, `${INDICATORS.OK} = OK`));
|
|
143
|
+
this.log(ux.colorize(`${STATUS_COLORS.LTS}`, `${INDICATORS.LTS}= Long Term Support (LTS)`));
|
|
144
|
+
this.log(ux.colorize(`${STATUS_COLORS.EOL}`, `${INDICATORS.EOL} = End of Life (EOL)`));
|
|
145
|
+
}
|
|
71
146
|
}
|
|
@@ -8,11 +8,14 @@ export default class ScanSbom extends Command {
|
|
|
8
8
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
background: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
};
|
|
13
|
+
static loadSbom(flags: Record<string, string>, config: Command['config']): Promise<Sbom>;
|
|
12
14
|
static getSbomArgs(flags: Record<string, string>): string[];
|
|
13
15
|
getScanOptions(): {};
|
|
14
|
-
run(): Promise<Sbom>;
|
|
16
|
+
run(): Promise<Sbom | undefined>;
|
|
15
17
|
private _getSbomFromScan;
|
|
18
|
+
private _getSbomInBackground;
|
|
16
19
|
private _getSbomFromFile;
|
|
17
20
|
private _saveSbom;
|
|
18
21
|
}
|