Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: pending_manager.py

Issue 5995005: Fix a truckload of bugs as I'm trying it the commit-queue out on real changes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/commit-queue
Patch Set: rebase against trunk Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « checkout.py ('k') | projects.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # coding=utf8 1 # coding=utf8
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 """Commit queue manager class. 5 """Commit queue manager class.
6 6
7 Security implications: 7 Security implications:
8 8
9 The following hypothesis are made: 9 The following hypothesis are made:
10 - Commit queue: 10 - Commit queue:
(...skipping 26 matching lines...) Expand all
37 breakpad.SendStack(e, 37 breakpad.SendStack(e,
38 ''.join(traceback.format_tb(sys.exc_info()[2])), 38 ''.join(traceback.format_tb(sys.exc_info()[2])),
39 maxlen=2000) 39 maxlen=2000)
40 40
41 41
42 class PendingCommit(base.Verified): 42 class PendingCommit(base.Verified):
43 """Represents a pending commit that is being processed.""" 43 """Represents a pending commit that is being processed."""
44 persistent = base.Verified.persistent + [ 44 persistent = base.Verified.persistent + [
45 # Important since they tell if we need to revalidate and send try jobs 45 # Important since they tell if we need to revalidate and send try jobs
46 # again or not if any of these value changes. 46 # again or not if any of these value changes.
47 'issue', 'patchset', 'rietveld_server', 'processed', 47 'issue', 'patchset', 'rietveld_server', 'processed', 'description',
48 # Only a cache, these values can be regenerated. 48 # Only a cache, these values can be regenerated.
49 'owner', 'reviewers', 'base_url', 49 'owner', 'reviewers', 'base_url',
50 # Only used after a patch was committed. Keeping here for completeness. 50 # Only used after a patch was committed. Keeping here for completeness.
51 'description', 'revision', 51 'revision',
52 ] 52 ]
53 53
54 def __init__(self, issue, owner, reviewers, patchset, base_url, 54 def __init__(self, issue, owner, reviewers, patchset, base_url, description,
55 rietveld_server): 55 rietveld_server):
56 super(PendingCommit, self).__init__() 56 super(PendingCommit, self).__init__()
57 self.issue = issue 57 self.issue = issue
58 self.owner = owner 58 self.owner = owner
59 self.reviewers = reviewers 59 self.reviewers = reviewers
60 self.patchset = patchset 60 self.patchset = patchset
61 self.base_url = base_url 61 self.base_url = base_url
62 self.rietveld_server = rietveld_server 62 self.rietveld_server = rietveld_server
63 self.description = None 63 self.description = description
64 self.revision = None 64 self.revision = None
65 self.processed = False 65 self.processed = False
66 66
67 def patch_url(self): 67 def patch_url(self):
68 return ('%s/download/issue%d_%d.diff' % 68 return ('%s/download/issue%d_%d.diff' %
69 (self.rietveld_server, self.issue, self.patchset)) 69 (self.rietveld_server, self.issue, self.patchset))
70 70
71 def pending_name(self): 71 def pending_name(self):
72 """The name that should be used for try jobs. 72 """The name that should be used for try jobs.
73 73
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 if issue_id not in known_issues: 111 if issue_id not in known_issues:
112 issue_data = self.rietveld.get_issue_properties(issue_id) 112 issue_data = self.rietveld.get_issue_properties(issue_id)
113 if issue_data.get('patchsets', None): 113 if issue_data.get('patchsets', None):
114 self.queue.pending_commits.append( 114 self.queue.pending_commits.append(
115 PendingCommit( 115 PendingCommit(
116 issue_data['issue'], 116 issue_data['issue'],
117 issue_data['owner_email'], 117 issue_data['owner_email'],
118 issue_data['reviewers'], 118 issue_data['reviewers'],
119 issue_data['patchsets'][-1], 119 issue_data['patchsets'][-1],
120 issue_data['base_url'], 120 issue_data['base_url'],
121 issue_data['description'].replace('\r', ''),
121 self.rietveld.url)) 122 self.rietveld.url))
122 except Exception, e: 123 except Exception, e:
123 # Swallow every exception in that code and move on. Make sure to send a 124 # Swallow every exception in that code and move on. Make sure to send a
124 # stack trace though. 125 # stack trace though.
125 send_stack(e) 126 send_stack(e)
126 127
127 def _fetch_pending_issues(self): 128 def _fetch_pending_issues(self):
128 """Returns the list of issue number for reviews on Rietveld with their last 129 """Returns the list of issue number for reviews on Rietveld with their last
129 patchset with commit+ flag set. 130 patchset with commit+ flag set.
130 """ 131 """
(...skipping 14 matching lines...) Expand all
145 # Silently ignore. 146 # Silently ignore.
146 continue 147 continue
147 148
148 # The patch is fine to try. 149 # The patch is fine to try.
149 revision = self.checkout.prepare() 150 revision = self.checkout.prepare()
150 self._apply_patch(pending, revision) 151 self._apply_patch(pending, revision)
151 previous_cwd = os.getcwd() 152 previous_cwd = os.getcwd()
152 try: 153 try:
153 os.chdir(self.checkout.project_path) 154 os.chdir(self.checkout.project_path)
154 for verifier in self.verifiers: 155 for verifier in self.verifiers:
155 verifier.verify(pending, 'HEAD') 156 verifier.verify(pending, revision)
156 finally: 157 finally:
157 os.chdir(previous_cwd) 158 os.chdir(previous_cwd)
158 except base.DiscardPending, e: 159 except base.DiscardPending, e:
159 self._discard_pending(e.pending, e.message) 160 self._discard_pending(e.pending, e.message)
160 except Exception, e: 161 except Exception, e:
161 # Swallow every exception in that code and move on. Make sure to send a 162 # Swallow every exception in that code and move on. Make sure to send a
162 # stack trace though. 163 # stack trace though.
163 send_stack(e) 164 send_stack(e)
164 165
165 def update_status(self): 166 def update_status(self):
(...skipping 17 matching lines...) Expand all
183 self._discard_pending(pending, 'Patch verification failed') 184 self._discard_pending(pending, 'Patch verification failed')
184 elif pending.succeeded(): 185 elif pending.succeeded():
185 # The item is removed right away. 186 # The item is removed right away.
186 self.queue.pending_commits.remove(pending) 187 self.queue.pending_commits.remove(pending)
187 try: 188 try:
188 self._last_minute_checks(pending) 189 self._last_minute_checks(pending)
189 commit_message = '%s\nReview URL: %s%s' % ( 190 commit_message = '%s\nReview URL: %s%s' % (
190 pending.description, 191 pending.description,
191 pending.rietveld_server, 192 pending.rietveld_server,
192 pending.issue) 193 pending.issue)
193 pending.revision = self.checkout.commit(commit_message, pending.user) 194 pending.revision = self.checkout.commit(commit_message, pending.owner)
194 self._close_issue(pending) 195 self._close_issue(pending)
195 except base.DiscardPending, e: 196 except base.DiscardPending, e:
196 self._discard_pending(e.pending, e.message) 197 self._discard_pending(e.pending, e.message)
197 except Exception, e: 198 except Exception, e:
198 send_stack(e) 199 send_stack(e)
199 self._discard_pending(pending, 'Commit queue had an internal error') 200 self._discard_pending(pending, 'Commit queue had an internal error')
200 201
201 def _validity_checks(self, pending): 202 def _validity_checks(self, pending):
202 """Returns True if this pending is worth looking at.""" 203 """Returns True if this pending is worth looking at."""
203 if not any(re.match(r, pending.base_url) for r in self.project_bases): 204 if not any(re.match(r, pending.base_url) for r in self.project_bases):
204 # Not a repository managed by this instance, silently ignore. 205 # Not a repository managed by this instance, silently ignore.
205 logging.info('%s not in whitelist' % pending.base_url) 206 logging.info('%s not in whitelist' % pending.base_url)
206 return False 207 return False
207 208
208 if any(re.match(r, pending.owner) for r in self.author_white_list): 209 if not any(re.match(r, pending.owner) for r in self.author_white_list):
209 # Can't commit because the owner is not on the whitelist. 210 # Can't commit because the owner is not on the whitelist.
210 logging.info('%s not in whitelist' % pending.owner) 211 logging.info('%s not in whitelist' % pending.owner)
211 return False 212 return False
212 213
213 # Need at least one reviewer matching at least one regexp in 214 # Need at least one reviewer matching at least one regexp in
214 # self.reviewers that is not also the owner of the issue. 215 # self.reviewers that is not also the owner of the issue.
215 def match_reviewer(reviewer): 216 def match_reviewer(reviewer):
216 return any(re.match(reviewer_regexp, reviewer) 217 return any(re.match(reviewer_regexp, reviewer)
217 for reviewer_regexp in self.reviewers) 218 for reviewer_regexp in self.reviewers)
218 219
219 if not any(match_reviewer(reviewer) for reviewer in pending.reviewers 220 if not any(match_reviewer(reviewer) for reviewer in pending.reviewers
220 if not reviewer == pending.owner): 221 if not reviewer == pending.owner):
221 return False 222 return False
222 return True 223 return True
223 224
224 225
225 def _last_minute_checks(self, pending): 226 def _last_minute_checks(self, pending):
226 """Does last minute checks before committing a pending patch. 227 """Does last minute checks before committing a pending patch.
227 228
228 Also prepares the patch to be committed. Throws if the patch should be 229 Also prepares the patch to be committed. Throws if the patch should be
229 discarded. 230 discarded.
230 """ 231 """
231 revision = self.checkout.prepare() 232 revision = self.checkout.prepare()
232 self._apply_patch(pending, revision) 233 self._apply_patch(pending, revision)
233 # Do a last minute verifications just before committing. 234 # Do a last minute verifications just before committing.
234 pending_data = self.rietveld.get_issue_properties(pending.issue) 235 pending_data = self.rietveld.get_issue_properties(pending.issue)
235 pending.description = pending_data['description'].replace('\r', '') 236 if pending.description != pending_data['description'].replace('\r', ''):
237 raise base.DiscardPending(pending,
238 'Commit queue failed due to updated description.')
236 pending.reviewers = pending_data['reviewers'] 239 pending.reviewers = pending_data['reviewers']
237 if pending.patchset != pending_data['patchsets'][-1]: 240 if pending.patchset != pending_data['patchsets'][-1]:
238 raise base.DiscardPending(pending, 241 raise base.DiscardPending(pending,
239 'Commit queue failed due to new patchset.') 242 'Commit queue failed due to new patchset.')
240 if pending_data['closed'] == 'true': 243 if pending_data['closed'] == 'true':
241 raise base.DiscardPending(pending, None) 244 raise base.DiscardPending(pending, None)
242 if not self._validity_checks(pending): 245 if not self._validity_checks(pending):
243 raise base.DiscardPending(pending, 246 raise base.DiscardPending(pending,
244 'This patch fails to pass validity checks to be automatically ' 247 'This patch fails to pass validity checks to be automatically '
245 'committed.') 248 'committed.')
246 TODO('Search for \'lgtm\' in the comments') 249 TODO('Search for \'lgtm\' in the comments')
247 250
248 def _close_issue(self, pending): 251 def _close_issue(self, pending):
249 """Closes a issue on Rietveld after a commit succeeded.""" 252 """Closes a issue on Rietveld after a commit succeeded."""
250 viewvc_url = self.checkout.get_setting('VIEW_VC') 253 viewvc_url = self.checkout.get_settings('VIEW_VC')
251 description = pending.description 254 description = pending.description
252 if viewvc_url: 255 if viewvc_url:
253 description += '\nCommitted: %s%s' % (viewvc_url, pending.revision) 256 description += '\nCommitted: %s%s' % (viewvc_url, pending.revision)
254 self.rietveld.close_issue(pending.issue) 257 self.rietveld.close_issue(pending.issue)
255 self.rietveld.update_description(pending.issue, description) 258 self.rietveld.update_description(pending.issue, description)
256 259
257 def _discard_pending(self, pending, message): 260 def _discard_pending(self, pending, message):
258 """Discards a pending commit. Attach an optional message to the review.""" 261 """Discards a pending commit. Attach an optional message to the review."""
259 self.rietveld.set_flag(pending.issue, pending.patchset, 'commit', 'False') 262 self.rietveld.set_flag(pending.issue, pending.patchset, 'commit', 'False')
260 if message: 263 if message:
(...skipping 15 matching lines...) Expand all
276 'Patch failed to apply against %s.' % revision) 279 'Patch failed to apply against %s.' % revision)
277 280
278 def load(self, filename): 281 def load(self, filename):
279 """Loads the commit queue state from a JSON file.""" 282 """Loads the commit queue state from a JSON file."""
280 self.queue = model.load_from_json_file(filename) 283 self.queue = model.load_from_json_file(filename)
281 self.queue.pending_commits = self.queue.pending_commits or [] 284 self.queue.pending_commits = self.queue.pending_commits or []
282 285
283 def save(self, filename): 286 def save(self, filename):
284 """Save the commit queue state in a simple JSON file.""" 287 """Save the commit queue state in a simple JSON file."""
285 model.save_to_json_file(filename, self.queue) 288 model.save_to_json_file(filename, self.queue)
OLDNEW
« no previous file with comments | « checkout.py ('k') | projects.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698