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

Unified Diff: commit-queue/commit_queue.py

Issue 135363007: Delete public commit queue to avoid confusion after move to internal repo (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 6 years, 10 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « commit-queue/codereview.settings ('k') | commit-queue/context.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: commit-queue/commit_queue.py
===================================================================
--- commit-queue/commit_queue.py (revision 249146)
+++ commit-queue/commit_queue.py (working copy)
@@ -1,397 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Commit queue executable.
-
-Reuse Rietveld and the Chromium Try Server to process and automatically commit
-patches.
-"""
-
-import logging
-import logging.handlers
-import optparse
-import os
-import shutil
-import signal
-import socket
-import sys
-import tempfile
-import time
-
-import find_depot_tools # pylint: disable=W0611
-import checkout
-import fix_encoding
-import rietveld
-import subprocess2
-
-import async_push
-import cq_alerts
-import creds
-import errors
-import projects
-import sig_handler
-
-
-ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
-
-
-class OnlyIssueRietveld(rietveld.Rietveld):
- """Returns a single issue for end-to-end in prod testing."""
- def __init__(self, url, email, password, extra_headers, only_issue):
- super(OnlyIssueRietveld, self).__init__(url, email, password, extra_headers)
- self._only_issue = only_issue
-
- def get_pending_issues(self):
- """If it's set to return a single issue, only return this one."""
- if self._only_issue:
- return [self._only_issue]
- return []
-
- def get_issue_properties(self, issue, messages):
- """Hacks the result to fake that the issue has the commit bit set."""
- data = super(OnlyIssueRietveld, self).get_issue_properties(issue, messages)
- if issue == self._only_issue:
- data['commit'] = True
- return data
-
- def set_flag(self, issue, patchset, flag, value):
- if issue == self._only_issue and flag == 'commit' and value == 'False':
- self._only_issue = None
- return super(OnlyIssueRietveld, self).set_flag(issue, patchset, flag, value)
-
-
-class FakeCheckout(object):
- def __init__(self):
- self.project_path = os.getcwd()
- self.project_name = os.path.basename(self.project_path)
-
- @staticmethod
- def prepare(_revision):
- logging.info('FakeCheckout is syncing')
- return unicode('FAKE')
-
- @staticmethod
- def apply_patch(*_args):
- logging.info('FakeCheckout is applying a patch')
-
- @staticmethod
- def commit(*_args):
- logging.info('FakeCheckout is committing patch')
- return 'FAKED'
-
- @staticmethod
- def get_settings(_key):
- return None
-
- @staticmethod
- def revisions(*_args):
- return None
-
-
-def AlertOnUncleanCheckout():
- """Sends an alert if the cq is running live with local edits."""
- diff = subprocess2.capture(['gclient', 'diff'], cwd=ROOT_DIR).strip()
- if diff:
- cq_alerts.SendAlert(
- 'CQ running with local diff.',
- ('Ruh-roh! Commit queue was started with an unclean checkout.\n\n'
- '$ gclient diff\n%s' % diff))
-
-
-def SetupLogging(options):
- """Configures the logging module."""
- logging.getLogger().setLevel(logging.DEBUG)
- if options.verbose:
- level = logging.DEBUG
- else:
- level = logging.INFO
- console_logging = logging.StreamHandler()
- console_logging.setFormatter(logging.Formatter(
- '%(asctime)s %(levelname)7s %(message)s'))
- console_logging.setLevel(level)
- logging.getLogger().addHandler(console_logging)
-
- log_directory = 'logs-' + options.project
- if not os.path.exists(log_directory):
- os.mkdir(log_directory)
-
- logging_rotating_file = logging.handlers.RotatingFileHandler(
- filename=os.path.join(log_directory, 'commit_queue.log'),
- maxBytes= 10*1024*1024,
- backupCount=50)
- logging_rotating_file.setLevel(logging.DEBUG)
- logging_rotating_file.setFormatter(logging.Formatter(
- '%(asctime)s %(levelname)-8s %(module)15s(%(lineno)4d): %(message)s'))
- logging.getLogger().addHandler(logging_rotating_file)
-
-
-class SignalInterrupt(Exception):
- """Exception that indicates being interrupted by a caught signal."""
-
- def __init__(self, signal_set=None, *args, **kwargs):
- super(SignalInterrupt, self).__init__(*args, **kwargs)
- self.signal_set = signal_set
-
-
-def SaveDatabaseCopyForDebugging(db_path):
- """Saves database file for debugging. Returns name of the saved file."""
- with tempfile.NamedTemporaryFile(
- dir=os.path.dirname(db_path),
- prefix='db.debug.',
- suffix='.json',
- delete=False) as tmp_file:
- with open(db_path) as db_file:
- shutil.copyfileobj(db_file, tmp_file)
- return tmp_file.name
-
-
-def main():
- # Set a default timeout for sockets. This is critical when talking to remote
- # services like AppEngine and buildbot.
- # TODO(phajdan.jr): This used to be 70s. Investigate lowering it again.
- socket.setdefaulttimeout(60.0 * 15)
-
- parser = optparse.OptionParser(
- description=sys.modules['__main__'].__doc__)
- project_choices = projects.supported_projects()
- parser.add_option('-v', '--verbose', action='store_true')
- parser.add_option(
- '--no-dry-run',
- action='store_false',
- dest='dry_run',
- default=True,
- help='Run for real instead of dry-run mode which is the default. '
- 'WARNING: while the CQ won\'t touch rietveld in dry-run mode, the '
- 'Try Server will. So it is recommended to use --only-issue')
- parser.add_option(
- '--only-issue',
- type='int',
- help='Limits to a single issue. Useful for live testing; WARNING: it '
- 'will fake that the issue has the CQ bit set, so only try with an '
- 'issue you don\'t mind about.')
- parser.add_option(
- '--fake',
- action='store_true',
- help='Run with a fake checkout to speed up testing')
- parser.add_option(
- '--no-try',
- action='store_true',
- help='Don\'t send try jobs.')
- parser.add_option(
- '-p',
- '--poll-interval',
- type='int',
- default=10,
- help='Minimum delay between each polling loop, default: %default')
- parser.add_option(
- '--query-only',
- action='store_true',
- help='Return internal state')
- parser.add_option(
- '--project',
- choices=project_choices,
- help='Project to run the commit queue against: %s' %
- ', '.join(project_choices))
- parser.add_option(
- '-u',
- '--user',
- default='commit-bot@chromium.org',
- help='User to use instead of %default')
- parser.add_option(
- '--rietveld',
- default='https://codereview.chromium.org',
- help='Rietveld server to use instead of %default')
- options, args = parser.parse_args()
- if args:
- parser.error('Unsupported args: %s' % args)
- if not options.project:
- parser.error('Need to pass a valid project to --project.\nOptions are: %s' %
- ', '.join(project_choices))
-
- SetupLogging(options)
- try:
- work_dir = os.path.join(ROOT_DIR, 'workdir')
- # Use our specific subversion config.
- checkout.SvnMixIn.svn_config = checkout.SvnConfig(
- os.path.join(ROOT_DIR, 'subversion_config'))
-
- url = options.rietveld
- gaia_creds = creds.Credentials(os.path.join(work_dir, '.gaia_pwd'))
- if options.dry_run:
- logging.debug('Dry run - skipping SCM check.')
- if options.only_issue:
- parser.error('--only-issue is not supported with dry run')
- else:
- print('Using read-only Rietveld')
- # Make sure rietveld is not modified. Pass empty email and
- # password to bypass authentication; this additionally
- # guarantees rietveld will not allow any changes.
- rietveld_obj = rietveld.ReadOnlyRietveld(url, email='', password='')
- else:
- AlertOnUncleanCheckout()
- print('WARNING: The Commit Queue is going to commit stuff')
- if options.only_issue:
- print('Using only issue %d' % options.only_issue)
- rietveld_obj = OnlyIssueRietveld(
- url,
- options.user,
- gaia_creds.get(options.user),
- None,
- options.only_issue)
- else:
- rietveld_obj = rietveld.Rietveld(
- url,
- options.user,
- gaia_creds.get(options.user),
- None)
-
- pc = projects.load_project(
- options.project,
- options.user,
- work_dir,
- rietveld_obj,
- options.no_try)
-
- if options.dry_run:
- if options.fake:
- # Disable the checkout.
- print 'Using no checkout'
- pc.context.checkout = FakeCheckout()
- else:
- print 'Using read-only checkout'
- pc.context.checkout = checkout.ReadOnlyCheckout(pc.context.checkout)
- # Save pushed events on disk.
- print 'Using read-only chromium-status interface'
- pc.context.status = async_push.AsyncPushStore()
-
- landmine_path = os.path.join(work_dir,
- pc.context.checkout.project_name + '.landmine')
- db_path = os.path.join(work_dir, pc.context.checkout.project_name + '.json')
- if os.path.isfile(db_path):
- if os.path.isfile(landmine_path):
- debugging_path = SaveDatabaseCopyForDebugging(db_path)
- os.remove(db_path)
- logging.warning(('Deleting database because previous shutdown '
- 'was unclean. The copy of the database is saved '
- 'as %s.') % debugging_path)
- else:
- try:
- pc.load(db_path)
- except ValueError as e:
- debugging_path = SaveDatabaseCopyForDebugging(db_path)
- os.remove(db_path)
- logging.warning(('Failed to parse database (%r), deleting it. '
- 'The copy of the database is saved as %s.') %
- (e, debugging_path))
- raise e
-
- # Create a file to indicate unclean shutdown.
- with open(landmine_path, 'w'):
- pass
-
- sig_handler.installHandlers(
- signal.SIGINT,
- signal.SIGHUP
- )
-
- # Sync every 5 minutes.
- SYNC_DELAY = 5*60
- try:
- if options.query_only:
- pc.look_for_new_pending_commit()
- pc.update_status()
- print(str(pc.queue))
- os.remove(landmine_path)
- return 0
-
- now = time.time()
- next_loop = now + options.poll_interval
- # First sync is on second loop.
- next_sync = now + options.poll_interval * 2
- while True:
- # In theory, we would gain in performance to parallelize these tasks. In
- # practice I'm not sure it matters.
- pc.look_for_new_pending_commit()
- pc.process_new_pending_commit()
- pc.update_status()
- pc.scan_results()
- if sig_handler.getTriggeredSignals():
- raise SignalInterrupt(signal_set=sig_handler.getTriggeredSignals())
- # Save the db at each loop. The db can easily be in the 1mb range so
- # it's slowing down the CQ a tad but it in the 100ms range even for that
- # size.
- pc.save(db_path)
-
- # More than a second to wait and due to sync.
- now = time.time()
- if (next_loop - now) >= 1 and (next_sync - now) <= 0:
- if sys.stdout.isatty():
- sys.stdout.write('Syncing while waiting \r')
- sys.stdout.flush()
- try:
- pc.context.checkout.prepare(None)
- except subprocess2.CalledProcessError as e:
- # Don't crash, most of the time it's the svn server that is dead.
- # How fun. Send a stack trace to annoy the maintainer.
- errors.send_stack(e)
- next_sync = time.time() + SYNC_DELAY
-
- now = time.time()
- next_loop = max(now, next_loop)
- while True:
- # Abort if any signals are set
- if sig_handler.getTriggeredSignals():
- raise SignalInterrupt(signal_set=sig_handler.getTriggeredSignals())
- delay = next_loop - now
- if delay <= 0:
- break
- if sys.stdout.isatty():
- sys.stdout.write('Sleeping for %1.1f seconds \r' % delay)
- sys.stdout.flush()
- time.sleep(min(delay, 0.1))
- now = time.time()
- if sys.stdout.isatty():
- sys.stdout.write('Running (please do not interrupt) \r')
- sys.stdout.flush()
- next_loop = time.time() + options.poll_interval
- except: # Catch all fatal exit conditions.
- logging.exception('CQ loop terminating')
- raise
- finally:
- logging.warning('Saving db...')
- pc.save(db_path)
- pc.close()
- logging.warning('db save successful.')
- except SignalInterrupt:
- # This is considered a clean shutdown: we only throw this exception
- # from selected places in the code where the database should be
- # in a known and consistent state.
- os.remove(landmine_path)
-
- print 'Bye bye (SignalInterrupt)'
- # 23 is an arbitrary value to signal loop.sh that it must stop looping.
- return 23
- except KeyboardInterrupt:
- # This is actually an unclean shutdown. Do not remove the landmine file.
- # One example of this is user hitting ctrl-c twice at an arbitrary point
- # inside the CQ loop. There are no guarantees about consistent state
- # of the database then.
-
- print 'Bye bye (KeyboardInterrupt - this is considered unclean shutdown)'
- # 23 is an arbitrary value to signal loop.sh that it must stop looping.
- return 23
- except errors.ConfigurationError as e:
- parser.error(str(e))
- return 1
-
- # CQ generally doesn't exit by itself, but if we ever get here, it looks
- # like a clean shutdown so remove the landmine file.
- # TODO(phajdan.jr): Do we ever get here?
- os.remove(landmine_path)
- return 0
-
-
-if __name__ == '__main__':
- fix_encoding.fix_encoding()
- sys.exit(main())
« no previous file with comments | « commit-queue/codereview.settings ('k') | commit-queue/context.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698