From d4e405a788c9e2bfe78cd808dc3d0231a3850b78 Mon Sep 17 00:00:00 2001 From: Filipe Miranda Date: Wed, 10 May 2023 11:37:48 -0300 Subject: [PATCH 1/4] feat: add authors matchconfig option Closes #100 --- src/labeler.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/labeler.ts b/src/labeler.ts index 96e152bc3..0f847d77d 100644 --- a/src/labeler.ts +++ b/src/labeler.ts @@ -6,6 +6,7 @@ import {Minimatch} from 'minimatch'; interface MatchConfig { all?: string[]; any?: string[]; + authors?: string[]; } type StringOrMatchConfig = string | MatchConfig; @@ -71,6 +72,15 @@ 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 +223,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 +252,12 @@ function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { } } + if (matchConfig.authors !== undefined) { + if (!checkAuthors(matchConfig.authors)) { + return false; + } + } + return true; } From 444d88415b2ce2a78605cf40cd4d37aeafe2f3df Mon Sep 17 00:00:00 2001 From: Filipe Miranda Date: Wed, 10 May 2023 11:45:49 -0300 Subject: [PATCH 2/4] chore: update README.md add new author matchconfig explanation --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) 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 From 165aa71bccc2760c2ce2775a7bf50b524a92c41f Mon Sep 17 00:00:00 2001 From: Filipe Miranda Date: Wed, 10 May 2023 12:04:24 -0300 Subject: [PATCH 3/4] test: add tests for new author matchconfig property --- __mocks__/@actions/github.ts | 5 ++++- __tests__/labeler.test.ts | 24 ++++++++++++++++++++++-- src/labeler.ts | 3 +-- 3 files changed, 27 insertions(+), 5 deletions(-) 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/src/labeler.ts b/src/labeler.ts index 0f847d77d..2955249b7 100644 --- a/src/labeler.ts +++ b/src/labeler.ts @@ -3,7 +3,7 @@ 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[]; @@ -77,7 +77,6 @@ function getPrAuthor(): string | undefined { if (!pullRequest) { return undefined; } - return pullRequest.user.login; } From 91d01da8b9456ba6e78c6c18f17fde2bf4e08dc0 Mon Sep 17 00:00:00 2001 From: Filipe Miranda Date: Wed, 10 May 2023 12:20:06 -0300 Subject: [PATCH 4/4] build: add build file --- dist/index.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) 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) {