]> git.wh0rd.org - home.git/blob - .bin/cros-repo
cros-board: update
[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 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
62 def 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
88 def 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
122 def 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
128 def 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
149 def 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
168 if __name__ == '__main__':
169 sys.exit(main(sys.argv[1:]))