From b0d9ea12c0655aedf7ea37d9d97a2c84ae8ba50e Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 20 Jan 2023 13:07:52 -0800 Subject: [PATCH] Add the allofany matching type (#423) This is true when all files match at any of the patterns. --- README.md | 8 +++++++- __tests__/labeler.test.ts | 15 +++++++++++++++ src/labeler.ts | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b6585c6d2..664230258 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,15 @@ For more control over matching, you can provide a match object instead of a simp ```yml - any: ['list', 'of', 'globs'] all: ['list', 'of', 'globs'] + allofany: ['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. +One two or three 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 +* `allofany`: match ANY globs against ALL changed paths A simple path glob is the equivalent to `any: ['glob']`. More specifically, the following two configurations are equivalent: ```yml @@ -87,6 +89,10 @@ source: frontend: - any: ['src/**/*.js'] all: ['!src/main.js'] + +# Add 'documentation' label to any change consisting entirely of text files +documentation: +- allofany: ['**/*.md', '**/*.txt', '**/*.1'] ``` ### Create Workflow diff --git a/__tests__/labeler.test.ts b/__tests__/labeler.test.ts index d684d28bd..81d0a7195 100644 --- a/__tests__/labeler.test.ts +++ b/__tests__/labeler.test.ts @@ -11,6 +11,7 @@ beforeAll(() => { }); const matchConfig = [{any: ['*.txt']}]; +const matchAllOfAnyConfig = [{allofany: ['*.txt', '*.md']}]; describe('checkGlobs', () => { it('returns true when our pattern does match changed files', () => { @@ -26,4 +27,18 @@ describe('checkGlobs', () => { expect(result).toBeFalsy(); }); + + it('returns true when our allofany pattern does match changed files', () => { + const changedFiles = ['foo.txt', 'bar.md']; + const result = checkGlobs(changedFiles, matchAllOfAnyConfig); + + expect(result).toBeTruthy(); + }); + + it('returns false when our allofany pattern does not match changed files', () => { + const changedFiles = ['foo.md', 'foo.docx']; + const result = checkGlobs(changedFiles, matchAllOfAnyConfig); + + expect(result).toBeFalsy(); + }); }); diff --git a/src/labeler.ts b/src/labeler.ts index b33073adc..c39e35bd4 100644 --- a/src/labeler.ts +++ b/src/labeler.ts @@ -6,6 +6,7 @@ import {Minimatch, IMinimatch} from 'minimatch'; interface MatchConfig { all?: string[]; any?: string[]; + allofany?: string[]; } type StringOrMatchConfig = string | MatchConfig; @@ -183,6 +184,20 @@ function isMatch(changedFile: string, matchers: IMinimatch[]): boolean { return true; } +function isMatchAny(changedFile: string, matchers: IMinimatch[]): boolean { + core.debug(` matching patterns against file ${changedFile}`); + for (const matcher of matchers) { + core.debug(` - ${printPattern(matcher)}`); + if (matcher.match(changedFile)) { + core.debug(` ${printPattern(matcher)} matched`); + return true; + } + } + + core.debug(` no patterns matched`); + return false; +} + // equivalent to "Array.some()" but expanded for debugging and clarity function checkAny(changedFiles: string[], globs: string[]): boolean { const matchers = globs.map(g => new Minimatch(g)); @@ -213,6 +228,20 @@ function checkAll(changedFiles: string[], globs: string[]): boolean { return true; } +function checkAllOfAny(changedFiles: string[], globs: string[]): boolean { + const matchers = globs.map(g => new Minimatch(g)); + core.debug(` checking "all" patterns`); + for (const changedFile of changedFiles) { + if (!isMatchAny(changedFile, matchers)) { + core.debug(` "allofany" pattern did not match against ${changedFile}`); + return false; + } + } + + core.debug(` "allofany" patterns matched all files`); + return true; +} + function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { if (matchConfig.all !== undefined) { if (!checkAll(changedFiles, matchConfig.all)) { @@ -226,6 +255,12 @@ function checkMatch(changedFiles: string[], matchConfig: MatchConfig): boolean { } } + if (matchConfig.allofany !== undefined) { + if (!checkAllOfAny(changedFiles, matchConfig.allofany)) { + return false; + } + } + return true; }