diff --git a/tools/code-owners-parser/CodeOwnersParser/MatchedCodeOwnerEntry.cs b/tools/code-owners-parser/CodeOwnersParser/MatchedCodeOwnerEntry.cs index 268b5868ff8..9a870666fb2 100644 --- a/tools/code-owners-parser/CodeOwnersParser/MatchedCodeOwnerEntry.cs +++ b/tools/code-owners-parser/CodeOwnersParser/MatchedCodeOwnerEntry.cs @@ -27,20 +27,9 @@ internal class MatchedCodeOwnerEntry public readonly CodeOwnerEntry Value; /// - /// The entry is valid if it obeys following conditions: - /// - The Value was obtained with a call to Azure.Sdk.Tools.CodeOwnersParser.CodeOwnersFile.ParseContent(). - /// - As a consequence, in the case of no match, the entry is not valid. - /// - the Value.PathExpression starts with "/". - /// - /// Once the validation described in the following issue is implemented: - /// https://github.com/Azure/azure-sdk-tools/issues/4859 - /// To be valid, the entry will also have to obey following conditions: - /// - if the Value.PathExpression ends with "/", at least one corresponding - /// directory exists in the repository - /// - if the Value.PathExpression does not end with "/", at least one corresponding - /// file exists in the repository. + /// See comment on IsCodeOwnersPathValid /// - public bool IsValid => this.Value.PathExpression.StartsWith("/"); + public bool IsValid => IsCodeOwnersPathValid(this.Value.PathExpression); /// /// Any CODEOWNERS path with these characters will be skipped. @@ -132,12 +121,7 @@ private static string NormalizeTargetPath(string targetPath, string codeownersPa private static Regex ConvertToRegex(string codeownersPath) { - // CODEOWNERS paths that do not start with "/" are relative and considered invalid. - // However, here we handle such cases to accomodate for parsing CODEOWNERS file - // paths that somehow slipped through validation. We do so by instead treating - // such paths as if they were absolute to repository root, i.e. starting with "/". - if (!codeownersPath.StartsWith("/")) - codeownersPath = "/" + codeownersPath; + codeownersPath = ConvertPathIfInvalid(codeownersPath); string pattern = codeownersPath; @@ -148,29 +132,64 @@ private static Regex ConvertToRegex(string codeownersPath) pattern = Regex.Escape(pattern); - // Denote that all paths are absolute by prepending "beginning of string" symbol. + pattern = AddStringAnchors(pattern); + + // Note that the "/**/" case is implicitly covered by "**/". + pattern = pattern.Replace("_DOUBLE_STAR_/", "(.*)"); + // This case is necessary to cover suffix case, e.g. "/foo/bar/**". + pattern = pattern.Replace("/_DOUBLE_STAR_", "(.*)"); + // This case is necessary to cover inline **, e.g. "/a**b/". + pattern = pattern.Replace("_DOUBLE_STAR_", "(.*)"); + pattern = pattern.Replace("_SINGLE_STAR_", "([^/]*)"); + return new Regex(pattern); + } + + private static string ConvertPathIfInvalid(string codeownersPath) + { + // CODEOWNERS paths that do not start with "/" are relative and considered invalid. + // However, here we handle such cases to accomodate for parsing CODEOWNERS file + // paths that somehow slipped through validation. We do so by instead treating + // such paths as if they were absolute to repository root, i.e. starting with "/". + if (!IsCodeOwnersPathValid(codeownersPath)) + codeownersPath = "/" + codeownersPath; + return codeownersPath; + } + + private static string AddStringAnchors(string pattern) + { + // Denote that all paths are absolute by pre-pending "beginning of string" symbol. pattern = "^" + pattern; // Lack of slash at the end denotes the path is a path to a file, // per our validation logic. - // Note we assume this is the case even if the path is invalid, - // even though in such case it might not necessarily be true. - if (!(pattern.EndsWith("/") - || pattern.EndsWith("_DOUBLE_STAR_"))) + // Note we assume this is path to a file even if the path is invalid, + // even though in such case the path might be a path to a directory. + if (!pattern.EndsWith("/")) { // Append "end of string", symbol, denoting the match has to be exact, // not a substring, as we are dealing with a file. pattern += "$"; } - // Note that the "/**/" case is implicitly covered by "**/". - pattern = pattern.Replace("_DOUBLE_STAR_/", "(.*)"); - // This case is necessary to cover suffix case, e.g. "/foo/bar/**". - pattern = pattern.Replace("/_DOUBLE_STAR_", "(.*)"); - // This case is necessary to cover inline **, e.g. "/a**b/". - pattern = pattern.Replace("_DOUBLE_STAR_", "(.*)"); - pattern = pattern.Replace("_SINGLE_STAR_", "([^/]*)"); - return new Regex(pattern); + return pattern; } + + /// + /// The entry is valid if it obeys following conditions: + /// - The Value was obtained with a call to + /// Azure.Sdk.Tools.CodeOwnersParser.CodeOwnersFile.ParseContent(). + /// - As a consequence, in the case of no match, the entry is not valid. + /// - the Value.PathExpression starts with "/". + /// + /// Once the validation described in the following issue is implemented: + /// https://github.com/Azure/azure-sdk-tools/issues/4859 + /// To be valid, the entry will also have to obey following conditions: + /// - if the Value.PathExpression ends with "/", at least one corresponding + /// directory exists in the repository + /// - if the Value.PathExpression does not end with "/", at least one corresponding + /// file exists in the repository. + /// + private static bool IsCodeOwnersPathValid(string codeownersPath) + => codeownersPath.StartsWith("/"); } }