#!/usr/bin/python3 # -*- coding: utf-8 -*- """Wrapper for syncing CrOS code. Actions: depot_tools clone depot_tools tree int switch to internal tree ext switch to external tree Operates on the repo in the cwd. """ import argparse import fnmatch import os import subprocess import sys REPO_URL = 'https://chromium.googlesource.com/external/repo' INT_MANIFEST = 'https://chrome-internal.googlesource.com/chromeos/manifest-internal' EXT_MANIFEST = 'https://chromium.googlesource.com/chromiumos/manifest' def run(cmd, **kwargs): """Run with command tracing.""" # Python 3.6 doesn't support capture_output. if sys.version_info < (3, 7): capture_output = kwargs.pop('capture_output', None) if capture_output: assert 'stdout' not in kwargs and 'stderr' not in kwargs kwargs['stdout'] = subprocess.PIPE kwargs['stderr'] = subprocess.PIPE print(kwargs.get('cwd', os.getcwd())) print(' '.join(cmd)) return subprocess.run(cmd, **kwargs) def expand_branch(opts): """Support branch shortnames.""" url = INT_MANIFEST if opts.action == 'int' else EXT_MANIFEST ret = run(['git', 'ls-remote', url, 'refs/heads/*'], capture_output=True, encoding='utf-8') branches = [x.split()[-1].split('/')[-1] for x in ret.stdout.splitlines()] ret = [] for branch in branches: if branch.lower() == opts.branch.lower(): ret = [branch] break elif fnmatch.fnmatch(branch.lower(), f'*{opts.branch.lower()}*'): ret.append(branch) if not ret: print(f'error: could not match branch {opts.branch}', file=sys.stderr) print(f'Available branches:\n{" ".join(sorted(branches))}', file=sys.stderr) sys.exit(1) if len(ret) > 1: print(f'error: too many branches match {opts.branch}:\n{" ".join(sorted(ret))}', file=sys.stderr) sys.exit(1) print(f'Expanded branch {opts.branch} into {ret[0]}') return ret[0] def get_repo_topdir(opts): """Find the top dir of this repo client checkout.""" topdir = os.getcwd() while True: rdir = os.path.join(topdir, '.repo') if os.path.exists(rdir): break topdir = os.path.dirname(topdir) assert topdir != '/' return topdir def set_git_config(opts): """Set .git/config settings in all the repos.""" topdir = get_repo_topdir(opts) rdir = os.path.join(topdir, '.repo') def gcfg(path, *args): cmd = ['git', f'--git-dir={path}', 'config', 'user.email'] + list(args) ret = run(cmd, capture_output=True, encoding='utf-8') return ret.stdout.strip() current_email = gcfg(os.path.join(rdir, 'manifests.git')) if current_email == opts.email: return print(f'Setting e-mail to {opts.email}') for root, dirs, files in os.walk(rdir): if root.endswith('.git') and not os.path.islink(os.path.join(root, 'config')): gcfg(root, opts.email) del dirs[:] def init_repo(opts): """Initialize CrOS checkout.""" cmd = ['repo', 'init', '-c'] if opts.action == 'int': cmd += ['-u', INT_MANIFEST] cmd += ['--repo-url', REPO_URL] elif opts.action == 'ext': cmd += ['-u', EXT_MANIFEST] cmd += ['--repo-url', REPO_URL] if opts.ref and os.path.realpath(opts.ref) != os.path.realpath(get_repo_topdir(opts)): cmd += ['--reference', opts.ref] if opts.manifest: cmd += ['-m', opts.manifest] if opts.group: cmd += ['-g', opts.group] if opts.branch: branch = expand_branch(opts) cmd += ['-b', branch] ret = run(cmd) if ret.returncode: return ret.returncode set_git_config(opts) print('\ncros-repo: all done') return 0 def clone_depot_tools(): """Clone depot_tools repo.""" ret = run(['git', 'clone', 'https://chromium.googlesource.com/chromium/tools/depot_tools']) return ret.returncode def get_parser(): """Get command line parser.""" parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('-b', '--branch', help='Switch branches (use "main" to get to ToT)') parser.add_argument('-r', '--ref', '--reference', default='~/chromiumos/', help='Patch to reference repo (default: %(default)s)') parser.add_argument('-g', '--group', help='Manifest group to use (e.g. "minilayout")') parser.add_argument('-m', '--manifest', help='Manifest file name to use (e.g. "full.xml")') parser.add_argument('-e', '--email', default='vapier@chromium.org', help='E-mail address to force (default: %(default)s)') parser.add_argument('action', nargs='?', choices={'depot_tools', 'dt', 'int', 'ext'}, help='What to do!') return parser def main(argv): """The main func.""" parser = get_parser() opts = parser.parse_args(argv) if opts.manifest: if '/' in opts.manifest: parser.error('manifest should be basename like "full.xml"') if opts.manifest.endswith('.xml'): opts.manifest = opts.manifest[:-4] opts.manifest += '.xml' if opts.ref: opts.ref = os.path.expanduser(opts.ref) if opts.action in {'depot_tools', 'dt'}: return clone_depot_tools() else: return init_repo(opts) if __name__ == '__main__': sys.exit(main(sys.argv[1:]))