view mozillatry.py @ 23:4016b1d28c79

command line switches
author Jeff Hammel <jhammel@mozilla.com>
date Fri, 07 Dec 2012 11:41:27 -0800
parents dde820879dba
children 484858dfbc03
line wrap: on
line source

#!/usr/bin/env python

"""
push patches to try
"""

import configuration
import optparse
import os
import sys

from subprocess import check_call as call

def reset(directory):
    """reset an hg directory to a good state"""
    assert os.path.exists(directory) and os.path.isdir(directory)
    hg_dir = os.path.join(directory, '.hg')
    assert os.path.exists(hg_dir) and os.path.isdir(hg_dir)
    call(['hg', 'revert', '--no-backup', '--all'], cwd=directory)
    call(['hg', 'qpop', '--all'], cwd=directory)
    try:
        shutil.rmtree(os.path.join(hg_dir, 'patches')) # remove patches
    except:
        pass

def update(directory):
    """update a mozilla-central checkout"""
    assert os.path.exists(directory) and os.path.isdir(directory)
    reset(directory)
    call(['hg', 'pull'], cwd=directory)
    call(['hg', 'update'], cwd=directory)
    call(['hg', 'qinit'], cwd=directory)

def push_to_try(patches, repo, commit, _try='ssh://hg.mozilla.org/try/'):
    """push a series of patches to try repository"""

    # ensure the repo is in a good state
    update(repo)

    try:
        # apply patches
        for patch in patches:
            call(['hg', 'qimport', patch], cwd=repo)
            call(['hg', 'qpush', '--all'], cwd=repo)
            call(['hg', 'qseries', '-v'], cwd=repo)

        # push to try
        call(['hg', 'qref', '--message', commit], cwd=repo)
        call(['hg', 'push', '-f', _try], cwd=repo)
    finally:
        reset(repo)

def try_syntax(opt=True, debug=True, platforms=('all',), unittests=('all',), talos=('all',), bug=None):
    """
    return try syntax; see also:
    - https://github.com/pbiggar/trychooser
    - http://trychooser.pub.build.mozilla.org/
    """

    assert opt or debug
    assert platforms
    message = ['try:']
    message += ['-b', '%s%s' % (('d' if debug else ''), ('o' if opt else ''))]
    message += ['-p', ','.join(platforms)]
    message += ['-u', (','.join(unittests) if unittests else 'none')]
    message += ['-t', (','.join(talos) if talos else 'none')]
    if bug:
        message += ['--post-to-bugzilla', 'Bug', str(bug)]
    return ' '.join(message)

### configuration parsing

class ConfigurationError(Exception):
    """error when checking configuration"""

class MozillaTryConfiguration(configuration.Configuration):

    default_config_file = os.path.join('~', '.mozutils')
    usage = '%prog [options] patch <patch2> <...>'
    load_help = 'load from config file'
    if os.path.exists(os.path.expanduser(default_config_file)):
        load_help += ' [DEFAULT: %s]' % default_config_file
    options = {'opt': {'default': True,
                       'help': "whether to try on opt builds"},
               'debug': {'default': True,
                         'help': "whether to try on debug builds"},
               'platforms': {'default': [],
                             'help': "platforms to run on",
                             "flags": ["-p", "--platform"]},
               'unittests': {'default': [],
                             'help': "unit tests to run",
                             'flags': ['-u', '--unittests']},
               'talostests': {'default': [],
                              'help': "talos tests to run",
                              'flags': ['-t', '--talostests']},
               'mozilla_central': {'help': "path to mozilla-central clone",
                                   'required': True,
                                   'flags': ["--m-c", "--mozilla-central"]}
               }

    # configuration items to interpolate as paths
    paths = ['mozilla_central']

    def __init__(self):
        configuration.Configuration.__init__(self, usage=self.usage, load='--config')

    def validate(self):
        """check configuration"""

        configuration.Configuration.validate(self)

        if (not self.config.get('opt')) and (not self.config.get('debug')):
            raise ConfigurationError("Must have opt or debug builds")

        for path in self.paths:
            self.config[path] = os.path.expanduser(self.config[path])

        try_directory = self.config.get('mozilla_central')
        if (try_directory is None) or (not os.path.exists(try_directory)):
            raise ConfigurationError("mozilla-central directory does not exist: %s" % try_directory)

        # TODO: make a 'path' type

    def configuration_files(self, options, args):
        configuration_files = configuration.Configuration.configuration_files(self, options, args)
        if not configuration_files:
            default_config = os.path.expanduser(self.default_config_file)
            if os.path.exists(default_config):
                configuration_files = [default_config]
        return configuration_files

    def load_configuration_file(self, filename):
        config = configuration.Configuration.load_configuration_file(self, filename)

        # ignore options that we don't care about
        config = dict([(key, value) for key, value in config.items()
                       if key in self.option_dict])
        return config

def main(args=sys.argv[1:]):

    # parse command line arguments
    parser = MozillaTryConfiguration()
    options, args = parser.parse_args()
    if not args:
        parser.print_usage()
        parser.exit()

    # get mozilla-central repository directory
    try_directory = options.mozilla_central

    # build try syntax
    commit = try_syntax(opt=options.opt,
                        debug=options.debug,
                        unittests=options.unittests,
                        talos=options.talostests
                        )
    print commit

    # push to try
    push_to_try(patches=args, repo=options.mozilla_central, commit=commit)

if __name__ == '__main__':
    main()