]> git.wh0rd.org - home.git/commitdiff
cros-repo: rewrite in python
authorMike Frysinger <vapier@gentoo.org>
Sat, 28 Sep 2019 07:22:16 +0000 (03:22 -0400)
committerMike Frysinger <vapier@gentoo.org>
Sat, 28 Sep 2019 07:22:16 +0000 (03:22 -0400)
.bin/cros-repo

index 8f8765ba23817979678c1945a27ed06f80d52664..474b1aa38e73696590e8d71d0391e8294d513b19 100755 (executable)
-#!/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:]))