@node-core/utils 4.0.0
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/LICENSE +7 -0
- package/README.md +158 -0
- package/bin/get-metadata.js +11 -0
- package/bin/git-node.js +30 -0
- package/bin/ncu-ci.js +600 -0
- package/bin/ncu-config.js +101 -0
- package/bin/ncu-team.js +76 -0
- package/components/git/backport.js +70 -0
- package/components/git/epilogue.js +18 -0
- package/components/git/land.js +223 -0
- package/components/git/metadata.js +94 -0
- package/components/git/release.js +99 -0
- package/components/git/security.js +35 -0
- package/components/git/status.js +32 -0
- package/components/git/sync.js +24 -0
- package/components/git/v8.js +121 -0
- package/components/git/vote.js +84 -0
- package/components/git/wpt.js +87 -0
- package/components/metadata.js +49 -0
- package/lib/auth.js +133 -0
- package/lib/backport_session.js +302 -0
- package/lib/cache.js +107 -0
- package/lib/cherry_pick.js +304 -0
- package/lib/ci/build-types/benchmark_run.js +72 -0
- package/lib/ci/build-types/citgm_build.js +194 -0
- package/lib/ci/build-types/citgm_comparison_build.js +174 -0
- package/lib/ci/build-types/commit_build.js +112 -0
- package/lib/ci/build-types/daily_build.js +24 -0
- package/lib/ci/build-types/fanned_build.js +87 -0
- package/lib/ci/build-types/health_build.js +63 -0
- package/lib/ci/build-types/job.js +114 -0
- package/lib/ci/build-types/linter_build.js +35 -0
- package/lib/ci/build-types/normal_build.js +89 -0
- package/lib/ci/build-types/pr_build.js +101 -0
- package/lib/ci/build-types/test_build.js +186 -0
- package/lib/ci/build-types/test_run.js +41 -0
- package/lib/ci/ci_failure_parser.js +325 -0
- package/lib/ci/ci_type_parser.js +203 -0
- package/lib/ci/ci_utils.js +106 -0
- package/lib/ci/failure_aggregator.js +152 -0
- package/lib/ci/jenkins_constants.js +28 -0
- package/lib/ci/run_ci.js +120 -0
- package/lib/cli.js +192 -0
- package/lib/collaborators.js +140 -0
- package/lib/config.js +72 -0
- package/lib/figures.js +7 -0
- package/lib/file.js +43 -0
- package/lib/github/templates/next-security-release.md +97 -0
- package/lib/github/tree.js +162 -0
- package/lib/landing_session.js +506 -0
- package/lib/links.js +123 -0
- package/lib/mergeable_state.js +3 -0
- package/lib/metadata_gen.js +61 -0
- package/lib/pr_checker.js +605 -0
- package/lib/pr_data.js +115 -0
- package/lib/pr_summary.js +62 -0
- package/lib/prepare_release.js +772 -0
- package/lib/prepare_security.js +117 -0
- package/lib/proxy.js +21 -0
- package/lib/queries/DefaultBranchRef.gql +8 -0
- package/lib/queries/LastCommit.gql +16 -0
- package/lib/queries/PR.gql +37 -0
- package/lib/queries/PRComments.gql +27 -0
- package/lib/queries/PRCommits.gql +45 -0
- package/lib/queries/PRs.gql +25 -0
- package/lib/queries/Reviews.gql +23 -0
- package/lib/queries/SearchIssue.gql +51 -0
- package/lib/queries/Team.gql +22 -0
- package/lib/queries/TreeEntries.gql +12 -0
- package/lib/queries/VotePRInfo.gql +28 -0
- package/lib/release/utils.js +53 -0
- package/lib/request.js +185 -0
- package/lib/review_state.js +5 -0
- package/lib/reviews.js +178 -0
- package/lib/run.js +106 -0
- package/lib/session.js +415 -0
- package/lib/sync_session.js +15 -0
- package/lib/team_info.js +95 -0
- package/lib/update-v8/applyNodeChanges.js +49 -0
- package/lib/update-v8/backport.js +258 -0
- package/lib/update-v8/commitUpdate.js +26 -0
- package/lib/update-v8/common.js +35 -0
- package/lib/update-v8/constants.js +86 -0
- package/lib/update-v8/index.js +56 -0
- package/lib/update-v8/majorUpdate.js +171 -0
- package/lib/update-v8/minorUpdate.js +105 -0
- package/lib/update-v8/updateMaintainingDependencies.js +34 -0
- package/lib/update-v8/updateV8Clone.js +53 -0
- package/lib/update-v8/updateVersionNumbers.js +122 -0
- package/lib/update-v8/util.js +62 -0
- package/lib/user.js +4 -0
- package/lib/user_status.js +5 -0
- package/lib/utils.js +66 -0
- package/lib/verbosity.js +26 -0
- package/lib/voting_session.js +136 -0
- package/lib/wpt/index.js +243 -0
- package/lib/wpt/templates/README.md +16 -0
- package/package.json +69 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { listBuilds, pad } from '../ci_utils.js';
|
|
2
|
+
|
|
3
|
+
const kHealthKeys = [
|
|
4
|
+
'success',
|
|
5
|
+
'pending',
|
|
6
|
+
'aborted',
|
|
7
|
+
'failed',
|
|
8
|
+
'unstable'
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
class Health {
|
|
12
|
+
constructor(builds) {
|
|
13
|
+
for (const key of kHealthKeys) {
|
|
14
|
+
this[key] = builds[key].length;
|
|
15
|
+
this.count = builds.count;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Produces a row for https://github.com/nodejs/reliability#ci-health-history
|
|
20
|
+
formatAsMarkdown() {
|
|
21
|
+
const { success, pending, aborted, failed, unstable, count } = this;
|
|
22
|
+
const rate = `${(success / (count - pending - aborted) * 100).toFixed(2)}%`;
|
|
23
|
+
// eslint-disable-next-line max-len
|
|
24
|
+
let result = '| UTC Time | RUNNING | SUCCESS | UNSTABLE | ABORTED | FAILURE | Green Rate |\n';
|
|
25
|
+
// eslint-disable-next-line max-len
|
|
26
|
+
result += '| ---------------- | ------- | ------- | -------- | ------- | ------- | ---------- |\n';
|
|
27
|
+
const time = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
|
28
|
+
result += `| ${time} | ${pad(pending, 7)} | ${pad(success, 8)}|`;
|
|
29
|
+
result += ` ${pad(unstable, 8)} | ${pad(aborted, 7)} | ${pad(failed, 7)} |`;
|
|
30
|
+
result += ` ${pad(rate, 10)} |\n`;
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class HealthBuild {
|
|
36
|
+
constructor(cli, request, ciType, builds) {
|
|
37
|
+
this.cli = cli;
|
|
38
|
+
this.request = request;
|
|
39
|
+
this.type = 'health';
|
|
40
|
+
this.ciType = ciType;
|
|
41
|
+
this.builds = builds;
|
|
42
|
+
this.name = 'health';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getResults() {
|
|
46
|
+
if (!this.builds) {
|
|
47
|
+
this.builds = await listBuilds(this.cli, this.request, this.ciType);
|
|
48
|
+
}
|
|
49
|
+
this.health = new Health(this.builds);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
formatAsJson() {
|
|
53
|
+
return this.health;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
formatAsMarkdown() {
|
|
57
|
+
return this.health.formatAsMarkdown();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
display() {
|
|
61
|
+
this.cli.log(this.formatAsMarkdown());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import qs from 'node:querystring';
|
|
2
|
+
|
|
3
|
+
import { CI_DOMAIN } from '../ci_type_parser.js';
|
|
4
|
+
import Cache from '../../cache.js';
|
|
5
|
+
import CIFailureParser from '../ci_failure_parser.js';
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
FAILURE_TYPES: { NCU_FAILURE },
|
|
9
|
+
FAILURE_CONSTRUCTORS: {
|
|
10
|
+
[NCU_FAILURE]: NCUFailure
|
|
11
|
+
},
|
|
12
|
+
CIResult
|
|
13
|
+
} = CIFailureParser;
|
|
14
|
+
|
|
15
|
+
export class Job {
|
|
16
|
+
constructor(cli, request, path, tree) {
|
|
17
|
+
this.cli = cli;
|
|
18
|
+
this.request = request;
|
|
19
|
+
this.path = path;
|
|
20
|
+
this.tree = tree;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get jobUrl() {
|
|
24
|
+
const { path } = this;
|
|
25
|
+
return `https://${CI_DOMAIN}/${path}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get apiUrl() {
|
|
29
|
+
const { tree, jobUrl } = this;
|
|
30
|
+
const query = tree ? `?tree=${qs.escape(tree)}` : '';
|
|
31
|
+
return `${jobUrl}api/json${query}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get consoleUrl() {
|
|
35
|
+
const { path } = this;
|
|
36
|
+
return `https://${CI_DOMAIN}/${path}consoleText`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get consoleUIUrl() {
|
|
40
|
+
const { path } = this;
|
|
41
|
+
return `https://${CI_DOMAIN}/${path}console`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async getBuildData(type = 'Build') {
|
|
45
|
+
const { cli, path } = this;
|
|
46
|
+
cli.startSpinner(`Querying data for ${path}`);
|
|
47
|
+
const data = await this.getAPIData();
|
|
48
|
+
cli.stopSpinner(`${type} data downloaded`);
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getCause(actions) {
|
|
53
|
+
if (actions && actions.find(item => item.causes)) {
|
|
54
|
+
const action = actions.find(item => item.causes);
|
|
55
|
+
return action.causes[0];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getAPIData() {
|
|
60
|
+
const { apiUrl, cli, request, path } = this;
|
|
61
|
+
cli.updateSpinner(`Querying API for ${path}`);
|
|
62
|
+
return request.json(apiUrl);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getConsoleText() {
|
|
66
|
+
const { cli, consoleUrl, request, path } = this;
|
|
67
|
+
cli.updateSpinner(`Querying console text for ${path}`);
|
|
68
|
+
const data = await request.text(consoleUrl);
|
|
69
|
+
return data.replace(/\r/g, '');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getCacheKey() {
|
|
73
|
+
return this.path
|
|
74
|
+
.replace(/job\//, '')
|
|
75
|
+
.replace(/\//g, '-')
|
|
76
|
+
.replace(/-$/, '');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async parseConsoleText() {
|
|
80
|
+
let text;
|
|
81
|
+
try {
|
|
82
|
+
text = await this.getConsoleText();
|
|
83
|
+
} catch (err) {
|
|
84
|
+
this.failures = [
|
|
85
|
+
new NCUFailure({
|
|
86
|
+
url: this.consoleUrl, builtOn: this.builtOn
|
|
87
|
+
}, err.message)
|
|
88
|
+
];
|
|
89
|
+
return this.failures;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const parser = new CIFailureParser(this, text);
|
|
93
|
+
let results = parser.parse();
|
|
94
|
+
if (!results) {
|
|
95
|
+
results = [
|
|
96
|
+
new CIResult({ url: this.jobUrl, builtOn: this.builtOn }, 'Unknown')
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.failures = results;
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// TODO(joyeecheung): do not cache pending jobs
|
|
106
|
+
export const jobCache = new Cache();
|
|
107
|
+
jobCache.wrap(Job, {
|
|
108
|
+
getConsoleText() {
|
|
109
|
+
return { key: this.getCacheKey(), ext: '.txt' };
|
|
110
|
+
},
|
|
111
|
+
getAPIData() {
|
|
112
|
+
return { key: this.getCacheKey(), ext: '.json' };
|
|
113
|
+
}
|
|
114
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { LINTER_TREE } from '../jenkins_constants.js';
|
|
2
|
+
import CIFailureParser from '../ci_failure_parser.js';
|
|
3
|
+
import { Job } from './job.js';
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
FAILURE_TYPES: { NCU_FAILURE },
|
|
7
|
+
FAILURE_CONSTRUCTORS: {
|
|
8
|
+
[NCU_FAILURE]: NCUFailure
|
|
9
|
+
}
|
|
10
|
+
} = CIFailureParser;
|
|
11
|
+
|
|
12
|
+
export class LinterBuild extends Job {
|
|
13
|
+
constructor(cli, request, jobName, id) {
|
|
14
|
+
const path = `job/${jobName}/${id}/`;
|
|
15
|
+
const tree = LINTER_TREE;
|
|
16
|
+
super(cli, request, path, tree);
|
|
17
|
+
|
|
18
|
+
this.failures = [];
|
|
19
|
+
this.builtOn = undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getResults() {
|
|
23
|
+
let data;
|
|
24
|
+
try {
|
|
25
|
+
data = await this.getAPIData();
|
|
26
|
+
} catch (err) {
|
|
27
|
+
this.failures = [
|
|
28
|
+
new NCUFailure(this, err.message)
|
|
29
|
+
];
|
|
30
|
+
return this.failures;
|
|
31
|
+
}
|
|
32
|
+
this.builtOn = data.builtOn;
|
|
33
|
+
return this.parseConsoleText();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { statusType } from '../ci_utils.js';
|
|
2
|
+
import { flatten } from '../../utils.js';
|
|
3
|
+
import { BUILD_TREE } from '../jenkins_constants.js';
|
|
4
|
+
import CIFailureParser from '../ci_failure_parser.js';
|
|
5
|
+
import { Job } from './job.js';
|
|
6
|
+
import { TestRun } from './test_run.js';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
FAILURE_TYPES: {
|
|
10
|
+
BUILD_FAILURE,
|
|
11
|
+
NCU_FAILURE
|
|
12
|
+
},
|
|
13
|
+
FAILURE_CONSTRUCTORS: {
|
|
14
|
+
[BUILD_FAILURE]: BuildFailure,
|
|
15
|
+
[NCU_FAILURE]: NCUFailure
|
|
16
|
+
}
|
|
17
|
+
} = CIFailureParser;
|
|
18
|
+
|
|
19
|
+
export class NormalBuild extends Job {
|
|
20
|
+
constructor(cli, request, jobName, id) {
|
|
21
|
+
const path = `job/${jobName}/${id}/`;
|
|
22
|
+
const tree = BUILD_TREE;
|
|
23
|
+
super(cli, request, path, tree);
|
|
24
|
+
|
|
25
|
+
this.failures = [];
|
|
26
|
+
this.builtOn = undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getResults() {
|
|
30
|
+
const { cli, request } = this;
|
|
31
|
+
|
|
32
|
+
let data;
|
|
33
|
+
try {
|
|
34
|
+
data = await this.getAPIData();
|
|
35
|
+
} catch (err) {
|
|
36
|
+
this.failures = [
|
|
37
|
+
new NCUFailure({ url: this.apiUrl }, err.message)
|
|
38
|
+
];
|
|
39
|
+
return this.failures;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { result, runs, builtOn } = data;
|
|
43
|
+
this.builtOn = builtOn;
|
|
44
|
+
|
|
45
|
+
if (result !== statusType.FAILURE) {
|
|
46
|
+
this.failures = [];
|
|
47
|
+
return this.failures;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!runs) return [];
|
|
51
|
+
|
|
52
|
+
if (!runs.length) {
|
|
53
|
+
this.failures = [
|
|
54
|
+
new BuildFailure(
|
|
55
|
+
{ url: this.jobUrl, builtOn }, 'Failed to trigger runs'
|
|
56
|
+
)
|
|
57
|
+
];
|
|
58
|
+
return this.failures;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const failed = runs.filter(run => {
|
|
62
|
+
return run.result === statusType.FAILURE;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!failed.length) {
|
|
66
|
+
return this.parseConsoleText();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const tests = failed.map(({ url }) => new TestRun(cli, request, url));
|
|
70
|
+
|
|
71
|
+
// Skip runs that are not actually triggered by this job
|
|
72
|
+
await Promise.all(tests.map(run => run.getData()));
|
|
73
|
+
const causes = tests.map(run => run.cause);
|
|
74
|
+
const actualRuns = tests.filter((_, index) => {
|
|
75
|
+
const upstream = causes[index].upstreamBuild;
|
|
76
|
+
if (!upstream) {
|
|
77
|
+
return true;
|
|
78
|
+
} else {
|
|
79
|
+
return this.jobUrl.includes(upstream + '');
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const failures = await Promise.all(
|
|
84
|
+
actualRuns.map(run => run.getResults())
|
|
85
|
+
);
|
|
86
|
+
this.failures = flatten(failures);
|
|
87
|
+
return this.failures;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { PR_TREE } from '../jenkins_constants.js';
|
|
2
|
+
import CIFailureParser from '../ci_failure_parser.js';
|
|
3
|
+
import { TestBuild } from './test_build.js';
|
|
4
|
+
import { CommitBuild } from './commit_build.js';
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
FAILURE_TYPES: {
|
|
8
|
+
BUILD_FAILURE,
|
|
9
|
+
NCU_FAILURE
|
|
10
|
+
},
|
|
11
|
+
FAILURE_CONSTRUCTORS: {
|
|
12
|
+
[BUILD_FAILURE]: BuildFailure,
|
|
13
|
+
[NCU_FAILURE]: NCUFailure
|
|
14
|
+
}
|
|
15
|
+
} = CIFailureParser;
|
|
16
|
+
|
|
17
|
+
export class PRBuild extends TestBuild {
|
|
18
|
+
constructor(cli, request, id) {
|
|
19
|
+
const path = `job/node-test-pull-request/${id}/`;
|
|
20
|
+
const tree = PR_TREE;
|
|
21
|
+
super(cli, request, path, tree);
|
|
22
|
+
|
|
23
|
+
this.commitBuild = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Get the failures and their reasons of this build
|
|
27
|
+
async getResults() {
|
|
28
|
+
const { cli, request } = this;
|
|
29
|
+
|
|
30
|
+
let data;
|
|
31
|
+
try {
|
|
32
|
+
data = await this.getBuildData();
|
|
33
|
+
} catch (err) {
|
|
34
|
+
this.failures = [
|
|
35
|
+
new NCUFailure({ url: this.apiUrl }, err.message)
|
|
36
|
+
];
|
|
37
|
+
return this.failures;
|
|
38
|
+
}
|
|
39
|
+
const {
|
|
40
|
+
result, subBuilds, changeSet, actions, timestamp
|
|
41
|
+
} = data;
|
|
42
|
+
|
|
43
|
+
// No builds found.
|
|
44
|
+
if (data.status === '404') {
|
|
45
|
+
const failure = new BuildFailure(this, 'No builds found for PR');
|
|
46
|
+
this.failures = [failure];
|
|
47
|
+
return {
|
|
48
|
+
result: data.result,
|
|
49
|
+
builds: { failed: [], aborted: [], pending: [], unstable: [] },
|
|
50
|
+
failures: this.failures
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.setBuildData(data);
|
|
55
|
+
|
|
56
|
+
// No sub build at all
|
|
57
|
+
const commitBuild = subBuilds[0];
|
|
58
|
+
if (!commitBuild) {
|
|
59
|
+
const failure = new BuildFailure(
|
|
60
|
+
this, 'Failed to trigger node-test-commit'
|
|
61
|
+
);
|
|
62
|
+
Object.assign(failure, {
|
|
63
|
+
source: this.sourceURL,
|
|
64
|
+
upstream: this.jobUrl,
|
|
65
|
+
builtOn: this.builtOn
|
|
66
|
+
});
|
|
67
|
+
this.failures = [failure];
|
|
68
|
+
return { result, builds: {}, failures: this.failures };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get result from the sub build
|
|
72
|
+
// assert.strictEqual(commitBuild.jobName, 'node-test-commit');
|
|
73
|
+
const allBuilds = commitBuild.build.subBuilds;
|
|
74
|
+
// TODO: fetch result, builtOn, timestamp in the commit build's own data
|
|
75
|
+
// ..or maybe they do not worth an additional API call?
|
|
76
|
+
// Note that we have to pass the actions down to detect resume builds.
|
|
77
|
+
const buildData = {
|
|
78
|
+
result, subBuilds: allBuilds, changeSet, actions, timestamp
|
|
79
|
+
};
|
|
80
|
+
const commitBuildId = commitBuild.buildNumber;
|
|
81
|
+
this.commitBuild = new CommitBuild(cli, request, commitBuildId);
|
|
82
|
+
const { builds, failures } = await this.commitBuild.getResults(buildData);
|
|
83
|
+
|
|
84
|
+
// Set up aliases for display
|
|
85
|
+
this.builds = builds;
|
|
86
|
+
this.failures = failures;
|
|
87
|
+
return { result, builds, failures };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
formatAsMarkdown() {
|
|
91
|
+
if (!this.commitBuild) {
|
|
92
|
+
let result = 'Failed to trigger node-test-commit';
|
|
93
|
+
if (this.builtOn) {
|
|
94
|
+
result += ` on ${this.builtOn}`;
|
|
95
|
+
}
|
|
96
|
+
result += `\n\nURL: ${this.jobUrl}`;
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
return super.formatAsMarkdown();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
import { shortSha } from '../../utils.js';
|
|
4
|
+
import {
|
|
5
|
+
fold,
|
|
6
|
+
getNodeName,
|
|
7
|
+
statusType
|
|
8
|
+
} from '../ci_utils.js';
|
|
9
|
+
import { CI_DOMAIN } from '../ci_type_parser.js';
|
|
10
|
+
import { Job } from './job.js';
|
|
11
|
+
|
|
12
|
+
const resultName = (result) => {
|
|
13
|
+
return result === null ? 'PENDING' : result.toUpperCase();
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getUrl = (path) => {
|
|
17
|
+
const urlPath = path.replace(`https://${CI_DOMAIN}/`, '').replace('api/json', '');
|
|
18
|
+
return `https://${CI_DOMAIN}/${urlPath}`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class TestBuild extends Job {
|
|
22
|
+
constructor(cli, request, path, tree) {
|
|
23
|
+
super(cli, request, path, tree);
|
|
24
|
+
|
|
25
|
+
// Should be assigned in getResults()
|
|
26
|
+
this.result = null;
|
|
27
|
+
this.params = {};
|
|
28
|
+
this.change = {};
|
|
29
|
+
this.date = undefined;
|
|
30
|
+
this.builds = {
|
|
31
|
+
failed: [], aborted: [], pending: [], unstable: []
|
|
32
|
+
};
|
|
33
|
+
this.failures = [];
|
|
34
|
+
this.builtOn = undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setBuildData({ result, changeSet, actions, timestamp, builtOn }) {
|
|
38
|
+
const params = actions.find(item => typeof item.parameters === 'object');
|
|
39
|
+
params.parameters.forEach(pair => {
|
|
40
|
+
this.params[pair.name] = pair.value;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
this.change = changeSet.items[0] || {};
|
|
44
|
+
this.date = new Date(timestamp);
|
|
45
|
+
this.result = result;
|
|
46
|
+
this.builtOn = builtOn;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setDailyBuildData({ result, changeSet, timestamp, builtOn }) {
|
|
50
|
+
this.change = changeSet.items[0] || {};
|
|
51
|
+
this.date = new Date(timestamp);
|
|
52
|
+
this.result = result;
|
|
53
|
+
this.builtOn = builtOn;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get sourceURL() {
|
|
57
|
+
const { params } = this;
|
|
58
|
+
|
|
59
|
+
if (params.PR_ID) { // from a node-test-pull-request build
|
|
60
|
+
const owner = params.TARGET_GITHUB_ORG;
|
|
61
|
+
const repo = params.TARGET_REPO_NAME;
|
|
62
|
+
const prid = params.PR_ID;
|
|
63
|
+
return `https://github.com/${owner}/${repo}/pull/${prid}/`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (params.GITHUB_ORG) { // from a node-test-commit build
|
|
67
|
+
const owner = params.GITHUB_ORG;
|
|
68
|
+
const repo = params.REPO_NAME;
|
|
69
|
+
const prm = params.GIT_REMOTE_REF.match(/refs\/pull\/(\d+)\/head/);
|
|
70
|
+
if (prm) {
|
|
71
|
+
return `https://github.com/${owner}/${repo}/pull/${prm[1]}/`;
|
|
72
|
+
} else {
|
|
73
|
+
const result =
|
|
74
|
+
`https://api.github.com/repos/${owner}/${repo}/git/` +
|
|
75
|
+
params.GIT_REMOTE_REF;
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get commit() {
|
|
82
|
+
const { change } = this;
|
|
83
|
+
if (!change.commitId) {
|
|
84
|
+
return 'Unknown';
|
|
85
|
+
}
|
|
86
|
+
return `[${shortSha(change.commitId)}] ${change.msg}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get author() {
|
|
90
|
+
const { change } = this;
|
|
91
|
+
if (!change.author) {
|
|
92
|
+
return 'Unknown';
|
|
93
|
+
}
|
|
94
|
+
return `${change.author.fullName} <${change.authorEmail}>`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
displayHeader() {
|
|
98
|
+
const { cli, result, change } = this;
|
|
99
|
+
cli.separator('Summary');
|
|
100
|
+
cli.table('Result', resultName(result));
|
|
101
|
+
cli.table('URL', this.jobUrl);
|
|
102
|
+
cli.table('Source', this.sourceURL);
|
|
103
|
+
cli.table('Commit', this.commit);
|
|
104
|
+
cli.table('Date', change.date);
|
|
105
|
+
cli.table('Author', this.author);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
displayFailure(failure) {
|
|
109
|
+
const { cli } = this;
|
|
110
|
+
const { url, reason } = failure;
|
|
111
|
+
cli.separator(getNodeName(url));
|
|
112
|
+
cli.table('URL', url);
|
|
113
|
+
if (failure.type) {
|
|
114
|
+
cli.table('Type', failure.type);
|
|
115
|
+
}
|
|
116
|
+
if (failure.builtOn) {
|
|
117
|
+
cli.table('Built On', failure.builtOn);
|
|
118
|
+
}
|
|
119
|
+
if (!reason.includes('\n') && reason.length < 40) {
|
|
120
|
+
cli.table('Reason', chalk.red(reason));
|
|
121
|
+
} else {
|
|
122
|
+
cli.table('Reason', '');
|
|
123
|
+
const lines = reason.split('\n');
|
|
124
|
+
cli.log(` ${chalk.red(lines[0])}`);
|
|
125
|
+
for (let i = 1; i < lines.length; ++i) {
|
|
126
|
+
cli.log(` ${lines[i]}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
displayBuilds() {
|
|
132
|
+
const { cli, failures, builds } = this;
|
|
133
|
+
for (const failure of failures) {
|
|
134
|
+
if (failure !== undefined) {
|
|
135
|
+
this.displayFailure(failure);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
cli.separator('Other builds');
|
|
139
|
+
for (const aborted of builds.aborted) {
|
|
140
|
+
cli.table('Aborted', getUrl(aborted.url));
|
|
141
|
+
}
|
|
142
|
+
for (const pending of builds.pending) {
|
|
143
|
+
cli.table('Pending', getUrl(pending.url));
|
|
144
|
+
}
|
|
145
|
+
for (const unstable of builds.unstable) {
|
|
146
|
+
cli.table('Unstable', getUrl(unstable.url));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
display() {
|
|
151
|
+
this.displayHeader();
|
|
152
|
+
this.displayBuilds();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
formatAsMarkdown() {
|
|
156
|
+
if (this.result === statusType.SUCCESS) {
|
|
157
|
+
return `Job ${this.jobUrl} is green.`;
|
|
158
|
+
}
|
|
159
|
+
const { failures } = this;
|
|
160
|
+
let output = `Failures in job ${this.jobUrl}\n\n`;
|
|
161
|
+
for (const failure of failures) {
|
|
162
|
+
if (failure === undefined) continue;
|
|
163
|
+
output += `#### [${getNodeName(failure.url)}](${failure.url})`;
|
|
164
|
+
if (!failure.reason.includes('\n') && failure.reason.length < 20) {
|
|
165
|
+
const builtOn = failure.builtOn ? `On ${failure.builtOn}: ` : '';
|
|
166
|
+
output += `\n\n${builtOn}${failure.reason}\n`;
|
|
167
|
+
} else {
|
|
168
|
+
output += '\n\n';
|
|
169
|
+
const builtOn = failure.builtOn ? ` on ${failure.builtOn}:` : '';
|
|
170
|
+
output += fold(`See failures${builtOn}`, failure.reason) + '\n\n';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return output;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
formatAsJson() {
|
|
177
|
+
const { jobUrl, failures, sourceURL } = this;
|
|
178
|
+
|
|
179
|
+
const result = failures.map(item => Object.assign({
|
|
180
|
+
source: sourceURL,
|
|
181
|
+
upstream: jobUrl
|
|
182
|
+
}, item));
|
|
183
|
+
|
|
184
|
+
return JSON.parse(JSON.stringify(result));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { RUN_TREE } from '../jenkins_constants.js';
|
|
2
|
+
import { getPath } from '../ci_utils.js';
|
|
3
|
+
import CIFailureParser from '../ci_failure_parser.js';
|
|
4
|
+
import { Job } from './job.js';
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
FAILURE_TYPES: { NCU_FAILURE },
|
|
8
|
+
FAILURE_CONSTRUCTORS: {
|
|
9
|
+
[NCU_FAILURE]: NCUFailure
|
|
10
|
+
}
|
|
11
|
+
} = CIFailureParser;
|
|
12
|
+
|
|
13
|
+
export class TestRun extends Job {
|
|
14
|
+
constructor(cli, request, url) {
|
|
15
|
+
const path = getPath(url);
|
|
16
|
+
const tree = RUN_TREE;
|
|
17
|
+
super(cli, request, path, tree);
|
|
18
|
+
|
|
19
|
+
this.failures = [];
|
|
20
|
+
this.cause = {};
|
|
21
|
+
this.builtOn = undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async getData() {
|
|
25
|
+
let data;
|
|
26
|
+
try {
|
|
27
|
+
data = await this.getAPIData();
|
|
28
|
+
} catch (err) {
|
|
29
|
+
this.failures = [
|
|
30
|
+
new NCUFailure({ url: this.apiUrl }, err.message)
|
|
31
|
+
];
|
|
32
|
+
return this.failures;
|
|
33
|
+
}
|
|
34
|
+
this.causes = this.getCause(data.actions) || {};
|
|
35
|
+
this.builtOn = data.builtOn;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async getResults() {
|
|
39
|
+
return this.parseConsoleText();
|
|
40
|
+
}
|
|
41
|
+
}
|