-
-
Notifications
You must be signed in to change notification settings - Fork 135
Expand file tree
/
Copy pathnode-labels.js
More file actions
158 lines (137 loc) · 5.14 KB
/
node-labels.js
File metadata and controls
158 lines (137 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use strict'
// order of entries in this map *does* matter for the resolved labels
// earlier entries override later entries
const subSystemLabelsMap = new Map([
// don't want to label it a c++ update when we're "only" bumping the Node.js version
[/^src\/(?!node_version\.h)/, 'c++'],
// meta is a very specific label for things that are policy and or meta-info related
[/^([A-Z]+$|CODE_OF_CONDUCT|ROADMAP|WORKING_GROUPS|GOVERNANCE|CHANGELOG|\.mail|\.git.+)/, 'meta'],
// things that edit top-level .md files are always a doc change
[/^\w+\.md$/, 'doc'],
// different variants of *Makefile and build files
[/^(tools\/)?(Makefile|BSDmakefile|create_android_makefiles)$/, 'build'],
[/^tools\/(install.py|genv8constants.py|getnodeversion.py|js2c.py|utils.py|configure.d\/.*)$/, 'build'],
[/^(configure|node.gyp|common.gypi)$/, 'build'],
/* Dependencies */
// libuv needs an explicit mapping, as the ordinary /deps/ mapping below would
// end up as libuv changes labeled with "uv" (which is a non-existing label)
[/^deps\/uv\//, 'libuv'],
[/^deps\/([^/]+)/, '$1'],
/* JS subsystems */
// Oddities first
[/^lib\/(punycode|\w+\/freelist|sys\.js)/, ''], // TODO: ignore better?
[/^lib\/constants\.js$/, 'lib / src'],
[/^lib\/_(debug_agent|debugger)\.js$/, 'debugger'],
[/^lib(\/\w+)?\/(_)?link(ed)?list/, 'timers'],
[/^lib\/\w+\/bootstrap_node/, 'lib / src'],
[/^lib\/\w+\/v8_prof_/, 'tools'],
[/^lib\/\w+\/socket_list/, 'net'],
[/^lib\/\w+\/streams$/, 'stream'],
// All other lib/ files map directly
[/^lib\/_(\w+)_\w+\.js?$/, '$1'], // e.g. _(stream)_wrap
[/^lib(\/internal)?\/(\w+)\.js?$/, '$2'], // other .js files
[/^lib\/internal\/(\w+)$/, '$1'] // internal subfolders
])
const jsSubsystemList = [
'debugger', 'assert', 'buffer', 'child_process', 'cluster', 'console',
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'module',
'net', 'os', 'path', 'process', 'querystring', 'readline', 'repl', 'stream',
'string_decoder', 'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'zlib'
]
const exclusiveLabelsMap = new Map([
[/^test\//, 'test'],
// automatically tag subsystem-specific API doc changes
[/^doc\/api\/(\w+).md$/, ['doc', '$1']],
[/^doc\//, 'doc'],
[/^benchmark\//, 'benchmark']
])
function resolveLabels (filepathsChanged, limitLib = true) {
const exclusiveLabels = matchExclusiveSubSystem(filepathsChanged)
return (exclusiveLabels.length > 0)
? exclusiveLabels
: matchAllSubSystem(filepathsChanged, limitLib)
}
function hasAllSubsystems (arr) {
return arr.every((val) => {
return jsSubsystemList.includes(val)
})
}
function matchExclusiveSubSystem (filepathsChanged) {
const isExclusive = filepathsChanged.every(matchesAnExclusiveLabel)
var labels = matchSubSystemsByRegex(exclusiveLabelsMap, filepathsChanged)
// if there are multiple API doc changes, do not apply subsystem tags for now
if (isExclusive &&
labels.includes('doc') &&
labels.length > 2) {
const nonDocLabels = labels.filter((val) => {
return val !== 'doc'
})
if (hasAllSubsystems(nonDocLabels)) {
labels = ['doc']
} else {
labels = []
}
}
return isExclusive ? labels : []
}
function matchAllSubSystem (filepathsChanged, limitLib) {
return matchSubSystemsByRegex(
subSystemLabelsMap, filepathsChanged, limitLib)
}
function matchSubSystemsByRegex (rxLabelsMap, filepathsChanged, limitLib) {
const jsLabelCount = []
// by putting matched labels into a map, we avoid duplicate labels
const labelsMap = filepathsChanged.reduce((map, filepath) => {
const mappedSubSystems = mappedSubSystemsForFile(rxLabelsMap, filepath)
if (!mappedSubSystems) {
// short-circuit
return map
}
for (var i = 0; i < mappedSubSystems.length; ++i) {
const mappedSubSystem = mappedSubSystems[i]
if (limitLib && jsSubsystemList.includes(mappedSubSystem)) {
if (jsLabelCount.length >= 4) {
for (const jsLabel of jsLabelCount) {
delete map[jsLabel]
}
map['lib / src'] = true
// short-circuit
return map
} else {
jsLabelCount.push(mappedSubSystem)
}
}
map[mappedSubSystem] = true
}
return map
}, {})
return Object.keys(labelsMap)
}
function mappedSubSystemsForFile (labelsMap, filepath) {
for (const [regex, label] of labelsMap) {
const matches = regex.exec(filepath)
if (matches === null) {
continue
}
const ret = []
const labels = Array.isArray(label) ? label : [label]
labels.forEach((label) => {
// label names starting with $ means we want to extract a matching
// group from the regex we've just matched against
if (label.startsWith('$')) {
const wantedMatchGroup = label.substr(1)
label = matches[wantedMatchGroup]
}
if (!label) {
return
}
// use label name as is when label doesn't look like a regex matching group
ret.push(label)
})
return ret
}
}
function matchesAnExclusiveLabel (filepath) {
return mappedSubSystemsForFile(exclusiveLabelsMap, filepath) !== undefined
}
exports.resolveLabels = resolveLabels