#!/usr/bin/python -tt # vim: ai ts=4 sts=4 et sw=4 # # Copyright (c) 2012 Intel Corporation # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; version 2 of the License # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., 59 # Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Authors: # Damien Lespiau import git, os, optparse, re, sys class ValidationError(Exception): pass class Logger: DEBUG_COLOR = 37 # white INFO_COLOR = 32 # green WARN_COLOR = 33 # yellow ERR_COLOR = 31 # red PREFIX_RE = re.compile('^<(.*?)>\s*(.*)') verbose = False @staticmethod def _color_print(head, color, msg = None, stream = sys.stdout): if os.getenv('ANSI_COLORS_DISABLED') is None: head = '\033[%dm%s:\033[0m' %(color, head) if msg: stream.write('%s %s\n' % (head, msg)) else: stream.write('%s ' % head) @staticmethod def _color_perror(head, color, msg): Logger._color_print(head, color, msg, sys.stderr) @staticmethod def _split_msg(head, msg): m = Logger.PREFIX_RE.match(msg) if m: head += ' <%s>' % m.group(1) msg = m.group(2) return head, msg @staticmethod def set_verbose(verbose): Logger.verbose = verbose @staticmethod def debug(msg): if not Logger.verbose: return head, msg = Logger._split_msg('Debug', msg) Logger._color_perror(head, Logger.DEBUG_COLOR, msg) @staticmethod def info(msg): if not Logger.verbose: return head, msg = Logger._split_msg('Info', msg) Logger._color_perror(head, Logger.INFO_COLOR, msg) @staticmethod def warning(msg): head, msg = Logger._split_msg('Warning', msg) Logger._color_perror(head, Logger.WARN_COLOR, msg) @staticmethod def error(msg): head, msg = Logger._split_msg('Error', msg) Logger._color_perror(head, Logger.ERR_COLOR, msg) sys.exit(1) class Config: def __init__(self, config_file=None): self.config = {} if not config_file: config_file = os.path.join(os.environ['HOME'], '.gfx-repos') try: execfile(config_file, self.config) except: print("Could not load %s" % config_file) sys.exit(1) self._validate() self._autocomplete() def _validate(self): for name in self.get_repositories(): desc = self.get_repository(name) if 'path' not in desc: raise ValidationError('Need to specify a \'path\' key in the ' 'repository description for %s' % name) def _autocomplete(self): for name in self.get_repositories(): desc = self.get_repository(name) # expand ~ or ~user in 'path' desc['path'] = os.path.expanduser(desc['path']) # default to the 'origin' remote if 'remote' not in desc: desc['remote'] = 'origin' def get_repositories(self): return self.config['repositories'] def get_repository(self, name): return self.config['repositories'][name] class GitRepository: def __init__(self, description): self.desc = description # I've seen the GitDB backend choke on packs from the kernel so # let's use GitCmdObjectDB self.repo = git.Repo(self.desc['path'], odbt=git.GitCmdObjectDB) def get_remote(self): return self.repo.remote(self.desc['remote']) def get_local_branch(self): # TODO: allow overrides the local branch name from config file return self.repo.head.reference.name def get_local_commit(self): # TODO: allow overrides the local branch name from config file return self.repo.head.commit.hexsha def is_published(self): branch_spec = self.get_remote().name + '/' + self.get_local_branch() sha = self.get_local_commit() Logger.debug('Checking if %s is in %s' % (sha, branch_spec)) # If we haven't found the commit we are looking for in the first 1000, # it's probably not there. We are trying to check if a local tip of a # branch is in the remote branch, it should usually be the first commit # of the remote branch. for commit in self.repo.iter_commits(branch_spec, max_count=1000): if commit.hexsha == sha: return True return False def parse_options(args): usage = "Usage: intel-submit-build [options] [config-file]" parser = optparse.OptionParser(usage, version="0.1") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="Verbose mode") (options, args) = parser.parse_args() if len(args) > 1: Logger.error(usage) sys.exit(1) Logger.set_verbose(options.verbose) return (options, args) (options, args) = parse_options(sys.argv[2:]) if args: config = Config(args[0]) else: config = Config() entry="""%(name)s: remote: %(remote)s branch: %(remote_branch)s commit: %(commit)s""" for name in config.get_repositories(): desc = config.get_repository(name) repo = GitRepository(desc) remote = repo.get_remote() Logger.info("Fetching %s for %s" % (remote.url, name)) remote.fetch() if not repo.is_published(): Logger.error("Could not find commit %s in the remote branch" % repo.get_local_commit()) print entry % { 'name': name, 'remote': remote.url, 'remote_branch': repo.get_local_branch(), 'commit': repo.get_local_commit() }