#!/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' REPO_URL = 'https://gerrit.googlesource.com/git-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 topdir != '/': rdir = os.path.join(topdir, '.repo') if os.path.exists(rdir): break topdir = os.path.dirname(topdir) return topdir def set_git_config(opts): """Set .git/config settings in all the repos.""" topdir = get_repo_topdir(opts) assert topdir != '/' 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] if opts.worktree: cmd += ['--worktree'] 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('--worktree', action='store_true', help='Enable worktree mode') 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:]))