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