diff --git a/src/PatternMatcher.php b/src/PatternMatcher.php index 0f2b1bf..cd00f29 100644 --- a/src/PatternMatcher.php +++ b/src/PatternMatcher.php @@ -62,6 +62,8 @@ private function isMatch(Pattern $pattern, string $filename): bool '*' => '[^\/]+', '?' => '[^\/]', '**' => '.*', + '/**' => '\/.*', + '**/' => '.*\/', '/**/' => '\/([^\/]+\/)*', ]; diff --git a/tests/Fixtures/CODEOWNERS.example b/tests/Fixtures/CODEOWNERS.example index 1ab69af..1bbb254 100644 --- a/tests/Fixtures/CODEOWNERS.example +++ b/tests/Fixtures/CODEOWNERS.example @@ -38,6 +38,22 @@ apps/ @octocat # directory in the root of your repository. /docs/ @doctocat +# A leading "**" followed by a slash means match in all directories. +# For example, "**/foo" matches file or directory "foo" anywhere, +# the same as pattern "foo". "**/foo/bar" matches file or directory +# "bar" anywhere that is directly under directory "foo". +**/foo @doctocat + +# A trailing "/**" matches everything inside. +# For example, "abc/**" matches all files inside directory "abc" +# with infinite depth. +abc/** @doctocat + +# A slash followed by two consecutive asterisks then a slash ma +# zero or more directories. For example, "a/**/b" matches "a/b", +# "a/x/b", "a/x/y/b" and so on. +a/**/b @doctocat + # In this example nobody owns the files # This line should be ignored by the parser. /src diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 04cd26f..9aeca64 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -51,6 +51,9 @@ public function testParsingResultsInPatterns() new Pattern('docs/*', ['docs@example.com']), new Pattern('apps/', ['@octocat']), new Pattern('/docs/', ['@doctocat']), + new Pattern('**/foo', ['@doctocat']), + new Pattern('abc/**', ['@doctocat']), + new Pattern('a/**/b', ['@doctocat']), ], $patterns); } @@ -66,6 +69,9 @@ public function testParsingStringResultsInPatterns() new Pattern('docs/*', ['docs@example.com']), new Pattern('apps/', ['@octocat']), new Pattern('/docs/', ['@doctocat']), + new Pattern('**/foo', ['@doctocat']), + new Pattern('abc/**', ['@doctocat']), + new Pattern('a/**/b', ['@doctocat']), ], $patterns); } } diff --git a/tests/PatternMatcherTest.php b/tests/PatternMatcherTest.php index bf8b4a3..5572bd6 100644 --- a/tests/PatternMatcherTest.php +++ b/tests/PatternMatcherTest.php @@ -66,6 +66,11 @@ public function provideCorrectMatchIsReturnedForFilename(): array // does NOT match "foo/bar/file.ext" // ** + [new Pattern('**/c', ['@owner']), 'a/c'], + [new Pattern('**/c', ['@owner']), 'b/c'], + [new Pattern('**/c', ['@owner']), 'a/b/c'], + [new Pattern('a/**', ['@owner']), 'a/b'], + [new Pattern('a/**', ['@owner']), 'a/b/c'], [new Pattern('a/**/b', ['@owner']), 'a/b'], [new Pattern('a/**/b', ['@owner']), 'a/x/b'], [new Pattern('a/**/b', ['@owner']), 'a/x/y/b'], @@ -126,6 +131,11 @@ public function provideNoMatchFoundExceptionIsThrownForFilename(): array [new Pattern('*.ext', ['@owner']), 'fooext'], [new Pattern('*.ext', ['@owner']), 'foo/ext'], [new Pattern('foo/*', ['@owner']), 'foo/bar/file.ext'], + + // ** + [new Pattern('**/foo', ['@owner']), 'foo.ext'], + [new Pattern('**/foo', ['@owner']), 'bar-foo/file.ext'], + [new Pattern('foo/**', ['@owner']), 'foo.ext'], ]; } }