Skip to content

Commit 8ab1074

Browse files
authored
feat: add scan progress descriptions and loaders (#277)
1 parent 759270b commit 8ab1074

File tree

4 files changed

+229
-19
lines changed

4 files changed

+229
-19
lines changed

package-lock.json

Lines changed: 196 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@oclif/plugin-help": "^6.2.29",
4343
"@oclif/plugin-update": "^4.6.45",
4444
"graphql": "^16.11.0",
45+
"ora": "^8.2.0",
4546
"packageurl-js": "^2.0.1",
4647
"terminal-link": "^4.0.0",
4748
"update-notifier": "^7.3.1"

src/commands/scan/eol.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'node:fs';
22
import path from 'node:path';
33
import { Command, Flags, ux } from '@oclif/core';
4+
import ora from 'ora';
45
import terminalLink from 'terminal-link';
56
import { batchSubmitPurls } from '../../api/nes/nes.client.ts';
67
import type { ScanResult } from '../../api/types/hd-cli.types.js';
@@ -70,20 +71,23 @@ export default class ScanEol extends Command {
7071

7172
private async getScan(flags: Record<string, string>, config: Command['config']): Promise<ScanResult> {
7273
if (flags.purls) {
73-
ux.action.start(`Scanning purls from ${flags.purls}`);
7474
const purls = this.getPurlsFromFile(flags.purls);
75-
return batchSubmitPurls(purls);
75+
return this.scanPurls(purls);
7676
}
7777

7878
const sbom = await ScanSbom.loadSbom(flags, config);
7979
return this.scanSbom(sbom);
8080
}
8181

8282
private getPurlsFromFile(filePath: string): string[] {
83+
const spinner = ora().start(`Loading purls from \`${filePath}\``);
8384
try {
8485
const purlsFileString = fs.readFileSync(filePath, 'utf8');
85-
return parsePurlsFile(purlsFileString);
86+
const purls = parsePurlsFile(purlsFileString);
87+
spinner.succeed(`Loaded purls from \`${filePath}\``);
88+
return purls;
8689
} catch (error) {
90+
spinner.fail(`Failed to read purls from \`${filePath}\``);
8791
this.error(`Failed to read purls file. ${getErrorMessage(error)}`);
8892
}
8993
}
@@ -100,21 +104,24 @@ export default class ScanEol extends Command {
100104
}
101105

102106
private async scanSbom(sbom: Sbom): Promise<ScanResult> {
103-
let scan: ScanResult;
104-
let purls: string[];
105-
106107
try {
107-
purls = await extractPurls(sbom);
108+
const purls = await extractPurls(sbom);
109+
return this.scanPurls(purls);
108110
} catch (error) {
109111
this.error(`Failed to extract purls from sbom. ${getErrorMessage(error)}`);
110112
}
113+
}
114+
115+
private async scanPurls(purls: string[]): Promise<ScanResult> {
116+
const spinner = ora().start('Scanning for EOL packages');
111117
try {
112-
scan = await batchSubmitPurls(purls);
118+
const scan = await batchSubmitPurls(purls);
119+
spinner.succeed('Scan completed');
120+
return scan;
113121
} catch (error) {
114-
this.error(`Failed to submit scan to NES from sbom. ${getErrorMessage(error)}`);
122+
spinner.fail('Scanning failed');
123+
this.error(`Failed to submit scan to NES. ${getErrorMessage(error)}`);
115124
}
116-
117-
return scan;
118125
}
119126

120127
private async saveReport(components: InsightsEolScanComponent[], createdOn?: string): Promise<void> {

src/commands/scan/sbom.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { spawn } from 'node:child_process';
22
import fs from 'node:fs';
33
import { join, resolve } from 'node:path';
4-
import { Command, Flags, ux } from '@oclif/core';
4+
import { Command, Flags } from '@oclif/core';
5+
import ora from 'ora';
56
import { filenamePrefix } from '../../config/constants.ts';
67
import type { Sbom } from '../../service/eol/cdx.svc.ts';
78
import { createSbom, validateIsCycloneDxSbom } from '../../service/eol/eol.svc.ts';
@@ -73,22 +74,31 @@ export default class ScanSbom extends Command {
7374
}
7475
let sbom: Sbom;
7576
const path = dir || process.cwd();
77+
78+
const spinner = ora();
79+
if (!background) {
80+
spinner.start(flags.file ? 'Loading SBOM file' : 'Generating SBOM');
81+
}
82+
7683
if (file) {
7784
sbom = this._getSbomFromFile(file);
78-
ux.action.stop();
7985
} else if (background) {
8086
this._getSbomInBackground(path);
8187
this.log(`The scan is running in the background. The file will be saved at ${path}/${filenamePrefix}.sbom.json`);
82-
ux.action.stop();
8388
return;
8489
} else {
8590
sbom = await this._getSbomFromScan(path);
86-
ux.action.stop();
8791
if (save) {
8892
this._saveSbom(path, sbom);
8993
}
9094
}
9195

96+
if (sbom) {
97+
spinner.succeed(flags.file ? 'Loaded SBOM file' : 'Generated SBOM');
98+
} else {
99+
spinner.fail(flags.file ? 'Failed to load SBOM file' : 'Failed to generate SBOM');
100+
}
101+
92102
if (!save) {
93103
this.log(JSON.stringify(sbom, null, 2));
94104
}
@@ -107,8 +117,6 @@ export default class ScanSbom extends Command {
107117
this.error(`Path is not a directory: ${dir}`);
108118
}
109119

110-
ux.action.start(`Scanning ${dir}`);
111-
112120
const options = this.getScanOptions();
113121
const sbom = await createSbom(dir, options);
114122
if (!sbom) {
@@ -149,8 +157,6 @@ export default class ScanSbom extends Command {
149157
this.error(`SBOM file not found: ${file}`);
150158
}
151159

152-
ux.action.start(`Loading sbom from ${file}`);
153-
154160
const fileContent = fs.readFileSync(file, {
155161
encoding: 'utf8',
156162
flag: 'r',

0 commit comments

Comments
 (0)