-
Notifications
You must be signed in to change notification settings - Fork 183
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Konrad Jamrozik
committed
Jan 11, 2023
1 parent
3418b9a
commit f784ccd
Showing
13 changed files
with
183 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 65 additions & 59 deletions
124
tools/code-owners-parser/CodeOwnersParser/CodeOwnersFile.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,109 +1,115 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
|
||
namespace Azure.Sdk.Tools.CodeOwnersParser | ||
{ | ||
public static class CodeOwnersFile | ||
public static class CodeownersFile | ||
{ | ||
public static List<CodeOwnerEntry> ParseFile(string filePathOrUrl) | ||
public static List<CodeownersEntry> GetCodeownersEntriesFromFileOrUrl(string codeownersFilePathOrUrl) | ||
{ | ||
string content = FileHelpers.GetFileContents(filePathOrUrl); | ||
return ParseContent(content); | ||
string content = FileHelpers.GetFileOrUrlContents(codeownersFilePathOrUrl); | ||
return GetCodeownersEntries(content); | ||
} | ||
|
||
public static List<CodeOwnerEntry> ParseContent(string fileContent) | ||
public static List<CodeownersEntry> GetCodeownersEntries(string codeownersContent) | ||
{ | ||
List<CodeOwnerEntry> entries = new List<CodeOwnerEntry>(); | ||
List<CodeownersEntry> entries = new List<CodeownersEntry>(); | ||
|
||
// An entry ends when we get to a path (a real path or a commented dummy path) | ||
using (StringReader sr = new StringReader(fileContent)) | ||
using (StringReader sr = new StringReader(codeownersContent)) | ||
{ | ||
CodeOwnerEntry entry = new CodeOwnerEntry(); | ||
CodeownersEntry entry = new CodeownersEntry(); | ||
|
||
// we are going to read line by line until we find a line that is not a comment OR that is using the placeholder entry inside the comment. | ||
// while we are trying to find the folder entry, we parse all comment lines to extract the labels from it. | ||
// when we find the path or placeholder, we add the completed entry and create a new one. | ||
while (sr.ReadLine() is { } line) | ||
{ | ||
line = NormalizeLine(line); | ||
|
||
if (string.IsNullOrWhiteSpace(line)) | ||
{ | ||
continue; | ||
} | ||
|
||
if (!line.StartsWith("#") || line.IndexOf(CodeOwnerEntry.MissingFolder, System.StringComparison.OrdinalIgnoreCase) >= 0) | ||
{ | ||
// If this is not a comment line OR this is a placeholder entry | ||
|
||
entry.ParseOwnersAndPath(line); | ||
|
||
// only add it if it is a valid entry | ||
if (entry.IsValid) | ||
{ | ||
entries.Add(entry); | ||
} | ||
|
||
// create a new entry. | ||
entry = new CodeOwnerEntry(); | ||
} | ||
else if (line.StartsWith("#")) | ||
{ | ||
// try to process the line in case there are markers that need to be extracted | ||
entry.ProcessLabelsOnLine(line); | ||
} | ||
|
||
ProcessCodeownersLine(line, entry, entries); | ||
} | ||
} | ||
return entries; | ||
} | ||
|
||
public static CodeOwnerEntry ParseAndFindOwnersForClosestMatch( | ||
string codeOwnersFilePathOrUrl, | ||
public static CodeownersEntry GetMatchingCodeownersEntry( | ||
string targetPath, | ||
bool useNewFindOwnersForClosestMatchImpl = false) | ||
string codeownersFilePathOrUrl, | ||
bool useNewImpl = false) | ||
{ | ||
var codeOwnerEntries = ParseFile(codeOwnersFilePathOrUrl); | ||
return FindOwnersForClosestMatch(codeOwnerEntries, targetPath, useNewFindOwnersForClosestMatchImpl); | ||
var codeownersEntries = GetCodeownersEntriesFromFileOrUrl(codeownersFilePathOrUrl); | ||
return GetMatchingCodeownersEntry(targetPath, codeownersEntries, useNewImpl); | ||
} | ||
|
||
public static CodeOwnerEntry FindOwnersForClosestMatch( | ||
List<CodeOwnerEntry> codeOwnerEntries, | ||
public static CodeownersEntry GetMatchingCodeownersEntry( | ||
string targetPath, | ||
bool useNewFindOwnersForClosestMatchImpl = false) | ||
List<CodeownersEntry> codeownersEntries, | ||
bool useNewImpl = false) | ||
{ | ||
Debug.Assert(targetPath != null); | ||
return useNewImpl | ||
? new MatchedCodeownersEntry(codeownersEntries, targetPath).Value | ||
: GetMatchingCodeownersEntryLegacyImpl(codeownersEntries, targetPath); | ||
} | ||
|
||
private static void ProcessCodeownersLine(string line, CodeownersEntry entry, List<CodeownersEntry> entries) | ||
{ | ||
return useNewFindOwnersForClosestMatchImpl | ||
? new MatchedCodeOwnerEntry(codeOwnerEntries, targetPath).Value | ||
: FindOwnersForClosestMatchLegacyImpl(codeOwnerEntries, targetPath); | ||
line = NormalizeLine(line); | ||
|
||
if (string.IsNullOrWhiteSpace(line)) | ||
{ | ||
return; | ||
} | ||
|
||
if (!IsCommentLine(line) || IsPlaceholderEntry(line)) | ||
{ | ||
entry.ParseOwnersAndPath(line); | ||
|
||
// only add it if it is a valid entry | ||
if (entry.IsValid) | ||
entries.Add(entry); | ||
} | ||
else if (line.StartsWith("#")) | ||
{ | ||
// try to process the line in case there are markers that need to be extracted | ||
entry.ProcessLabelsOnLine(line); | ||
} | ||
} | ||
|
||
private static CodeOwnerEntry FindOwnersForClosestMatchLegacyImpl(List<CodeOwnerEntry> codeOwnerEntries, | ||
private static bool IsPlaceholderEntry(string line) | ||
=> line.Contains(CodeownersEntry.MissingFolder, StringComparison.OrdinalIgnoreCase); | ||
|
||
private static bool IsCommentLine(string line) | ||
=> line.StartsWith("#"); | ||
|
||
private static string NormalizeLine(string line) | ||
=> !string.IsNullOrEmpty(line) | ||
// Remove tabs and trim extra whitespace | ||
? line.Replace('\t', ' ').Trim() | ||
: line; | ||
|
||
private static CodeownersEntry GetMatchingCodeownersEntryLegacyImpl( | ||
List<CodeownersEntry> codeownersEntries, | ||
string targetPath) | ||
{ | ||
// Normalize the start and end of the paths by trimming slash | ||
targetPath = targetPath.Trim('/'); | ||
|
||
// We want to find the match closest to the bottom of the codeowners file. | ||
// CODEOWNERS sorts the paths in order of 'RepoPath', 'ServicePath' and then 'PackagePath'. | ||
for (int i = codeOwnerEntries.Count - 1; i >= 0; i--) | ||
for (int i = codeownersEntries.Count - 1; i >= 0; i--) | ||
{ | ||
string codeOwnerPath = codeOwnerEntries[i].PathExpression.Trim('/'); | ||
string codeownersPath = codeownersEntries[i].PathExpression.Trim('/'); | ||
// Note that this only matches on paths without glob patterns which is good enough | ||
// for our current scenarios but in the future might need to support globs | ||
if (targetPath.StartsWith(codeOwnerPath, StringComparison.OrdinalIgnoreCase)) | ||
if (targetPath.StartsWith(codeownersPath, StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return codeOwnerEntries[i]; | ||
return codeownersEntries[i]; | ||
} | ||
} | ||
|
||
return new CodeOwnerEntry(); | ||
return new CodeownersEntry(); | ||
} | ||
|
||
private static string NormalizeLine(string line) | ||
=> !string.IsNullOrEmpty(line) | ||
// Remove tabs and trim extra whitespace | ||
? line.Replace('\t', ' ').Trim() | ||
: line; | ||
} | ||
} |
Oops, something went wrong.