|
@@ -379,7 +379,7 @@ def parse_code_owners(data: str) -> Tuple[List[str], List[str], List[str]]:
|
|
|
if rule.startswith("#") or not len(rule):
|
|
|
continue
|
|
|
|
|
|
- assignees = rule.strip().split()[1:]
|
|
|
+ _, assignees = get_codeowners_path_and_owners(rule)
|
|
|
for assignee in assignees:
|
|
|
if "/" not in assignee:
|
|
|
if re.match(r"[^@]+@[^@]+\.[^@]+", assignee):
|
|
@@ -393,6 +393,13 @@ def parse_code_owners(data: str) -> Tuple[List[str], List[str], List[str]]:
|
|
|
return teams, usernames, emails
|
|
|
|
|
|
|
|
|
+def get_codeowners_path_and_owners(rule):
|
|
|
+ # Regex does a negative lookbehind for a backslash. Matches on whitespace without a preceding backslash.
|
|
|
+ pattern = re.compile(r"(?<!\\)\s")
|
|
|
+ path, *code_owners = (i for i in pattern.split(rule.strip()) if i)
|
|
|
+ return path, code_owners
|
|
|
+
|
|
|
+
|
|
|
def convert_codeowners_syntax(codeowners, associations, code_mapping):
|
|
|
"""Converts CODEOWNERS text into IssueOwner syntax
|
|
|
codeowners: CODEOWNERS text
|
|
@@ -408,7 +415,7 @@ def convert_codeowners_syntax(codeowners, associations, code_mapping):
|
|
|
result += f"{rule}\n"
|
|
|
continue
|
|
|
|
|
|
- path, *code_owners = (x.strip() for x in rule.split())
|
|
|
+ path, code_owners = get_codeowners_path_and_owners(rule)
|
|
|
# Escape invalid paths https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#syntax-exceptions
|
|
|
# Check if path has whitespace
|
|
|
# Check if path has '#' not as first character
|