From 88f380314c626fc30f1c06bff479121fe488bad3 Mon Sep 17 00:00:00 2001 From: Naka Masato Date: Thu, 22 Oct 2020 23:52:31 +0900 Subject: [PATCH] enable to add label by title --- dist/index.js | 87 ++++++++++++++++++++++++++++++++++------ src/main.ts | 107 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 171 insertions(+), 23 deletions(-) diff --git a/dist/index.js b/dist/index.js index 6f976fef6..e32197039 100644 --- a/dist/index.js +++ b/dist/index.js @@ -14545,17 +14545,31 @@ function run() { console.log("Could not get pull request number from context, exiting"); return; } + const prTitle = getPrTitle(); + core.info(`prTitle: ${prTitle}`); const client = new github.GitHub(token); const { data: pullRequest } = yield client.pulls.get({ owner: github.context.repo.owner, repo: github.context.repo.repo, pull_number: prNumber }); + const labels = []; + const labelsToRemove = []; + // add label based on title + const labelTitleRegex = yield getLabelTitleRegex(client, configPath); + for (const [label, regexs] of labelTitleRegex.entries()) { + core.debug(`processing ${label}`); + if (checkRegexs(prTitle, regexs)) { + labels.push(label); + } + else if (pullRequest.labels.find(l => l.name === label)) { + labelsToRemove.push(label); + } + } + // add label based on changed files core.debug(`fetching changed files for pr #${prNumber}`); const changedFiles = yield getChangedFiles(client, prNumber); const labelGlobs = yield getLabelGlobs(client, configPath); - const labels = []; - const labelsToRemove = []; for (const [label, globs] of labelGlobs.entries()) { core.debug(`processing ${label}`); if (checkGlobs(changedFiles, globs)) { @@ -14585,6 +14599,13 @@ function getPrNumber() { } return pullRequest.number; } +function getPrTitle() { + const pullRequest = github.context.payload.pull_request; + if (!pullRequest) { + return ''; + } + return pullRequest.title; +} function getChangedFiles(client, prNumber) { return __awaiter(this, void 0, void 0, function* () { const listFilesOptions = client.pulls.listFiles.endpoint.merge({ @@ -14623,19 +14644,53 @@ function fetchContent(client, repoPath) { } function getLabelGlobMapFromObject(configObject) { const labelGlobs = new Map(); - for (const label in configObject) { - if (typeof configObject[label] === "string") { - labelGlobs.set(label, [configObject[label]]); - } - else if (configObject[label] instanceof Array) { - labelGlobs.set(label, configObject[label]); - } - else { - throw Error(`found unexpected type for label ${label} (should be string or array of globs)`); + for (const configType in configObject) { + if (configType == "changed_file") { + for (const label in configObject["changed_file"]) { + const val = configObject["changed_file"][label]; + if (typeof val === "string") { + labelGlobs.set(label, [val]); + } + else if (val instanceof Array) { + labelGlobs.set(label, val); + } + else { + throw Error(`found unexpected type for label ${label} (should be string or array of globs)`); + } + } } } return labelGlobs; } +function getLabelTitleRegex(client, configurationPath) { + return __awaiter(this, void 0, void 0, function* () { + const configurationContent = yield fetchContent(client, configurationPath); + // loads (hopefully) a `{[label:string]: string | StringOrMatchConfig[]}`, but is `any`: + const configObject = yaml.safeLoad(configurationContent); + // transform `any` => `Map` or throw if yaml is malformed: + return getLabelTitleRegexMapFromObject(configObject); + }); +} +function getLabelTitleRegexMapFromObject(configObject) { + const titleRegexs = new Map(); + for (const configType in configObject) { + if (configType == "title") { + for (const label in configObject["title"]) { + const val = configObject["title"][label]; + if (typeof val === "string") { + titleRegexs.set(label, [new RegExp(val, 'i')]); + } + else if (val instanceof Array) { + titleRegexs.set(label, val.map(regexStr => new RegExp(regexStr, 'i'))); + } + else { + throw Error(`found unexpected type for label ${label} (should be string or array of regex)`); + } + } + } + } + return titleRegexs; +} function toMatchConfig(config) { if (typeof config === "string") { return { @@ -14657,6 +14712,14 @@ function checkGlobs(changedFiles, globs) { } return false; } +function checkRegexs(prTitle, regexs) { + for (const regex of regexs) { + if (regex.test(prTitle)) { + return true; + } + } + return false; +} function isMatch(changedFile, matchers) { core.debug(` matching patterns against file ${changedFile}`); for (const matcher of matchers) { @@ -29691,7 +29754,7 @@ module.exports = new Schema({ /***/ 954: /***/ (function(module) { -module.exports = {"_args":[["@octokit/rest@16.43.1","/Users/dakale/dev/GitHub/actions/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":{"@octokit/types":"2.14.0","deprecation":"2.3.1","once":"1.4.0","os-name":"3.1.0"},"_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/dakale/dev/GitHub/actions/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"}; +module.exports = {"_args":[["@octokit/rest@16.43.1","/Users/masato-naka/repos/MasatoNaka/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":{"@octokit/types":"2.14.0","deprecation":"2.3.1","once":"1.4.0","os-name":"3.1.0"},"_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/masato-naka/repos/MasatoNaka/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"}; /***/ }), diff --git a/src/main.ts b/src/main.ts index 971fd9932..97f2e82ed 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,6 +22,9 @@ async function run() { return; } + const prTitle = getPrTitle(); + core.info(`prTitle: ${prTitle}`); + const client = new github.GitHub(token); const { data: pullRequest } = await client.pulls.get({ @@ -30,6 +33,24 @@ async function run() { pull_number: prNumber }); + const labels: string[] = []; + const labelsToRemove: string[] = []; + + // add label based on title + const labelTitleRegex: Map = await getLabelTitleRegex( + client, + configPath + ); + for (const [label, regexs] of labelTitleRegex.entries()) { + core.debug(`processing ${label}`); + if (checkRegexs(prTitle, regexs)) { + labels.push(label); + } else if (pullRequest.labels.find(l => l.name === label)) { + labelsToRemove.push(label); + } + } + + // add label based on changed files core.debug(`fetching changed files for pr #${prNumber}`); const changedFiles: string[] = await getChangedFiles(client, prNumber); const labelGlobs: Map = await getLabelGlobs( @@ -37,8 +58,6 @@ async function run() { configPath ); - const labels: string[] = []; - const labelsToRemove: string[] = []; for (const [label, globs] of labelGlobs.entries()) { core.debug(`processing ${label}`); if (checkGlobs(changedFiles, globs)) { @@ -70,6 +89,15 @@ function getPrNumber(): number | undefined { return pullRequest.number; } +function getPrTitle(): string { + const pullRequest = github.context.payload.pull_request; + if (!pullRequest) { + return ''; + } + + return pullRequest.title; +} + async function getChangedFiles( client: github.GitHub, prNumber: number @@ -125,21 +153,66 @@ function getLabelGlobMapFromObject( configObject: any ): Map { const labelGlobs: Map = new Map(); - for (const label in configObject) { - if (typeof configObject[label] === "string") { - labelGlobs.set(label, [configObject[label]]); - } else if (configObject[label] instanceof Array) { - labelGlobs.set(label, configObject[label]); - } else { - throw Error( - `found unexpected type for label ${label} (should be string or array of globs)` - ); + for (const configType in configObject) { + if (configType == "changed_file") { + for (const label in configObject["changed_file"]) { + const val = configObject["changed_file"][label] + if (typeof val === "string") { + labelGlobs.set(label, [val]); + } else if (val instanceof Array) { + labelGlobs.set(label, val); + } else { + throw Error( + `found unexpected type for label ${label} (should be string or array of globs)` + ); + } + } } } return labelGlobs; } +async function getLabelTitleRegex( + client: github.GitHub, + configurationPath: string +): Promise> { + const configurationContent: string = await fetchContent( + client, + configurationPath + ); + + // 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: + return getLabelTitleRegexMapFromObject(configObject); +} + +function getLabelTitleRegexMapFromObject( + configObject: any +): Map { + const titleRegexs: Map = new Map(); + for (const configType in configObject) { + if (configType == "title") { + for (const label in configObject["title"]) { + const val = configObject["title"][label]; + if (typeof val === "string") { + titleRegexs.set(label, [new RegExp(val, 'i')]); + } else if (val instanceof Array) { + titleRegexs.set(label, val.map(regexStr => new RegExp(regexStr, 'i')) ); + } else { + throw Error( + `found unexpected type for label ${label} (should be string or array of regex)` + ); + } + } + } + } + + return titleRegexs; +} + function toMatchConfig(config: StringOrMatchConfig): MatchConfig { if (typeof config === "string") { return { @@ -168,6 +241,18 @@ function checkGlobs( return false; } +function checkRegexs( + prTitle: string, + regexs: RegExp[] +): boolean { + for (const regex of regexs) { + if (regex.test(prTitle)) { + return true; + } + } + return false; +} + function isMatch(changedFile: string, matchers: IMinimatch[]): boolean { core.debug(` matching patterns against file ${changedFile}`); for (const matcher of matchers) {