Skip to content

Commit 982de68

Browse files
committed
Define the flow for validating all issues in all repositories
1 parent 1dc28c9 commit 982de68

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

normalize_repos/validate_issues.py

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Standard library
2+
import logging
3+
import yaml
4+
5+
# Local/library specific
6+
import log
7+
8+
logger = logging.getLogger("normalize_repos")
9+
log.reset_handler()
10+
11+
TRIAGE_LABEL = "🚦 status: awaiting triage"
12+
13+
14+
def dump_invalid_issues(invalid_issues):
15+
"""
16+
Dump all invalid issues in a file in the `tmp/` directory.
17+
@param invalid_issues: the hash of repos and their list of invalid issues
18+
"""
19+
20+
for invalid_issue_list in invalid_issues.values():
21+
for invalid_issue in invalid_issue_list:
22+
issue = invalid_issue['issue']
23+
invalid_issue['issue'] = issue.title
24+
invalid_issue['url'] = issue.html_url
25+
26+
logger.log(logging.INFO, f"Dumping issues in a file...")
27+
with open('/tmp/invalid_issues.yml', 'w') as file:
28+
yaml.dump(invalid_issues, file)
29+
logger.log(log.SUCCESS, "done.")
30+
31+
32+
def are_issue_labels_valid(issue, required_groups):
33+
"""
34+
Check if the given issue is valid based on the labels applied to it.
35+
@param issue: the issue whose labels are being validated
36+
@param required_groups: the label groups which must be applied on all issues
37+
@return: whether the issues is or isn't valid, and why
38+
"""
39+
40+
labels = issue.get_labels()
41+
label_names = {label.name for label in labels}
42+
if issue.pull_request:
43+
logger.log(logging.INFO, f"Skipping '{issue.title}' because it is a PR.")
44+
return True, None # PRs are exempt
45+
if TRIAGE_LABEL in label_names:
46+
logger.log(logging.INFO, f"Skipping '{issue.title}' because it is awaiting triage.")
47+
return True, None # Issues that haven't been triaged are exempt
48+
49+
missing_groups = []
50+
for group in required_groups:
51+
required_labels = {
52+
label.qualified_name
53+
for label in group.labels
54+
}
55+
if not label_names.intersection(required_labels):
56+
missing_groups.append(group.name)
57+
if missing_groups:
58+
logger.log(logging.INFO, f"Issue '{issue.title}' has missing labels.")
59+
return False, f"Missing labels from groups: {', '.join(missing_groups)}"
60+
61+
logger.log(logging.INFO, f"Issue '{issue.title}' is OK.")
62+
return True, None
63+
64+
65+
def get_invalid_issues_in_repo(repo, required_groups):
66+
"""
67+
Get a list of invalid issues in the given repo with the reason for marking
68+
them as such.
69+
@param repo: the repo in which to check for the validity of issues
70+
@param required_groups: the label groups which must be applied on all issues
71+
@return: a list of invalid issues and their causes
72+
"""
73+
74+
logger.log(logging.INFO, f"Getting issues for repo '{repo.name}'...")
75+
issues = repo.get_issues(state="open")
76+
logger.log(log.SUCCESS, f"done.")
77+
78+
invalid_issues = []
79+
log.change_indent(+1)
80+
for issue in issues:
81+
logger.log(logging.INFO, f"Checking labels on '{issue.title}'...")
82+
are_valid, reason = are_issue_labels_valid(issue, required_groups)
83+
if not are_valid:
84+
invalid_issues.append({
85+
"issue": issue,
86+
"reason": reason
87+
})
88+
logger.log(log.SUCCESS, "done.")
89+
log.change_indent(-1)
90+
return invalid_issues
91+
92+
93+
def get_required_groups(groups):
94+
"""
95+
Get the list of all the groups, at least one label of which is required to
96+
be present on every triaged issue.
97+
@param groups: the groups to filter
98+
@return: the filtered list of groups that that are required by definition
99+
"""
100+
101+
logger.log(logging.INFO, f"Filtering {len(groups)} groups...")
102+
required_groups = [group for group in groups if group.is_required]
103+
logger.log(log.SUCCESS, f"done. Required {len(required_groups)} groups.")
104+
return required_groups
105+
106+
107+
def validate_issues(repos, groups):
108+
"""
109+
Validate the labels on all issues in all repos for the organisation. This is
110+
the main entrypoint of the module.
111+
"""
112+
113+
required_groups = get_required_groups(groups)
114+
invalid_issues = {}
115+
116+
logger.log(logging.INFO, f"Finding issues with invalid labels...")
117+
log.change_indent(+1)
118+
for repo in list(repos):
119+
if repo.name != 'vocabulary':
120+
continue
121+
logger.log(logging.INFO, f"Checking issues in repo '{repo.name}'...")
122+
invalid_issues[repo.name] = get_invalid_issues_in_repo(repo, required_groups)
123+
logger.log(log.SUCCESS, f"done.")
124+
log.change_indent(-1)
125+
logger.log(log.SUCCESS, f"done.")
126+
127+
dump_invalid_issues(invalid_issues)
128+
129+
130+
__all__ = [validate_issues]

0 commit comments

Comments
 (0)