Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions tracer/src/Datadog.Trace/Ci/CodeOwners.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,18 @@ private static bool TryParseSectionHeader(string raw, [NotNullWhen(true)] out Se
/// </summary>
private static Regex CompileGlob(string pattern)
{
var patternWithoutLeadingGlobstar = pattern.StartsWith("**/", StringComparison.Ordinal)
? pattern.Substring(3)
: pattern;

// Escape regex metachars first.
var rx = Regex.Escape(pattern);
var rx = Regex.Escape(patternWithoutLeadingGlobstar);

// A globstar surrounded by slashes matches zero or more directories.
rx = rx.Replace("/\\*\\*/", "/(?:[^/]+/)*");

// Temporary sentinel for ** that we restore after dealing with single *.
rx = rx.Replace("\\*\\*", "§§DOUBLESTAR§§");
rx = rx.Replace("\\*\\*", ".*"); // multi-level wildcard
rx = rx.Replace("\\*", "[^/]*"); // single‑level wildcard
rx = rx.Replace("§§DOUBLESTAR§§", ".*"); // multi‑level wildcard
rx = rx.Replace("\\?", "."); // single char

if (pattern.EndsWith("/"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ public CodeOwnersTests()
// New GitHub quirks
[InlineData("/x/logs/error.txt", "[\"@octo-org/octocats\"]")] // **/logs pattern
[InlineData("docs/getting-started.md", "[\"docs@example.com\"]")] // docs/* pattern
[InlineData("/globstar/target", "[\"@globstar-owner\"]")]
[InlineData("/globstar/one/target", "[\"@globstar-owner\"]")]
[InlineData("/globstar/one/two/target", "[\"@globstar-owner\"]")]
[InlineData("/globstar/one/not-target", "[\"@global-owner1\",\"@global-owner2\"]")]
// Leading globstar
[InlineData("foo", "[\"@foo-owner\"]")]
[InlineData("one/foo", "[\"@foo-owner\"]")]
[InlineData("one/two/foo", "[\"@foo-owner\"]")]
[InlineData("foo/bar", "[\"@foo-bar-owner\"]")]
[InlineData("one/foo/bar", "[\"@foo-bar-owner\"]")]
[InlineData("one/two/foo/bar", "[\"@foo-bar-owner\"]")]
[InlineData("foobar", "[\"@global-owner1\",\"@global-owner2\"]")]
// Trailing globstar
[InlineData("abc/file", "[\"@abc-owner\"]")]
[InlineData("abc/one/file", "[\"@abc-owner\"]")]
[InlineData("abc/one/two/file", "[\"@abc-owner\"]")]
[InlineData("abc", "[\"@global-owner1\",\"@global-owner2\"]")]
[InlineData("abcd/file", "[\"@global-owner1\",\"@global-owner2\"]")]
// An embedded **/ must not receive leading-globstar semantics
[InlineData("prefixtarget", "[\"@global-owner1\",\"@global-owner2\"]")]
public void CheckGithubCodeOwners(string value, string expected)
{
var match = _githubCodeOwners.Match(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,17 @@ apps/ @octocat
# directory in the root of your repository except for the `/apps/github`
# subdirectory, as its owners are left empty.
/apps/ @octocat
/apps/github
/apps/github

# A globstar between slashes matches zero or more directories.
/globstar/**/target @globstar-owner

# A leading globstar matches at any depth, including the repository root.
**/foo @foo-owner
**/foo/bar @foo-bar-owner

# A trailing globstar matches everything inside the directory.
abc/** @abc-owner

# Consecutive stars outside a globstar position are not a leading globstar.
prefix**/target @embedded-star-owner
Loading