]> git.wh0rd.org - home.git/blame - .bin/cros-repo
cros-repo: rewrite in python
[home.git] / .bin / cros-repo
CommitLineData
6c6826b7
MF
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
4"""Wrapper for syncing CrOS code.
5
6Actions:
7 depot_tools clone depot_tools tree
8 int switch to internal tree
9 ext switch to external tree
10
11Operates on the repo in the cwd.
12"""
13
14import argparse
15import fnmatch
16import os
17import subprocess
18import sys
19
20
21REPO_URL = 'https://chromium.googlesource.com/external/repo'
22INT_MANIFEST = 'https://chrome-internal.googlesource.com/chromeos/manifest-internal'
23EXT_MANIFEST = 'https://chromium.googlesource.com/chromiumos/manifest'
24
25
26def run(cmd, **kwargs):
27 """Run with command tracing."""
28 # Python 3.6 doesn't support capture_output.
29 if sys.version_info < (3, 7):
30 capture_output = kwargs.pop('capture_output', None)
31 if capture_output:
32 assert 'stdout' not in kwargs and 'stderr' not in kwargs
33 kwargs['stdout'] = subprocess.PIPE
34 kwargs['stderr'] = subprocess.PIPE
35
36 print(kwargs.get('cwd', os.getcwd()))
37 print(' '.join(cmd))
38 return subprocess.run(cmd, **kwargs)
39
40
41def expand_branch(opts):
42 """Support branch shortnames."""
43 url = INT_MANIFEST if opts.action == 'int' else EXT_MANIFEST
44 ret = run(['git', 'ls-remote', url, 'refs/heads/*'], capture_output=True, encoding='utf-8')
45 branches = [x.split()[-1].split('/')[-1] for x in ret.stdout.splitlines()]
46 ret = []
47 for branch in branches:
48 if fnmatch.fnmatch(branch.lower(), f'*{opts.branch.lower()}*'):
49 ret.append(branch)
50 if not ret:
51 print(f'error: could not match branch {opts.branch}', file=sys.stderr)
52 print(f'Available branches:\n{" ".join(sorted(branches))}', file=sys.stderr)
53 sys.exit(1)
54 if len(ret) > 1:
55 print(f'error: too many branches match {opts.branch}:\n{" ".join(sorted(ret))}',
56 file=sys.stderr)
57 sys.exit(1)
58 print(f'Expanded branch {opts.branch} into {ret[0]}')
59 return ret[0]
60
61
62def set_git_config(opts):
63 """Set .git/config settings in all the repos."""
64 topdir = os.getcwd()
65 while True:
66 rdir = os.path.join(topdir, '.repo')
67 if os.path.exists(rdir):
68 break
69 topdir = os.path.dirname(topdir)
70 assert topdir != '/'
71
72 def gcfg(path, *args):
73 cmd = ['git', f'--git-dir={path}', 'config', 'user.email'] + list(args)
74 ret = run(cmd, capture_output=True, encoding='utf-8')
75 return ret.stdout.strip()
76
77 current_email = gcfg(os.path.join(rdir, 'manifests.git'))
78 if current_email == opts.email:
79 return
80
81 print(f'Setting e-mail to {opts.email}')
82 for root, dirs, files in os.walk(rdir):
83 if root.endswith('.git') and not os.path.islink(os.path.join(root, 'config')):
84 gcfg(root, opts.email)
85 del dirs[:]
86
87
88def init_repo(opts):
89 """Initialize CrOS checkout."""
90 cmd = ['repo', 'init']
91
92 if opts.action == 'int':
93 cmd += ['-u', INT_MANIFEST]
94 cmd += ['--repo-url', REPO_URL]
95 elif opts.action == 'ext':
96 cmd += ['-u', EXT_MANIFEST]
97 cmd += ['--repo-url', REPO_URL]
98
99 if opts.ref:
100 cmd += ['--reference', opts.ref]
101
102 if opts.manifest:
103 cmd += ['-m', opts.manifest]
104
105 if opts.group:
106 cmd += ['-g', opts.group]
107
108 if opts.branch:
109 branch = expand_branch(opts)
110 cmd += ['-b', branch]
111
112 ret = run(cmd)
113 if ret.returncode:
114 return ret.returncode
115
116 set_git_config(opts)
117
118 print('\ncros-repo: all done')
119 return 0
120
121
122def clone_depot_tools():
123 """Clone depot_tools repo."""
124 ret = run(['git', 'clone', 'https://chromium.googlesource.com/chromium/tools/depot_tools'])
125 return ret.returncode
126
127
128def get_parser():
129 """Get command line parser."""
130 parser = argparse.ArgumentParser(
131 description=__doc__,
132 formatter_class=argparse.RawDescriptionHelpFormatter)
133 parser.add_argument('-b', '--branch',
134 help='Switch branches (use "master" to get to ToT)')
135 parser.add_argument('-r', '--ref', '--reference', default='~/chromiumos/',
136 help='Patch to reference repo (default: %(default)s)')
137 parser.add_argument('-g', '--group',
138 help='Manifest group to use (e.g. "minilayout")')
139 parser.add_argument('-m', '--manifest',
140 help='Manifest file name to use (e.g. "full.xml")')
141 parser.add_argument('-e', '--email', default='vapier@chromium.org',
142 help='E-mail address to force (default: %(default)s)')
143 parser.add_argument('action', nargs='?',
144 choices={'depot_tools', 'dt', 'int', 'ext'},
145 help='What to do!')
146 return parser
147
148
149def main(argv):
150 """The main func."""
151 parser = get_parser()
152 opts = parser.parse_args(argv)
153 if opts.manifest:
154 if '/' in opts.manifest:
155 parser.error('manifest should be basename like "full.xml"')
156 if opts.manifest.endswith('.xml'):
157 opts.manifest = opts.manifest[:-4]
158 opts.manifest += '.xml'
159 if opts.ref:
160 opts.ref = os.path.expanduser(opts.ref)
161
162 if opts.action in {'depot_tools', 'dt'}:
163 return clone_depot_tools()
164 else:
165 return init_repo(opts)
166
167
168if __name__ == '__main__':
169 sys.exit(main(sys.argv[1:]))