From b76b7b86ca793c64a1775a0c8f4752a9e8244533 Mon Sep 17 00:00:00 2001 From: Jameel Al-Aziz Date: Sat, 14 Sep 2019 03:11:24 -0700 Subject: [PATCH 1/4] Add support for exclusions Paths can be negated to stop searching through the remaining patterns in a the glob list. All changed files tested and at least one matching file will result in a label being added. Fixes actions/labeler#9 --- README.md | 11 +++++++++++ src/main.ts | 29 +++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fe9cebea7..6c905578c 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,19 @@ repo: # Add 'test' label to any change to *.spec.js files within the source dir test: - src/**/*.spec.js + +# Add 'source' label to any change to src files within the source dir EXCEPT for the build sub-folder +source: +- !src/build/* +- src/**/* ``` +#### Exclusions + +Exclusions are supported by preceding the pattern with `"!"`. + +Patterns are evaluated in order and the first match will result in the label being assigned. If an exclusion rule matches, all remaining patterns are skipped for the path being evaluated. + ### Create Workflow Create a workflow (eg: `.github/workflows/labeler.yml` see [Creating a Workflow file](https://help.github.com/en/articles/configuring-a-workflow#creating-a-workflow-file)) to utilize the labeler action with content: diff --git a/src/main.ts b/src/main.ts index 338fd4693..9c09a5cc5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core'; import * as github from '@actions/github'; import * as yaml from 'js-yaml'; -import {Minimatch} from 'minimatch'; +import {Minimatch, IMinimatch} from 'minimatch'; async function run() { try { @@ -116,15 +116,28 @@ function getLabelGlobMapFromObject(configObject: any): Map { return labelGlobs; } +function printPattern(matcher: IMinimatch): string { + return (matcher.negate ? "!" : "") + matcher.pattern; +} + function checkGlobs(changedFiles: string[], globs: string[]): boolean { - for (const glob of globs) { - core.debug(` checking pattern ${glob}`); - const matcher = new Minimatch(glob); - for (const changedFile of changedFiles) { - core.debug(` - ${changedFile}`); + const matchers = globs.map(g => new Minimatch(g)); + for (const changedFile of changedFiles) { + core.debug(` testing patterns against ${changedFile}`); + for (const matcher of matchers) { + core.debug(` - ${printPattern(matcher)}`); if (matcher.match(changedFile)) { - core.debug(` ${changedFile} matches`); - return true; + // match and not an exclusion rule + if (!matcher.negate) { + core.debug(` ${printPattern(matcher)} matches`); + return true; + } + } else { + // non-match, but is an exclusion rule + if (matcher.negate) { + core.debug(` ${printPattern(matcher)} excluded`); + break; + } } } } From dbe0ea8a3e6498b1c460f4c31b89bb02a89ef306 Mon Sep 17 00:00:00 2001 From: Jameel Al-Aziz Date: Tue, 26 May 2020 22:20:43 -0700 Subject: [PATCH 2/4] Add support for "AND"-ed matches A new "rich" matcher object can be provided instead of a normal glob. The matcher object has two fields that accept an array of globs: * Globs in "all" must all match every changed file. * Globs in "some" must all match at least one changed file. Combined with negated globs, this allows for a precise control of when labels are applied. --- dist/index.js | 444 +++++++++++++++++++++----------------------------- src/main.ts | 109 ++++++++++--- 2 files changed, 269 insertions(+), 284 deletions(-) diff --git a/dist/index.js b/dist/index.js index ce5480155..b892dfef0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -419,6 +419,13 @@ module.exports = new Schema({ }); +/***/ }), + +/***/ 34: +/***/ (function(module) { + +module.exports = require("https"); + /***/ }), /***/ 39: @@ -2278,21 +2285,13 @@ const windowsRelease = release => { const ver = (version || [])[0]; - // Server 2008, 2012, 2016, and 2019 versions are ambiguous with desktop versions and must be detected at runtime. + // Server 2008, 2012 and 2016 versions are ambiguous with desktop versions and must be detected at runtime. // If `release` is omitted or we're on a Windows system, and the version number is an ambiguous version // then use `wmic` to get the OS caption: https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx - // If `wmic` is obsoloete (later versions of Windows 10), use PowerShell instead. - // If the resulting caption contains the year 2008, 2012, 2016 or 2019, it is a server version, so return a server OS name. + // If the resulting caption contains the year 2008, 2012 or 2016, it is a server version, so return a server OS name. if ((!release || release === os.release()) && ['6.1', '6.2', '6.3', '10.0'].includes(ver)) { - let stdout; - try { - stdout = execa.sync('powershell', ['(Get-CimInstance -ClassName Win32_OperatingSystem).caption']).stdout || ''; - } catch (_) { - stdout = execa.sync('wmic', ['os', 'get', 'Caption']).stdout || ''; - } - - const year = (stdout.match(/2008|2012|2016|2019/) || [])[0]; - + const stdout = execa.sync('wmic', ['os', 'get', 'Caption']).stdout || ''; + const year = (stdout.match(/2008|2012|2016/) || [])[0]; if (year) { return `Server ${year}`; } @@ -3489,7 +3488,7 @@ module.exports = require("child_process"); var net = __webpack_require__(631); var tls = __webpack_require__(16); var http = __webpack_require__(605); -var https = __webpack_require__(211); +var https = __webpack_require__(34); var events = __webpack_require__(614); var assert = __webpack_require__(357); var util = __webpack_require__(669); @@ -4168,9 +4167,9 @@ function getChangedFiles(client, prNumber) { function getLabelGlobs(client, configurationPath) { return __awaiter(this, void 0, void 0, function* () { const configurationContent = yield fetchContent(client, configurationPath); - // loads (hopefully) a `{[label:string]: string | string[]}`, but is `any`: + // loads (hopefully) a `{[label:string]: string | StringOrMatchConfig[]}`, but is `any`: const configObject = yaml.safeLoad(configurationContent); - // transform `any` => `Map` or throw if yaml is malformed: + // transform `any` => `Map` or throw if yaml is malformed: return getLabelGlobMapFromObject(configObject); }); } @@ -4188,7 +4187,7 @@ function fetchContent(client, repoPath) { function getLabelGlobMapFromObject(configObject) { const labelGlobs = new Map(); for (const label in configObject) { - if (typeof configObject[label] === 'string') { + if (typeof configObject[label] === "string") { labelGlobs.set(label, [configObject[label]]); } else if (configObject[label] instanceof Array) { @@ -4200,20 +4199,67 @@ function getLabelGlobMapFromObject(configObject) { } return labelGlobs; } +function toMatchConfig(config) { + if (typeof config === "string") { + return { + all: [config] + }; + } + return config; +} function checkGlobs(changedFiles, globs) { for (const glob of globs) { - core.debug(` checking pattern ${glob}`); - const matcher = new minimatch_1.Minimatch(glob); - for (const changedFile of changedFiles) { - core.debug(` - ${changedFile}`); - if (matcher.match(changedFile)) { - core.debug(` ${changedFile} matches`); - return true; - } + core.debug(` checking pattern ${JSON.stringify(glob)}`); + const matchConfig = toMatchConfig(glob); + if (checkMatch(changedFiles, matchConfig)) { + return true; } } return false; } +// equivalent to "Array.some()" but expanded for debugging and clarity +function checkSome(changedFiles, glob) { + core.debug(` checking "some" pattern ${glob}`); + const matcher = new minimatch_1.Minimatch(glob); + for (const changedFile of changedFiles) { + core.debug(` - ${changedFile}`); + if (matcher.match(changedFile)) { + core.debug(` ${changedFile} matches`); + return true; + } + } + return false; +} +// equivalent to "Array.every()" but expanded for debugging and clarity +function checkAll(changedFiles, glob) { + core.debug(` checking "all" pattern ${glob}`); + const matcher = new minimatch_1.Minimatch(glob); + for (const changedFile of changedFiles) { + core.debug(` - ${changedFile}`); + if (!matcher.match(changedFile)) { + core.debug(` ${changedFile} did not match`); + return false; + } + } + return true; +} +function checkMatch(changedFiles, matchConfig) { + if (matchConfig.all !== undefined) { + for (const glob of matchConfig.all) { + if (!checkAll(changedFiles, glob)) { + return false; + } + } + } + if (matchConfig.some !== undefined) { + for (const glob of matchConfig.some) { + if (!checkSome(changedFiles, glob)) { + return false; + } + } + } + return true; +} function addLabels(client, prNumber, labels) { return __awaiter(this, void 0, void 0, function* () { yield client.issues.addLabels({ @@ -4230,16 +4276,39 @@ run(); /***/ }), /***/ 211: -/***/ (function(module) { +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, '__esModule', { value: true }); + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var osName = _interopDefault(__webpack_require__(2)); + +function getUserAgent() { + try { + return `Node.js/${process.version.substr(1)} (${osName()}; ${process.arch})`; + } catch (error) { + if (/wmic os get Caption/.test(error.message)) { + return "Windows "; + } + + return ""; + } +} + +exports.getUserAgent = getUserAgent; +//# sourceMappingURL=index.js.map -module.exports = require("https"); /***/ }), /***/ 215: /***/ (function(module) { -module.exports = {"_from":"@octokit/rest@^16.43.1","_id":"@octokit/rest@16.43.1","_inBundle":false,"_integrity":"sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==","_location":"/@octokit/rest","_phantomChildren":{"@octokit/types":"2.14.0","deprecation":"2.3.1","once":"1.4.0","os-name":"3.1.0"},"_requested":{"type":"range","registry":true,"raw":"@octokit/rest@^16.43.1","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawSpec":"^16.43.1","saveSpec":null,"fetchSpec":"^16.43.1"},"_requiredBy":["/@actions/github"],"_resolved":"https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz","_shasum":"3b11e7d1b1ac2bbeeb23b08a17df0b20947eda6b","_spec":"@octokit/rest@^16.43.1","_where":"/Users/pjquirk/Source/GitHub/pjquirk/labeler/node_modules/@actions/github","author":{"name":"Gregor Martynus","url":"https://github.com/gr2m"},"bugs":{"url":"https://github.com/octokit/rest.js/issues"},"bundleDependencies":false,"bundlesize":[{"path":"./dist/octokit-rest.min.js.gz","maxSize":"33 kB"}],"contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"dependencies":{"@octokit/auth-token":"^2.4.0","@octokit/plugin-paginate-rest":"^1.1.1","@octokit/plugin-request-log":"^1.0.0","@octokit/plugin-rest-endpoint-methods":"2.4.0","@octokit/request":"^5.2.0","@octokit/request-error":"^1.0.2","atob-lite":"^2.0.0","before-after-hook":"^2.0.0","btoa-lite":"^1.0.0","deprecation":"^2.0.0","lodash.get":"^4.4.2","lodash.set":"^4.3.2","lodash.uniq":"^4.5.0","octokit-pagination-methods":"^1.1.0","once":"^1.4.0","universal-user-agent":"^4.0.0"},"deprecated":false,"description":"GitHub REST API client for Node.js","devDependencies":{"@gimenete/type-writer":"^0.1.3","@octokit/auth":"^1.1.1","@octokit/fixtures-server":"^5.0.6","@octokit/graphql":"^4.2.0","@types/node":"^13.1.0","bundlesize":"^0.18.0","chai":"^4.1.2","compression-webpack-plugin":"^3.1.0","cypress":"^3.0.0","glob":"^7.1.2","http-proxy-agent":"^4.0.0","lodash.camelcase":"^4.3.0","lodash.merge":"^4.6.1","lodash.upperfirst":"^4.3.1","lolex":"^5.1.2","mkdirp":"^1.0.0","mocha":"^7.0.1","mustache":"^4.0.0","nock":"^11.3.3","npm-run-all":"^4.1.2","nyc":"^15.0.0","prettier":"^1.14.2","proxy":"^1.0.0","semantic-release":"^17.0.0","sinon":"^8.0.0","sinon-chai":"^3.0.0","sort-keys":"^4.0.0","string-to-arraybuffer":"^1.0.0","string-to-jsdoc-comment":"^1.0.0","typescript":"^3.3.1","webpack":"^4.0.0","webpack-bundle-analyzer":"^3.0.0","webpack-cli":"^3.0.0"},"files":["index.js","index.d.ts","lib","plugins"],"homepage":"https://github.com/octokit/rest.js#readme","keywords":["octokit","github","rest","api-client"],"license":"MIT","name":"@octokit/rest","nyc":{"ignore":["test"]},"publishConfig":{"access":"public"},"release":{"publish":["@semantic-release/npm",{"path":"@semantic-release/github","assets":["dist/*","!dist/*.map.gz"]}]},"repository":{"type":"git","url":"git+https://github.com/octokit/rest.js.git"},"scripts":{"build":"npm-run-all build:*","build:browser":"npm-run-all build:browser:*","build:browser:development":"webpack --mode development --entry . --output-library=Octokit --output=./dist/octokit-rest.js --profile --json > dist/bundle-stats.json","build:browser:production":"webpack --mode production --entry . --plugin=compression-webpack-plugin --output-library=Octokit --output-path=./dist --output-filename=octokit-rest.min.js --devtool source-map","build:ts":"npm run -s update-endpoints:typescript","coverage":"nyc report --reporter=html && open coverage/index.html","generate-bundle-report":"webpack-bundle-analyzer dist/bundle-stats.json --mode=static --no-open --report dist/bundle-report.html","lint":"prettier --check '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","lint:fix":"prettier --write '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","postvalidate:ts":"tsc --noEmit --target es6 test/typescript-validate.ts","prebuild:browser":"mkdirp dist/","pretest":"npm run -s lint","prevalidate:ts":"npm run -s build:ts","start-fixtures-server":"octokit-fixtures-server","test":"nyc mocha test/mocha-node-setup.js \"test/*/**/*-test.js\"","test:browser":"cypress run --browser chrome","update-endpoints":"npm-run-all update-endpoints:*","update-endpoints:fetch-json":"node scripts/update-endpoints/fetch-json","update-endpoints:typescript":"node scripts/update-endpoints/typescript","validate:ts":"tsc --target es6 --noImplicitAny index.d.ts"},"types":"index.d.ts","version":"16.43.1"}; +module.exports = {"_args":[["@octokit/rest@16.43.1","/Users/jalaziz/Development/forks/labeler"]],"_from":"@octokit/rest@16.43.1","_id":"@octokit/rest@16.43.1","_inBundle":false,"_integrity":"sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==","_location":"/@octokit/rest","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.1","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawSpec":"16.43.1","saveSpec":null,"fetchSpec":"16.43.1"},"_requiredBy":["/@actions/github"],"_resolved":"https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz","_spec":"16.43.1","_where":"/Users/jalaziz/Development/forks/labeler","author":{"name":"Gregor Martynus","url":"https://github.com/gr2m"},"bugs":{"url":"https://github.com/octokit/rest.js/issues"},"bundlesize":[{"path":"./dist/octokit-rest.min.js.gz","maxSize":"33 kB"}],"contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"dependencies":{"@octokit/auth-token":"^2.4.0","@octokit/plugin-paginate-rest":"^1.1.1","@octokit/plugin-request-log":"^1.0.0","@octokit/plugin-rest-endpoint-methods":"2.4.0","@octokit/request":"^5.2.0","@octokit/request-error":"^1.0.2","atob-lite":"^2.0.0","before-after-hook":"^2.0.0","btoa-lite":"^1.0.0","deprecation":"^2.0.0","lodash.get":"^4.4.2","lodash.set":"^4.3.2","lodash.uniq":"^4.5.0","octokit-pagination-methods":"^1.1.0","once":"^1.4.0","universal-user-agent":"^4.0.0"},"description":"GitHub REST API client for Node.js","devDependencies":{"@gimenete/type-writer":"^0.1.3","@octokit/auth":"^1.1.1","@octokit/fixtures-server":"^5.0.6","@octokit/graphql":"^4.2.0","@types/node":"^13.1.0","bundlesize":"^0.18.0","chai":"^4.1.2","compression-webpack-plugin":"^3.1.0","cypress":"^3.0.0","glob":"^7.1.2","http-proxy-agent":"^4.0.0","lodash.camelcase":"^4.3.0","lodash.merge":"^4.6.1","lodash.upperfirst":"^4.3.1","lolex":"^5.1.2","mkdirp":"^1.0.0","mocha":"^7.0.1","mustache":"^4.0.0","nock":"^11.3.3","npm-run-all":"^4.1.2","nyc":"^15.0.0","prettier":"^1.14.2","proxy":"^1.0.0","semantic-release":"^17.0.0","sinon":"^8.0.0","sinon-chai":"^3.0.0","sort-keys":"^4.0.0","string-to-arraybuffer":"^1.0.0","string-to-jsdoc-comment":"^1.0.0","typescript":"^3.3.1","webpack":"^4.0.0","webpack-bundle-analyzer":"^3.0.0","webpack-cli":"^3.0.0"},"files":["index.js","index.d.ts","lib","plugins"],"homepage":"https://github.com/octokit/rest.js#readme","keywords":["octokit","github","rest","api-client"],"license":"MIT","name":"@octokit/rest","nyc":{"ignore":["test"]},"publishConfig":{"access":"public"},"release":{"publish":["@semantic-release/npm",{"path":"@semantic-release/github","assets":["dist/*","!dist/*.map.gz"]}]},"repository":{"type":"git","url":"git+https://github.com/octokit/rest.js.git"},"scripts":{"build":"npm-run-all build:*","build:browser":"npm-run-all build:browser:*","build:browser:development":"webpack --mode development --entry . --output-library=Octokit --output=./dist/octokit-rest.js --profile --json > dist/bundle-stats.json","build:browser:production":"webpack --mode production --entry . --plugin=compression-webpack-plugin --output-library=Octokit --output-path=./dist --output-filename=octokit-rest.min.js --devtool source-map","build:ts":"npm run -s update-endpoints:typescript","coverage":"nyc report --reporter=html && open coverage/index.html","generate-bundle-report":"webpack-bundle-analyzer dist/bundle-stats.json --mode=static --no-open --report dist/bundle-report.html","lint":"prettier --check '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","lint:fix":"prettier --write '{lib,plugins,scripts,test}/**/*.{js,json,ts}' 'docs/*.{js,json}' 'docs/src/**/*' index.js README.md package.json","postvalidate:ts":"tsc --noEmit --target es6 test/typescript-validate.ts","prebuild:browser":"mkdirp dist/","pretest":"npm run -s lint","prevalidate:ts":"npm run -s build:ts","start-fixtures-server":"octokit-fixtures-server","test":"nyc mocha test/mocha-node-setup.js \"test/*/**/*-test.js\"","test:browser":"cypress run --browser chrome","update-endpoints":"npm-run-all update-endpoints:*","update-endpoints:fetch-json":"node scripts/update-endpoints/fetch-json","update-endpoints:typescript":"node scripts/update-endpoints/typescript","validate:ts":"tsc --target es6 --noImplicitAny index.d.ts"},"types":"index.d.ts","version":"16.43.1"}; /***/ }), @@ -4655,7 +4724,7 @@ function range(a, b, str) { module.exports = authenticationRequestError; -const { RequestError } = __webpack_require__(497); +const { RequestError } = __webpack_require__(463); function authenticationRequestError(state, error, options) { if (!error.headers) throw error; @@ -4724,7 +4793,7 @@ function authenticationRequestError(state, error, options) { module.exports = parseOptions; const { Deprecation } = __webpack_require__(692); -const { getUserAgent } = __webpack_require__(619); +const { getUserAgent } = __webpack_require__(796); const once = __webpack_require__(969); const pkg = __webpack_require__(215); @@ -5247,7 +5316,7 @@ function hasLastPage (link) { module.exports = validate; -const { RequestError } = __webpack_require__(497); +const { RequestError } = __webpack_require__(463); const get = __webpack_require__(854); const set = __webpack_require__(883); @@ -5403,7 +5472,7 @@ function validate(octokit, options) { module.exports = authenticationRequestError; -const { RequestError } = __webpack_require__(497); +const { RequestError } = __webpack_require__(463); function authenticationRequestError(state, error, options) { /* istanbul ignore next */ @@ -5607,7 +5676,7 @@ Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var isPlainObject = _interopDefault(__webpack_require__(696)); -var universalUserAgent = __webpack_require__(796); +var universalUserAgent = __webpack_require__(562); function lowercaseKeys(object) { if (!object) { @@ -5957,7 +6026,7 @@ function withDefaults(oldDefaults, newDefaults) { }); } -const VERSION = "6.0.1"; +const VERSION = "5.5.3"; const userAgent = `octokit-endpoint.js/${VERSION} ${universalUserAgent.getUserAgent()}`; // DEFAULTS has all properties set that EndpointOptions has, except url. // So we use RequestParameters and add method as additional required property. @@ -6369,28 +6438,14 @@ class Command { return cmdStr; } } -/** - * Sanitizes an input into a string so it can be passed into issueCommand safely - * @param input input to sanitize into a string - */ -function toCommandValue(input) { - if (input === null || input === undefined) { - return ''; - } - else if (typeof input === 'string' || input instanceof String) { - return input; - } - return JSON.stringify(input); -} -exports.toCommandValue = toCommandValue; function escapeData(s) { - return toCommandValue(s) + return (s || '') .replace(/%/g, '%25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A'); } function escapeProperty(s) { - return toCommandValue(s) + return (s || '') .replace(/%/g, '%25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') @@ -6503,7 +6558,7 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau var Stream = _interopDefault(__webpack_require__(413)); var http = _interopDefault(__webpack_require__(605)); var Url = _interopDefault(__webpack_require__(835)); -var https = _interopDefault(__webpack_require__(211)); +var https = _interopDefault(__webpack_require__(34)); var zlib = _interopDefault(__webpack_require__(761)); // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js @@ -9927,15 +9982,13 @@ class GitHub extends rest_1.Octokit { static getOctokitOptions(args) { const token = args[0]; const options = Object.assign({}, args[1]); // Shallow clone - don't mutate the object provided by the caller - // Base URL - GHES or Dotcom - options.baseUrl = options.baseUrl || this.getApiBaseUrl(); // Auth const auth = GitHub.getAuthString(token, options); if (auth) { options.auth = auth; } // Proxy - const agent = GitHub.getProxyAgent(options.baseUrl, options); + const agent = GitHub.getProxyAgent(options); if (agent) { // Shallow clone - don't mutate the object provided by the caller options.request = options.request ? Object.assign({}, options.request) : {}; @@ -9946,7 +9999,6 @@ class GitHub extends rest_1.Octokit { } static getGraphQL(args) { const defaults = {}; - defaults.baseUrl = this.getGraphQLBaseUrl(); const token = args[0]; const options = args[1]; // Authorization @@ -9957,7 +10009,7 @@ class GitHub extends rest_1.Octokit { }; } // Proxy - const agent = GitHub.getProxyAgent(defaults.baseUrl, options); + const agent = GitHub.getProxyAgent(options); if (agent) { defaults.request = { agent }; } @@ -9973,31 +10025,17 @@ class GitHub extends rest_1.Octokit { } return typeof options.auth === 'string' ? options.auth : `token ${token}`; } - static getProxyAgent(destinationUrl, options) { + static getProxyAgent(options) { var _a; if (!((_a = options.request) === null || _a === void 0 ? void 0 : _a.agent)) { - if (httpClient.getProxyUrl(destinationUrl)) { + const serverUrl = 'https://api.github.com'; + if (httpClient.getProxyUrl(serverUrl)) { const hc = new httpClient.HttpClient(); - return hc.getAgent(destinationUrl); + return hc.getAgent(serverUrl); } } return undefined; } - static getApiBaseUrl() { - return process.env['GITHUB_API_URL'] || 'https://api.github.com'; - } - static getGraphQLBaseUrl() { - let url = process.env['GITHUB_GRAPHQL_URL'] || 'https://api.github.com/graphql'; - // Shouldn't be a trailing slash, but remove if so - if (url.endsWith('/')) { - url = url.substr(0, url.length - 1); - } - // Remove trailing "/graphql" - if (url.toUpperCase().endsWith('/GRAPHQL')) { - url = url.substr(0, url.length - '/graphql'.length); - } - return url; - } } exports.GitHub = GitHub; //# sourceMappingURL=github.js.map @@ -10049,13 +10087,11 @@ var ExitCode; /** * Sets env variable for this action and future actions in the job * @param name the name of the variable to set - * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + * @param val the value of the variable */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any function exportVariable(name, val) { - const convertedVal = command_1.toCommandValue(val); - process.env[name] = convertedVal; - command_1.issueCommand('set-env', { name }, convertedVal); + process.env[name] = val; + command_1.issueCommand('set-env', { name }, val); } exports.exportVariable = exportVariable; /** @@ -10094,22 +10130,12 @@ exports.getInput = getInput; * Sets the value of an output. * * @param name name of the output to set - * @param value value to store. Non-string values will be converted to a string via JSON.stringify + * @param value value to store */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any function setOutput(name, value) { command_1.issueCommand('set-output', { name }, value); } exports.setOutput = setOutput; -/** - * Enables or disables the echoing of commands into stdout for the rest of the step. - * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. - * - */ -function setCommandEcho(enabled) { - command_1.issue('echo', enabled ? 'on' : 'off'); -} -exports.setCommandEcho = setCommandEcho; //----------------------------------------------------------------------- // Results //----------------------------------------------------------------------- @@ -10143,18 +10169,18 @@ function debug(message) { exports.debug = debug; /** * Adds an error issue - * @param message error issue message. Errors will be converted to string via toString() + * @param message error issue message */ function error(message) { - command_1.issue('error', message instanceof Error ? message.toString() : message); + command_1.issue('error', message); } exports.error = error; /** * Adds an warning issue - * @param message warning issue message. Errors will be converted to string via toString() + * @param message warning issue message */ function warning(message) { - command_1.issue('warning', message instanceof Error ? message.toString() : message); + command_1.issue('warning', message); } exports.warning = warning; /** @@ -10212,9 +10238,8 @@ exports.group = group; * Saves state for current action, the state can only be retrieved by this action's post job execution. * * @param name name of the state to store - * @param value value to store. Non-string values will be converted to a string via JSON.stringify + * @param value value to store */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any function saveState(name, value) { command_1.issueCommand('save-state', { name }, value); } @@ -10336,69 +10361,6 @@ function resolveCommand(parsed) { module.exports = resolveCommand; -/***/ }), - -/***/ 497: -/***/ (function(__unusedmodule, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, '__esModule', { value: true }); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var deprecation = __webpack_require__(692); -var once = _interopDefault(__webpack_require__(969)); - -const logOnce = once(deprecation => console.warn(deprecation)); -/** - * Error with extra properties to help with debugging - */ - -class RequestError extends Error { - constructor(message, statusCode, options) { - super(message); // Maintains proper stack trace (only available on V8) - - /* istanbul ignore next */ - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = "HttpError"; - this.status = statusCode; - Object.defineProperty(this, "code", { - get() { - logOnce(new deprecation.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`.")); - return statusCode; - } - - }); - this.headers = options.headers || {}; // redact request credentials without mutating original request options - - const requestCopy = Object.assign({}, options.request); - - if (options.request.headers.authorization) { - requestCopy.headers = Object.assign({}, options.request.headers, { - authorization: options.request.headers.authorization.replace(/ .*$/, " [REDACTED]") - }); - } - - requestCopy.url = requestCopy.url // client_id & client_secret can be passed as URL query parameters to increase rate limit - // see https://developer.github.com/v3/#increasing-the-unauthenticated-rate-limit-for-oauth-applications - .replace(/\bclient_secret=\w+/g, "client_secret=[REDACTED]") // OAuth tokens can be passed as URL query parameters, although it is not recommended - // see https://developer.github.com/v3/#oauth2-token-sent-in-a-header - .replace(/\baccess_token=\w+/g, "access_token=[REDACTED]"); - this.request = requestCopy; - } - -} - -exports.RequestError = RequestError; -//# sourceMappingURL=index.js.map - - /***/ }), /***/ 510: @@ -10552,7 +10514,7 @@ function hasFirstPage (link) { Object.defineProperty(exports, "__esModule", { value: true }); const url = __webpack_require__(835); const http = __webpack_require__(605); -const https = __webpack_require__(211); +const https = __webpack_require__(34); const pm = __webpack_require__(950); let tunnel; var HttpCodes; @@ -10578,7 +10540,6 @@ var HttpCodes; HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; - HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; @@ -10603,18 +10564,8 @@ function getProxyUrl(serverUrl) { return proxyUrl ? proxyUrl.href : ''; } exports.getProxyUrl = getProxyUrl; -const HttpRedirectCodes = [ - HttpCodes.MovedPermanently, - HttpCodes.ResourceMoved, - HttpCodes.SeeOther, - HttpCodes.TemporaryRedirect, - HttpCodes.PermanentRedirect -]; -const HttpResponseRetryCodes = [ - HttpCodes.BadGateway, - HttpCodes.ServiceUnavailable, - HttpCodes.GatewayTimeout -]; +const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect]; +const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout]; const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; const ExponentialBackoffCeiling = 10; const ExponentialBackoffTimeSlice = 5; @@ -10739,22 +10690,18 @@ class HttpClient { */ async request(verb, requestUrl, data, headers) { if (this._disposed) { - throw new Error('Client has already been disposed.'); + throw new Error("Client has already been disposed."); } let parsedUrl = url.parse(requestUrl); let info = this._prepareRequest(verb, parsedUrl, headers); // Only perform retries on reads since writes may not be idempotent. - let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1 - ? this._maxRetries + 1 - : 1; + let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1; let numTries = 0; let response; while (numTries < maxTries) { response = await this.requestRaw(info, data); // Check if it's an authentication challenge - if (response && - response.message && - response.message.statusCode === HttpCodes.Unauthorized) { + if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { let authenticationHandler; for (let i = 0; i < this.handlers.length; i++) { if (this.handlers[i].canHandleAuthentication(response)) { @@ -10772,32 +10719,21 @@ class HttpClient { } } let redirectsRemaining = this._maxRedirects; - while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 && - this._allowRedirects && - redirectsRemaining > 0) { - const redirectUrl = response.message.headers['location']; + while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 + && this._allowRedirects + && redirectsRemaining > 0) { + const redirectUrl = response.message.headers["location"]; if (!redirectUrl) { // if there's no location to redirect to, we won't break; } let parsedRedirectUrl = url.parse(redirectUrl); - if (parsedUrl.protocol == 'https:' && - parsedUrl.protocol != parsedRedirectUrl.protocol && - !this._allowRedirectDowngrade) { - throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); + if (parsedUrl.protocol == 'https:' && parsedUrl.protocol != parsedRedirectUrl.protocol && !this._allowRedirectDowngrade) { + throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true."); } // we need to finish reading the response before reassigning response // which will leak the open socket. await response.readBody(); - // strip authorization header if redirected to a different hostname - if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { - for (let header in headers) { - // header names are case insensitive - if (header.toLowerCase() === 'authorization') { - delete headers[header]; - } - } - } // let's make the request with the new redirectUrl info = this._prepareRequest(verb, parsedRedirectUrl, headers); response = await this.requestRaw(info, data); @@ -10848,8 +10784,8 @@ class HttpClient { */ requestRawWithCallback(info, data, onResult) { let socket; - if (typeof data === 'string') { - info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); + if (typeof (data) === 'string') { + info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8'); } let callbackCalled = false; let handleResult = (err, res) => { @@ -10862,7 +10798,7 @@ class HttpClient { let res = new HttpClientResponse(msg); handleResult(null, res); }); - req.on('socket', sock => { + req.on('socket', (sock) => { socket = sock; }); // If we ever get disconnected, we want the socket to timeout eventually @@ -10877,10 +10813,10 @@ class HttpClient { // res should have headers handleResult(err, null); }); - if (data && typeof data === 'string') { + if (data && typeof (data) === 'string') { req.write(data, 'utf8'); } - if (data && typeof data !== 'string') { + if (data && typeof (data) !== 'string') { data.on('close', function () { req.end(); }); @@ -10907,34 +10843,31 @@ class HttpClient { const defaultPort = usingSsl ? 443 : 80; info.options = {}; info.options.host = info.parsedUrl.hostname; - info.options.port = info.parsedUrl.port - ? parseInt(info.parsedUrl.port) - : defaultPort; - info.options.path = - (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); + info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort; + info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); info.options.method = method; info.options.headers = this._mergeHeaders(headers); if (this.userAgent != null) { - info.options.headers['user-agent'] = this.userAgent; + info.options.headers["user-agent"] = this.userAgent; } info.options.agent = this._getAgent(info.parsedUrl); // gives handlers an opportunity to participate if (this.handlers) { - this.handlers.forEach(handler => { + this.handlers.forEach((handler) => { handler.prepareRequest(info.options); }); } return info; } _mergeHeaders(headers) { - const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); + const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {}); if (this.requestOptions && this.requestOptions.headers) { return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers)); } return lowercaseKeys(headers || {}); } _getExistingOrDefaultHeader(additionalHeaders, header, _default) { - const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); + const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {}); let clientHeader; if (this.requestOptions && this.requestOptions.headers) { clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; @@ -10972,7 +10905,7 @@ class HttpClient { proxyAuth: proxyUrl.auth, host: proxyUrl.hostname, port: proxyUrl.port - } + }, }; let tunnelAgent; const overHttps = proxyUrl.protocol === 'https:'; @@ -10999,9 +10932,7 @@ class HttpClient { // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options // we have to cast it to any and change it directly - agent.options = Object.assign(agent.options || {}, { - rejectUnauthorized: false - }); + agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false }); } return agent; } @@ -11062,7 +10993,7 @@ class HttpClient { msg = contents; } else { - msg = 'Failed request: (' + statusCode + ')'; + msg = "Failed request: (" + statusCode + ")"; } let err = new Error(msg); // attach statusCode and body obj (if available) to the error object @@ -11162,6 +11093,36 @@ function hasPreviousPage (link) { } +/***/ }), + +/***/ 562: +/***/ (function(__unusedmodule, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, '__esModule', { value: true }); + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var osName = _interopDefault(__webpack_require__(2)); + +function getUserAgent() { + try { + return `Node.js/${process.version.substr(1)} (${osName()}; ${process.arch})`; + } catch (error) { + if (/wmic os get Caption/.test(error.message)) { + return "Windows "; + } + + return ""; + } +} + +exports.getUserAgent = getUserAgent; +//# sourceMappingURL=index.js.map + + /***/ }), /***/ 563: @@ -12342,36 +12303,6 @@ module.exports = new Schema({ module.exports = require("events"); -/***/ }), - -/***/ 619: -/***/ (function(__unusedmodule, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, '__esModule', { value: true }); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var osName = _interopDefault(__webpack_require__(2)); - -function getUserAgent() { - try { - return `Node.js/${process.version.substr(1)} (${osName()}; ${process.arch})`; - } catch (error) { - if (/wmic os get Caption/.test(error.message)) { - return "Windows "; - } - - throw error; - } -} - -exports.getUserAgent = getUserAgent; -//# sourceMappingURL=index.js.map - - /***/ }), /***/ 621: @@ -13800,12 +13731,12 @@ Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var endpoint = __webpack_require__(385); -var universalUserAgent = __webpack_require__(796); +var universalUserAgent = __webpack_require__(211); var isPlainObject = _interopDefault(__webpack_require__(696)); var nodeFetch = _interopDefault(__webpack_require__(454)); var requestError = __webpack_require__(463); -const VERSION = "5.4.2"; +const VERSION = "5.3.2"; function getBufferResponse(response) { return response.arrayBuffer(); @@ -14033,7 +13964,7 @@ function getUserAgent() { return "Windows "; } - return ""; + throw error; } } @@ -29764,7 +29695,7 @@ Object.defineProperty(exports, '__esModule', { value: true }); var request = __webpack_require__(753); var universalUserAgent = __webpack_require__(796); -const VERSION = "4.4.0"; +const VERSION = "4.3.1"; class GraphqlError extends Error { constructor(request, response) { @@ -29783,7 +29714,7 @@ class GraphqlError extends Error { } -const NON_VARIABLE_OPTIONS = ["method", "baseUrl", "url", "headers", "request", "query", "mediaType"]; +const NON_VARIABLE_OPTIONS = ["method", "baseUrl", "url", "headers", "request", "query"]; function graphql(request, query, options) { options = typeof query === "string" ? options = Object.assign({ query @@ -30259,10 +30190,12 @@ function getProxyUrl(reqUrl) { } let proxyVar; if (usingSsl) { - proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY']; + proxyVar = process.env["https_proxy"] || + process.env["HTTPS_PROXY"]; } else { - proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY']; + proxyVar = process.env["http_proxy"] || + process.env["HTTP_PROXY"]; } if (proxyVar) { proxyUrl = url.parse(proxyVar); @@ -30274,7 +30207,7 @@ function checkBypass(reqUrl) { if (!reqUrl.hostname) { return false; } - let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; + let noProxy = process.env["no_proxy"] || process.env["NO_PROXY"] || ''; if (!noProxy) { return false; } @@ -30295,10 +30228,7 @@ function checkBypass(reqUrl) { upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); } // Compare request host against noproxy - for (let upperNoProxyItem of noProxy - .split(',') - .map(x => x.trim().toUpperCase()) - .filter(x => x)) { + for (let upperNoProxyItem of noProxy.split(',').map(x => x.trim().toUpperCase()).filter(x => x)) { if (upperReqHosts.some(x => x === upperNoProxyItem)) { return true; } diff --git a/src/main.ts b/src/main.ts index 9c09a5cc5..3acae2475 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,13 @@ import * as github from '@actions/github'; import * as yaml from 'js-yaml'; import {Minimatch, IMinimatch} from 'minimatch'; +interface MatchConfig { + all?: string[]; + some?: string[]; +} + +type StringOrMatchConfig = string | MatchConfig; + async function run() { try { const token = core.getInput('repo-token', {required: true}); @@ -18,7 +25,7 @@ async function run() { core.debug(`fetching changed files for pr #${prNumber}`); const changedFiles: string[] = await getChangedFiles(client, prNumber); - const labelGlobs: Map = await getLabelGlobs( + const labelGlobs: Map = await getLabelGlobs( client, configPath ); @@ -72,16 +79,16 @@ async function getChangedFiles( async function getLabelGlobs( client: github.GitHub, configurationPath: string -): Promise> { +): Promise> { const configurationContent: string = await fetchContent( client, configurationPath ); - // loads (hopefully) a `{[label:string]: string | string[]}`, but is `any`: + // loads (hopefully) a `{[label:string]: string | StringOrMatchConfig[]}`, but is `any`: const configObject: any = yaml.safeLoad(configurationContent); - // transform `any` => `Map` or throw if yaml is malformed: + // transform `any` => `Map` or throw if yaml is malformed: return getLabelGlobMapFromObject(configObject); } @@ -99,10 +106,12 @@ async function fetchContent( return Buffer.from(response.data.content, response.data.encoding).toString(); } -function getLabelGlobMapFromObject(configObject: any): Map { - const labelGlobs: Map = new Map(); +function getLabelGlobMapFromObject( + configObject: any +): Map { + const labelGlobs: Map = new Map(); for (const label in configObject) { - if (typeof configObject[label] === 'string') { + if (typeof configObject[label] === "string") { labelGlobs.set(label, [configObject[label]]); } else if (configObject[label] instanceof Array) { labelGlobs.set(label, configObject[label]); @@ -116,34 +125,80 @@ function getLabelGlobMapFromObject(configObject: any): Map { return labelGlobs; } -function printPattern(matcher: IMinimatch): string { - return (matcher.negate ? "!" : "") + matcher.pattern; +function toMatchConfig(config: StringOrMatchConfig): MatchConfig { + if (typeof config === "string") { + return { + some: [config] + }; + } + + return config; +} + +function checkGlobs( + changedFiles: string[], + globs: StringOrMatchConfig[] +): boolean { + for (const glob of globs) { + core.debug(` checking pattern ${JSON.stringify(glob)}`); + const matchConfig = toMatchConfig(glob); + if (checkMatch(changedFiles, matchConfig)) { + return true; + } + } + return false; } -function checkGlobs(changedFiles: string[], globs: string[]): boolean { - const matchers = globs.map(g => new Minimatch(g)); +// equivalent to "Array.some()" but expanded for debugging and clarity +function checkSome(changedFiles: string[], glob: string): boolean { + core.debug(` checking "some" pattern ${glob}`); + const matcher = new Minimatch(glob); for (const changedFile of changedFiles) { - core.debug(` testing patterns against ${changedFile}`); - for (const matcher of matchers) { - core.debug(` - ${printPattern(matcher)}`); - if (matcher.match(changedFile)) { - // match and not an exclusion rule - if (!matcher.negate) { - core.debug(` ${printPattern(matcher)} matches`); - return true; - } - } else { - // non-match, but is an exclusion rule - if (matcher.negate) { - core.debug(` ${printPattern(matcher)} excluded`); - break; - } - } + core.debug(` - ${changedFile}`); + if (matcher.match(changedFile)) { + core.debug(` ${changedFile} matches`); + return true; } } + return false; } +// equivalent to "Array.every()" but expanded for debugging and clarity +function checkAll(changedFiles: string[], glob: string): boolean { + core.debug(` checking "all" pattern ${glob}`); + const matcher = new Minimatch(glob); + for (const changedFile of changedFiles) { + core.debug(` - ${changedFile}`); + if (!matcher.match(changedFile)) { + core.debug(` ${changedFile} did not match`); + return false; + } + } + + return true; +} + +function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { + if (matchConfig.all !== undefined) { + for (const glob of matchConfig.all) { + if (!checkAll(changedFiles, glob)) { + return false; + } + } + } + + if (matchConfig.some !== undefined) { + for (const glob of matchConfig.some) { + if (!checkSome(changedFiles, glob)) { + return false; + } + } + } + + return true; +} + async function addLabels( client: github.GitHub, prNumber: number, From 9c94f651858767b6ca094bb396c1b827d4f8d469 Mon Sep 17 00:00:00 2001 From: Jameel Al-Aziz Date: Sun, 31 May 2020 23:03:00 -0700 Subject: [PATCH 3/4] Rename `some` to `any` --- dist/index.js | 12 ++++++------ src/main.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dist/index.js b/dist/index.js index b892dfef0..5edc4203b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -4202,7 +4202,7 @@ function getLabelGlobMapFromObject(configObject) { function toMatchConfig(config) { if (typeof config === "string") { return { - all: [config] + any: [config] }; } return config; @@ -4218,8 +4218,8 @@ function checkGlobs(changedFiles, globs) { return false; } // equivalent to "Array.some()" but expanded for debugging and clarity -function checkSome(changedFiles, glob) { - core.debug(` checking "some" pattern ${glob}`); +function checkAny(changedFiles, glob) { + core.debug(` checking "any" pattern ${glob}`); const matcher = new minimatch_1.Minimatch(glob); for (const changedFile of changedFiles) { core.debug(` - ${changedFile}`); @@ -4251,9 +4251,9 @@ function checkMatch(changedFiles, matchConfig) { } } } - if (matchConfig.some !== undefined) { - for (const glob of matchConfig.some) { - if (!checkSome(changedFiles, glob)) { + if (matchConfig.any !== undefined) { + for (const glob of matchConfig.any) { + if (!checkAny(changedFiles, glob)) { return false; } } diff --git a/src/main.ts b/src/main.ts index 3acae2475..dab60ecfd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,7 +5,7 @@ import {Minimatch, IMinimatch} from 'minimatch'; interface MatchConfig { all?: string[]; - some?: string[]; + any?: string[]; } type StringOrMatchConfig = string | MatchConfig; @@ -128,7 +128,7 @@ function getLabelGlobMapFromObject( function toMatchConfig(config: StringOrMatchConfig): MatchConfig { if (typeof config === "string") { return { - some: [config] + any: [config] }; } @@ -150,8 +150,8 @@ function checkGlobs( } // equivalent to "Array.some()" but expanded for debugging and clarity -function checkSome(changedFiles: string[], glob: string): boolean { - core.debug(` checking "some" pattern ${glob}`); +function checkAny(changedFiles: string[], glob: string): boolean { + core.debug(` checking "any" pattern ${glob}`); const matcher = new Minimatch(glob); for (const changedFile of changedFiles) { core.debug(` - ${changedFile}`); @@ -188,9 +188,9 @@ function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { } } - if (matchConfig.some !== undefined) { - for (const glob of matchConfig.some) { - if (!checkSome(changedFiles, glob)) { + if (matchConfig.any !== undefined) { + for (const glob of matchConfig.any) { + if (!checkAny(changedFiles, glob)) { return false; } } From 59aa1aca1a9c76becbdcfadb4efa8bd1548918f0 Mon Sep 17 00:00:00 2001 From: Jameel Al-Aziz Date: Mon, 1 Jun 2020 00:03:57 -0700 Subject: [PATCH 4/4] Update README --- README.md | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6c905578c..2d430629a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,35 @@ Note that only pull requests being opened from the same repository can be labele Create a `.github/labeler.yml` file with a list of labels and [minimatch](https://github.com/isaacs/minimatch) globs to match to apply the label. -The key is the name of the label in your repository that you want to add (eg: "merge conflict", "needs-updating") and the value is the path (glob) of the changed files (eg: `src/**/*`, `tests/*.spec.js`) +The key is the name of the label in your repository that you want to add (eg: "merge conflict", "needs-updating") and the value is the path (glob) of the changed files (eg: `src/**/*`, `tests/*.spec.js`) or a match object. + +#### Match Object + +For more control over matching, you can provide a match object instead of a simple path glob. The match object is defined as: + +```yml +- any: ['list', 'of', 'globs'] + all: ['list', 'of', 'globs'] +``` + +One or both fields can be provided for fine-grained matching. Unlike the top-level list, the list of path globs provided to `any` and `all` must ALL match against a path for the label to be applied. + +The fields are defined as follows: +* `any`: match ALL globs against ANY changed path +* `all`: match ALL globs against ALL changed paths + +A simple path glob is the equivalent to `any: ['glob']`. More specifically, the following two configurations are equivalent: +```yml +label1: +- example1/* +``` +and +```yml +label1: +- any: ['example1/*'] +``` + +From a boolean logic perspective, top-level match objects are `OR`-ed together and indvidual match rules within an object are `AND`-ed. Combined with `!` negation, you can write complex matching rules. #### Basic Examples @@ -39,17 +67,15 @@ repo: test: - src/**/*.spec.js -# Add 'source' label to any change to src files within the source dir EXCEPT for the build sub-folder +# Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder source: -- !src/build/* -- src/**/* -``` - -#### Exclusions +- any: ['src/**/*', '!src/docs/*'] -Exclusions are supported by preceding the pattern with `"!"`. - -Patterns are evaluated in order and the first match will result in the label being assigned. If an exclusion rule matches, all remaining patterns are skipped for the path being evaluated. +# Add 'frontend` label to any change to *.js files as long as the `main.js` hasn't changed +frontend: +- any: ['src/**/*.js'] + all: ['!src/main.js'] +``` ### Create Workflow