diff --git a/lib/jenkins-events.js b/lib/jenkins-events.js index a57cff19..e38d86aa 100644 --- a/lib/jenkins-events.js +++ b/lib/jenkins-events.js @@ -72,6 +72,8 @@ module.exports = (app, events) => { // create unique logger which is easily traceable throughout the entire app // by having e.g. "nodejs/nodejs.org/#1337" part of every subsequent log statement data.logger = logger.child({ identifier, event }, true) + // prevent data.logger from plausibly being serialised to JSON due to circular references + Object.defineProperty(data, 'logger', { enumerable: false }) data.logger.info('Emitting Jenkins event') debug(data) diff --git a/lib/node-labels.js b/lib/node-labels.js deleted file mode 100644 index b017bb27..00000000 --- a/lib/node-labels.js +++ /dev/null @@ -1,307 +0,0 @@ -'use strict' - -// order of entries in this map *does* matter for the resolved labels -// earlier entries override later entries -const subSystemLabelsMap = new Map([ - /* src subsystems */ - [/^src\/async-wrap/, ['c++', 'async_wrap']], - [/^src\/(?:base64|node_buffer|string_)/, ['c++', 'buffer']], - [/^src\/cares/, ['c++', 'cares']], - [/^src\/(?:process_wrap|spawn_)/, ['c++', 'child_process']], - [/^src\/node_crypto/, ['c++', 'crypto']], - [/^src\/(?:debug-|node_debug)/, ['c++', 'debugger']], - [/^src\/udp_/, ['c++', 'dgram']], - [/^src\/(?:fs_|node_file|node_stat_watcher)/, ['c++', 'fs']], - [/^src\/node_http_parser/, ['c++', 'http_parser']], - [/^src\/node_i18n/, ['c++', 'intl']], - [/^src\/uv\./, ['c++', 'libuv']], - [/^src\/(?:connect(?:ion)?|pipe|tcp)_/, ['c++', 'net']], - [/^src\/node_os/, ['c++', 'os']], - [/^src\/(?:node_main|signal_)/, ['c++', 'process']], - [/^src\/timer_/, ['c++', 'timers']], - [/^src\/(?:CNNICHashWhitelist|node_root_certs|tls_)/, ['c++', 'tls']], - [/^src\/tty_/, ['c++', 'tty']], - [/^src\/node_url/, ['c++', 'url-whatwg']], - [/^src\/node_util/, ['c++', 'util']], - [/^src\/(?:node_v8|v8abbr)/, ['c++', 'V8 Engine']], - [/^src\/node_contextify/, ['c++', 'vm']], - [/^src\/.*win32.*/, ['c++', 'windows']], - [/^src\/node_zlib/, ['c++', 'zlib']], - [/^src\/tracing/, ['c++', 'tracing']], - [/^src\/node_api/, ['c++', 'n-api']], - [/^src\/node_http2/, ['c++', 'http2', 'dont-land-on-v6.x']], - [/^src\/node_report/, ['c++', 'report']], - [/^src\/node_wasi/, ['c++', 'wasi']], - [/^src\/node_worker/, ['c++', 'worker']], - [/^src\/quic\/*/, ['c++', 'quic', 'dont-land-on-v14.x', 'dont-land-on-v12.x']], - [/^src\/node_bob*/, ['c++', 'quic', 'dont-land-on-v14.x', 'dont-land-on-v12.x']], - - // don't label python files as c++ - [/^src\/.+\.py$/, 'lib / src'], - - // properly label changes to v8 inspector integration-related files - [/^src\/inspector_/, ['c++', 'inspector']], - - // don't want to label it a c++ update when we're "only" bumping the Node.js version - [/^src\/(?!node_version\.h)/, 'c++'], - // BUILDING.md should be marked as 'build' in addition to 'doc' - [/^BUILDING\.md$/, ['build', 'doc']], - // 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|\.travis\.yml)$/, 'build'], - [/^tools\/(install\.py|genv8constants\.py|getnodeversion\.py|js2c\.py|utils\.py|configure\.d\/.*)$/, 'build'], - [/^vcbuild\.bat$/, ['build', 'windows']], - [/^(android-)?configure|node\.gyp|common\.gypi$/, 'build'], - // more specific tools - [/^tools\/gyp/, ['tools', 'build']], - [/^tools\/doc\//, ['tools', 'doc']], - [/^tools\/icu\//, ['tools', 'intl']], - [/^tools\/(?:osx-pkg\.pmdoc|pkgsrc)\//, ['tools', 'macos', 'install']], - [/^tools\/(?:(?:mac)?osx-)/, ['tools', 'macos']], - [/^tools\/test-npm/, ['tools', 'test', 'npm']], - [/^tools\/test/, ['tools', 'test']], - [/^tools\/(?:certdata|mkssldef|mk-ca-bundle)/, ['tools', 'openssl', 'tls']], - [/^tools\/msvs\//, ['tools', 'windows', 'install']], - [/^tools\/[^/]+\.bat$/, ['tools', 'windows']], - [/^tools\/make-v8/, ['tools', 'V8 Engine']], - // all other tool changes should be marked as such - [/^tools\//, 'tools'], - [/^\.eslint|\.remark|\.editorconfig/, 'tools'], - - /* 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\/v8\/tools\/gen-postmortem-metadata\.py/, ['V8 Engine', 'post-mortem']], - [/^deps\/v8\//, 'V8 Engine'], - [/^deps\/uvwasi\//, 'wasi'], - [/^deps\/nghttp2\/nghttp2\.gyp/, ['build', 'http2', 'dont-land-on-v6.x']], - [/^deps\/nghttp2\//, ['http2', 'dont-land-on-v6.x']], - [/^deps\/ngtcp2\//, 'quic', 'dont-land-on-v14.x', 'dont-land-on-v12.x'], - [/^deps\/nghttp3\//, 'quic', 'dont-land-on-v14.x', 'dont-land-on-v12.x'], - [/^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'], - [/^lib\/.*http2/, ['http2', 'dont-land-on-v6.x']], - [/^lib\/worker_threads.js$/, ['worker']], - [/^lib\/internal\/url\.js$/, ['url-whatwg']], - [/^lib\/internal\/modules\/esm/, 'ES Modules'], - [/^lib\/internal\/quic\/*/, ['quic', 'dont-land-on-v14.x', 'dont-land-on-v12.x']], - // 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', 'async_hooks', 'buffer', 'child_process', 'cluster', - 'console', 'crypto', 'dgram', 'dns', 'domain', 'events', 'esm', 'fs', 'http', - 'https', 'http2', 'module', 'net', 'os', 'path', 'process', 'querystring', - 'quic', 'readline', 'repl', 'report', 'stream', 'string_decoder', 'timers', - 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker', 'zlib' -] - -const exclusiveLabelsMap = new Map([ - // more specific tests - [/^test\/addons\//, ['test', 'addons']], - [/^test\/debugger\//, ['test', 'debugger']], - [/^test\/doctool\//, ['test', 'doc', 'tools']], - [/^test\/timers\//, ['test', 'timers']], - [/^test\/pseudo-tty\//, ['test', 'tty']], - [/^test\/inspector\//, ['test', 'inspector']], - [/^test\/cctest\/test_inspector/, ['test', 'inspector']], - [/^test\/cctest\/test_url/, ['test', 'url-whatwg']], - [/^test\/addons-napi\//, ['test', 'n-api']], - [/^test\/async-hooks\//, ['test', 'async_hooks']], - [/^test\/report\//, ['test', 'report']], - [/^test\/fixtures\/es-module/, ['test', 'ES Modules']], - [/^test\/es-module\//, ['test', 'ES Modules']], - - [/^test\//, 'test'], - - // specific map for modules.md as it should be labeled 'module' not 'modules' - [/^doc\/api\/modules.md$/, ['doc', 'module']], - // specific map for esm.md as it should be labeled 'ES Modules' not 'esm' - [/^doc\/api\/esm.md$/, ['doc', 'ES Modules']], - // n-api is treated separately since it is not a JS core module but is still - // considered a subsystem of sorts - [/^doc\/api\/n-api.md$/, ['doc', 'n-api']], - // quic - [/^doc\/api\/quic.md$/, ['doc', 'quic', 'dont-land-on-v14.x', 'dont-land-on-v12.x']], - // add worker label to PRs that affect doc/api/worker_threads.md - [/^doc\/api\/worker_threads.md$/, ['doc', 'worker']], - // automatically tag JS subsystem-specific API doc changes - [/^doc\/api\/(\w+)\.md$/, ['doc', '$1']], - // add deprecations label to PRs that affect doc/api/deprecations.md - [/^doc\/api\/deprecations.md$/, ['doc', 'deprecations']], - - [/^doc\//, 'doc'], - - // more specific benchmarks - [/^benchmark\/buffers\//, ['benchmark', 'buffer']], - [/^benchmark\/(?:arrays|es)\//, ['benchmark', 'V8 Engine']], - [/^benchmark\/_http/, ['benchmark', 'http']], - [/^benchmark\/(?:misc|fixtures)\//, 'benchmark'], - [/^benchmark\/streams\//, ['benchmark', 'stream']], - [/^benchmark\/([^/]+)\//, ['benchmark', '$1']], - - [/^benchmark\//, 'benchmark'] -]) - -function resolveLabels (filepathsChanged, baseBranch, limitLabels = true) { - const exclusiveLabels = matchExclusiveSubSystem(filepathsChanged) - - if (typeof baseBranch !== 'string') { - if (typeof baseBranch === 'boolean') { - limitLabels = baseBranch - } - baseBranch = '' - } - - const labels = (exclusiveLabels.length > 0) - ? exclusiveLabels - : matchAllSubSystem(filepathsChanged, limitLabels) - - // Add version labels if PR is made against a version branch - const m = /^(v\d+\.(?:\d+|x))(?:-staging|$)/.exec(baseBranch) - if (m) { - labels.push(m[1]) - } - - return labels -} - -function hasAllSubsystems (arr) { - return arr.every((val) => { - return jsSubsystemList.includes(val) - }) -} - -// This function is needed to help properly identify when a PR should always -// (just) be labeled as 'doc' when it is all changes in doc/api/ that do not -// match subsystem names (e.g. _toc.md, all.md) -function hasAllDocChanges (arr) { - return arr.every((val) => { - return /^doc\//.test(val) - }) -} - -function hasAllTestChanges (arr) { - return arr.every((val) => { - return /^test\//.test(val) - }) -} - -function matchExclusiveSubSystem (filepathsChanged) { - const isExclusive = filepathsChanged.every(matchesAnExclusiveLabel) - var labels = matchSubSystemsByRegex(exclusiveLabelsMap, filepathsChanged) - var nonMetaLabels = labels.filter((label) => { - return !/^dont-/.test(label) - }) - - // if there are multiple API doc changes, do not apply subsystem tags for now - if (isExclusive && - nonMetaLabels.includes('doc') && - nonMetaLabels.length > 2 && - !hasAllTestChanges(filepathsChanged)) { - const nonDocLabels = nonMetaLabels.filter((val) => { - return val !== 'doc' - }) - if (hasAllSubsystems(nonDocLabels) || hasAllDocChanges(filepathsChanged)) { - labels = ['doc'] - } else { - labels = [] - } - } - return isExclusive ? labels : [] -} - -function matchAllSubSystem (filepathsChanged, limitLabels) { - return matchSubSystemsByRegex( - subSystemLabelsMap, filepathsChanged, limitLabels) -} - -function matchSubSystemsByRegex (rxLabelsMap, filepathsChanged, limitLabels) { - const labelCount = [] - // 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 (limitLabels && hasLibOrSrcChanges(filepathsChanged)) { - if (labelCount.length >= 4) { - for (const label of labelCount) { - // don't delete the c++ label as we always want that if it has matched - if (label !== 'c++') delete map[label] - } - map['lib / src'] = true - // short-circuit - return map - } else { - labelCount.push(mappedSubSystem) - } - } - - map[mappedSubSystem] = true - } - - return map - }, {}) - - return Object.keys(labelsMap) -} - -function hasLibOrSrcChanges (filepathsChanged) { - return filepathsChanged.some((filepath) => filepath.startsWith('lib/') || filepath.startsWith('src/')) -} - -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 diff --git a/lib/node-repo.js b/lib/node-repo.js index 4855ba51..ae26f65f 100644 --- a/lib/node-repo.js +++ b/lib/node-repo.js @@ -2,80 +2,17 @@ /* eslint-disable camelcase */ -const LRU = require('lru-cache') const Aigle = require('aigle') const request = require('request') const githubClient = require('./github-client') const { createPrComment } = require('./github-comment') -const resolveLabels = require('./node-labels').resolveLabels const { Owners } = require('./node-owners') -const existingLabelsCache = new LRU({ max: 1, maxAge: 1000 * 60 * 60 }) const fiveSeconds = 5 * 1000 const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)) -async function deferredResolveLabelsThenUpdatePr (options) { - const timeoutMillis = (options.timeoutInSec || 0) * 1000 - await sleep(timeoutMillis) - return resolveLabelsThenUpdatePr(options) -} - -async function resolveLabelsThenUpdatePr (options) { - const times = options.retries || 5 - const interval = options.retryInterval || fiveSeconds - const retry = fn => Aigle.retry({ times, interval }, fn) - - const filepathsChanged = await retry(() => listFiles({ - owner: options.owner, - repo: options.repo, - pull_number: options.prId, - logger: options.logger - })) - options.logger.debug('Fetching PR files for labelling') - - const resolvedLabels = resolveLabels(filepathsChanged, options.baseBranch) - - return fetchExistingThenUpdatePr(options, resolvedLabels) -} - -async function fetchExistingThenUpdatePr (options, labels) { - try { - const existingLabels = await fetchExistingLabels(options) - const labelsToAdd = stringsInCommon(existingLabels, labels) - options.logger.debug('Resolved labels: ' + labelsToAdd, labels, existingLabels) - - return updatePrWithLabels(options, labelsToAdd) - } catch (err) { - options.logger.error(err, 'Error retrieving existing repo labels') - - return updatePrWithLabels(options, labels) - } -} - -async function updatePrWithLabels (options, labels) { - // no need to request github if we didn't resolve any labels - if (!labels.length) { - return - } - - options.logger.debug('Trying to add labels: ' + labels) - - try { - await githubClient.issues.addLabels({ - owner: options.owner, - repo: options.repo, - issue_number: options.prId, - labels: labels - }) - - options.logger.info('Added labels: ' + labels) - } catch (err) { - options.logger.error(err, 'Error while adding labels') - } -} - async function removeLabelFromPR (options, label) { // no need to request github if we didn't resolve a label if (!label) { @@ -104,46 +41,6 @@ async function removeLabelFromPR (options, label) { return label } -async function fetchExistingLabels (options) { - const cacheKey = `${options.owner}:${options.repo}` - - if (existingLabelsCache.has(cacheKey)) { - return existingLabelsCache.get(cacheKey) - } - - const labelsResult = await fetchLabelPages(options, 1) - const existingLabels = labelsResult.data || labelsResult || [] - const existingLabelNames = existingLabels.map((label) => label.name) - - // cache labels so we don't have to fetch these *all the time* - existingLabelsCache.set(cacheKey, existingLabelNames) - options.logger.debug('Filled existing repo labels cache: ' + existingLabelNames) - - return existingLabelNames -} - -async function fetchLabelPages (options, startPageNum, cb) { - // the github client API is somewhat misleading, - // this fetches *all* repo labels not just for an issue - const result = await githubClient.issues.listLabelsForRepo({ - owner: options.owner, - repo: options.repo, - page: startPageNum, - per_page: 100 - }) - - const existingLabels = result.data || [] - if (!githubClient.hasNextPage(result)) { - return existingLabels - } - - const remainingLabels = await fetchLabelPages( - options, - startPageNum + 1) - - return existingLabels.concat(remainingLabels) -} - function getBotPrLabels (options, cb) { githubClient.issues.listEvents({ owner: options.owner, @@ -175,13 +72,6 @@ function getBotPrLabels (options, cb) { }, cb) } -function stringsInCommon (arr1, arr2) { - const loweredArr2 = arr2.map((str) => str.toLowerCase()) - // we want the original string cases in arr1, therefore we don't lowercase them - // before comparing them cause that would wrongly make "V8" -> "v8" - return arr1.filter((str) => loweredArr2.indexOf(str.toLowerCase()) !== -1) -} - async function deferredResolveOwnersThenPingPr (options) { const timeoutMillis = (options.timeoutInSec || 0) * 1000 await sleep(timeoutMillis) @@ -288,12 +178,9 @@ async function pingOwners (options, owners) { exports.getBotPrLabels = getBotPrLabels exports.removeLabelFromPR = removeLabelFromPR -exports.fetchExistingThenUpdatePr = fetchExistingThenUpdatePr -exports.resolveLabelsThenUpdatePr = deferredResolveLabelsThenUpdatePr exports.resolveOwnersThenPingPr = deferredResolveOwnersThenPingPr // exposed for testability -exports._fetchExistingLabels = fetchExistingLabels exports._testExports = { pingOwners, getCodeOwnersFile, getCodeOwnersUrl, getDefaultBranch, listFiles, getCommentForOwners } diff --git a/package-lock.json b/package-lock.json index c4dcc8dd..4aa78ad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2417,9 +2417,9 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -2514,9 +2514,9 @@ } }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-escaper": { @@ -3156,9 +3156,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.flattendeep": { "version": "4.4.0", @@ -3187,12 +3187,6 @@ "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", "dev": true }, - "lolex": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", - "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=", - "dev": true - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3206,6 +3200,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -3528,9 +3523,9 @@ "dev": true }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true }, "npm-run-path": { @@ -3744,12 +3739,12 @@ } }, "original": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz", - "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", "dev": true, "requires": { - "url-parse": "~1.4.0" + "url-parse": "^1.4.3" } }, "os-homedir": { @@ -4197,7 +4192,8 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, "psl": { "version": "1.1.31", @@ -4239,9 +4235,9 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "querystringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", - "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, "range-parser": { @@ -6550,12 +6546,12 @@ } }, "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", + "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", "dev": true, "requires": { - "querystringify": "^2.0.0", + "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, @@ -6786,15 +6782,16 @@ "dev": true }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true }, "yaml": { "version": "1.8.3", diff --git a/package.json b/package.json index 5e9ebcb9..9e3158fa 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,10 @@ "events-async": "^1.2.1", "express": "^4.13.4", "glob": "^7.0.3", - "lru-cache": "^4.0.1", "request": "^2.88.0" }, "devDependencies": { "eventsource": "^0.2.1", - "lolex": "^1.5.1", "nock": "^12.0.3", "nodemon": "^2.0.4", "proxyquire": "^1.7.10", diff --git a/scripts/attempt-backport.js b/scripts/attempt-backport.js deleted file mode 100644 index 3a95d9b7..00000000 --- a/scripts/attempt-backport.js +++ /dev/null @@ -1,241 +0,0 @@ -'use strict' - -const { spawn } = require('child_process') -const debug = require('debug')('attempt-backport') -const request = require('request') -const { fetchExistingThenUpdatePr, removeLabelFromPR, getBotPrLabels } = require('../lib/node-repo') - -const enabledRepos = ['node'] -const nodeVersions = [ - { version: 7 }, - { version: 6, lts: true }, - { version: 4, lts: true } -] -const queue = [] -let inProgress = false - -module.exports = function (app) { - if (!global._node_repo_dir) return - - app.on('pull_request.opened', handlePrUpdate) - // Pull Request updates - app.on('pull_request.synchronize', handlePrUpdate) - - // to trigger polling manually - app.get('/attempt-backport/pr/:owner/:repo/:id', (req, res) => { - const owner = req.params.owner - const repo = req.params.repo - const prId = parseInt(req.params.id, 10) - const options = { owner, repo, prId, logger: req.log } - - if (~enabledRepos.indexOf(repo)) { - for (const node of nodeVersions) { - queueAttemptBackport(options, node.version, !!node.lts) - } - } - - if (!inProgress) processNextBackport() - - res.end() - }) -} - -function handlePrUpdate (event, owner, repo) { - if (!~enabledRepos.indexOf(repo)) return - - if (event.pull_request.base.ref !== 'master') return - - const prId = event.number - const options = { owner, repo, prId, logger: event.logger } - - debug(`/${owner}/${repo}/pull/${prId} sync`) - for (const node of nodeVersions) { - queueAttemptBackport(options, node.version, !!node.lts) - } - - if (!inProgress) processNextBackport() -} - -function processNextBackport () { - const item = queue.shift() - if (!item) return - - if (typeof item !== 'function') { - debug(`item was not a function! - queue size: ${queue.length}`) - return - } else if (inProgress) { - debug(`was still in progress! - queue size: ${queue.length}`) - return - } - item() -} - -function queueAttemptBackport (options, version, isLTS) { - queue.push(function () { - options.logger.debug(`processing a new backport to v${version}`) - attemptBackport(options, version, isLTS, processNextBackport) - }) -} - -function attemptBackport (options, version, isLTS, cb) { - // Start - gitAmAbort() - - function wrapCP (cmd, args, opts, callback) { - let exited = false - - if (arguments.length === 3) { - callback = opts - opts = {} - } - - opts.cwd = global._node_repo_dir - - const cp = spawn(cmd, args, opts) - const argsString = [cmd, ...args].join(' ') - - cp.on('error', function (err) { - debug(`child_process err: ${err}`) - if (!exited) onError() - }) - cp.on('exit', function (code) { - exited = true - if (!cb) { - debug(`error before exit, code: ${code}, on '${argsString}'`) - return - } else if (code > 0) { - debug(`exit code > 0: ${code}, on '${argsString}'`) - onError() - return - } - callback() - }) - // Useful when debugging. - - cp.stdout.on('data', (data) => { - options.logger.debug(data.toString()) - }) - cp.stderr.on('data', (data) => { - options.logger.debug(data.toString()) - }) - - return cp - } - - function onError () { - if (!cb) return - const _cb = cb - setImmediate(() => { - options.logger.debug(`backport to ${version} failed`) - - if (!isLTS) { - options.logger.debug(`Should have added (but temporary disabled): dont-land-on-v${version}.x`) - } else { - getBotPrLabels(options, (err, ourLabels) => { - if (err) { - options.logger.error(err, 'Error fetching existing bot labels') - return - } - - const label = `lts-watch-v${version}.x` - if (!ourLabels.includes(label)) return - - removeLabelFromPR(options, label) - }) - } - - setImmediate(() => { - inProgress = false - _cb() - }) - }) - cb = null - } - - function gitAmAbort () { - // TODO(Fishrock123): this should probably just merge into wrapCP - let exited = false - options.logger.debug('aborting any previous backport attempt...') - - const cp = spawn('git', ['am', '--abort'], { cwd: global._node_repo_dir }) - const argsString = 'git am --abort' - - cp.on('error', function (err) { - debug(`child_process err: ${err}`) - if (!exited) onError() - }) - cp.on('exit', function (code) { - exited = true - if (!cb) { - debug(`error before exit, code: ${code}, on '${argsString}'`) - return - } - gitRemoteUpdate() - }) - } - - function gitRemoteUpdate () { - options.logger.debug('updating git remotes...') - wrapCP('git', ['remote', 'update', '-p'], gitCheckout) - } - - function gitCheckout () { - options.logger.debug(`checking out origin/v${version}.x-staging...`) - wrapCP('git', ['checkout', `origin/v${version}.x-staging`], gitCleanFXD) - } - - function gitCleanFXD () { - wrapCP('git', ['clean', '-fdx'], fetchDiff) - } - - function fetchDiff () { - options.logger.debug(`fetching diff from pr ${options.prId}...`) - - const url = `https://patch-diff.githubusercontent.com/raw/${options.owner}/${options.repo}/pull/${options.prId}.patch` - - const req = request(url) - - req.on('error', function (err) { - debug(`request err: ${err}`) - return onError() - }) - req.on('response', function (response) { - if (response.statusCode !== 200) { - debug(`request non-200 status: ${response.statusCode}`) - return onError() - } - }) - - gitAttemptBackport(req) - } - - function gitAttemptBackport (req) { - options.logger.debug(`attempting a backport to v${version}...`) - const cp = wrapCP('git', ['am', '-3'], { stdio: 'pipe' }, function done () { - // Success! - if (isLTS) { - fetchExistingThenUpdatePr(options, [`lts-watch-v${version}.x`]) - } else { - getBotPrLabels(options, (err, ourLabels) => { - if (err) { - options.logger.error(err, 'Error fetching existing bot labels') - return - } - - const label = `dont-land-on-v${version}.x` - if (!ourLabels.includes(label)) return - - removeLabelFromPR(options, label) - }) - } - - setImmediate(() => { - options.logger.debug(`backport to v${version} successful`) - inProgress = false - cb() - }) - }) - - req.pipe(cp.stdin) - } -} diff --git a/scripts/node-subsystem-label.js b/scripts/node-subsystem-label.js deleted file mode 100644 index ce3bd0b9..00000000 --- a/scripts/node-subsystem-label.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict' - -const debug = require('debug')('node_subsystem_label') - -const nodeRepo = require('../lib/node-repo') - -const timeoutInSec = process.env.WAIT_SECONDS_BEFORE_RESOLVING_LABELS || 2 - -module.exports = function (app, events) { - events.on('pull_request.opened', handlePrCreated) -} - -function handlePrCreated (event, owner, repo) { - const prId = event.number - const logger = event.logger - const baseBranch = event.pull_request.base.ref - - // subsystem labelling is for node core only - if (repo !== 'node') return - - debug(`/${owner}/${repo}/pull/${prId} opened`) - // by not hard coding the owner repo to nodejs/node here, - // we can test these this script in a different repo than - // *actual* node core as long as the repo is named "node" - return nodeRepo.resolveLabelsThenUpdatePr({ - owner, - repo, - prId, - logger, - baseBranch, - timeoutInSec - }) -} diff --git a/server.js b/server.js index 41b63ea1..ffb1bec8 100644 --- a/server.js +++ b/server.js @@ -4,20 +4,6 @@ require('dotenv').load({ silent: true }) const glob = require('glob') const logger = require('./lib/logger') -const { spawnSync } = require('child_process') - -if (process.env.NODE_REPO_DIR) { - const fs = require('fs') - global._node_repo_dir = fs.realpathSync(process.env.NODE_REPO_DIR) - const out = spawnSync('git', ['status'], { cwd: global._node_repo_dir }) - - if (out.status !== 0) { - logger.info(out.stdout) - logger.error(out.stderr) - logger.error('Bad NODE_REPO_DIR. Backport patch testing disabled.') - global._node_repo_dir = false - } -} const port = process.env.PORT || 3000 const scriptsToLoad = process.env.SCRIPTS || './scripts/**/*.js' diff --git a/test/_fixtures/repo-labels-page-2.json b/test/_fixtures/repo-labels-page-2.json deleted file mode 100644 index ad0ccb5c..00000000 --- a/test/_fixtures/repo-labels-page-2.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "data": [ - { - "id": 280213872, - "url": "https://api.github.com/repos/nodejs/node/labels/v0.12", - "name": "v0.12", - "color": "c7def8", - "default": false - }, - { - "id": 280213880, - "url": "https://api.github.com/repos/nodejs/node/labels/v4.x", - "name": "v4.x", - "color": "eb6420", - "default": false - }, - { - "id": 441404503, - "url": "https://api.github.com/repos/nodejs/node/labels/v7.x", - "name": "v7.x", - "color": "fbca04", - "default": false - }, - { - "id": 176191361, - "url": "https://api.github.com/repos/nodejs/node/labels/V8 Engine", - "name": "V8 Engine", - "color": "0052cc", - "default": false - }, - { - "id": 386816750, - "url": "https://api.github.com/repos/nodejs/node/labels/V8_inspector", - "name": "V8_inspector", - "color": "ededed", - "default": false - }, - { - "id": 155436007, - "url": "https://api.github.com/repos/nodejs/node/labels/vm", - "name": "vm", - "color": "bfdadc", - "default": false - }, - { - "id": 166236401, - "url": "https://api.github.com/repos/nodejs/node/labels/windows", - "name": "windows", - "color": "9944dd", - "default": false - }, - { - "id": 151728680, - "url": "https://api.github.com/repos/nodejs/node/labels/wontfix", - "name": "wontfix", - "color": "ededed", - "default": true - }, - { - "id": 155436008, - "url": "https://api.github.com/repos/nodejs/node/labels/zlib", - "name": "zlib", - "color": "009800", - "default": false - } - ], - "meta": { - "x-ratelimit-limit":"60", - "x-ratelimit-remaining":"52", - "x-ratelimit-reset":"1531354196", - "x-github-request-id":"EBA1:936F:5CE9930:7925DDD:5B4692FA", - "x-github-media-type":"github.v3; format=json", - "link":"; rel=\"prev\", ; rel=\"first\"", - "etag":"\"9408108c2d89606f9539deefc6d90acd\"", - "status":"200 OK" - } -} diff --git a/test/_fixtures/repo-labels.json b/test/_fixtures/repo-labels.json deleted file mode 100644 index b3d5e759..00000000 --- a/test/_fixtures/repo-labels.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "data": [ - { - "url": "https://api.github.com/repos/nodejs/node/labels/confirmed-bug", - "name": "confirmed-bug", - "color": "fc2929" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/duplicate", - "name": "duplicate", - "color": "ededed" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/help%20wanted", - "name": "help wanted", - "color": "159818" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/invalid", - "name": "invalid", - "color": "ededed" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/question", - "name": "question", - "color": "cc317c" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/wontfix", - "name": "wontfix", - "color": "ededed" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/in%20progress", - "name": "in progress", - "color": "ededed" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/tls", - "name": "tls", - "color": "fad8c7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/crypto", - "name": "crypto", - "color": "009800" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/buffer", - "name": "buffer", - "color": "f7c6c7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/http", - "name": "http", - "color": "c7def8" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/https", - "name": "https", - "color": "0052cc" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/assert", - "name": "assert", - "color": "d4c5f9" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/util", - "name": "util", - "color": "d4c5f9" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/cluster", - "name": "cluster", - "color": "f7c6c7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/child_process", - "name": "child_process", - "color": "f7c6c7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/dgram", - "name": "dgram", - "color": "fbca04" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/c++", - "name": "c++", - "color": "e11d21" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/doc", - "name": "doc", - "color": "006b75" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/debugger", - "name": "debugger", - "color": "bfd4f2" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/dns", - "name": "dns", - "color": "fbca04" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/domain", - "name": "domain", - "color": "e11d21" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/events", - "name": "events", - "color": "bfdadc" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/fs", - "name": "fs", - "color": "0052cc" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/module", - "name": "module", - "color": "fbca04" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/net", - "name": "net", - "color": "eb6420" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/os", - "name": "os", - "color": "d4c5f9" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/querystring", - "name": "querystring", - "color": "5319e7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/readline", - "name": "readline", - "color": "d4c5f9" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/repl", - "name": "repl", - "color": "5319e7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/V8 Engine", - "name": "V8 Engine", - "color": "0052cc" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/timers", - "name": "timers", - "color": "f7c6c7" - }, - { - "url": "https://api.github.com/repos/nodejs/node/labels/v6.x", - "name": "v6.x", - "color": "c2e0c6" - } - ], - "meta": { - "x-ratelimit-limit":"60", - "x-ratelimit-remaining":"53", - "x-ratelimit-reset":"1531354196", - "x-github-request-id":"EB9F:936D:4E2E967:6642386:5B469223", - "x-github-media-type":"github.v3; format=json", - "link":"; rel=\"next\", ; rel=\"last\"", - "etag":"\"86cc32d7673902f0b0774ab892d78f14\"", - "status":"200 OK" - } -} diff --git a/test/integration/node-labels-webhook.test.js b/test/integration/node-labels-webhook.test.js deleted file mode 100644 index 34fac9f1..00000000 --- a/test/integration/node-labels-webhook.test.js +++ /dev/null @@ -1,200 +0,0 @@ -'use strict' - -const tap = require('tap') -const url = require('url') -const nock = require('nock') -const supertest = require('supertest') -const proxyquire = require('proxyquire') - -const testStubs = { - './github-secret': { - isValid: () => true, - - // necessary to make makes proxyquire return this stub - // whenever *any* module tries to require('./github-secret') - '@global': true - } -} - -process.env.WAIT_SECONDS_BEFORE_RESOLVING_LABELS = 0 - -const readFixture = require('../read-fixture') - -// Clearing the require cache is needed due to labels being cached into a singleton variable. -// To ensure every test can run on its own without relying on other tests having run already -// resulted in the cache being filled up, we enforce all tests to run without any "cache warming", -// hence labels has to be fetched every time -function clearRequireCache () { - for (const modulePath of Object.keys(require.cache)) { - delete require.cache[modulePath] - } -} - -function initializeApp () { - const { app, events } = proxyquire('../../app', testStubs) - clearRequireCache() - require('../../scripts/node-subsystem-label')(app, events) - return app -} - -setupNoRequestMatchHandler() - -tap.test('Sends POST request to https://api.github.com/repos/nodejs/node/issues//labels', (t) => { - const app = initializeApp() - const expectedLabels = ['timers'] - const webhookPayload = readFixture('pull-request-opened.json') - - const filesScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/pulls/19/files') - .reply(200, readFixture('pull-request-files.json')) - - const existingRepoLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/labels') - .reply(200, readFixture('repo-labels.json')) - - const newLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .post('/repos/nodejs/node/issues/19/labels', { labels: expectedLabels }) - .reply(200) - - t.plan(1) - t.tearDown(() => { - filesScope.done() - existingRepoLabelsScope.done() - newLabelsScope.done() - }) - - supertest(app) - .post('/hooks/github') - .set('x-github-event', 'pull_request') - .send(webhookPayload) - .expect(200) - .end((err, res) => { - t.equal(err, null) - }) -}) - -tap.test('Adds v6.x label when PR is targeting the v6.x-staging branch', (t) => { - const app = initializeApp() - const expectedLabels = ['timers', 'v6.x'] - const webhookPayload = readFixture('pull-request-opened-v6.x.json') - - const filesScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/pulls/19/files') - .reply(200, readFixture('pull-request-files.json')) - - const existingRepoLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/labels') - .reply(200, readFixture('repo-labels.json')) - - const newLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .post('/repos/nodejs/node/issues/19/labels', { labels: expectedLabels }) - .reply(200) - - t.plan(1) - t.tearDown(() => { - filesScope.done() - existingRepoLabelsScope.done() - newLabelsScope.done() - }) - - supertest(app) - .post('/hooks/github') - .set('x-github-event', 'pull_request') - .send(webhookPayload) - .expect(200) - .end((err, res) => { - t.equal(err, null) - }) -}) - -// reported bug: https://github.com/nodejs/github-bot/issues/58 -tap.test('Does not create labels which does not already exist', (t) => { - const app = initializeApp() - const webhookPayload = readFixture('pull-request-opened-mapproxy.json') - - const filesScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/pulls/7972/files') - .reply(200, readFixture('pull-request-files-mapproxy.json')) - - const existingRepoLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/labels') - .reply(200, readFixture('repo-labels.json')) - - t.plan(1) - t.tearDown(() => { - filesScope.done() - existingRepoLabelsScope.done() - }) - - supertest(app) - .post('/hooks/github') - .set('x-github-event', 'pull_request') - .send(webhookPayload) - .expect(200) - .end((err, res) => { - t.equal(err, null) - }) -}) - -// reported bug: https://github.com/nodejs/github-bot/issues/92 -tap.test('Adds V8 Engine label when PR has deps/v8 file changes', (t) => { - const app = initializeApp() - const expectedLabels = ['V8 Engine'] - const webhookPayload = readFixture('pull-request-opened-v8.json') - - const filesScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/pulls/9422/files') - .reply(200, readFixture('pull-request-files-v8.json')) - - const existingRepoLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get('/repos/nodejs/node/labels') - .reply(200, readFixture('repo-labels.json')) - - const newLabelsScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .post('/repos/nodejs/node/issues/9422/labels', { labels: expectedLabels }) - .reply(200) - - t.plan(1) - t.tearDown(() => { - filesScope.done() - existingRepoLabelsScope.done() - newLabelsScope.done() - }) - - supertest(app) - .post('/hooks/github') - .set('x-github-event', 'pull_request') - .send(webhookPayload) - .expect(200) - .end((err, res) => { - t.equal(err, null) - }) -}) - -function ignoreQueryParams (pathAndQuery) { - return url.parse(pathAndQuery, true).pathname -} - -// nock doesn't make the tests explode if an unexpected external request is made, -// we therefore have to attach an explicit "no match" handler too make tests fail -// if there's made outgoing request we didn't expect -function setupNoRequestMatchHandler () { - nock.emitter.on('no match', (req) => { - // requests against the app is expected and we shouldn't need to tell nock about it - if (req.hostname === '127.0.0.1') return - - const reqUrl = `${req._headers.host}${req.path}` - throw new Error(`Unexpected request was sent to ${reqUrl}`) - }) -} diff --git a/test/unit/node-build-label.test.js b/test/unit/node-build-label.test.js deleted file mode 100644 index da0b4746..00000000 --- a/test/unit/node-build-label.test.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -const tap = require('tap') - -const nodeLabels = require('../../lib/node-labels') - -tap.test('label: "build" when build related files has been changed', (t) => { - const buildRelatedFiles = [ - 'configure', - 'node.gyp', - 'common.gypi', - 'BSDmakefile', - 'Makefile', - 'tools/Makefile', - 'tools/install.py', - 'tools/create_android_makefiles', - 'tools/genv8constants.py', - 'tools/getnodeversion.py', - 'tools/js2c.py', - 'tools/utils.py', - 'tools/configure.d/nodedownload.py' - ] - - buildRelatedFiles.forEach((filepath) => { - const labels = nodeLabels.resolveLabels([ filepath ]) - - t.same(labels, ['build'], filepath + ' got "build" label') - }) - - t.end() -}) - -tap.test('labels: not "build" when Makefile in ./deps has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/v8/Makefile' - ]) - - t.notOk(labels.includes('build')) - - t.end() -}) diff --git a/test/unit/node-js-subsystem-labels.test.js b/test/unit/node-js-subsystem-labels.test.js deleted file mode 100644 index dc9b51d3..00000000 --- a/test/unit/node-js-subsystem-labels.test.js +++ /dev/null @@ -1,175 +0,0 @@ -/* eslint-disable no-multi-spaces */ -'use strict' - -const tap = require('tap') - -const nodeLabels = require('../../lib/node-labels') - -tap.test('label: lib oddities', (t) => { - const libFiles = [ - 'lib/_debug_agent.js', - 'lib/_http_agent.js', - 'lib/_http_client.js', - 'lib/_http_common.js', - 'lib/_http_incoming.js', - 'lib/_http_outgoing.js', - 'lib/_http_server.js', - 'lib/_linklist.js', - 'lib/_stream_duplex.js', - 'lib/_stream_passthrough.js', - 'lib/_stream_readable.js', - 'lib/_stream_transform.js', - 'lib/_stream_wrap.js', - 'lib/_stream_writable.js', - 'lib/_tls_common.js', - 'lib/_tls_legacy.js', - 'lib/_tls_wrap.js', - 'lib/constants.js', - 'lib/punycode.js', // ignored - 'lib/sys.js', // ignored - 'lib/internal/freelist.js', // ignored - 'lib/internal/process', - 'lib/internal/readme.md', // ignored - 'lib/internal/socket_list.js', - 'lib/internal/v8_prof_polyfill.js', - 'lib/internal/v8_prof_processor.js' - ] - - const labels = nodeLabels.resolveLabels(libFiles, false) - - t.same(labels, [ - 'debugger', // _debug_agent - 'http', // _http_* - 'timers', // linklist - 'stream', // _stream_* - 'tls', // _tls_* - 'lib / src', // constants - 'process', // internal/process/ - 'net', // socket_list - 'tools' // v8_prof_* - ]) - - t.end() -}) - -tap.test('label: lib internals oddities duplicates', (t) => { - const libFiles = [ - 'lib/internal/bootstrap_node.js', - 'lib/internal/linkedlist.js', - 'lib/internal/streams' - ] - - const labels = nodeLabels.resolveLabels(libFiles) - - t.same(labels, [ - 'lib / src', // bootstrap_node - 'timers', // linkedlist - 'stream' // internal/streams/ - ]) - - t.end() -}) - -tap.test('label: lib normal without "lib / src" limiting', (t) => { - const libFiles = [ - 'lib/_debugger.js', - 'lib/assert.js', - 'lib/buffer.js', - 'lib/child_process.js', - 'lib/cluster.js', - 'lib/console.js', - 'lib/crypto.js', - 'lib/dgram.js', - 'lib/dns.js', - 'lib/domain.js', - 'lib/events.js', - 'lib/fs.js', - 'lib/http.js', - 'lib/https.js', - 'lib/module.js', - 'lib/net.js', - 'lib/os.js', - 'lib/path.js', - 'lib/process.js', - 'lib/querystring.js', - 'lib/readline.js', - 'lib/repl.js', - 'lib/stream.js', - 'lib/string_decoder.js', - 'lib/timers.js', - 'lib/tls.js', - 'lib/tty.js', - 'lib/url.js', - 'lib/util.js', - 'lib/v8.js', - 'lib/vm.js', - 'lib/zlib.js' - ] - - const labels = nodeLabels.resolveLabels(libFiles, false) - - t.same(labels, libFiles.map((path) => /lib\/(_)?(\w+)\.js/.exec(path)[2])) - - t.end() -}) - -tap.test('label: lib internals without "lib / src" limiting', (t) => { - const libFiles = [ - 'lib/internal/child_process.js', - 'lib/internal/cluster.js', - 'lib/internal/module.js', - 'lib/internal/net.js', - 'lib/internal/process.js', - 'lib/internal/readline.js', - 'lib/internal/repl.js', - 'lib/internal/util.js' - ] - - const labels = nodeLabels.resolveLabels(libFiles, false) - - t.same(labels, libFiles.map((path) => /lib\/internal\/(\w+)\.js/.exec(path)[1])) - - t.end() -}) - -tap.test('label: add subsystem when ./doc/api/.md has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'doc/api/fs.md' - ], false) - - t.same(labels, ['doc', 'fs']) - - t.end() -}) - -tap.test('label: only "doc" with multiple API doc files changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'doc/api/fs.md', - 'doc/api/stream.md' - ], false) - - t.same(labels, ['doc']) - - t.end() -}) - -tap.test('label: "doc,module" when doc/api/modules.md was changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'doc/api/modules.md' - ], false) - - t.same(labels, ['doc', 'module']) - - t.end() -}) - -tap.test('label: appropriate labels for files in internal subdirectories', (t) => { - const labels = nodeLabels.resolveLabels([ - 'lib/internal/cluster/master.js', - 'lib/internal/process/next_tick.js' - ], false) - - t.same(labels, ['cluster', 'process']) - - t.end() -}) diff --git a/test/unit/node-labels.test.js b/test/unit/node-labels.test.js deleted file mode 100644 index 1e3895f3..00000000 --- a/test/unit/node-labels.test.js +++ /dev/null @@ -1,716 +0,0 @@ -'use strict' - -const tap = require('tap') - -const nodeLabels = require('../../lib/node-labels') - -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() -}) - -// This ensures older mislabelling issues doesn't happen again -// https://github.com/nodejs/node/pull/6432 -// https://github.com/nodejs/node/pull/6448 -tap.test('no labels: when ./test/ and ./lib/ files has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'lib/punycode.js', - 'test/parallel/test-assert.js' - ]) - - 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: "doc" & "deprecations" when ./doc/api/deprecations.md has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'doc/api/deprecations.md' - ]) - - t.same(labels, ['doc', 'deprecations']) - - t.end() -}) - -tap.test('label: "c++" when ./src/* has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'src/node.cc' - ]) - - t.same(labels, ['c++']) - - t.end() -}) - -const srcCases = [ - ['async_wrap', ['async-wrap-inl.h', 'async-wrap.h', 'async-wrap.cc']], - ['buffer', - ['base64.h', - 'node_buffer.cc', - 'node_buffer.h', - 'string_bytes.cc', - 'string_bytes.h', - 'string_search.cc', - 'string_search.h']], - ['cares', ['cares_wrap.cc']], - ['child_process', ['process_wrap.cc', 'spawn_sync.cc', 'spawn_sync.h']], - ['crypto', - ['node_crypto.cc', - 'node_crypto.h', - 'node_crypto_bio.cc', - 'node_crypto_bio.h', - 'node_crypto_clienthello-inl.h', - 'node_crypto_clienthello.cc', - 'node_crypto_clienthello.h', - 'node_crypto_groups.h']], - ['debugger', ['debug-agent.cc', 'debug-agent.h', 'node_debug_options.cc']], - ['dgram', ['udp_wrap.cc', 'udp_wrap.h']], - ['fs', - ['fs_event_wrap.cc', - 'node_file.cc', - 'node_file.h', - 'node_stat_watcher.cc', - 'node_stat_watcher.h']], - ['http_parser', ['node_http_parser.cc', 'node_http_parser.h']], - ['intl', ['node_i18n.cc', 'node_i18n.h']], - ['libuv', ['uv.cc']], - ['net', - ['connect_wrap.cc', - 'connect_wrap.h', - 'connection_wrap.cc', - 'connection_wrap.h', - 'pipe_wrap.cc', - 'pipe_wrap.h', - 'tcp_wrap.cc', - 'tcp_wrap.h']], - ['os', ['node_os.cc']], - ['process', ['node_main.cc', 'signal_wrap.cc']], - ['timers', ['timer_wrap.cc']], - ['tracing', - ['tracing/agent.cc', - 'tracing/agent.h', - 'tracing/node_trace_buffer.cc', - 'tracing/node_trace_buffer.h', - 'tracing/node_trace_writer.cc', - 'tracing/node_trace_writer.h', - 'tracing/trace_event.cc', - 'tracing/trace_event.h']], - ['tls', - ['CNNICHashWhitelist.inc', - 'node_root_certs.h', - 'tls_wrap.cc', - 'tls_wrap.h']], - ['tty', ['tty_wrap.cc', 'tty_wrap.h']], - [['url-whatwg'], - ['node_url.cc', 'node_url.h']], - ['util', ['node_util.cc']], - ['V8 Engine', ['node_v8.cc', 'v8abbr.h']], - ['vm', ['node_contextify.cc']], - ['windows', - ['backtrace_win32.cc', - 'node_win32_etw_provider-inl.h', - 'node_win32_etw_provider.cc', - 'node_win32_etw_provider.h', - 'node_win32_perfctr_provider.cc', - 'node_win32_perfctr_provider.h']], - ['zlib', ['node_zlib.cc']] -] -for (const info of srcCases) { - let labels = info[0] - if (!Array.isArray(labels)) { - labels = [labels] - } - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./src/${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([`src/${file}`]) - - t.same(resolved, ['c++'].concat(labels)) - - 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: not "c++" when ./src/*.py has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'src/nolttng_macros.py', - 'src/notrace_macros.py', - 'src/perfctr_macros.py' - ]) - - t.same(labels, ['lib / src']) - - t.end() -}) - -tap.test('label: "inspector" when ./src/inspector_* has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'src/inspector_socket.cc' - ]) - - t.same(labels, ['c++', 'inspector']) - - t.end() -}) - -tap.test('label: "V8 Engine" when ./deps/v8/ files has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/v8/src/arguments.cc' - ]) - - t.same(labels, ['V8 Engine']) - - t.end() -}) - -tap.test('label: "libuv" when ./deps/uv/ files has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/uv/src/fs-poll.c' - ]) - - t.same(labels, ['libuv']) - - t.end() -}) - -tap.test('label: "wasi" when ./deps/uvwasi/ files has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/uvwasi/src/uvwasi.c' - ]) - - t.same(labels, ['wasi']) - - t.end() -}) - -tap.test('label: "V8 Engine", "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 Engine', 'openssl']) - - t.end() -}) - -// -// Planned tests to be resolved later -// - -tap.test('label: "repl" when ./lib/repl.js has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'lib/repl.js', - 'test/debugger/test-debugger-pid.js', - 'test/debugger/test-debugger-repl-break-in-module.js', - 'test/debugger/test-debugger-repl-term.js' - ]) - - t.same(labels, ['repl']) - - t.end() -}) - -tap.test('label: "lib / src" when 4 or more JS sub-systems have been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'lib/assert.js', - 'lib/dns.js', - 'lib/repl.js', - 'lib/process.js', - 'lib/module.js' - ]) - - t.same(labels, ['lib / src']) - - t.end() -}) - -// https://github.com/nodejs/node/pull/12366 should have been labelled "lib / src" -// https://github.com/nodejs/github-bot/issues/137 -tap.test('label: "lib / src" when 4 or more native files have been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'node.gyp', - 'src/cares_wrap.cc', - 'src/fs_event_wrap.cc', - 'src/node.cc', - 'src/node_api.cc', - 'src/node_buffer.cc', - 'src/node_config.cc', - 'src/node_constants.cc', - 'src/node_contextify.cc', - 'src/node_file.cc', - 'src/node_file.h', - 'src/node_http_parser.cc', - 'src/node_http_parser.h', - 'src/node_i18n.cc', - 'src/node_revert.cc', - 'src/node_serdes.cc', - 'src/node_zlib.cc', - 'src/process_wrap.cc', - 'src/signal_wrap.cc', - 'src/string_bytes.cc', - 'src/timer_wrap.cc', - 'src/uv.cc' - ]) - - t.same(labels, ['c++', 'lib / src']) - - t.end() -}) - -// https://github.com/nodejs/node/pull/7488 wrongfully labelled with "lib / src" -tap.test('label: not "lib / src" when only deps have been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/v8/test/cctest/interpreter/bytecode_expectations/ArrayLiterals.golden', - 'deps/v8/test/cctest/interpreter/bytecode_expectations/ArrayLiteralsWide.golden', - 'deps/v8/test/cctest/interpreter/bytecode_expectations/AssignmentsInBinaryExpression.golden', - 'deps/v8/test/cctest/interpreter/bytecode_expectations/BasicBlockToBoolean.golden', - 'deps/v8/test/cctest/interpreter/bytecode_expectations/BasicLoops.golden' - ]) - - t.same(labels, ['V8 Engine']) - - t.end() -}) - -tap.test('label: "JS sub-systems when less than 4 sub-systems have changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'lib/assert.js', - 'lib/dns.js', - 'lib/repl.js', - 'lib/process.js' - ]) - - t.same(labels, ['assert', 'dns', 'repl', 'process']) - - t.end() -}) - -tap.test('label: "meta" when meta-info files have changed', (t) => { - // e.g. LICENSE, AUTHORS, some ./*.md files - const labels = nodeLabels.resolveLabels([ - '.gitattributes', - '.gitignore', - '.mailmap', - 'AUTHORS', - 'LICENSE', - 'CHANGELOG.md', - 'CODE_OF_CONDUCT.md', - 'GOVERNANCE.md', - 'ROADMAP.md', - 'WORKING_GROUPS.md' - ]) - - t.same(labels, ['meta']) - - t.end() -}) - -tap.test('label: not "meta" when other top-level have been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'BUILDING.md', - 'README.md', - 'COLLABORATOR_GUIDE.md', - 'CONTRIBUTING.md', - 'configure' - ]) - - t.same(labels.indexOf('meta'), -1) - - t.end() -}) - -tap.test('label: "doc" when top-level .md files have changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'BUILDING.md', - 'README.md' - ]) - - t.same(labels, ['build', 'doc']) - - t.end() -}) - -tap.test('label: not "doc" when other top-level files have been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'LICENSE', - 'configure', - '.mailmap' - ]) - - t.same(labels.indexOf('doc'), -1) - - t.end() -}) - -tap.test('label: version labels (old)', (t) => { - const labels = nodeLabels.resolveLabels([ - 'common.gypi' - ], 'v0.12') - - t.same(labels, ['build', 'v0.12']) - - t.end() -}) - -tap.test('label: version labels (old, staging)', (t) => { - const labels = nodeLabels.resolveLabels([ - 'common.gypi' - ], 'v0.12-staging') - - t.same(labels, ['build', 'v0.12']) - - t.end() -}) - -tap.test('label: version labels (new)', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/v8/include/v8-version.h', - 'deps/v8/src/crankshaft/hydrogen.cc', - 'deps/v8/test/mjsunit/regress/regress-5033.js' - ], 'v6.x') - - t.same(labels, ['V8 Engine', 'v6.x']) - - t.end() -}) - -tap.test('label: version labels (new, staging)', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/v8/include/v8-version.h', - 'deps/v8/src/crankshaft/hydrogen.cc', - 'deps/v8/test/mjsunit/regress/regress-5033.js' - ], 'v6.x-staging') - - t.same(labels, ['V8 Engine', 'v6.x']) - - t.end() -}) - -tap.test('label: no version labels (master)', (t) => { - const labels = nodeLabels.resolveLabels([ - 'deps/v8/include/v8-version.h', - 'deps/v8/src/crankshaft/hydrogen.cc', - 'deps/v8/test/mjsunit/regress/regress-5033.js' - ], 'master') - - t.same(labels, ['V8 Engine']) - - t.end() -}) - -tap.test('label: build label (windows)', (t) => { - const labels = nodeLabels.resolveLabels([ - 'vcbuild.bat' - ]) - - t.same(labels, ['build', 'windows']) - - t.end() -}) - -tap.test('label: doc label for non-subsystem API doc changes', (t) => { - const labels = nodeLabels.resolveLabels([ - 'doc/api/_toc.md', - 'doc/api/all.md' - ]) - - t.same(labels, ['doc']) - - t.end() -}) - -const specificBenchmarks = [ - [[], ['fixtures/alice.html', 'misc/freelist.js']], - ['assert', ['assert/deepequal-buffer.js']], - ['buffer', ['buffers/buffer-base64-decode.js']], - ['child_process', ['child_process/child-process-exec-stdout.js']], - ['crypto', ['crypto/aes-gcm-throughput.js']], - ['dgram', ['dgram/bind-params.js']], - ['domain', ['domain/domain-fn-args.js']], - ['events', ['events/ee-emit.js']], - ['fs', ['fs/readfile.js']], - ['http', ['_http-benchmarkers.js', 'http/simple.js']], - ['module', ['module/module-loader.js']], - ['net', ['net/net-c2s.js']], - ['os', ['os/loadavg.js']], - ['path', ['path/basename-posix.js']], - ['process', ['process/memoryUsage.js']], - ['querystring', ['querystring/querystring-parse.js']], - ['stream', ['streams/readable-readall.js']], - ['string_decoder', ['string_decoder/string-decoder.js']], - ['timers', ['timers/set-immediate-depth.js']], - ['tls', ['tls/throughput.js']], - ['url', ['url/url-resolve.js']], - ['util', ['util/format.js']], - ['V8 Engine', ['arrays/var-int.js', 'es/defaultparams-bench.js']], - ['vm', ['vm/run-in-context.js']] -] -for (const info of specificBenchmarks) { - let labels = info[0] - if (!Array.isArray(labels)) { - labels = ['benchmark', labels] - } else { - labels = ['benchmark'].concat(labels) - } - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./benchmark/${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([`benchmark/${file}`]) - - t.same(resolved, labels) - - t.end() - }) - } -} - -const moreTools = [ - '.eslintignore', '.editorconfig', '.eslintrc.yaml', '.remarkrc' -] -for (const file of moreTools) { - tap.test(`label: "tools" when ${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([`${file}`]) - - t.same(resolved, ['tools']) - - t.end() - }) -} - -const specificTests = [ - ['addons', ['addons/async-hello-world/binding.cc']], - ['debugger', ['debugger/test-debugger-repl.js']], - [['doc', 'tools'], ['doctool/test-doctool-html.js']], - [['inspector'], - ['inspector/test-inspector.js', 'cctest/test_inspector_socket.cc']], - ['timers', ['timers/test-timers-reliability.js']], - ['tty', ['pseudo-tty/stdin-setrawmode.js']], - [['url-whatwg'], - ['cctest/test_url.cc']] -] -for (const info of specificTests) { - let labels = info[0] - if (!Array.isArray(labels)) { - labels = ['test', labels] - } else { - labels = ['test'].concat(labels) - } - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./test/${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([`test/${file}`]) - - t.same(resolved, labels) - - t.end() - }) - } -} - -const specificTools = [ - ['build', ['gyp/gyp_main.py', 'gyp_node.py']], - ['doc', ['doc/generate.js']], - ['intl', ['icu/icu-generate.gyp']], - ['macos', - ['macosx-firewall.sh', - 'osx-codesign.sh']], - [['macos', 'install'], - ['osx-pkg.pmdoc/index.xml.tmpl', - 'pkgsrc/description']], - [['test', 'npm'], ['test-npm.sh', 'test-npm-package.js']], - [['test'], ['test.py']], - [['openssl', 'tls'], ['certdata.txt', 'mkssldef.py', 'mk-ca-bundle.pl']], - [['windows'], ['sign.bat']], - [['windows', 'install'], ['msvs/msi/product.wxs']], - [['V8 Engine'], ['make-v8.sh']] -] -for (const info of specificTools) { - let labels = info[0] - if (!Array.isArray(labels)) { - labels = ['tools', labels] - } else { - labels = ['tools'].concat(labels) - } - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./tools/${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([`tools/${file}`]) - - t.same(resolved, labels) - - t.end() - }) - } -} - -[ - [['V8 Engine', 'post-mortem'], - ['deps/v8/tools/gen-postmortem-metadata.py']], - [['c++', 'n-api'], - ['src/node_api.cc', 'src/node_api.h', 'src/node_api_types.h']], - [['test', 'n-api'], - ['test/addons-napi/foo']], - [['doc', 'n-api'], - ['doc/api/n-api.md']] -].forEach((info) => { - const labels = info[0] - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([file]) - - t.same(resolved, labels) - - t.end() - }) - } -}); - -[ - [['async_hooks'], ['lib/async_hooks.js']], - [['test', 'async_hooks'], ['test/async-hooks/test-connection.ssl.js']] -].forEach((info) => { - const labels = info[0] - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([file]) - - t.same(resolved, labels) - - t.end() - }) - } -}) - -tap.test('label: "build" when ./android-configure has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - 'android-configure' - ]) - - t.same(labels, ['build']) - - t.end() -}) - -tap.test('label: "build" when ./.travis.yml has been changed', (t) => { - const labels = nodeLabels.resolveLabels([ - '.travis.yml' - ]) - - t.same(labels, ['build']) - - t.end() -}); - -[ - [['http2', 'dont-land-on-v6.x'], - ['lib/http2.js', - 'lib/internal/http2/core.js', - 'deps/nghttp2/lib/nghttp2_buf.c']], - [['c++', 'http2', 'dont-land-on-v6.x'], - ['src/node_http2.cc', - 'src/node_http2.h', - 'src/node_http2_core.h', - 'src/node_http2_core-inl.h']], - [['build', 'http2', 'dont-land-on-v6.x'], - ['deps/nghttp2/nghttp2.gyp']], - [['doc', 'http2'], ['doc/api/http2.md']] -].forEach((info) => { - const labels = info[0] - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([file]) - - t.same(resolved, labels) - - t.end() - }) - } -}); - -[ - [['c++', 'report'], - ['src/node_report.cc', - 'src/node_report.h', - 'src/node_report_module.cc', - 'src/node_report_utils.cc']], - [['doc', 'report'], ['doc/api/report.md']], - [['test', 'report'], ['test/report/test-report-config.js']] -].forEach((info) => { - const labels = info[0] - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([file]) - - t.same(resolved, labels) - - t.end() - }) - } -}); - -[ - // wasi - [['wasi'], - ['lib/wasi.js']], - [['c++', 'wasi'], - ['src/node_wasi.cc', - 'src/node_wasi.h']], - [['doc', 'wasi'], ['doc/api/wasi.md']], - - // worker - [['worker'], - ['lib/worker_threads.js', - 'lib/internal/worker.js', - 'lib/internal/worker/io.js']], - [['c++', 'worker'], - ['src/node_worker.cc', - 'src/node_worker.h']], - [['doc', 'worker'], ['doc/api/worker_threads.md']] -].forEach((info) => { - const labels = info[0] - const files = info[1] - for (const file of files) { - tap.test(`label: "${labels.join('","')}" when ./${file} has been changed`, (t) => { - const resolved = nodeLabels.resolveLabels([file]) - - t.same(resolved, labels) - - t.end() - }) - } -}) diff --git a/test/unit/node-repo.test.js b/test/unit/node-repo.test.js index 1efcba95..32d12312 100644 --- a/test/unit/node-repo.test.js +++ b/test/unit/node-repo.test.js @@ -1,7 +1,6 @@ 'use strict' const tap = require('tap') -const lolex = require('lolex') const nock = require('nock') const nodeRepo = require('../../lib/node-repo') @@ -10,91 +9,6 @@ const logger = require('../../lib/logger') const readFixture = require('../read-fixture') const { ignoreQueryParams } = require('../common') -tap.test('fetchExistingLabels(): caches existing repository labels', async (t) => { - const owner = 'nodejs' - const repo = 'node1' - // Test passes if nock is only called once, no other checks to run - const scope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get(`/repos/${owner}/${repo}/labels`) - .once() // should only be called once - .reply(200, []) - - await nodeRepo._fetchExistingLabels({ owner, repo, logger }) - await nodeRepo._fetchExistingLabels({ owner, repo, logger }) - scope.done() -}) - -tap.test('fetchExistingLabels(): cache expires after one hour', async (t) => { - const owner = 'nodejs' - const repo = 'node2' - const clock = lolex.install() - - // Test passes if nock is only called once, no other checks to run - const scope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get(`/repos/${owner}/${repo}/labels`) - .twice() // should be called twice - .reply(200, []) - - t.tearDown(() => { - clock.uninstall() - }) - - await nodeRepo._fetchExistingLabels({ owner, repo, logger }) - - // fetch labels again after 1 hour and 1 minute - clock.tick(1000 * 60 * 61) - - await nodeRepo._fetchExistingLabels({ owner, repo, logger }) - scope.done() -}) - -tap.test('fetchExistingLabels(): yields an array of existing label names', async (t) => { - const labelsFixture = readFixture('repo-labels.json') - const owner = 'nodejs' - const repo = 'node3' - - const scope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get(`/repos/${owner}/${repo}/labels`) - .reply(200, labelsFixture.data) - - t.plan(1) - - const existingLabels = await nodeRepo._fetchExistingLabels({ owner, repo, logger }) - t.ok(existingLabels.includes('cluster')) - scope.done() -}) - -tap.test('fetchExistingLabels(): can retrieve more than 100 labels', async (t) => { - const labelsFixturePage1 = readFixture('repo-labels.json') - const labelsFixturePage2 = readFixture('repo-labels-page-2.json') - const owner = 'nodejs' - const repo = 'node4' - const headers = { - 'Link': `; rel="next"` - } - - const firstPageScope = nock('https://api.github.com') - .filteringPath(ignoreQueryParams) - .get(`/repos/${owner}/${repo}/labels`) - .reply(200, labelsFixturePage1.data, headers) - - const secondPageScope = nock('https://api.github.com') - .get(`/repos/${owner}/${repo}/labels`) - .query({ page: 2, per_page: 100 }) - .reply(200, labelsFixturePage2.data) - - t.plan(2) - - const existingLabels = await nodeRepo._fetchExistingLabels({ owner, repo, logger }) - t.ok(existingLabels.includes('cluster')) - t.ok(existingLabels.includes('windows')) - firstPageScope.done() - secondPageScope.done() -}) - tap.test('getBotPrLabels(): returns labels added by nodejs-github-bot', (t) => { const events = readFixture('pull-request-events.json')