Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 50 additions & 16 deletions lib/node-labels.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
'use strict'

const subSystemLabels = {
'c++': /^src\//
// order of entries in this map *does* matter for the resolved labels
const subSystemLabelsMap = {
// don't want to label it a c++ update when we're "only" bumping the Node.js version
'c++': /^src\/(?!node_version\.h)/,

This comment was marked as off-topic.

// 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)
'libuv': /^deps\/uv\//,
'$1': /^deps\/([^/]+)/
}

const rxTests = /^test\//
const exclusiveLabelsMap = {
test: /^test\//,
doc: /^doc\//,
benchmark: /^benchmark\//
}

function resolveLabels (filepathsChanged) {
if (isOnly(rxTests, filepathsChanged)) {
return ['test']
}
const exclusiveLabels = matchExclusiveSubSystem(filepathsChanged)

return (exclusiveLabels.length > 0)
? exclusiveLabels
: matchAllSubSystem(filepathsChanged)
}

return matchBySubSystemRegex(filepathsChanged)
function matchExclusiveSubSystem (filepathsChanged) {
const labels = matchSubSystemsByRegex(exclusiveLabelsMap, filepathsChanged)
return labels.length === 1 ? labels : []
}

function isOnly (regexToMatch, filepathsChanged) {
return filepathsChanged.every((filepath) => regexToMatch.test(filepath))
function matchAllSubSystem (filepathsChanged) {
return matchSubSystemsByRegex(subSystemLabelsMap, filepathsChanged)
}

function matchBySubSystemRegex (filepathsChanged) {
function matchSubSystemsByRegex (rxLabelsMap, filepathsChanged) {
// by putting matched labels into a map, we avoid duplicate labels
const labelsMap = filepathsChanged.reduce((map, filepath) => {
const mappedSubSystem = mappedSubSystemForFile(filepath)
const mappedSubSystem = mappedSubSystemForFile(rxLabelsMap, filepath)

if (mappedSubSystem) {
map[mappedSubSystem] = true
Expand All @@ -33,12 +48,31 @@ function matchBySubSystemRegex (filepathsChanged) {
return Object.keys(labelsMap)
}

function mappedSubSystemForFile (filepath) {
return Object.keys(subSystemLabels).find((labelName) => {
const rxForLabel = subSystemLabels[labelName]
function mappedSubSystemForFile (labelsMap, filepath) {
return Object.keys(labelsMap).map((labelName) => {
const rxForLabel = labelsMap[labelName]
const matches = rxForLabel.exec(filepath)

// return undefined when subsystem regex didn't match,
// we'll filter out these values with the .filter() below
if (matches === null) {
return undefined
}

// label names starting with $ means we want to extract a matching
// group from the regex we've just matched against
if (labelName.startsWith('$')) {
const wantedMatchGroup = labelName.substr(1)
return matches[wantedMatchGroup]
}

// use label name as is when label doesn't look like a regex matching group
return labelName
}).filter(withoutUndefinedValues)[0]
}

return rxForLabel.test(filepath) ? labelName : undefined
})
function withoutUndefinedValues (label) {
return label !== undefined
}

exports.resolveLabels = resolveLabels
75 changes: 75 additions & 0 deletions test/node-labels.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@ tap.test('label: "test" when only ./test/ files has been changed', (t) => {
t.end()
})

tap.test('no labels: when ./test/ and ./doc/ files has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'test/debugger/test-debugger-pid.js',
'doc/api/fs.md'
])

t.same(labels, [])

t.end()
})

tap.test('label: "doc" when only ./doc/ files has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'doc/api/fs.md',
'doc/api/http.md',
'doc/onboarding.md'
])

t.same(labels, ['doc'])

t.end()
})

tap.test('label: "benchmark" when only ./benchmark/ files has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'benchmark/http_server_lag.js',
'benchmark/http/check_is_http_token.js'
])

t.same(labels, ['benchmark'])

t.end()
})

tap.test('label: "c++" when ./src/* has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'src/async-wrap.h',
Expand All @@ -27,6 +61,47 @@ tap.test('label: "c++" when ./src/* has been changed', (t) => {
t.end()
})

tap.test('label: not "c++" when ./src/node_version.h has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'src/node_version.h'
])

t.same(labels, [])

t.end()
})

tap.test('label: "v8" when ./deps/v8/ files has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'deps/v8/src/arguments.cc'
])

t.same(labels, ['v8'])

t.end()
})

tap.test('label: "libuv" when ./deps/ub/ files has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'deps/uv/src/fs-poll.c'
])

t.same(labels, ['libuv'])

t.end()
})

tap.test('label: "v8", "openssl" when ./deps/v8/ and ./deps/openssl/ files has been changed', (t) => {
const labels = nodeLabels.resolveLabels([
'deps/v8/src/arguments.cc',
'deps/openssl/openssl/ssl/ssl_rsa.c'
])

t.same(labels, ['v8', 'openssl'])

t.end()
})

//
// Planned tests to be resolved later
//
Expand Down