]> git.ipfire.org Git - thirdparty/gcc.git/blob - maintainer-scripts/branch_changer.py
Set +x for two python scripts.
[thirdparty/gcc.git] / maintainer-scripts / branch_changer.py
1 #!/usr/bin/env python3
2
3 # The script requires simplejson, requests, semantic_version packages, in case
4 # of openSUSE:
5 # zypper in python3-simplejson python3-requests
6 # pip3 install semantic_version
7
8 import requests
9 import json
10 import argparse
11 import re
12
13 from semantic_version import Version
14
15 base_url = 'https://gcc.gnu.org/bugzilla/rest.cgi/'
16 statuses = ['UNCONFIRMED', 'ASSIGNED', 'SUSPENDED', 'NEW', 'WAITING', 'REOPENED']
17 search_summary = ' Regression]'
18 regex = '(.*\[)([0-9\./]*)( [rR]egression])(.*)'
19
20 class Bug:
21 def __init__(self, data):
22 self.data = data
23 self.versions = None
24 self.fail_versions = []
25 self.is_regression = False
26
27 self.parse_summary()
28 self.parse_known_to_fail()
29
30 def parse_summary(self):
31 m = re.match(regex, self.data['summary'])
32 if m != None:
33 self.versions = m.group(2).split('/')
34 self.is_regression = True
35 self.regex_match = m
36
37 def parse_known_to_fail(self):
38 v = self.data['cf_known_to_fail'].strip()
39 if v != '':
40 self.fail_versions = [x for x in re.split(' |,', v) if x != '']
41
42 def name(self):
43 return 'PR%d (%s)' % (self.data['id'], self.data['summary'])
44
45 def remove_release(self, release):
46 # Do not remove last value of [x Regression]
47 if len(self.versions) == 1:
48 return
49 self.versions = list(filter(lambda x: x != release, self.versions))
50
51 def add_release(self, releases):
52 parts = releases.split(':')
53 assert len(parts) == 2
54 for i, v in enumerate(self.versions):
55 if v == parts[0]:
56 self.versions.insert(i + 1, parts[1])
57 break
58
59 def add_known_to_fail(self, release):
60 if release in self.fail_versions:
61 return False
62 else:
63 self.fail_versions.append(release)
64 return True
65
66 def update_summary(self, api_key, doit):
67 summary = self.data['summary']
68 new_summary = self.serialize_summary()
69 if new_summary != summary:
70 print(self.name())
71 print(' changing summary: "%s" to "%s"' % (summary, new_summary))
72 self.modify_bug(api_key, {'summary': new_summary}, doit)
73
74 return True
75
76 return False
77
78 def change_milestone(self, api_key, old_milestone, new_milestone, comment, new_fail_version, doit):
79 old_major = Bug.get_major_version(old_milestone)
80 new_major = Bug.get_major_version(new_milestone)
81
82 print(self.name())
83 args = {}
84 if old_major == new_major:
85 args['target_milestone'] = new_milestone
86 print(' changing target milestone: "%s" to "%s" (same branch)' % (old_milestone, new_milestone))
87 elif self.is_regression and new_major in self.versions:
88 args['target_milestone'] = new_milestone
89 print(' changing target milestone: "%s" to "%s" (regresses with the new milestone)' % (old_milestone, new_milestone))
90 else:
91 print(' not changing target milestone: not a regression or does not regress with the new milestone')
92
93 if 'target_milestone' in args and comment != None:
94 print(' adding comment: "%s"' % comment)
95 args['comment'] = {'comment': comment }
96
97 if new_fail_version != None:
98 if self.add_known_to_fail(new_fail_version):
99 s = self.serialize_known_to_fail()
100 print(' changing known_to_fail: "%s" to "%s"' % (self.data['cf_known_to_fail'], s))
101 args['cf_known_to_fail'] = s
102
103 if len(args.keys()) != 0:
104 self.modify_bug(api_key, args, doit)
105 return True
106 else:
107 return False
108
109 def serialize_summary(self):
110 assert self.versions != None
111 assert self.is_regression == True
112
113 new_version = '/'.join(self.versions)
114 new_summary = self.regex_match.group(1) + new_version + self.regex_match.group(3) + self.regex_match.group(4)
115 return new_summary
116
117 def serialize_known_to_fail(self):
118 assert type(self.fail_versions) is list
119 return ', '.join(sorted(self.fail_versions, key = lambda x: Version(x, partial = True)))
120
121 def modify_bug(self, api_key, params, doit):
122 u = base_url + 'bug/' + str(self.data['id'])
123
124 data = {
125 'ids': [self.data['id']],
126 'api_key': api_key }
127
128 data.update(params)
129
130 if doit:
131 r = requests.put(u, data = json.dumps(data), headers = {"content-type": "text/javascript"})
132 print(r)
133
134 @staticmethod
135 def get_major_version(release):
136 parts = release.split('.')
137 assert len(parts) == 2 or len(parts) == 3
138 return '.'.join(parts[:-1])
139
140 @staticmethod
141 def get_bugs(api_key, query):
142 u = base_url + 'bug'
143 r = requests.get(u, params = query)
144 return [Bug(x) for x in r.json()['bugs']]
145
146 def search(api_key, remove, add, limit, doit):
147 bugs = Bug.get_bugs(api_key, {'api_key': api_key, 'summary': search_summary, 'bug_status': statuses})
148 bugs = list(filter(lambda x: x.is_regression, bugs))
149
150 modified = 0
151 for bug in bugs:
152 if remove != None:
153 bug.remove_release(remove)
154 if add != None:
155 bug.add_release(add)
156
157 if bug.update_summary(api_key, doit):
158 modified += 1
159 if modified == limit:
160 break
161
162 print('\nModified PRs: %d' % modified)
163
164 def replace_milestone(api_key, limit, old_milestone, new_milestone, comment, add_known_to_fail, doit):
165 bugs = Bug.get_bugs(api_key, {'api_key': api_key, 'bug_status': statuses, 'target_milestone': old_milestone})
166
167 modified = 0
168 for bug in bugs:
169 if bug.change_milestone(api_key, old_milestone, new_milestone, comment, add_known_to_fail, doit):
170 modified += 1
171 if modified == limit:
172 break
173
174 print('\nModified PRs: %d' % modified)
175
176 parser = argparse.ArgumentParser(description='')
177 parser.add_argument('api_key', help = 'API key')
178 parser.add_argument('--remove', nargs = '?', help = 'Remove a release from summary')
179 parser.add_argument('--add', nargs = '?', help = 'Add a new release to summary, e.g. 6:7 will add 7 where 6 is included')
180 parser.add_argument('--limit', nargs = '?', help = 'Limit number of bugs affected by the script')
181 parser.add_argument('--doit', action = 'store_true', help = 'Really modify BUGs in the bugzilla')
182 parser.add_argument('--new-target-milestone', help = 'Set a new target milestone, e.g. 4.9.3:4.9.4 will set milestone to 4.9.4 for all PRs having milestone set to 4.9.3')
183 parser.add_argument('--add-known-to-fail', help = 'Set a new known to fail for all PRs affected by --new-target-milestone')
184 parser.add_argument('--comment', help = 'Comment a PR for which we set a new target milestore')
185
186 args = parser.parse_args()
187 # Python3 does not have sys.maxint
188 args.limit = int(args.limit) if args.limit != None else 10**10
189
190 if args.remove != None or args.add != None:
191 search(args.api_key, args.remove, args.add, args.limit, args.doit)
192 if args.new_target_milestone != None:
193 t = args.new_target_milestone.split(':')
194 assert len(t) == 2
195 replace_milestone(args.api_key, args.limit, t[0], t[1], args.comment, args.add_known_to_fail, args.doit)