Skip to content

Commit d81bbf3

Browse files
joyeecheungpriyank-p
authored andcommitted
ncu-ci: improve ncu-ci output
- Refactor the ncu-ci command handling - Display health statistics and a TODO list in the `ncu-ci walk <type> --stats` output
1 parent 8d64553 commit d81bbf3

File tree

2 files changed

+268
-136
lines changed

2 files changed

+268
-136
lines changed

bin/ncu-ci

+197-132
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const {
1111
} = require('../lib/ci/ci_type_parser');
1212

1313
const {
14-
PRBuild, BenchmarkRun, CommitBuild,
14+
PRBuild, BenchmarkRun, CommitBuild, HealthBuild,
1515
listBuilds, FailureAggregator, jobCache
1616
} = require('../lib/ci/ci_result_parser');
1717
const clipboardy = require('clipboardy');
@@ -126,169 +126,234 @@ const argv = yargs
126126
.help()
127127
.argv;
128128

129-
async function getResults(cli, request, job) {
130-
let build;
131-
const { type, jobid } = job;
132-
if (type === PR) {
133-
build = new PRBuild(cli, request, jobid);
134-
await build.getResults();
135-
} else if (type === COMMIT) {
136-
build = new CommitBuild(cli, request, jobid);
137-
await build.getResults();
138-
} else if (type === BENCHMARK) {
139-
build = new BenchmarkRun(cli, request, jobid);
140-
await build.getResults();
141-
} else {
142-
yargs.showHelp();
143-
return;
144-
}
145-
return build;
146-
}
129+
const commandToType = {
130+
'commit': COMMIT,
131+
'pr': PR,
132+
'benchmark': BENCHMARK
133+
};
147134

