pbx-code-owners 1.0.0a4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pbx-code-owners might be problematic. Click here for more details.
- code_owners/__init__.py +1 -0
- code_owners/config.py +17 -0
- code_owners/parser.py +80 -0
- pbx_code_owners-1.0.0a4.dist-info/METADATA +10 -0
- pbx_code_owners-1.0.0a4.dist-info/RECORD +8 -0
- pbx_code_owners-1.0.0a4.dist-info/WHEEL +5 -0
- pbx_code_owners-1.0.0a4.dist-info/entry_points.txt +2 -0
- pbx_code_owners-1.0.0a4.dist-info/top_level.txt +1 -0
code_owners/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .parser import main # noqa: F401
|
code_owners/config.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
DEFAULT_BRANCH = os.environ.get("DEFAULT_BRANCH", "master")
|
|
5
|
+
CODEOWNERS_FILE = Path(os.getcwd()) / "codeowners.yml"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
CI_SERVER_URL = os.environ["CI_SERVER_URL"]
|
|
9
|
+
CI_PROJECT_ID = os.environ["CI_PROJECT_ID"]
|
|
10
|
+
CI_MERGE_REQUEST_IID = os.environ["CI_MERGE_REQUEST_IID"]
|
|
11
|
+
GITLAB_TOKEN = os.environ["MR_QA_GUARD_TOKEN"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
GITLAB_DISCUSSIONS_URL = (
|
|
15
|
+
f"{CI_SERVER_URL}/api/v4/projects/{CI_PROJECT_ID}"
|
|
16
|
+
f"/merge_requests/{CI_MERGE_REQUEST_IID}/discussions"
|
|
17
|
+
)
|
code_owners/parser.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import subprocess # nosec
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from code_owners import config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def load_codeowners() -> dict:
|
|
10
|
+
with open(config.CODEOWNERS_FILE) as file:
|
|
11
|
+
return yaml.safe_load(file)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_changed_paths() -> list[str]:
|
|
15
|
+
subprocess.run(["git", "fetch", "origin", "master:master"]) # nosec
|
|
16
|
+
git_diff = subprocess.run( # nosec
|
|
17
|
+
["git", "diff", "--name-only", config.DEFAULT_BRANCH], stdout=subprocess.PIPE
|
|
18
|
+
)
|
|
19
|
+
return git_diff.stdout.decode().splitlines()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def match_any_path(sub_paths: list[str], base_paths: list[str]) -> bool:
|
|
23
|
+
# TODO: implement wildcards
|
|
24
|
+
matched_paths = (
|
|
25
|
+
sub_path
|
|
26
|
+
for sub_path in sub_paths
|
|
27
|
+
for base_path in base_paths
|
|
28
|
+
if sub_path.startswith(base_path)
|
|
29
|
+
)
|
|
30
|
+
try:
|
|
31
|
+
next(matched_paths)
|
|
32
|
+
except StopIteration:
|
|
33
|
+
return False
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def discussion_exists(content: str) -> bool:
|
|
38
|
+
url = config.GITLAB_DISCUSSIONS_URL
|
|
39
|
+
headers = {"PRIVATE-TOKEN": config.GITLAB_TOKEN}
|
|
40
|
+
resp = requests.get(url, headers=headers, timeout=(3, 30))
|
|
41
|
+
resp.raise_for_status()
|
|
42
|
+
discussions = resp.json()
|
|
43
|
+
filtered_discussions = (
|
|
44
|
+
discussion
|
|
45
|
+
for discussion in discussions
|
|
46
|
+
if discussion["notes"][0]["body"] == content
|
|
47
|
+
)
|
|
48
|
+
try:
|
|
49
|
+
next(filtered_discussions)
|
|
50
|
+
except StopIteration:
|
|
51
|
+
return False
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def notify_owner(owner: str, section_name: str) -> None:
|
|
56
|
+
content = f'Section "{section_name}" requires {owner} approval'
|
|
57
|
+
if discussion_exists(content):
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
url = config.GITLAB_DISCUSSIONS_URL
|
|
61
|
+
headers = {"PRIVATE-TOKEN": config.GITLAB_TOKEN}
|
|
62
|
+
resp = requests.post(url, headers=headers, json={"body": content}, timeout=(3, 30))
|
|
63
|
+
resp.raise_for_status()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main() -> None:
|
|
67
|
+
codeowners = load_codeowners()
|
|
68
|
+
changed_paths = get_changed_paths()
|
|
69
|
+
if not changed_paths:
|
|
70
|
+
print("No changes, checking codeowners stopped")
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
for section in codeowners["sections"]:
|
|
74
|
+
if "." in section["paths"] or match_any_path(changed_paths, section["paths"]):
|
|
75
|
+
for owner in section["owners"]:
|
|
76
|
+
notify_owner(owner, section["name"])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
main()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
code_owners/__init__.py,sha256=KpQV6L_w-gZB_tmXakX9cryakyfR6HfJbxw2I5F30Qk,39
|
|
2
|
+
code_owners/config.py,sha256=0SkzTj2VI-kLjzYRWcKb3HC3384xNrHScfTqxkAvNOI,490
|
|
3
|
+
code_owners/parser.py,sha256=edSnS6NIRv1521SS2wZ-HSGpJMv-Bl2mB0LEhvKR2FU,2225
|
|
4
|
+
pbx_code_owners-1.0.0a4.dist-info/METADATA,sha256=6-TZAFTn0uvPrJGItU51qB7-lCOsPSBjIngjsGVjS94,191
|
|
5
|
+
pbx_code_owners-1.0.0a4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
6
|
+
pbx_code_owners-1.0.0a4.dist-info/entry_points.txt,sha256=f95eIPd8GX6RCorAFvYPHsC9PsAnWwjqTuAT5DA5y3Y,53
|
|
7
|
+
pbx_code_owners-1.0.0a4.dist-info/top_level.txt,sha256=sL_gdls-fhXq9nUJZAzuPTtGYSYn-SF_D_t9hwGqArA,12
|
|
8
|
+
pbx_code_owners-1.0.0a4.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
code_owners
|