Skip to content

Commit

Permalink
feat: add ncu-ci cigtm <jobid> (#454)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alicia Lopez committed Jul 9, 2020
1 parent 4b485b3 commit 9dee734
Show file tree
Hide file tree
Showing 8 changed files with 4,389 additions and 17 deletions.
39 changes: 31 additions & 8 deletions bin/ncu-ci
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ const {
JobParser,
parseJobFromURL,
CI_TYPES_KEYS: {
PR, COMMIT, BENCHMARK
PR, COMMIT, BENCHMARK, CITGM
}
} = require('../lib/ci/ci_type_parser');

const {
PRBuild, BenchmarkRun, CommitBuild, HealthBuild,
listBuilds, FailureAggregator, jobCache
PRBuild,
BenchmarkRun,
CommitBuild,
CITGMBuild,
HealthBuild,
listBuilds,
FailureAggregator,
jobCache
} = require('../lib/ci/ci_result_parser');
const clipboardy = require('clipboardy');
const { writeJson, writeFile } = require('../lib/file');
Expand All @@ -28,7 +34,8 @@ const commandKeys = [
'walk',
'url',
'pr',
'commit'
'commit',
'citgm'
];

// eslint-disable-next-line no-unused-vars
Expand Down Expand Up @@ -118,6 +125,18 @@ const argv = yargs
},
handler
})
.command({
command: 'citgm <jobid>',
desc: 'Show results of a citgm-smoker CI job',
builder: (yargs) => {
yargs
.positional('jobid', {
describe: 'id of the job',
type: 'number'
});
},
handler
})
.demandCommand(1, 'must provide a valid command')
.option('copy', {
default: false,
Expand All @@ -144,7 +163,8 @@ const argv = yargs
const commandToType = {
commit: COMMIT,
pr: PR,
benchmark: BENCHMARK
benchmark: BENCHMARK,
citgm: CITGM
};

class CICommand {
Expand Down Expand Up @@ -188,6 +208,9 @@ class CICommand {
case COMMIT:
build = new CommitBuild(cli, request, job.jobid);
break;
case CITGM:
build = new CITGMBuild(cli, request, job.jobid);
break;
case BENCHMARK:
build = new BenchmarkRun(cli, request, job.jobid);
break;
Expand All @@ -208,8 +231,7 @@ class CICommand {
}
}

async aggregate() { // noop
}
async aggregate() {} // noop

async serialize() {
const { argv, cli } = this;
Expand Down Expand Up @@ -238,7 +260,7 @@ class CICommand {
if (this.json.length) {
writeJson(argv.json, this.json);
cli.separator('');
cli.log(`Written JSON to ${argv.json}`);
cli.log(`Wrote JSON to ${argv.json}`);
} else {
cli.error('No JSON generated');
}
Expand Down Expand Up @@ -357,6 +379,7 @@ async function main(command, argv) {
}
case 'pr':
case 'commit':
case 'citgm':
case 'benchmark': {
commandHandler = new JobCommand(cli, request, argv, command);
break;
Expand Down
37 changes: 36 additions & 1 deletion docs/ncu-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Commands:
ncu-ci commit <jobid> Show results of a node-test-commit CI job
ncu-ci benchmark <jobid> Show results of a benchmark-node-micro-benchmarks CI
job
ncu-ci citgm <jobid> Show results of a citgm-smoker job
Options:
--version Show version number [boolean]
Expand Down Expand Up @@ -62,7 +63,7 @@ node on git:master ❯ ncu-ci rate commit

`ncu-ci walk <type>` walks CI and displays failures, where `<type>` can be either `pr` for `node-test-pull-request` or `commit` for `node-test-commit`.

Example
Example:
```sh
node on git:master ❯ ncu-ci walk commit
✔ Done--------------------------------------------------------------------------------
Expand Down Expand Up @@ -226,6 +227,40 @@ Notifying upstream projects of job completion
Finished: SUCCESS
```
### `ncu-ci citgm <jobid>`
`ncu-ci citgm <jobid>` shows the results of a given citgm-smoker job.
Example:
```
node on git:master ❯ ncu-ci citgm 2400 10:25AM
--------------------------------------------------------------------------------
[1/1] Running CITGM: 2400
--------------------------------------------------------------------------------
✔ Header data downloaded
✔ Report data downloaded
----------------------------------- Summary ------------------------------------
Result FAILURE
URL https://ci.nodejs.org/job/citgm-smoker/2400/testReport/
Source https://github.com/nodejs/node/pull/34093/
Commit [9ec07f42864c] 2020-06-30, Version 14.5.0 (Current)
Date 2020-06-29 21:17:56 -0700
Author Shelley Vohr <shelley.vohr@gmail.com>
----------------------------------- Failures -----------------------------------
┌────────────────────────┬───────────────────────┬───────────────────────┬─────────────────────────┬─────────────────────┬─────────────────┬────────────────────┐
│ (index) │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │
├────────────────────────┼───────────────────────┼───────────────────────┼─────────────────────────┼─────────────────────┼─────────────────┼────────────────────┤
│ debian9-64 │ 'coffeescript-v2.5.1''through2-v4.0.2' │ │ │ │ │
│ rhel7-s390x │ 'through2-v4.0.2' │ │ │ │ │ │
│ fedora-latest-x64 │ 'coffeescript-v2.5.1''through2-v4.0.2' │ │ │ │ │
│ ubuntu1604-64 │ 'coffeescript-v2.5.1''through2-v4.0.2' │ │ │ │ │
│ osx1014 │ 'acorn-v7.3.1''coffeescript-v2.5.1''clinic-v6.0.2''ember-cli-v3.19.0''semver-v7.3.2''watchify-v3.11.1'
│ ubuntu1804-64 │ 'coffeescript-v2.5.1''through2-v4.0.2' │ │ │ │ │
│ fedora-last-latest-x64 │ 'coffeescript-v2.5.1''through2-v4.0.2' │ │ │ │ │
│ centos7-ppcle │ 'coffeescript-v2.5.1''clinic-v6.0.2''torrent-stream-v1.2.0''through2-v4.0.2' │ │ │
└────────────────────────┴───────────────────────┴───────────────────────┴─────────────────────────┴─────────────────────┴─────────────────┴────────────────────┘
```
## Caveats
The CI failures are parsed using pattern matching and could be incorrect. Feel
Expand Down
168 changes: 161 additions & 7 deletions lib/ci/ci_result_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,24 @@ const PR_TREE =
const COMMIT_TREE =
`result,url,number,${ACTION_TREE},${CHANGE_TREE},builtOn,` +
`subBuilds[${BUILD_FIELDS}]`;
const CITGM_MAIN_TREE =
`result,url,number,${ACTION_TREE},${CHANGE_TREE},builtOn`;

// com.tikal.jenkins.plugins.multijob.MultiJobBuild
const FANNED_TREE =
`result,url,number,subBuilds[phaseName,${BUILD_FIELDS}]`;

// hudson.matrix.MatrixBuild
const BUILD_TREE = 'result,runs[url,number,result],builtOn';
const LINTER_TREE = 'result,url,number,builtOn';
const CAUSE_TREE = 'upstreamBuild,upstreamProject,shortDescription,_class';
const RUN_TREE = `actions[causes[${CAUSE_TREE}]],builtOn`;

// hudson.tasks.test.MatrixTestResult
const RESULT_TREE = 'result[suites[cases[name,status]]]';
const CITGM_REPORT_TREE =
`failCount,skipCount,totalCount,childReports[child[url],${RESULT_TREE}]`;

function getPath(url) {
return url.replace(`https://${CI_DOMAIN}/`, '').replace('api/json', '');
}
Expand Down Expand Up @@ -115,11 +124,11 @@ class Job {
return `https://${CI_DOMAIN}/${path}console`;
}

async getBuildData() {
async getBuildData(type = 'Build') {
const { cli, path } = this;
cli.startSpinner(`Querying data for ${path}`);
const data = await this.getAPIData();
cli.stopSpinner('Build data downloaded');
cli.stopSpinner(`${type} data downloaded`);
return data;
}

Expand All @@ -133,13 +142,13 @@ class Job {
async getAPIData() {
const { cli, request, path } = this;
const url = this.apiUrl;
cli.updateSpinner(`Querying API of ${path}`);
cli.updateSpinner(`Querying API for ${path}`);
return request.json(url);
}

async getConsoleText() {
const { cli, request, path } = this;
cli.updateSpinner(`Querying console text of ${path}`);
cli.updateSpinner(`Querying console text for ${path}`);
const data = await request.text(this.consoleUrl);
return data.replace(/\r/g, '');
}
Expand Down Expand Up @@ -334,10 +343,13 @@ class TestBuild extends Job {
}

formatAsJson() {
const result = this.failures.map(item => Object.assign({
source: this.sourceURL,
upstream: this.jobUrl
const { jobUrl, failures, sourceURL } = this;

const result = failures.map(item => Object.assign({
source: sourceURL,
upstream: jobUrl
}, item));

return JSON.parse(JSON.stringify(result));
}
}
Expand Down Expand Up @@ -740,6 +752,147 @@ class PRBuild extends TestBuild {
}
}

class CITGMBuild extends TestBuild {
constructor(cli, request, id) {
const path = `job/citgm-smoker/${id}/`;
const tree = CITGM_MAIN_TREE;

super(cli, request, path, tree);

this.id = id;
}

async getResults() {
const { id } = this;

let headerData;
try {
headerData = await this.getBuildData('Summary');
} catch (err) {
this.failures = [
new NCUFailure({ url: this.apiUrl }, err.message)
];
return this.failures;
}
const { result } = headerData;

this.setBuildData(headerData);

// CITGM jobs store results in a different location than
// they do summary data, so we need to update the endpoint
// and issue a second API call in order to fetch result data.
this.tree = CITGM_REPORT_TREE;
this.path = `job/citgm-smoker/${this.id}/testReport/`;

let resultData;
try {
resultData = await this.getBuildData('Results');
} catch (err) {
this.failures = [
new NCUFailure({ url: this.apiUrl }, err.message)
];
return this.failures;
}

this.results = this.parseResults(resultData);

// Update id again so that it correctly displays in Summary output.
this.path = `job/citgm-smoker/${id}/`;

return { result };
}

parseResults(data) {
const { childReports, totalCount, skipCount, failCount } = data;
const results = { all: {}, failures: {}, statistics: {} };

const passCount = totalCount - failCount - skipCount;
results.statistics.passed = passCount;
results.statistics.total = totalCount;
results.statistics.skipped = skipCount;
results.statistics.failed = failCount;

childReports.forEach(platform => {
const cases = flatten(platform.result.suites[0].cases);
const url = platform.child.url;
const nodeName = getNodeName(url);

results.all[nodeName] = { url, modules: cases };

const failedModules = cases.filter(c => c.status === 'FAILED');
results.failures[nodeName] = { url, modules: failedModules };
});

return results;
}

displayBuilds() {
const { cli, results } = this;
const { failed, skipped, passed, total } = results.statistics;

cli.separator('Statistics');
console.table({
Failed: failed,
Skipped: skipped,
Passed: passed,
Total: total
});

cli.separator('Failures');
const output = {};
for (const platform in results.failures) {
const modules = results.failures[platform].modules;
const failures = modules.map(f => f.name);

output[platform] = failures;
}

console.table(output);
}

formatAsJson() {
const { jobUrl, results, sourceURL } = this;

const result = {
source: sourceURL,
upstream: jobUrl,
...results.statistics,
...results.failures
};

return JSON.parse(JSON.stringify(result));
}

formatAsMarkdown() {
const { jobUrl, result, results, id } = this;

let output = `# CITGM Data for [${id}](${jobUrl})\n\n`;

const { failed, skipped, passed, total } = results.statistics;

output += `## Statistics for job [${id}](${jobUrl})\n\n`;
output += '| FAILED | SKIPPED | PASSED | TOTAL |\n';
output += '| -------- | --------- | -------- | ------- |\n';
output += `| ${pad(failed, 8)} | ${pad(skipped, 9)} |`;
output += ` ${pad(passed, 8)} | ${pad(total, 7)} |\n\n`;

if (result === SUCCESS) {
output += `Job [${id}](${jobUrl}) is green.`;
return output;
}

output += `## Failures in job [${id}](${jobUrl})\n\n`;
for (const failure in results.failures) {
const data = results.failures[failure];
output += `### [${failure}](${data.url})\n\n`;

const failures = data.modules.map(f => `* ${f.name}`);
output += `${failures.join('\n')}\n\n`;
}
return output;
}
}

function filterBuild(builds, type) {
return builds
.filter(build => build.result === type)
Expand Down Expand Up @@ -1034,6 +1187,7 @@ module.exports = {
PRBuild,
BenchmarkRun,
CommitBuild,
CITGMBuild,
HealthBuild,
jobCache,
parseJobFromURL,
Expand Down
Loading

0 comments on commit 9dee734

Please sign in to comment.