148-
async function runQueue(queue, cli, request, argv) {
149-
let json = [];
150-
let markdown = '';
135+
class CICommand {
136+
constructor(cli, request, argv) {
137+
this.cli = cli;
138+
this.request = request;
139+
this.argv = argv;
140+
this.queue = [];
141+
this.json = [];
142+
this.markdown = '';
143+
}
151144

152-
for (let i = 0; i < queue.length; ++i) {
153-
const job = queue[i];
154-
cli.separator('');
155-
const progress = `[${i + 1}/${queue.length}]`;
156-
if (job.link) {
157-
cli.log(`${progress} Running ${job.link}`);
158-
} else {
159-
cli.log(`${progress} Running ${job.type}: ${job.jobid}`);
145+
async drain() {
146+
if (this.queue.length === 0) {
147+
return;
160148
}
161-
cli.separator('');
162-
const build = await getResults(cli, request, job);
163-
build.display();
164149

165-
json = json.concat(build.formatAsJson());
166-
if ((argv.copy || argv.markdown) && !argv.stats) {
167-
markdown += build.formatAsMarkdown();
150+
const { cli, queue, argv, request } = this;
151+
152+
for (let i = 0; i < queue.length; ++i) {
153+
const job = queue[i];
154+
cli.separator('');
155+
const progress = `[${i + 1}/${queue.length}]`;
156+
if (job.link) {
157+
cli.log(`${progress} Running ${job.link}`);
158+
} else if (job.jobid) {
159+
cli.log(`${progress} Running ${job.type}: ${job.jobid}`);
160+
} else {
161+
cli.log(`${progress} Running ${job.type}`);
162+
}
163+
cli.separator('');
164+
165+
let build;
166+
switch (job.type) {
167+
case 'health':
168+
build = new HealthBuild(cli, request, job.ciType, job.builds);
169+
break;
170+
case PR:
171+
build = new PRBuild(cli, request, job.jobid);
172+
break;
173+
case COMMIT:
174+
build = new CommitBuild(cli, request, job.jobid);
175+
break;
176+
case BENCHMARK:
177+
build = new BenchmarkRun(cli, request, job.jobid);
178+
break;
179+
default:
180+
throw new Error(`Unknown job type ${job.type}`);
181+
}
182+
183+
await build.getResults();
184+
build.display();
185+
186+
const json = build.formatAsJson();
187+
if (json !== undefined) {
188+
this.json = this.json.concat(json);
189+
}
190+
if ((argv.copy || argv.markdown) && !argv.stats) {
191+
this.markdown += build.formatAsMarkdown();
192+
}
168193
}
169194
}
170195

171-
return {
172-
json,
173-
markdown
174-
};
175-
}
196+
async aggregate() { // noop
197+
}
198+
199+
async serialize() {
200+
const { argv, cli } = this;
176201

177-
function pad(any, length) {
178-
return (any + '').padEnd(length);
202+
if (argv.copy) {
203+
if (this.markdown) {
204+
clipboardy.writeSync(this.markdown);
205+
cli.separator('');
206+
cli.log(`Written markdown to clipboard`);
207+
} else {
208+
cli.error('No markdown generated');
209+
}
210+
}
211+
212+
if (argv.markdown) {
213+
if (this.markdown) {
214+
writeFile(argv.markdown, this.markdown);
215+
cli.separator('');
216+
cli.log(`Written markdown to ${argv.markdown}`);
217+
} else {
218+
cli.error('No markdown generated');
219+
}
220+
}
221+
222+
if (argv.json) {
223+
if (this.json.length) {
224+
writeJson(argv.json, this.json);
225+
cli.separator('');
226+
cli.log(`Written JSON to ${argv.json}`);
227+
} else {
228+
cli.error('No JSON generated');
229+
}
230+
}
231+
}
179232
}
180233

181-
// Produces a row for https://github.com/nodejs/reliability#ci-health-history
182-
function displayHealth(builds, cli) {
183-
const [
184-
count, success, pending, aborted, failed, unstable
185-
] = [
186-
builds.count, builds.success.length, builds.pending.length,
187-
builds.aborted.length, builds.failed.length, builds.unstable.length
188-
];
189-
const rate = `${(success / (count - pending - aborted) * 100).toFixed(2)}%`;
190-
// eslint-disable-next-line max-len
191-
cli.log('| UTC Time | RUNNING | SUCCESS | UNSTABLE | ABORTED | FAILURE | Green Rate |');
192-
// eslint-disable-next-line max-len
193-
cli.log('| ---------------- | ------- | ------- | -------- | ------- | ------- | ---------- |');
194-
const time = new Date().toISOString().slice(0, 16).replace('T', ' ');
195-
let result = `| ${time} | ${pad(pending, 7)} | ${pad(success, 8)}|`;
196-
result += ` ${pad(unstable, 8)} | ${pad(aborted, 7)} | ${pad(failed, 7)} |`;
197-
result += ` ${pad(rate, 10)} |`;
198-
cli.log(result);
234+
class RateCommand extends CICommand {
235+
async initialize() {
236+
this.queue.push({
237+
type: 'health',
238+
ciType: commandToType[this.argv.type]
239+
});
240+
}
199241
}
200242

201-
async function main(command, argv) {
202-
const cli = new CLI();
203-
const credentials = await auth({
204-
github: true,
205-
jenkins: true
206-
});
207-
const request = new Request(credentials);
208-
const queue = [];
243+
class WalkCommand extends CICommand {
244+
constructor(cli, request, argv) {
245+
super(cli, request, argv);
246+
if (argv.cache) {
247+
jobCache.enable();
248+
}
249+
}
209250

210-
const commandToType = {
211-
'commit': COMMIT,
212-
'pr': PR,
213-
'benchmark': BENCHMARK
214-
};
251+
async initialize() {
252+
const ciType = commandToType[this.argv.type];
253+
const builds = await listBuilds(this.cli, this.request, ciType);
254+
this.queue.push({ type: 'health', ciType, builds });
255+
for (const build of builds.failed.slice(0, this.argv.limit)) {
256+
this.queue.push(build);
257+
}
258+
}
215259

216-
if (command === 'rate' || command === 'walk') {
217-
const type = commandToType[argv.type];
218-
const builds = await listBuilds(cli, request, type);
219-
if (command === 'walk') {
220-
if (argv.cache) {
221-
jobCache.enable();
222-
}
223-
for (const build of builds.failed.slice(0, argv.limit)) {
224-
queue.push(build);
225-
}
226-
} else {
227-
displayHealth(builds, cli);
260+
async aggregate() {
261+
const { argv, cli } = this;
262+
const aggregator = new FailureAggregator(cli, this.json);
263+
this.json = aggregator.aggregate();
264+
cli.log('');
265+
cli.separator('Stats');
266+
cli.log('');
267+
aggregator.display();
268+
269+
if (argv.markdown || argv.copy) {
270+
this.markdown = aggregator.formatAsMarkdown();
228271
}
229272
}
273+
}
274+
275+
class JobCommand extends CICommand {
276+
constructor(cli, request, argv, command) {
277+
super(cli, request, argv);
278+
this.command = command;
279+
}
280+
281+
async initialize() {
282+
this.queue.push({
283+
type: commandToType[this.command],
284+
jobid: this.argv.jobid
285+
});
286+
}
287+
}
230288

231-
if (command === 'url') {
289+
class URLCommand extends CICommand {
290+
async initialize() {
291+
const { argv, cli, request, queue } = this;
232292
let parsed = parseJobFromURL(argv.url);
233293
if (parsed) {
234294
queue.push({
235295
type: parsed.type,
236296
jobid: parsed.jobid
237297
});
238-
} else {
239-
const parser = await JobParser.fromPR(argv.url, cli, request);
240-
if (!parser) { // Not a valid PR URL
241-
return yargs.showHelp();
242-
}
243-
const ciMap = parser.parse();
244-
for (const [type, ci] of ciMap) {
245-
queue.push({
246-
type: type,
247-
jobid: ci.jobid
248-
});
249-
}
298+
return;
250299
}
251-
} else if (commandToType[command]) {
252-
queue.push({
253-
type: commandToType[command],
254-
jobid: argv.jobid
255-
});
256-
}
257300

258-
if (queue.length > 0) {
259-
const data = await runQueue(queue, cli, request, argv);
301+
// Parse CI links from PR.
302+
const parser = await JobParser.fromPR(argv.url, cli, request);
303+
if (!parser) { // Not a valid PR URL
304+
cli.error(`${argv.url} is not a valid PR URL`);
305+
return;
306+
}
307+
const ciMap = parser.parse();
308+
if (ciMap.size === 0) {
309+
cli.info(`No CI run detected from ${argv.url}`);
310+
}
311+
for (const [type, ci] of ciMap) {
312+
queue.push({
313+
type: type,
314+
jobid: ci.jobid
315+
});
316+
}
317+
}
318+
}
260319

261-
if (command === 'walk' && argv.stats) {
262-
const aggregator = new FailureAggregator(cli, data.json);
263-
data.json = aggregator.aggregate();
264-
cli.log('');
265-
cli.separator('Stats');
266-
cli.log('');
267-
aggregator.display();
320+
async function main(command, argv) {
321+
const cli = new CLI();
322+
const credentials = await auth({
323+
github: true,
324+
jenkins: true
325+
});
326+
const request = new Request(credentials);
268327

269-
if (argv.markdown || argv.copy) {
270-
data.markdown = aggregator.formatAsMarkdown();
271-
}
328+
let commandHandler;
329+
// Prepare queue.
330+
switch (command) {
331+
case 'rate': {
332+
commandHandler = new RateCommand(cli, request, argv);
333+
break;
272334
}
273-
274-
if (argv.copy) {
275-
clipboardy.writeSync(data.markdown);
276-
cli.separator('');
277-
cli.log(`Written markdown to clipboard`);
335+
case 'walk': {
336+
commandHandler = new WalkCommand(cli, request, argv);
337+
break;
278338
}
279-
280-
if (argv.markdown) {
281-
writeFile(argv.markdown, data.markdown);
282-
cli.separator('');
283-
cli.log(`Written markdown to ${argv.markdown}`);
339+
case 'url': {
340+
commandHandler = new URLCommand(cli, request, argv);
341+
break;
284342
}
285-
286-
if (argv.json) {
287-
writeJson(argv.json, data.json);
288-
cli.separator('');
289-
cli.log(`Written JSON to ${argv.json}`);
343+
case 'pr':
344+
case 'commit':
345+
case 'benchmark': {
346+
commandHandler = new JobCommand(cli, request, argv, command);
347+
break;
290348
}
349+
default:
350+
return yargs.showHelp();
291351
}
352+
353+
await commandHandler.initialize();
354+
await commandHandler.drain();
355+
await commandHandler.aggregate();
356+
await commandHandler.serialize();
292357
}
293358

294359
function handler(argv) {

0 commit comments

Comments
 (0)