-#!/bin/bash
-
-usage() {
- cat <<-EOF
- Usage: repo-cros [options]
-
- Options:
- depot_tools clone depot_tools tree
- int switch to internal tree
- ext switch to external tree
- -b <branch> switch branches (use "master" to get to ToT)
- -r <path> patch to reference repo (e.g. ~/chromiumos/)
- -g <group>
- -m <manifest>
- -e <email>
-
- Operates on the repo in ${PWD}
- EOF
- exit ${1:-1}
-}
-
-v() {
- printf '%s\n%s\n' "${PWD}" "$*"
- "$@"
-}
-
-email="vapier@chromium.org"
-REF=
-BRANCH=
-MANIFEST=
-MANIFEST_NAME=
-RGROUPS=()
-REPO_URL=
-while [[ $# -gt 0 ]] ; do
- case $1 in
- depot_tools|dt)
- exec git clone https://git.chromium.org/chromium/tools/depot_tools.git
- ;;
- int)
- MANIFEST='https://chrome-internal.googlesource.com/chromeos/manifest-internal.git'
- REPO_URL='https://chromium.googlesource.com/external/repo.git'
- ;;
- ext)
- MANIFEST='https://chromium.googlesource.com/chromiumos/manifest.git'
- REPO_URL='https://chromium.googlesource.com/external/repo.git'
- ;;
- -b)
- BRANCH=$2
- shift
- ;;
- -r)
- REF=$(realpath "${2:-$(echo ~/chromiumos)}")
- shift
- ;;
- -g)
- RGROUPS+=( "$2" )
- shift
- ;;
- -m)
- MANIFEST_NAME="${2%.xml}.xml"
- shift
- ;;
- -e)
- email=$2
- shift
- ;;
- *)
- usage
- ;;
- esac
- shift
-done
-
-if [[ ${#BRANCH} -eq 3 ]] && [[ -d ${REF} ]] ; then
- BRANCH=$(git --git-dir="${REF}/.repo/manifests.git" branch -a | grep -o "release-${BRANCH}.*")
-fi
-
-v repo init \
- ${MANIFEST:+-u "${MANIFEST}"} \
- ${REPO_URL:+--repo-url="${REPO_URL}"} \
- ${REF:+--reference "${REF}"} \
- ${MANIFEST_NAME:+-m "${MANIFEST_NAME}"} \
- ${RGROUPS:+-g "${RGROUPS[*]}"} \
- ${BRANCH:+-b "${BRANCH}"}
-
-rdir=$(realpath "`pwd`")
-while [[ ! -d ${rdir}/.repo ]] ; do
- rdir=${rdir%/*}
- [[ ${rdir:-/} == "/" ]] && break
-done
-rdir+="/.repo"
-if [[ -d ${rdir} ]] ; then
- gcfg() { git --git-dir="$1" config user.email "${@:2}" ; }
- if [[ $(gcfg "${rdir}/manifests.git") != "${email}" ]] ; then
- echo "${rdir}: setting e-mail to ${email}"
- find "${rdir}" -type d -name '*.git' | \
- while read d ; do
- gcfg "${d}" ${email}
- done
- fi
-fi
-
-exit 0
+#!/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 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 set_git_config(opts):
+ """Set .git/config settings in all the repos."""
+ topdir = os.getcwd()
+ while True:
+ rdir = os.path.join(topdir, '.repo')
+ if os.path.exists(rdir):
+ break
+ topdir = os.path.dirname(topdir)
+ assert topdir != '/'
+
+ 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']
+
+ 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:
+ 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 "master" 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:]))