@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,325 @@
|
|
|
1
|
+
function unique(arr) {
|
|
2
|
+
return Array.from(new Set(arr).values());
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function pickContext(matches, text, {
|
|
6
|
+
index = 0, // which one in the matches should be picked
|
|
7
|
+
contextBefore = 0,
|
|
8
|
+
contextAfter = 0
|
|
9
|
+
}) {
|
|
10
|
+
if (index < 0) { index = matches.length + index; }
|
|
11
|
+
const match = matches[index];
|
|
12
|
+
|
|
13
|
+
const offset = text.indexOf(match);
|
|
14
|
+
let after = offset + match.length;
|
|
15
|
+
for (let i = 0; i < contextAfter; ++i) {
|
|
16
|
+
const next = text.indexOf('\n', after + 1);
|
|
17
|
+
after = next > 0 ? next : after;
|
|
18
|
+
}
|
|
19
|
+
let before = offset;
|
|
20
|
+
for (let i = 0; i < contextBefore; ++i) {
|
|
21
|
+
const next = text.lastIndexOf('\n', before - 1);
|
|
22
|
+
before = next > 0 ? next : before;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return text.slice(before, after);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const BUILD_FAILURE = 'BUILD_FAILURE';
|
|
29
|
+
const JS_TEST_FAILURE = 'JS_TEST_FAILURE';
|
|
30
|
+
const CC_TEST_FAILURE = 'CC_TEST_FAILURE';
|
|
31
|
+
const JENKINS_FAILURE = 'JENKINS_FAILURE';
|
|
32
|
+
const GIT_FAILURE = 'GIT_FAILURE';
|
|
33
|
+
const NCU_FAILURE = 'NCU_FAILURE';
|
|
34
|
+
const RESUME_FAILURE = 'RESUME_FAILURE';
|
|
35
|
+
const INFRA_FAILURE = 'INFRA_FAILURE';
|
|
36
|
+
|
|
37
|
+
const FAILURE_TYPES = {
|
|
38
|
+
BUILD_FAILURE, JS_TEST_FAILURE, CC_TEST_FAILURE,
|
|
39
|
+
JENKINS_FAILURE, GIT_FAILURE, NCU_FAILURE, RESUME_FAILURE,
|
|
40
|
+
INFRA_FAILURE
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
class CIResult {
|
|
44
|
+
constructor(ctx, reason) {
|
|
45
|
+
this.url = ctx.url || ctx.consoleUIUrl || ctx.jobUrl;
|
|
46
|
+
this.builtOn = ctx.builtOn;
|
|
47
|
+
this.reason = reason;
|
|
48
|
+
// Default: the first line is the highlight, we will slice
|
|
49
|
+
// the context to make it so.
|
|
50
|
+
// TODO: better highlights
|
|
51
|
+
this.highlight = 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Usually need a fix to the build files
|
|
56
|
+
class BuildFailure extends CIResult {
|
|
57
|
+
constructor(ctx, reason) {
|
|
58
|
+
super(ctx, reason);
|
|
59
|
+
this.type = BUILD_FAILURE;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Usually needs to fix something in the Jenkins agent (or just restart it)
|
|
64
|
+
class InfraFailure extends CIResult {
|
|
65
|
+
constructor(ctx, reason) {
|
|
66
|
+
super(ctx, reason);
|
|
67
|
+
this.type = INFRA_FAILURE;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Usually needs a fix in the test or the core
|
|
72
|
+
class JSTestFailure extends CIResult {
|
|
73
|
+
constructor(ctx, reason) {
|
|
74
|
+
super(ctx, reason);
|
|
75
|
+
this.type = JS_TEST_FAILURE;
|
|
76
|
+
// Example: not ok 749 parallel/test-http-readable-data-event
|
|
77
|
+
this.file = this.reason.split('\n')[this.highlight].split(' ').pop();
|
|
78
|
+
this.severity = this.reason.match(/^\s+severity: (\w+)/m)[1];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Usually needs a fix in the test or the core
|
|
83
|
+
class CCTestFailure extends CIResult {
|
|
84
|
+
constructor(ctx, reason) {
|
|
85
|
+
super(ctx, reason);
|
|
86
|
+
this.type = CC_TEST_FAILURE;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Usually needs someone to log into the machines and fix it
|
|
91
|
+
class JenkinsFailure extends CIResult {
|
|
92
|
+
constructor(ctx, reason) {
|
|
93
|
+
super(ctx, reason);
|
|
94
|
+
this.type = JENKINS_FAILURE;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Usually need a fix to the build scripts or in workers
|
|
99
|
+
class GitFailure extends CIResult {
|
|
100
|
+
constructor(ctx, reason) {
|
|
101
|
+
super(ctx, reason);
|
|
102
|
+
this.type = GIT_FAILURE;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Failures in this tool, we wrap them to avoid exceptions when
|
|
107
|
+
// walking the CI
|
|
108
|
+
class NCUFailure extends CIResult {
|
|
109
|
+
constructor(ctx, reason) {
|
|
110
|
+
super(ctx, reason);
|
|
111
|
+
this.type = NCU_FAILURE;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Refs: https://github.com/nodejs/build/issues/1496
|
|
116
|
+
class ResumeFailure extends CIResult {
|
|
117
|
+
constructor(ctx, reason) {
|
|
118
|
+
super(ctx, reason);
|
|
119
|
+
this.type = RESUME_FAILURE;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function failureMatcher(Failure, patterns, ctx, text) {
|
|
124
|
+
for (const pattern of patterns) {
|
|
125
|
+
const matches = text.match(pattern.pattern);
|
|
126
|
+
if (!matches) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const reason = pickContext(matches, text, pattern.context).trim();
|
|
130
|
+
return [new Failure(ctx, reason)];
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// The elements are ranked by priority
|
|
136
|
+
const FAILURE_FILTERS = [{
|
|
137
|
+
// NOTE(mmarchini): infra-related issues should have the highest priority, as
|
|
138
|
+
// they can cause other issues to happen.
|
|
139
|
+
filter(ctx, text) {
|
|
140
|
+
const patterns = [{
|
|
141
|
+
pattern: /Read-only file system/g,
|
|
142
|
+
context: { index: 0, contextBefore: 1, contextAfter: 0 }
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
pattern: /Device or resource busy/g,
|
|
146
|
+
context: { index: 0, contextBefore: 1, contextAfter: 0 }
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
pattern: /There is not enough space in the file system./g,
|
|
150
|
+
context: { index: 0, contextBefore: 1, contextAfter: 0 }
|
|
151
|
+
}
|
|
152
|
+
];
|
|
153
|
+
return failureMatcher(InfraFailure, patterns, ctx, text);
|
|
154
|
+
}
|
|
155
|
+
}, {
|
|
156
|
+
// TODO: match indentation to avoid skipping context with '...'
|
|
157
|
+
filter(ctx, text) {
|
|
158
|
+
const pattern = /^not ok \d+[\s\S]+? {2}\.\.\.\r?\n/mg;
|
|
159
|
+
const matches = text.match(pattern);
|
|
160
|
+
if (!matches) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const nonFlaky = matches.filter((m) => !m.includes('# TODO :'));
|
|
164
|
+
if (!nonFlaky.length) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return unique(nonFlaky).map(
|
|
168
|
+
match => new JSTestFailure(ctx, match)
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}, {
|
|
172
|
+
filter(ctx, text) {
|
|
173
|
+
const patterns = [{
|
|
174
|
+
pattern: /\[ {2}FAILED {2}\].+/g,
|
|
175
|
+
context: { index: 0, contextBefore: 5, contextAfter: 0 }
|
|
176
|
+
}];
|
|
177
|
+
return failureMatcher(CCTestFailure, patterns, ctx, text);
|
|
178
|
+
}
|
|
179
|
+
}, {
|
|
180
|
+
// VS compilation error
|
|
181
|
+
filter(ctx, text) {
|
|
182
|
+
const patterns = [{
|
|
183
|
+
pattern: /error C\d+:/mg,
|
|
184
|
+
context: { index: 0, contextBefore: 0, contextAfter: 5 }
|
|
185
|
+
}];
|
|
186
|
+
return failureMatcher(BuildFailure, patterns, ctx, text);
|
|
187
|
+
}
|
|
188
|
+
}, {
|
|
189
|
+
filter(ctx, text) {
|
|
190
|
+
const patterns = [{
|
|
191
|
+
pattern: /java\.io\.IOException.+/g,
|
|
192
|
+
context: { index: -1, contextBefore: 0, contextAfter: 5 }
|
|
193
|
+
}, {
|
|
194
|
+
pattern: /Build timed out/g,
|
|
195
|
+
context: { index: 0, contextBefore: 0, contextAfter: 1 }
|
|
196
|
+
}];
|
|
197
|
+
return failureMatcher(JenkinsFailure, patterns, ctx, text);
|
|
198
|
+
}
|
|
199
|
+
}, {
|
|
200
|
+
filter(ctx, text) {
|
|
201
|
+
const patterns = [{
|
|
202
|
+
pattern:
|
|
203
|
+
/Changes not staged for commit:[\s\S]+no changes added to commit/mg,
|
|
204
|
+
context: { index: 0, contextBefore: 0, contextAfter: 0 }
|
|
205
|
+
}, {
|
|
206
|
+
pattern:
|
|
207
|
+
// eslint-disable-next-line max-len
|
|
208
|
+
/error: Your local changes to the following files[\s\S]+Failed to merge in the changes./g,
|
|
209
|
+
context: { index: 0, contextBefore: 0, contextAfter: 0 }
|
|
210
|
+
}, {
|
|
211
|
+
pattern: /warning: failed to remove .+/g,
|
|
212
|
+
context: { index: 0, contextBefore: 0, contextAfter: 0 }
|
|
213
|
+
}];
|
|
214
|
+
return failureMatcher(GitFailure, patterns, ctx, text);
|
|
215
|
+
}
|
|
216
|
+
}, {
|
|
217
|
+
filter(ctx, text) {
|
|
218
|
+
const patterns = [{
|
|
219
|
+
pattern: /ERROR: Error fetching .+/g,
|
|
220
|
+
context: { index: 0, contextBefore: 0, contextAfter: 5 }
|
|
221
|
+
}, {
|
|
222
|
+
pattern: /hudson\.plugins\.git\.GitException+/g,
|
|
223
|
+
context: { index: 0, contextBefore: 0, contextAfter: 5 }
|
|
224
|
+
}, {
|
|
225
|
+
pattern: /Cannot rebase: .+/g,
|
|
226
|
+
context: { index: 0, contextBefore: 0, contextAfter: 1 }
|
|
227
|
+
}];
|
|
228
|
+
return failureMatcher(GitFailure, patterns, ctx, text);
|
|
229
|
+
}
|
|
230
|
+
}, {
|
|
231
|
+
filter(ctx, text) {
|
|
232
|
+
const patterns = [{
|
|
233
|
+
pattern: /sh: line /g,
|
|
234
|
+
context: { index: 0, contextBefore: 0, contextAfter: 1 }
|
|
235
|
+
}, {
|
|
236
|
+
pattern: /fatal error:/g,
|
|
237
|
+
context: { index: 0, contextBefore: 0, contextAfter: 1 }
|
|
238
|
+
}, {
|
|
239
|
+
pattern: /dtrace: failed to compile script/g,
|
|
240
|
+
context: { index: 0, contextBefore: 0, contextAfter: 1 }
|
|
241
|
+
}, {
|
|
242
|
+
pattern: /ERROR: .+/g,
|
|
243
|
+
// Pick the last one
|
|
244
|
+
context: { index: -1, contextBefore: 0, contextAfter: 5 }
|
|
245
|
+
}, {
|
|
246
|
+
// Pick the first one
|
|
247
|
+
pattern: /Error: .+/g,
|
|
248
|
+
context: { index: 0, contextBefore: 0, contextAfter: 5 }
|
|
249
|
+
}];
|
|
250
|
+
return failureMatcher(BuildFailure, patterns, ctx, text);
|
|
251
|
+
}
|
|
252
|
+
}, {
|
|
253
|
+
filter(ctx, text) {
|
|
254
|
+
const pattern = /fatal: .+/g;
|
|
255
|
+
const matches = text.match(pattern);
|
|
256
|
+
if (!matches) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
const reason = unique(matches).join('\n');
|
|
260
|
+
return [new BuildFailure(ctx, reason)];
|
|
261
|
+
}
|
|
262
|
+
}, {
|
|
263
|
+
filter(ctx, text) {
|
|
264
|
+
const patterns = [{
|
|
265
|
+
pattern: /FATAL: .+/g,
|
|
266
|
+
context: { index: -1, contextBefore: 0, contextAfter: 5 }
|
|
267
|
+
}, {
|
|
268
|
+
pattern: /make.*: write error/mg,
|
|
269
|
+
context: { index: 0, contextBefore: 0, contextAfter: 3 }
|
|
270
|
+
}, {
|
|
271
|
+
pattern: /error: .+/g,
|
|
272
|
+
context: { index: 0, contextBefore: 0, contextAfter: 5 }
|
|
273
|
+
}, {
|
|
274
|
+
pattern: /Makefile:.+failed/g,
|
|
275
|
+
context: { index: 0, contextBefore: 0, contextAfter: 5 }
|
|
276
|
+
}, {
|
|
277
|
+
pattern: /make.*: .+ Error \d.*/g,
|
|
278
|
+
context: { index: 0, contextBefore: 0, contextAfter: 3 }
|
|
279
|
+
}, {
|
|
280
|
+
pattern: /warning: failed .+/g,
|
|
281
|
+
context: { index: 0, contextBefore: 0, contextAfter: 3 }
|
|
282
|
+
}];
|
|
283
|
+
return failureMatcher(BuildFailure, patterns, ctx, text);
|
|
284
|
+
}
|
|
285
|
+
}];
|
|
286
|
+
|
|
287
|
+
export default class CIFailureParser {
|
|
288
|
+
constructor(ctx, text) {
|
|
289
|
+
this.ctx = ctx;
|
|
290
|
+
this.text = text;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
parse() {
|
|
294
|
+
const text = this.text;
|
|
295
|
+
for (const { filter } of FAILURE_FILTERS) {
|
|
296
|
+
const result = filter(this.ctx, text);
|
|
297
|
+
// TODO: we may want to concat certain types of failures
|
|
298
|
+
if (result) {
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
CIFailureParser.FAILURE_TYPES = FAILURE_TYPES;
|
|
307
|
+
CIFailureParser.FAILURE_CONSTRUCTORS = {
|
|
308
|
+
BUILD_FAILURE: BuildFailure,
|
|
309
|
+
JENKINS_FAILURE: JenkinsFailure,
|
|
310
|
+
JS_TEST_FAILURE: JSTestFailure,
|
|
311
|
+
CC_TEST_FAILURE: CCTestFailure,
|
|
312
|
+
GIT_FAILURE: GitFailure,
|
|
313
|
+
NCU_FAILURE: NCUFailure,
|
|
314
|
+
RESUME_FAILURE: ResumeFailure
|
|
315
|
+
};
|
|
316
|
+
CIFailureParser.CIResult = CIResult;
|
|
317
|
+
CIFailureParser.FAILURE_TYPES_NAME = {
|
|
318
|
+
BUILD_FAILURE: 'Build Failure',
|
|
319
|
+
JENKINS_FAILURE: 'Jenkins Failure',
|
|
320
|
+
JS_TEST_FAILURE: 'JSTest Failure',
|
|
321
|
+
CC_TEST_FAILURE: 'CCTest Failure',
|
|
322
|
+
GIT_FAILURE: 'Git Failure',
|
|
323
|
+
NCU_FAILURE: 'node-core-utils failure',
|
|
324
|
+
RESUME_FAILURE: 'resume failure'
|
|
325
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { parsePRFromURL } from '../links.js';
|
|
2
|
+
import PRData from '../pr_data.js';
|
|
3
|
+
import { ascending } from '../utils.js';
|
|
4
|
+
|
|
5
|
+
const CI_URL_RE = /\/\/ci\.nodejs\.org(\S+)/mg;
|
|
6
|
+
export const CI_DOMAIN = 'ci.nodejs.org';
|
|
7
|
+
|
|
8
|
+
// constants
|
|
9
|
+
const CITGM = 'CITGM';
|
|
10
|
+
const CITGM_NOBUILD = 'CITGM_NOBUILD';
|
|
11
|
+
const PR = 'PR';
|
|
12
|
+
const COMMIT = 'COMMIT';
|
|
13
|
+
const BENCHMARK = 'BENCHMARK';
|
|
14
|
+
const LIBUV = 'LIBUV';
|
|
15
|
+
const NOINTL = 'NOINTL';
|
|
16
|
+
const V8 = 'V8';
|
|
17
|
+
const LINTER = 'LINTER';
|
|
18
|
+
const DAILY_MASTER = 'DAILY_MASTER';
|
|
19
|
+
|
|
20
|
+
const CI_TYPE_ENUM = {
|
|
21
|
+
FULL_CI: 1 << 0,
|
|
22
|
+
JOB_CI: 1 << 2
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const CI_PROVIDERS = {
|
|
26
|
+
GITHUB: 'github-check',
|
|
27
|
+
NODEJS: 'nodejs'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const { JOB_CI, FULL_CI } = CI_TYPE_ENUM;
|
|
31
|
+
|
|
32
|
+
export const CI_TYPES = new Map([
|
|
33
|
+
[CITGM, {
|
|
34
|
+
name: 'CITGM',
|
|
35
|
+
jobName: 'citgm-smoker',
|
|
36
|
+
pattern: /job\/citgm-smoker\/(\d+)/,
|
|
37
|
+
type: JOB_CI
|
|
38
|
+
}],
|
|
39
|
+
[CITGM_NOBUILD, {
|
|
40
|
+
name: 'CITGM',
|
|
41
|
+
jobName: 'citgm-smoker-nobuild',
|
|
42
|
+
pattern: /job\/citgm-smoker-nobuild\/(\d+)/,
|
|
43
|
+
type: JOB_CI
|
|
44
|
+
}],
|
|
45
|
+
[PR, {
|
|
46
|
+
name: 'Full PR',
|
|
47
|
+
jobName: 'node-test-pull-request',
|
|
48
|
+
pattern: /job\/node-test-pull-request\/(\d+)/,
|
|
49
|
+
type: JOB_CI | FULL_CI
|
|
50
|
+
}],
|
|
51
|
+
[COMMIT, {
|
|
52
|
+
name: 'Full Commit',
|
|
53
|
+
jobName: 'node-test-commit',
|
|
54
|
+
pattern: /job\/node-test-commit\/(\d+)/,
|
|
55
|
+
type: JOB_CI | FULL_CI
|
|
56
|
+
}],
|
|
57
|
+
[BENCHMARK, {
|
|
58
|
+
name: 'Benchmark',
|
|
59
|
+
jobName: 'benchmark-node-micro-benchmarks',
|
|
60
|
+
pattern: /job\/benchmark-node-micro-benchmarks\/(\d+)/,
|
|
61
|
+
type: JOB_CI
|
|
62
|
+
}],
|
|
63
|
+
[LIBUV, {
|
|
64
|
+
name: 'libuv',
|
|
65
|
+
jobName: 'libuv-test-commit',
|
|
66
|
+
pattern: /job\/libuv-test-commit\/(\d+)/,
|
|
67
|
+
type: JOB_CI
|
|
68
|
+
}],
|
|
69
|
+
[NOINTL, {
|
|
70
|
+
name: 'No Intl',
|
|
71
|
+
jobName: 'node-test-commit-nointl',
|
|
72
|
+
pattern: /job\/node-test-commit-nointl\/(\d+)/,
|
|
73
|
+
type: JOB_CI
|
|
74
|
+
}],
|
|
75
|
+
[V8, {
|
|
76
|
+
name: 'V8',
|
|
77
|
+
jobName: 'node-test-commit-v8-linux',
|
|
78
|
+
pattern: /job\/node-test-commit-v8-linux\/(\d+)/,
|
|
79
|
+
type: JOB_CI
|
|
80
|
+
}],
|
|
81
|
+
[LINTER, {
|
|
82
|
+
name: 'Linter',
|
|
83
|
+
jobName: 'node-test-linter',
|
|
84
|
+
pattern: /job\/node-test-linter\/(\d+)/,
|
|
85
|
+
type: JOB_CI
|
|
86
|
+
}],
|
|
87
|
+
[DAILY_MASTER, {
|
|
88
|
+
name: 'Node Daily Master',
|
|
89
|
+
jobName: 'node-daily-master',
|
|
90
|
+
pattern: /job\/node-daily-master\/(\d+)/,
|
|
91
|
+
type: JOB_CI
|
|
92
|
+
}]
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
export function isFullCI(key) {
|
|
96
|
+
const data = CI_TYPES.get(key);
|
|
97
|
+
if (!data) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return !!(data.type & FULL_CI);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Given a ci.nodejs.org/*** link, parse the job type and ID
|
|
104
|
+
export function parseJobFromURL(url) {
|
|
105
|
+
if (typeof url !== 'string') {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const [type, info] of CI_TYPES) {
|
|
110
|
+
const match = url.match(info.pattern);
|
|
111
|
+
if (match) {
|
|
112
|
+
return {
|
|
113
|
+
link: url,
|
|
114
|
+
jobid: parseInt(match[1]),
|
|
115
|
+
type
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Parse links of CI Jobs posted in a GitHub thread
|
|
125
|
+
*/
|
|
126
|
+
export class JobParser {
|
|
127
|
+
/**
|
|
128
|
+
* @param {{bodyText: string, publishedAt: string}[]} thread
|
|
129
|
+
*/
|
|
130
|
+
constructor(thread) {
|
|
131
|
+
this.thread = thread.sort(
|
|
132
|
+
(a, b) => ascending(a.publishedAt, b.publishedAt)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @returns {Map<string, {link: string, date: string, jobid: number}>}
|
|
138
|
+
*/
|
|
139
|
+
parse() {
|
|
140
|
+
const thread = this.thread;
|
|
141
|
+
const result = new Map();
|
|
142
|
+
for (const c of thread) {
|
|
143
|
+
const text = c.bodyText;
|
|
144
|
+
if (!text.includes(CI_DOMAIN)) continue;
|
|
145
|
+
const jobs = this.parseText(text);
|
|
146
|
+
for (const job of jobs) {
|
|
147
|
+
// Always take the last one
|
|
148
|
+
// TODO(joyeecheung): exlcude links wrapped in `<del>`
|
|
149
|
+
result.set(job.type, {
|
|
150
|
+
link: job.link,
|
|
151
|
+
date: c.publishedAt,
|
|
152
|
+
jobid: job.jobid
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} text
|
|
161
|
+
* @returns {{link: string, jobid: number, type: string}}
|
|
162
|
+
*/
|
|
163
|
+
parseText(text) {
|
|
164
|
+
const links = text.match(CI_URL_RE);
|
|
165
|
+
if (!links) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const result = [];
|
|
170
|
+
for (const link of links) {
|
|
171
|
+
const parsed = parseJobFromURL(`https:${link}`);
|
|
172
|
+
if (parsed) {
|
|
173
|
+
result.push(parsed);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
JobParser.fromPR = async function(url, cli, request) {
|
|
182
|
+
const argv = parsePRFromURL(url);
|
|
183
|
+
if (!argv) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const data = new PRData(argv, cli, request);
|
|
187
|
+
await data.getThreadData();
|
|
188
|
+
const thread = data.getThread();
|
|
189
|
+
return new JobParser(thread);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const CI_TYPES_KEYS = {
|
|
193
|
+
CITGM,
|
|
194
|
+
CITGM_NOBUILD,
|
|
195
|
+
PR,
|
|
196
|
+
COMMIT,
|
|
197
|
+
BENCHMARK,
|
|
198
|
+
LIBUV,
|
|
199
|
+
V8,
|
|
200
|
+
NOINTL,
|
|
201
|
+
LINTER,
|
|
202
|
+
DAILY_MASTER
|
|
203
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import qs from 'node:querystring';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CI_DOMAIN,
|
|
5
|
+
parseJobFromURL,
|
|
6
|
+
CI_TYPES
|
|
7
|
+
} from './ci_type_parser.js';
|
|
8
|
+
|
|
9
|
+
export const statusType = {
|
|
10
|
+
SUCCESS: 'SUCCESS',
|
|
11
|
+
FAILURE: 'FAILURE',
|
|
12
|
+
ABORTED: 'ABORTED',
|
|
13
|
+
UNSTABLE: 'UNSTABLE'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function getPath(url) {
|
|
17
|
+
return url.replace(`https://${CI_DOMAIN}/`, '').replace('api/json', '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getNodeName(url) {
|
|
21
|
+
const re = /\/nodes=(.+?)\//;
|
|
22
|
+
if (re.test(url)) {
|
|
23
|
+
return url.match(re)[1];
|
|
24
|
+
}
|
|
25
|
+
const parts = url.split('/');
|
|
26
|
+
return parts[parts.length - 3];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function fold(summary, code) {
|
|
30
|
+
const dataBlock = '```\n' + code + '\n```';
|
|
31
|
+
const summaryBlock = `\n<summary>${summary}</summary>\n`;
|
|
32
|
+
return `<details>${summaryBlock}\n${dataBlock}\n</details>`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function pad(any, length) {
|
|
36
|
+
return (any + '').padEnd(length);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function markdownRow(...args) {
|
|
40
|
+
let result = '';
|
|
41
|
+
for (const item of args) {
|
|
42
|
+
result += `| ${item} `;
|
|
43
|
+
}
|
|
44
|
+
return result + '|\n';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function filterBuild(builds, type) {
|
|
48
|
+
return builds
|
|
49
|
+
.filter(build => build.result === type)
|
|
50
|
+
.map(build => parseJobFromURL(build.url));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function listBuilds(cli, request, type, since) {
|
|
54
|
+
// assert(type === COMMIT || type === PR)
|
|
55
|
+
const { jobName } = CI_TYPES.get(type);
|
|
56
|
+
const tree = 'builds[url,result,timestamp]';
|
|
57
|
+
const url = `https://${CI_DOMAIN}/job/${jobName}/api/json?tree=${qs.escape(tree)}`;
|
|
58
|
+
|
|
59
|
+
cli.startSpinner(`Querying ${url}`);
|
|
60
|
+
|
|
61
|
+
const result = await request.json(url);
|
|
62
|
+
let builds = result.builds;
|
|
63
|
+
if (since) {
|
|
64
|
+
builds = builds.filter(build => build.timestamp > since);
|
|
65
|
+
}
|
|
66
|
+
const failed = filterBuild(builds, statusType.FAILURE);
|
|
67
|
+
const aborted = filterBuild(builds, statusType.ABORTED);
|
|
68
|
+
const pending = filterBuild(builds, null);
|
|
69
|
+
const unstable = filterBuild(builds, statusType.UNSTABLE);
|
|
70
|
+
const success = filterBuild(builds, statusType.SUCCESS);
|
|
71
|
+
cli.stopSpinner('Done');
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
success,
|
|
75
|
+
failed,
|
|
76
|
+
aborted,
|
|
77
|
+
pending,
|
|
78
|
+
unstable,
|
|
79
|
+
count: builds.length
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getHighlight(f) {
|
|
84
|
+
if (!f.reason) {
|
|
85
|
+
f.reason = 'failure not found';
|
|
86
|
+
return f.reason;
|
|
87
|
+
}
|
|
88
|
+
return f.reason.split('\n')[f.highlight]
|
|
89
|
+
.replace(/not ok \d+ /, '')
|
|
90
|
+
.replace(
|
|
91
|
+
/JNLP4-connect connection from \S+/, 'JNLP4-connect connection from ...'
|
|
92
|
+
)
|
|
93
|
+
.replace(/FATAL: Could not checkout \w+/, 'FATAL: Could not checkout ...')
|
|
94
|
+
.replace(
|
|
95
|
+
/error: pathspec .+ did not match any file\(s\) known to git/,
|
|
96
|
+
'error: pathspec ... did not match any file(s) known to git')
|
|
97
|
+
.replace(
|
|
98
|
+
/failed: no workspace for .+/,
|
|
99
|
+
'failed: no workspace for ...'
|
|
100
|
+
)
|
|
101
|
+
.replace(
|
|
102
|
+
/fatal: loose object \w+ \(stored in .git\/objects\/.+\) is corrupt/,
|
|
103
|
+
'fatal: loose object ... (stored in .git/objects/...) is corrupt')
|
|
104
|
+
.replace(/hudson\.plugins\.git\.GitException: /, '')
|
|
105
|
+
.replace(/java\.io\.IOException: /, '');
|
|
106
|
+
}
|