diff --git a/README.md b/README.md index 47bc342c7..6e1cee00b 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,17 @@ For more control over matching, you can provide a match object instead of a simp ```yml - any: ['list', 'of', 'globs'] all: ['list', 'of', 'globs'] + authors: ['list', 'of', 'authors'] # github users ``` 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. +Also, the `authors` field is a list of GitHub usernames that must ALL match the PR author 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 +* `authors`: match ALL authors against the PR author A simple path glob is the equivalent to `any: ['glob']`. More specifically, the following two configurations are equivalent: ```yml @@ -80,6 +84,10 @@ source: frontend: - any: ['src/**/*.js'] all: ['!src/main.js'] + +dev-team: +- any: ['src/**/*'] + authors: ['user1', 'user2', 'user3'] ``` ### Create Workflow diff --git a/__mocks__/@actions/github.ts b/__mocks__/@actions/github.ts index ea8319f63..47ae8b11d 100644 --- a/__mocks__/@actions/github.ts +++ b/__mocks__/@actions/github.ts @@ -1,7 +1,10 @@ export const context = { payload: { pull_request: { - number: 123 + number: 123, + user: { + login: 'monalisa' + } } }, repo: { diff --git a/__tests__/labeler.test.ts b/__tests__/labeler.test.ts index d684d28bd..3d8c52baf 100644 --- a/__tests__/labeler.test.ts +++ b/__tests__/labeler.test.ts @@ -1,4 +1,4 @@ -import {checkGlobs} from '../src/labeler'; +import {MatchConfig, checkGlobs} from '../src/labeler'; import * as core from '@actions/core'; @@ -10,7 +10,7 @@ beforeAll(() => { }); }); -const matchConfig = [{any: ['*.txt']}]; +const matchConfig: MatchConfig[] = [{any: ['*.txt']}]; describe('checkGlobs', () => { it('returns true when our pattern does match changed files', () => { @@ -26,4 +26,24 @@ describe('checkGlobs', () => { expect(result).toBeFalsy(); }); + + it('returns true when PR author is in the list of authors', () => { + const matchConfigWithAuthor: MatchConfig[] = [ + {any: ['*.txt'], authors: ['monalisa', 'hubot']} + ]; + const changedFiles = ['foo.txt']; + + const result = checkGlobs(changedFiles, matchConfigWithAuthor); + expect(result).toBeTruthy(); + }); + + it('returns false when PR author is not in the list of authors', () => { + const matchConfigWithAuthor: MatchConfig[] = [ + {any: ['*.txt'], authors: ['foo', 'bar']} + ]; + const changedFiles = ['foo.txt']; + + const result = checkGlobs(changedFiles, matchConfigWithAuthor); + expect(result).toBeFalsy(); + }); }); diff --git a/dist/index.js b/dist/index.js index 6d753d52f..c0678fd58 100644 --- a/dist/index.js +++ b/dist/index.js @@ -96,6 +96,13 @@ function getPrNumber() { } return pullRequest.number; } +function getPrAuthor() { + const pullRequest = github.context.payload.pull_request; + if (!pullRequest) { + return undefined; + } + return pullRequest.user.login; +} function getChangedFiles(client, prNumber) { return __awaiter(this, void 0, void 0, function* () { const listFilesOptions = client.rest.pulls.listFiles.endpoint.merge({ @@ -207,6 +214,19 @@ function checkAll(changedFiles, globs) { core.debug(` "all" patterns matched all files`); return true; } +function checkAuthors(authors) { + const prAuthor = getPrAuthor(); + if (!prAuthor) { + core.info('Could not get pull request author from context, exiting'); + return false; + } + if (authors.includes(prAuthor)) { + core.debug(` author ${prAuthor} is on the list`); + return true; + } + core.debug(` author ${prAuthor} is not on the list`); + return false; +} function checkMatch(changedFiles, matchConfig) { if (matchConfig.all !== undefined) { if (!checkAll(changedFiles, matchConfig.all)) { @@ -218,6 +238,11 @@ function checkMatch(changedFiles, matchConfig) { return false; } } + if (matchConfig.authors !== undefined) { + if (!checkAuthors(matchConfig.authors)) { + return false; + } + } return true; } function addLabels(client, prNumber, labels) { diff --git a/src/labeler.ts b/src/labeler.ts index 96e152bc3..2955249b7 100644 --- a/src/labeler.ts +++ b/src/labeler.ts @@ -3,9 +3,10 @@ import * as github from '@actions/github'; import * as yaml from 'js-yaml'; import {Minimatch} from 'minimatch'; -interface MatchConfig { +export interface MatchConfig { all?: string[]; any?: string[]; + authors?: string[]; } type StringOrMatchConfig = string | MatchConfig; @@ -71,6 +72,14 @@ function getPrNumber(): number | undefined { return pullRequest.number; } +function getPrAuthor(): string | undefined { + const pullRequest = github.context.payload.pull_request; + if (!pullRequest) { + return undefined; + } + return pullRequest.user.login; +} + async function getChangedFiles( client: ClientType, prNumber: number @@ -213,6 +222,22 @@ function checkAll(changedFiles: string[], globs: string[]): boolean { return true; } +function checkAuthors(authors: string[]): boolean { + const prAuthor = getPrAuthor(); + if (!prAuthor) { + core.info('Could not get pull request author from context, exiting'); + return false; + } + + if (authors.includes(prAuthor)) { + core.debug(` author ${prAuthor} is on the list`); + return true; + } + + core.debug(` author ${prAuthor} is not on the list`); + return false; +} + function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { if (matchConfig.all !== undefined) { if (!checkAll(changedFiles, matchConfig.all)) { @@ -226,6 +251,12 @@ function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { } } + if (matchConfig.authors !== undefined) { + if (!checkAuthors(matchConfig.authors)) { + return false; + } + } + return true; }