-
Notifications
You must be signed in to change notification settings - Fork 69
/
Copy pathcheck_rules_on_wiki.py
173 lines (144 loc) · 5.89 KB
/
check_rules_on_wiki.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import argparse
import os
import re
from os import path
from typing import Optional
from urllib.request import Request, urlopen
class CheckWikiPage:
"""Check if the rules on the wiki page are up-to-date."""
wiki_page_url = "https://wiki.mozilla.org/BugBot"
github_tree_address = "https://github.com/mozilla/bugbot/blob/master/bugbot/rules/"
rules_path = "bugbot/rules/"
deleted_rules = {
"fuzzing_bisection_without_regressed_by.py", # replaced with `bisection_without_regressed_by.py`
"severity_tracked.py", # dropped in favor of `tracked_attention.py`
"tracked_bad_severity.py", # dropped in favor of `tracked_attention.py`
"newbie_with_ni.py",
}
skipped_rules = {
# Disabled rules:
"workflow/p1.py",
"workflow/p2.py",
"workflow/p2_no_activity.py",
"workflow/p3_p4_p5.py",
# Temporary scripts:
"survey_sec_bugs.py", # not running by cron
# Not user-facing rules:
"stepstoreproduce.py", # the autofix is currently disabled
"triage_owner_rotations.py",
"triage_rotations_outdated.py",
"vacant_team_manager.py",
"defect_with_please_or_enable.py",
"missed_landing_comment.py",
"meta_defect.py",
"feature_but_type_defect.py",
"workflow/multi_nag.py",
"multi_nag.py",
"multifix_regression.py",
"several_dups.py",
"several_votes.py",
"several_cc.py",
"several_comments.py",
"several_see_also.py",
"pdfjs_update.py",
"leave_open_sec.py",
# Experimental rules:
"accessibilitybug.py",
"performancebug.py",
}
def __init__(self) -> None:
self.missed_tree: Optional[list] = None
self.missed_wiki: Optional[list] = None
def get_rules_on_wiki_page(self) -> set:
"""Get the list of rules on the wiki page."""
req = Request(self.wiki_page_url)
with urlopen(req) as resp:
wiki_page_content = resp.read().decode("utf-8")
pat = re.compile(rf"""['"]{re.escape(self.github_tree_address)}(.*)['"]""")
rules = pat.findall(wiki_page_content)
if not rules:
raise Exception(f"No rules found on the wiki page {self.wiki_page_url}")
return set(rules)
def get_rules_in_the_tree(self) -> set:
"""Get the list of rules in the tree."""
rules = {
os.path.join(root, file)[len(self.rules_path) :].strip()
for root, dirs, files in os.walk(self.rules_path)
for file in files
if file.endswith(".py") and file != "__init__.py"
}
if not rules:
raise Exception(f"No rules found in the tree {self.rules_path}")
return rules
def check(self) -> None:
"""Check if the rules on the wiki page are up-to-date."""
rules_in_the_tree = self.get_rules_in_the_tree()
rules_on_wiki_page = self.get_rules_on_wiki_page()
self.missed_wiki = sorted(
rule
for rule in rules_in_the_tree
if rule not in rules_on_wiki_page and rule not in self.skipped_rules
)
self.missed_tree = sorted(
rule
for rule in rules_on_wiki_page
if rule not in rules_in_the_tree
and rule not in self.deleted_rules
and not (
rule.startswith("..") and path.exists(path.join(self.rules_path, rule))
)
)
def print_markdown_output(self) -> None:
"""Print the output in markdown format."""
if self.missed_wiki is None or self.missed_tree is None:
self.check()
if self.missed_wiki:
print("## The following rules are not on the wiki page:")
for rule in self.missed_wiki:
print(f"- [{rule}]({self.github_tree_address + rule})")
if self.missed_tree:
print("## The following rules are not in the tree:")
for rule in self.missed_tree:
wiki_id = rule.replace("/", ".2F")
print(f"- [{rule}]({self.wiki_page_url}#{wiki_id})")
def raise_on_mismatch(self) -> None:
"""Raise an exception if the rules on the wiki page are not up-to-date."""
if self.missed_wiki is None or self.missed_tree is None:
self.check()
if self.missed_wiki or self.missed_tree:
raise Exception(
"The rules in the tree and on the wiki page are not in sync."
)
def check_with_markdown_output(self):
"""Check if the rules on the wiki page are up-to-date and return a markdown output."""
if self.missed_wiki is None or self.missed_tree is None:
self.check()
if self.missed_wiki:
print("## The following rules are not on the wiki page:")
for rule in self.missed_wiki:
print(f"- [{rule}]({self.github_tree_address + rule})")
if self.missed_tree:
print("## The following rules are not in the tree:")
for rule in self.missed_tree:
wiki_id = rule.replace("/", ".2F")
print(f"- [{rule}]({self.wiki_page_url}#{wiki_id})")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Check if the rules on the wiki page are up-to-date."
)
parser.add_argument(
"-ci",
"--error-on-mismatch",
dest="error_on_mismatch",
action="store_true",
default=False,
help="Throw an error if the rules are not up-to-date.",
)
args = parser.parse_args()
check_wiki_page = CheckWikiPage()
check_wiki_page.print_markdown_output()
if args.error_on_mismatch:
check_wiki_page.raise_on_mismatch()