OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 executable. | 5 """Commit queue executable. |
6 | 6 |
7 Reuse Rietveld and the Chromium Try Server to process and automatically commit | 7 Reuse Rietveld and the Chromium Try Server to process and automatically commit |
8 patches. | 8 patches. |
9 """ | 9 """ |
10 | 10 |
(...skipping 17 matching lines...) Expand all Loading... |
28 import errors | 28 import errors |
29 import projects | 29 import projects |
30 import sig_handler | 30 import sig_handler |
31 | 31 |
32 | 32 |
33 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | 33 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
34 | 34 |
35 | 35 |
36 class OnlyIssueRietveld(rietveld.Rietveld): | 36 class OnlyIssueRietveld(rietveld.Rietveld): |
37 """Returns a single issue for end-to-end in prod testing.""" | 37 """Returns a single issue for end-to-end in prod testing.""" |
38 def __init__(self, url, email, password, extra_headers, only_issue): | 38 def __init__(self, url, email, password, extra_headers, only_issue, |
| 39 only_revert_issue): |
39 super(OnlyIssueRietveld, self).__init__(url, email, password, extra_headers) | 40 super(OnlyIssueRietveld, self).__init__(url, email, password, extra_headers) |
40 self._only_issue = only_issue | 41 self._only_issue = only_issue |
| 42 self._only_revert_issue = only_revert_issue |
41 | 43 |
42 def get_pending_issues(self): | 44 def get_pending_issues(self): |
43 """If it's set to return a single issue, only return this one.""" | 45 """If it's set to return a single issue, only return this one.""" |
44 if self._only_issue: | 46 if self._only_issue: |
45 return [self._only_issue] | 47 return [self._only_issue] |
46 return [] | 48 return [] |
47 | 49 |
| 50 def get_revert_issues(self): |
| 51 """If it's set to return a single revert issue, only return this one.""" |
| 52 if self._only_revert_issue: |
| 53 return [self._only_revert_issue] |
| 54 return [] |
| 55 |
48 def get_issue_properties(self, issue, messages): | 56 def get_issue_properties(self, issue, messages): |
49 """Hacks the result to fake that the issue has the commit bit set.""" | 57 """Hacks the result to fake that the issue has the commit bit set.""" |
50 data = super(OnlyIssueRietveld, self).get_issue_properties(issue, messages) | 58 data = super(OnlyIssueRietveld, self).get_issue_properties(issue, messages) |
51 if issue == self._only_issue: | 59 if issue == self._only_issue: |
52 data['commit'] = True | 60 data['commit'] = True |
| 61 elif issue == self._only_revert_issue: |
| 62 data['revert'] = True |
53 return data | 63 return data |
54 | 64 |
55 def set_flag(self, issue, patchset, flag, value): | 65 def set_flag(self, issue, patchset, flag, value): |
56 if issue == self._only_issue and flag == 'commit' and value == 'False': | 66 if issue == self._only_issue and flag == 'commit' and value == 'False': |
57 self._only_issue = None | 67 self._only_issue = None |
58 return super(OnlyIssueRietveld, self).set_flag(issue, patchset, flag, value) | 68 return super(OnlyIssueRietveld, self).set_flag(issue, patchset, flag, value) |
59 | 69 |
60 | 70 |
61 class ReadOnlyRietveld(rietveld.Rietveld): | 71 class ReadOnlyRietveld(rietveld.Rietveld): |
62 def __init__(self, url, email, password, extra_headers, only_issue): | 72 def __init__(self, url, email, password, extra_headers, only_issue, |
| 73 only_revert_issue): |
63 super(ReadOnlyRietveld, self).__init__(url, email, password, extra_headers) | 74 super(ReadOnlyRietveld, self).__init__(url, email, password, extra_headers) |
64 self._only_issue = only_issue | 75 self._only_issue = only_issue |
65 self._restricted = bool(only_issue) | 76 self._only_revert_issue = only_revert_issue |
| 77 self._restricted = bool(only_issue) or bool(only_revert_issue) |
66 | 78 |
67 def _send(self, request_path, **kwargs): | 79 def _send(self, request_path, **kwargs): |
68 """Ignore all post requests.""" | 80 """Ignore all post requests.""" |
69 if kwargs.get('payload'): | 81 if kwargs.get('payload'): |
70 logging.warn('Ignoring POST to %s', request_path) | 82 logging.warn('Ignoring POST to %s', request_path) |
71 return | 83 return |
72 return super(ReadOnlyRietveld, self)._send(request_path, **kwargs) | 84 return super(ReadOnlyRietveld, self)._send(request_path, **kwargs) |
73 | 85 |
74 def get_pending_issues(self): | 86 def get_pending_issues(self): |
75 """If it's set to return a single issue, only return this one.""" | 87 """If it's set to return a single issue, only return this one.""" |
76 if self._restricted: | 88 if self._restricted: |
77 if self._only_issue: | 89 if self._only_issue: |
78 return [self._only_issue] | 90 return [self._only_issue] |
79 return [] | 91 return [] |
80 return super(ReadOnlyRietveld, self).get_pending_issues() | 92 return super(ReadOnlyRietveld, self).get_pending_issues() |
81 | 93 |
| 94 def get_revert_issues(self): |
| 95 """If it's set to return a single issue, only return this one.""" |
| 96 if self._restricted: |
| 97 if self._only_revert_issue: |
| 98 return [self._only_revert_issue] |
| 99 return [] |
| 100 return super(ReadOnlyRietveld, self).get_revert_issues() |
| 101 |
82 def get_issue_properties(self, issue, messages): | 102 def get_issue_properties(self, issue, messages): |
83 """Hacks the result to fake that the issue has the commit bit set.""" | 103 """Hacks the result to fake that the issue has the commit bit set.""" |
84 data = super(ReadOnlyRietveld, self).get_issue_properties(issue, messages) | 104 data = super(ReadOnlyRietveld, self).get_issue_properties(issue, messages) |
85 if issue == self._only_issue: | 105 if issue == self._only_issue: |
86 data['commit'] = True | 106 data['commit'] = True |
| 107 elif issue == self._only_revert_issue: |
| 108 data['revert'] = True |
87 return data | 109 return data |
88 | 110 |
89 def set_flag(self, issue, patchset, flag, value): | 111 def set_flag(self, issue, patchset, flag, value): |
90 if issue == self._only_issue and flag == 'commit' and value == 'False': | 112 if issue == self._only_issue and flag == 'commit' and value == 'False': |
91 self._only_issue = None | 113 self._only_issue = None |
92 return super(ReadOnlyRietveld, self).set_flag(issue, patchset, flag, value) | 114 return super(ReadOnlyRietveld, self).set_flag(issue, patchset, flag, value) |
93 | 115 |
94 | 116 |
95 class FakeCheckout(object): | 117 class FakeCheckout(object): |
96 def __init__(self): | 118 def __init__(self): |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 description=sys.modules['__main__'].__doc__) | 180 description=sys.modules['__main__'].__doc__) |
159 project_choices = projects.supported_projects() | 181 project_choices = projects.supported_projects() |
160 parser.add_option('-v', '--verbose', action='store_true') | 182 parser.add_option('-v', '--verbose', action='store_true') |
161 parser.add_option( | 183 parser.add_option( |
162 '--no-dry-run', | 184 '--no-dry-run', |
163 action='store_false', | 185 action='store_false', |
164 dest='dry_run', | 186 dest='dry_run', |
165 default=True, | 187 default=True, |
166 help='Run for real instead of dry-run mode which is the default. ' | 188 help='Run for real instead of dry-run mode which is the default. ' |
167 'WARNING: while the CQ won\'t touch rietveld in dry-run mode, the ' | 189 'WARNING: while the CQ won\'t touch rietveld in dry-run mode, the ' |
168 'Try Server will. So it is recommended to use --only-issue') | 190 'Try Server will. So it is recommended to use --only-issue or ' |
| 191 '--only-revert-issue') |
169 parser.add_option( | 192 parser.add_option( |
170 '--only-issue', | 193 '--only-issue', |
171 type='int', | 194 type='int', |
172 help='Limits to a single issue. Useful for live testing; WARNING: it ' | 195 help='Limits to a single issue. Useful for live testing; WARNING: it ' |
173 'will fake that the issue has the CQ bit set, so only try with an ' | 196 'will fake that the issue has the CQ bit set, so only try with an ' |
174 'issue you don\'t mind about.') | 197 'issue you don\'t mind about.') |
175 parser.add_option( | 198 parser.add_option( |
| 199 '--only-revert-issue', |
| 200 type='int', |
| 201 help='Limits to a single issue to revert. Useful for live testing; ' |
| 202 'WARNING: it will fake that the issue has the revert bit set, so only ' |
| 203 'try with an issue you don\'t mind about.') |
| 204 parser.add_option( |
176 '--fake', | 205 '--fake', |
177 action='store_true', | 206 action='store_true', |
178 help='Run with a fake checkout to speed up testing') | 207 help='Run with a fake checkout to speed up testing') |
179 parser.add_option( | 208 parser.add_option( |
180 '--no-try', | 209 '--no-try', |
181 action='store_true', | 210 action='store_true', |
182 help='Don\'t send try jobs.') | 211 help='Don\'t send try jobs.') |
183 parser.add_option( | 212 parser.add_option( |
184 '-p', | 213 '-p', |
185 '--poll-interval', | 214 '--poll-interval', |
(...skipping 29 matching lines...) Expand all Loading... |
215 os.path.join(ROOT_DIR, 'subversion_config')) | 244 os.path.join(ROOT_DIR, 'subversion_config')) |
216 | 245 |
217 url = 'https://chromiumcodereview.appspot.com' | 246 url = 'https://chromiumcodereview.appspot.com' |
218 gaia_creds = creds.Credentials(os.path.join(work_dir, '.gaia_pwd')) | 247 gaia_creds = creds.Credentials(os.path.join(work_dir, '.gaia_pwd')) |
219 if options.dry_run: | 248 if options.dry_run: |
220 logging.debug('Dry run - skipping SCM check.') | 249 logging.debug('Dry run - skipping SCM check.') |
221 if options.only_issue: | 250 if options.only_issue: |
222 print( | 251 print( |
223 'Using read-only Rietveld; using only issue %d' % | 252 'Using read-only Rietveld; using only issue %d' % |
224 options.only_issue) | 253 options.only_issue) |
| 254 elif options.only_revert_issue: |
| 255 print( |
| 256 'Using read-only Rietveld; using only issue %d to revert' % |
| 257 options.only_revert_issue) |
225 else: | 258 else: |
226 print('Using read-only Rietveld') | 259 print('Using read-only Rietveld') |
227 # Make sure rietveld is not modified. | 260 # Make sure rietveld is not modified. |
228 rietveld_obj = ReadOnlyRietveld( | 261 rietveld_obj = ReadOnlyRietveld( |
229 url, | 262 url, |
230 options.user, | 263 options.user, |
231 gaia_creds.get(options.user), | 264 gaia_creds.get(options.user), |
232 None, | 265 None, |
233 options.only_issue) | 266 options.only_issue, |
| 267 options.only_revert_issue) |
234 else: | 268 else: |
235 AlertOnUncleanCheckout() | 269 AlertOnUncleanCheckout() |
236 print('WARNING: The Commit Queue is going to commit stuff') | 270 print('WARNING: The Commit Queue is going to commit stuff') |
237 if options.only_issue: | 271 if options.only_issue or options.only_revert_issue: |
238 print('Using only issue %d' % options.only_issue) | 272 if options.only_issue: |
| 273 print('Using only issue %d' % options.only_issue) |
| 274 else: |
| 275 print('Using only issue %d to revert' % options.only_revert_issue) |
239 rietveld_obj = OnlyIssueRietveld( | 276 rietveld_obj = OnlyIssueRietveld( |
240 url, | 277 url, |
241 options.user, | 278 options.user, |
242 gaia_creds.get(options.user), | 279 gaia_creds.get(options.user), |
243 None, | 280 None, |
244 options.only_issue) | 281 options.only_issue, |
| 282 options.only_revert_issue) |
245 else: | 283 else: |
246 rietveld_obj = rietveld.Rietveld( | 284 rietveld_obj = rietveld.Rietveld( |
247 url, | 285 url, |
248 options.user, | 286 options.user, |
249 gaia_creds.get(options.user), | 287 gaia_creds.get(options.user), |
250 None) | 288 None) |
251 | 289 |
252 pc = projects.load_project( | 290 pc = projects.load_project( |
253 options.project, | 291 options.project, |
254 options.user, | 292 options.user, |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
300 pc.process_new_pending_commit() | 338 pc.process_new_pending_commit() |
301 pc.update_status() | 339 pc.update_status() |
302 pc.scan_results() | 340 pc.scan_results() |
303 if sig_handler.getTriggeredSignals(): | 341 if sig_handler.getTriggeredSignals(): |
304 raise KeyboardInterrupt() | 342 raise KeyboardInterrupt() |
305 # Save the db at each loop. The db can easily be in the 1mb range so | 343 # Save the db at each loop. The db can easily be in the 1mb range so |
306 # it's slowing down the CQ a tad but it in the 100ms range even for that | 344 # it's slowing down the CQ a tad but it in the 100ms range even for that |
307 # size. | 345 # size. |
308 pc.save(db_path) | 346 pc.save(db_path) |
309 | 347 |
310 # More than a second to wait and due to sync. | 348 # More than a 2 seconds to wait and due to sync. |
311 now = time.time() | 349 now = time.time() |
312 if (next_loop - now) >= 1 and (next_sync - now) <= 0: | 350 if (next_loop - now) >= 2 and (next_sync - now) <= 0: |
313 if sys.stdout.isatty(): | 351 if sys.stdout.isatty(): |
314 sys.stdout.write('Syncing while waiting \r') | 352 sys.stdout.write('Syncing while waiting \r') |
315 sys.stdout.flush() | 353 sys.stdout.flush() |
316 try: | 354 try: |
317 pc.context.checkout.prepare(None) | 355 pc.context.checkout.prepare(None) |
318 except subprocess2.CalledProcessError as e: | 356 except subprocess2.CalledProcessError as e: |
319 # Don't crash, most of the time it's the svn server that is dead. | 357 # Don't crash, most of the time it's the svn server that is dead. |
320 # How fun. Send a stack trace to annoy the maintainer. | 358 # How fun. Send a stack trace to annoy the maintainer. |
321 errors.send_stack(e) | 359 errors.send_stack(e) |
322 next_sync = time.time() + SYNC_DELAY | 360 next_sync = time.time() + SYNC_DELAY |
(...skipping 30 matching lines...) Expand all Loading... |
353 return 23 | 391 return 23 |
354 except errors.ConfigurationError as e: | 392 except errors.ConfigurationError as e: |
355 parser.error(str(e)) | 393 parser.error(str(e)) |
356 return 1 | 394 return 1 |
357 return 0 | 395 return 0 |
358 | 396 |
359 | 397 |
360 if __name__ == '__main__': | 398 if __name__ == '__main__': |
361 fix_encoding.fix_encoding() | 399 fix_encoding.fix_encoding() |
362 sys.exit(main()) | 400 sys.exit(main()) |
OLD | NEW |