blob: 6670c0d3ea7bd07f250d4b93c1e23fe9d80d94a5 [file] [log] [blame]
David Pursehouse8898e2f2012-11-14 07:51:03 +09001#!/usr/bin/env python
Mike Frysingerf6013762019-06-13 02:30:51 -04002# -*- coding:utf-8 -*-
Mike Frysingerf241f8c2020-02-20 17:08:43 -05003#
4# Copyright (C) 2008 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017
Mike Frysinger87fb5a12019-06-13 01:54:46 -040018"""Repo launcher.
19
20This is a standalone tool that people may copy to anywhere in their system.
21It is used to get an initial repo client checkout, and after that it runs the
22copy of repo in the checkout.
23"""
24
Mike Frysingerc92ce5c2019-06-13 01:14:23 -040025from __future__ import print_function
26
Mike Frysinger84094102020-02-11 02:10:28 -050027import datetime
Mike Frysinger3ba716f2019-06-13 01:48:12 -040028import os
29import platform
Mike Frysinger949bc342020-02-18 21:37:00 -050030import shlex
Mike Frysinger3ba716f2019-06-13 01:48:12 -040031import subprocess
32import sys
33
34
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050035# Keep basic logic in sync with repo_trace.py.
36class Trace(object):
37 """Trace helper logic."""
38
39 REPO_TRACE = 'REPO_TRACE'
40
41 def __init__(self):
42 self.set(os.environ.get(self.REPO_TRACE) == '1')
43
44 def set(self, value):
45 self.enabled = bool(value)
46
47 def print(self, *args, **kwargs):
48 if self.enabled:
49 print(*args, **kwargs)
50
51
52trace = Trace()
53
54
Mike Frysinger3ba716f2019-06-13 01:48:12 -040055def exec_command(cmd):
56 """Execute |cmd| or return None on failure."""
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050057 trace.print(':', ' '.join(cmd))
Mike Frysinger3ba716f2019-06-13 01:48:12 -040058 try:
59 if platform.system() == 'Windows':
60 ret = subprocess.call(cmd)
61 sys.exit(ret)
62 else:
63 os.execvp(cmd[0], cmd)
Mike Frysinger72b6dc82020-02-12 17:04:32 -050064 except Exception:
Mike Frysinger3ba716f2019-06-13 01:48:12 -040065 pass
66
67
68def check_python_version():
69 """Make sure the active Python version is recent enough."""
70 def reexec(prog):
71 exec_command([prog] + sys.argv)
72
73 MIN_PYTHON_VERSION = (3, 6)
74
75 ver = sys.version_info
76 major = ver.major
77 minor = ver.minor
78
79 # Abort on very old Python 2 versions.
80 if (major, minor) < (2, 7):
81 print('repo: error: Your Python version is too old. '
82 'Please use Python {}.{} or newer instead.'.format(
83 *MIN_PYTHON_VERSION), file=sys.stderr)
84 sys.exit(1)
85
86 # Try to re-exec the version specific Python 3 if needed.
87 if (major, minor) < MIN_PYTHON_VERSION:
88 # Python makes releases ~once a year, so try our min version +10 to help
89 # bridge the gap. This is the fallback anyways so perf isn't critical.
90 min_major, min_minor = MIN_PYTHON_VERSION
91 for inc in range(0, 10):
92 reexec('python{}.{}'.format(min_major, min_minor + inc))
93
94 # Try the generic Python 3 wrapper, but only if it's new enough. We don't
95 # want to go from (still supported) Python 2.7 to (unsupported) Python 3.5.
96 try:
97 proc = subprocess.Popen(
98 ['python3', '-c', 'import sys; '
99 'print(sys.version_info.major, sys.version_info.minor)'],
100 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
101 (output, _) = proc.communicate()
102 python3_ver = tuple(int(x) for x in output.decode('utf-8').split())
103 except (OSError, subprocess.CalledProcessError):
104 python3_ver = None
105
106 # The python3 version looks like it's new enough, so give it a try.
107 if python3_ver and python3_ver >= MIN_PYTHON_VERSION:
108 reexec('python3')
109
110 # We're still here, so diagnose things for the user.
111 if major < 3:
112 print('repo: warning: Python 2 is no longer supported; '
113 'Please upgrade to Python {}.{}+.'.format(*MIN_PYTHON_VERSION),
114 file=sys.stderr)
115 else:
116 print('repo: error: Python 3 version is too old; '
117 'Please use Python {}.{} or newer.'.format(*MIN_PYTHON_VERSION),
118 file=sys.stderr)
119 sys.exit(1)
120
121
122if __name__ == '__main__':
Mike Frysinger19ec7972020-02-16 12:02:01 -0500123 check_python_version()
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400124
125
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700126# repo default configuration
127#
Mark E. Hamilton55536282016-02-03 15:49:43 -0700128REPO_URL = os.environ.get('REPO_URL', None)
129if not REPO_URL:
130 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -0500131REPO_REV = os.environ.get('REPO_REV')
132if not REPO_REV:
133 REPO_REV = 'stable'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135# increment this whenever we make important changes to this script
Mike Frysinger19ec7972020-02-16 12:02:01 -0500136VERSION = (2, 4)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137
138# increment this if the MAINTAINER_KEYS block is modified
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500139KEYRING_VERSION = (2, 3)
Mike Frysingere4433652016-09-13 18:06:07 -0400140
141# Each individual key entry is created by using:
142# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700143MAINTAINER_KEYS = """
144
145 Repo Maintainer <repo@android.kernel.org>
146-----BEGIN PGP PUBLIC KEY BLOCK-----
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700147
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
Mike Frysinger9cc1d702020-02-13 18:28:03 -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
213cZ7aFsJF4PtcDrfdejyAxbtsSHI=
214=82Tj
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215-----END PGP PUBLIC KEY BLOCK-----
216"""
217
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700218GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500219# NB: The version of git that the repo launcher requires may be much older than
220# the version of git that the main repo source tree requires. Keeping this at
221# an older version also makes it easier for users to upgrade/rollback as needed.
222#
223# git-1.7 is in (EOL) Ubuntu Precise.
224MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700225repodir = '.repo' # name of repo's private directory
226S_repo = 'repo' # special repo repository
227S_manifests = 'manifests' # special manifest repository
228REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700229GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700230GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700231
232
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400233import collections
David Jamesbf79c662013-12-26 14:20:13 -0800234import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700237import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700238import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900239
240if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700241 import urllib.request
242 import urllib.error
243else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700244 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900245 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700246 urllib = imp.new_module('urllib')
247 urllib.request = urllib2
248 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700249
Conley Owens5e0ee142013-09-26 15:50:49 -0700250
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251home_dot_repo = os.path.expanduser('~/.repoconfig')
252gpg_dir = os.path.join(home_dot_repo, 'gnupg')
253
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900254
Mike Frysingerd8fda902020-02-14 00:24:38 -0500255def GetParser(gitc_init=False):
256 """Setup the CLI parser."""
257 if gitc_init:
258 usage = 'repo gitc-init -u url -c client [options]'
259 else:
260 usage = 'repo init -u url [options]'
261
262 parser = optparse.OptionParser(usage=usage)
263
Mike Frysingerf700ac72020-02-06 00:04:21 -0500264 # Logging.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500265 group = parser.add_option_group('Logging options')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500266 group.add_option('-v', '--verbose',
267 dest='output_mode', action='store_true',
268 help='show all output')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500269 group.add_option('-q', '--quiet',
Mike Frysingeredd3d452020-02-21 23:55:07 -0500270 dest='output_mode', action='store_false',
271 help='only show errors')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Mike Frysingerf700ac72020-02-06 00:04:21 -0500273 # Manifest.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500274 group = parser.add_option_group('Manifest options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500275 group.add_option('-u', '--manifest-url',
276 help='manifest repository location', metavar='URL')
277 group.add_option('-b', '--manifest-branch',
278 help='manifest branch or revision', metavar='REVISION')
279 group.add_option('-m', '--manifest-name',
280 help='initial manifest file', metavar='NAME.xml')
Mike Frysingerd8fda902020-02-14 00:24:38 -0500281 cbr_opts = ['--current-branch']
282 # The gitc-init subcommand allocates -c itself, but a lot of init users
283 # want -c, so try to satisfy both as best we can.
284 if not gitc_init:
285 cbr_opts += ['-c']
286 group.add_option(*cbr_opts,
Mike Frysingerf700ac72020-02-06 00:04:21 -0500287 dest='current_branch_only', action='store_true',
288 help='fetch only current manifest branch from server')
289 group.add_option('--mirror', action='store_true',
290 help='create a replica of the remote repositories '
291 'rather than a client working directory')
292 group.add_option('--reference',
293 help='location of mirror directory', metavar='DIR')
294 group.add_option('--dissociate', action='store_true',
295 help='dissociate from reference mirrors after clone')
296 group.add_option('--depth', type='int', default=None,
297 help='create a shallow clone with given depth; '
298 'see git clone')
299 group.add_option('--partial-clone', action='store_true',
300 help='perform partial clone (https://git-scm.com/'
301 'docs/gitrepository-layout#_code_partialclone_code)')
302 group.add_option('--clone-filter', action='store', default='blob:none',
303 help='filter for use with --partial-clone '
304 '[default: %default]')
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500305 group.add_option('--worktree', action='store_true',
306 help=optparse.SUPPRESS_HELP)
Mike Frysingerf700ac72020-02-06 00:04:21 -0500307 group.add_option('--archive', action='store_true',
308 help='checkout an archive instead of a git repository for '
309 'each project. See git archive.')
310 group.add_option('--submodules', action='store_true',
311 help='sync any submodules associated with the manifest repo')
312 group.add_option('-g', '--groups', default='default',
313 help='restrict manifest projects to ones with specified '
314 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
315 metavar='GROUP')
316 group.add_option('-p', '--platform', default='auto',
317 help='restrict manifest projects to ones with a specified '
318 'platform group [auto|all|none|linux|darwin|...]',
319 metavar='PLATFORM')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500320 group.add_option('--no-clone-bundle',
321 dest='clone_bundle', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500322 help='disable use of /clone.bundle on HTTP/HTTPS')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500323 group.add_option('--no-tags',
324 dest='tags', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500325 help="don't fetch tags in the manifest")
Doug Anderson49cd59b2011-06-13 21:42:06 -0700326
Mike Frysingerf700ac72020-02-06 00:04:21 -0500327 # Tool.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500328 group = parser.add_option_group('repo Version options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500329 group.add_option('--repo-url', metavar='URL',
330 help='repo repository location ($REPO_URL)')
Mike Frysinger58ac1672020-03-14 14:35:26 -0400331 group.add_option('--repo-rev', metavar='REV',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500332 help='repo branch or revision ($REPO_REV)')
Mike Frysinger58ac1672020-03-14 14:35:26 -0400333 group.add_option('--repo-branch', dest='repo_rev',
334 help=optparse.SUPPRESS_HELP)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500335 group.add_option('--no-repo-verify',
336 dest='repo_verify', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500337 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700338
Mike Frysingerf700ac72020-02-06 00:04:21 -0500339 # Other.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500340 group = parser.add_option_group('Other options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500341 group.add_option('--config-name',
342 action='store_true', default=False,
343 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700344
Mike Frysingerd8fda902020-02-14 00:24:38 -0500345 # gitc-init specific settings.
346 if gitc_init:
347 group = parser.add_option_group('GITC options')
348 group.add_option('-f', '--manifest-file',
349 help='Optional manifest file to use for this GITC client.')
350 group.add_option('-c', '--gitc-client',
351 help='Name of the gitc_client instance to create or modify.')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700352
Mike Frysingerd8fda902020-02-14 00:24:38 -0500353 return parser
Simran Basi1efc2b42015-08-05 15:04:22 -0700354
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900355
Mike Frysinger62285d22020-02-12 08:01:38 -0500356# This is a poor replacement for subprocess.run until we require Python 3.6+.
357RunResult = collections.namedtuple(
358 'RunResult', ('returncode', 'stdout', 'stderr'))
359
360
361class RunError(Exception):
362 """Error when running a command failed."""
363
364
365def run_command(cmd, **kwargs):
366 """Run |cmd| and return its output."""
367 check = kwargs.pop('check', False)
368 if kwargs.pop('capture_output', False):
369 kwargs.setdefault('stdout', subprocess.PIPE)
370 kwargs.setdefault('stderr', subprocess.PIPE)
371 cmd_input = kwargs.pop('input', None)
372
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500373 def decode(output):
374 """Decode |output| to text."""
375 if output is None:
376 return output
377 try:
378 return output.decode('utf-8')
379 except UnicodeError:
380 print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
381 file=sys.stderr)
382 # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
383 return output.decode('utf-8', 'replace')
384
Mike Frysinger62285d22020-02-12 08:01:38 -0500385 # Run & package the results.
386 proc = subprocess.Popen(cmd, **kwargs)
387 (stdout, stderr) = proc.communicate(input=cmd_input)
Mike Frysinger71928c12020-02-21 23:45:08 -0500388 dbg = ': ' + ' '.join(cmd)
389 if cmd_input is not None:
390 dbg += ' 0<|'
391 if stdout == subprocess.PIPE:
392 dbg += ' 1>|'
393 if stderr == subprocess.PIPE:
394 dbg += ' 2>|'
395 elif stderr == subprocess.STDOUT:
396 dbg += ' 2>&1'
397 trace.print(dbg)
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500398 ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
Mike Frysinger62285d22020-02-12 08:01:38 -0500399
400 # If things failed, print useful debugging output.
401 if check and ret.returncode:
402 print('repo: error: "%s" failed with exit status %s' %
403 (cmd[0], ret.returncode), file=sys.stderr)
404 print(' cwd: %s\n cmd: %r' %
405 (kwargs.get('cwd', os.getcwd()), cmd), file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900406
Mike Frysinger62285d22020-02-12 08:01:38 -0500407 def _print_output(name, output):
408 if output:
409 print(' %s:\n >> %s' % (name, '\n >> '.join(output.splitlines())),
410 file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900411
Mike Frysinger62285d22020-02-12 08:01:38 -0500412 _print_output('stdout', ret.stdout)
413 _print_output('stderr', ret.stderr)
414 raise RunError(ret)
415
416 return ret
417
418
Simran Basi8ce50412015-08-28 14:25:44 -0700419_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700420
421
Simran Basi8ce50412015-08-28 14:25:44 -0700422def get_gitc_manifest_dir():
423 global _gitc_manifest_dir
424 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700425 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700426 try:
427 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
428 for line in gitc_config:
429 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
430 if match:
431 _gitc_manifest_dir = match.group('gitc_manifest_dir')
432 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700433 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700434 return _gitc_manifest_dir
435
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700436
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700437def gitc_parse_clientdir(gitc_fs_path):
438 """Parse a path in the GITC FS and return its client name.
439
440 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
441
442 @returns: The GITC client name
443 """
444 if gitc_fs_path == GITC_FS_ROOT_DIR:
445 return None
446 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
447 manifest_dir = get_gitc_manifest_dir()
448 if manifest_dir == '':
449 return None
450 if manifest_dir[-1] != '/':
451 manifest_dir += '/'
452 if gitc_fs_path == manifest_dir:
453 return None
454 if not gitc_fs_path.startswith(manifest_dir):
455 return None
456 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
457 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
458
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700459
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700460class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700461
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700462 """Indicate the remote clone of repo itself failed.
463 """
464
465
Simran Basi1efc2b42015-08-05 15:04:22 -0700466def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700467 """Installs repo by cloning it over the network.
468 """
Mike Frysingerd8fda902020-02-14 00:24:38 -0500469 parser = GetParser(gitc_init=gitc_init)
470 opt, args = parser.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800471 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500472 parser.print_usage()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700473 sys.exit(1)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500474 opt.quiet = opt.output_mode is False
475 opt.verbose = opt.output_mode is True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700476
Mike Frysingere1111f52020-03-14 16:28:31 -0400477 url = opt.repo_url or REPO_URL
Mike Frysinger58ac1672020-03-14 14:35:26 -0400478 branch = opt.repo_rev or REPO_REV
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700479
480 if branch.startswith('refs/heads/'):
481 branch = branch[len('refs/heads/'):]
482 if branch.startswith('refs/'):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400483 print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700484 raise CloneFailure()
485
David Jamesbf79c662013-12-26 14:20:13 -0800486 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700487 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700488 gitc_manifest_dir = get_gitc_manifest_dir()
489 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400490 print('fatal: GITC filesystem is not available. Exiting...',
491 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700492 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700493 gitc_client = opt.gitc_client
494 if not gitc_client:
495 gitc_client = gitc_parse_clientdir(os.getcwd())
496 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400497 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700498 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700499 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700500 if not os.path.exists(client_dir):
501 os.makedirs(client_dir)
502 os.chdir(client_dir)
503 if os.path.exists(repodir):
504 # This GITC Client has already initialized repo so continue.
505 return
506
David Jamesbf79c662013-12-26 14:20:13 -0800507 os.mkdir(repodir)
508 except OSError as e:
509 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400510 print('fatal: cannot make %s directory: %s'
511 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900512 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700513 # name. Instead exit immediately.
514 #
515 sys.exit(1)
516
517 _CheckGitVersion()
518 try:
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500519 if not opt.repo_verify:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100520 do_verify = False
Mike Frysinger910dfe82020-02-29 02:55:19 -0500521 print('repo: warning: verification of repo code has been disabled;\n'
522 'repo will not be able to verify the integrity of itself.\n',
523 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700524 else:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100525 if NeedSetupGnuPG():
526 do_verify = SetupGnuPG(opt.quiet)
527 else:
528 do_verify = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700529
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500530 if not opt.quiet:
531 print('Downloading Repo source from', url)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700532 dst = os.path.abspath(os.path.join(repodir, S_repo))
Mike Frysingeredd3d452020-02-21 23:55:07 -0500533 _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700534
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100535 if do_verify:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700536 rev = _Verify(dst, branch, opt.quiet)
537 else:
538 rev = 'refs/remotes/origin/%s^0' % branch
539
540 _Checkout(dst, branch, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200541
542 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400543 print("warning: '%s' does not look like a git-repo repository, is "
544 "REPO_URL set correctly?" % url, file=sys.stderr)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200545
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700546 except CloneFailure:
547 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400548 print('fatal: repo init failed; run without --quiet to see why',
549 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700550 raise
551
552
Mike Frysinger62285d22020-02-12 08:01:38 -0500553def run_git(*args, **kwargs):
554 """Run git and return execution details."""
555 kwargs.setdefault('capture_output', True)
556 kwargs.setdefault('check', True)
557 try:
558 return run_command([GIT] + list(args), **kwargs)
559 except OSError as e:
560 print(file=sys.stderr)
561 print('repo: error: "%s" is not available' % GIT, file=sys.stderr)
562 print('repo: error: %s' % e, file=sys.stderr)
563 print(file=sys.stderr)
564 print('Please make sure %s is installed and in your path.' % GIT,
565 file=sys.stderr)
566 sys.exit(1)
567 except RunError:
568 raise CloneFailure()
569
570
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400571# The git version info broken down into components for easy analysis.
572# Similar to Python's sys.version_info.
573GitVersion = collections.namedtuple(
574 'GitVersion', ('major', 'minor', 'micro', 'full'))
575
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900576
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400577def ParseGitVersion(ver_str=None):
578 if ver_str is None:
579 # Load the version ourselves.
Mike Frysinger62285d22020-02-12 08:01:38 -0500580 ver_str = run_git('--version').stdout
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400581
Conley Owensff0a3c82014-01-30 14:46:03 -0800582 if not ver_str.startswith('git version '):
583 return None
584
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400585 full_version = ver_str[len('git version '):].strip()
586 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800587 to_tuple = []
588 for num_str in num_ver_str.split('.')[:3]:
589 if num_str.isdigit():
590 to_tuple.append(int(num_str))
591 else:
592 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400593 to_tuple.append(full_version)
594 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800595
596
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400597def _CheckGitVersion():
Mike Frysinger62285d22020-02-12 08:01:38 -0500598 ver_act = ParseGitVersion()
Conley Owensff0a3c82014-01-30 14:46:03 -0800599 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400600 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700601 raise CloneFailure()
602
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700603 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900604 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400605 print('fatal: git %s or later required' % need, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700606 raise CloneFailure()
607
608
Mike Frysinger84094102020-02-11 02:10:28 -0500609def SetGitTrace2ParentSid(env=None):
610 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
611 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
612 # (1) Be unique (2) be valid filename (3) be fixed length.
613 #
614 # Since we always export this variable, we try to avoid more expensive calls.
615 # e.g. We don't attempt hostname lookups or hashing the results.
616 if env is None:
617 env = os.environ
618
619 KEY = 'GIT_TRACE2_PARENT_SID'
620
621 now = datetime.datetime.utcnow()
622 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
623
624 # If it's already set, then append ourselves.
625 if KEY in env:
626 value = env[KEY] + '/' + value
627
628 _setenv(KEY, value, env=env)
629
630
631def _setenv(key, value, env=None):
632 """Set |key| in the OS environment |env| to |value|."""
633 if env is None:
634 env = os.environ
635 # Environment handling across systems is messy.
636 try:
637 env[key] = value
638 except UnicodeEncodeError:
639 env[key] = value.encode()
640
641
Conley Owensc9129d92012-10-01 16:12:28 -0700642def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700643 if not os.path.isdir(home_dot_repo):
644 return True
645
646 kv = os.path.join(home_dot_repo, 'keyring-version')
647 if not os.path.exists(kv):
648 return True
649
650 kv = open(kv).read()
651 if not kv:
652 return True
653
David Pursehouse685f0802012-11-14 08:34:39 +0900654 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700655 if kv < KEYRING_VERSION:
656 return True
657 return False
658
659
Conley Owensc9129d92012-10-01 16:12:28 -0700660def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800661 try:
662 os.mkdir(home_dot_repo)
663 except OSError as e:
664 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400665 print('fatal: cannot make %s directory: %s'
666 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700667 sys.exit(1)
668
David Jamesbf79c662013-12-26 14:20:13 -0800669 try:
670 os.mkdir(gpg_dir, stat.S_IRWXU)
671 except OSError as e:
672 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400673 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
674 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700675 sys.exit(1)
676
Mike Frysinger19a1f222020-02-14 16:28:13 -0500677 if not quiet:
678 print('repo: Updating release signing keys to keyset ver %s' %
679 ('.'.join(str(x) for x in KEYRING_VERSION),))
680 # NB: We use --homedir (and cwd below) because some environments (Windows) do
681 # not correctly handle full native paths. We avoid the issue by changing to
682 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
683 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
684 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700685 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500686 # gpg can be pretty chatty. Always capture the output and if something goes
687 # wrong, the builtin check failure will dump stdout & stderr for debugging.
688 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
689 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900690 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900691 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700692 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400693 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
694 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
695 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700696 return False
697
Mike Frysinger3164d402019-11-11 05:40:22 -0500698 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
699 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700 return True
701
702
Mike Frysinger62285d22020-02-12 08:01:38 -0500703def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700704 """Set a git configuration option to the specified value.
705 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500706 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700707
708
Mike Frysinger949bc342020-02-18 21:37:00 -0500709def _GetRepoConfig(name):
710 """Read a repo configuration option."""
711 config = os.path.join(home_dot_repo, 'config')
712 if not os.path.exists(config):
713 return None
714
715 cmd = ['config', '--file', config, '--get', name]
716 ret = run_git(*cmd, check=False)
717 if ret.returncode == 0:
718 return ret.stdout
719 elif ret.returncode == 1:
720 return None
721 else:
722 print('repo: error: git %s failed:\n%s' % (' '.join(cmd), ret.stderr),
723 file=sys.stderr)
724 raise RunError()
725
726
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700727def _InitHttp():
728 handlers = []
729
Sarah Owens1f7627f2012-10-31 09:21:55 -0700730 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700731 try:
732 import netrc
733 n = netrc.netrc()
734 for host in n.hosts:
735 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700736 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800737 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900738 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700739 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700740 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
741 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700742
743 if 'http_proxy' in os.environ:
744 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700745 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700746 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700747 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
748 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
749 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700750
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700751
Mike Frysingeredd3d452020-02-21 23:55:07 -0500752def _Fetch(url, cwd, src, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500753 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -0500754 if not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -0500756 err = None
757 if not quiet and sys.stdout.isatty():
758 cmd.append('--progress')
759 elif not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700760 err = subprocess.PIPE
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700761 cmd.append(src)
762 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800763 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger4847e052020-02-22 00:07:35 -0500764 run_git(*cmd, stderr=err, capture_output=False, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700766
Mike Frysingeredd3d452020-02-21 23:55:07 -0500767def _DownloadBundle(url, cwd, quiet, verbose):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700768 if not url.endswith('/'):
769 url += '/'
770 url += 'clone.bundle'
771
Mike Frysinger62285d22020-02-12 08:01:38 -0500772 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
773 check=False)
774 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700775 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
776 if m:
777 new_url = m.group(1)
778 old_url = m.group(2)
779 if url.startswith(old_url):
780 url = new_url + url[len(old_url):]
781 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700782
783 if not url.startswith('http:') and not url.startswith('https:'):
784 return False
785
Mike Frysinger62285d22020-02-12 08:01:38 -0500786 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700787 try:
788 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700789 r = urllib.request.urlopen(url)
790 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200791 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700792 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400793 print('fatal: Cannot get %s' % url, file=sys.stderr)
794 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700795 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700796 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400797 print('fatal: Cannot get %s' % url, file=sys.stderr)
798 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700799 raise CloneFailure()
800 try:
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500801 if verbose:
802 print('Downloading clone bundle %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700803 while True:
804 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400805 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700806 return True
807 dest.write(buf)
808 finally:
809 r.close()
810 finally:
811 dest.close()
812
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700813
Mike Frysinger62285d22020-02-12 08:01:38 -0500814def _ImportBundle(cwd):
815 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700816 try:
Mike Frysingeredd3d452020-02-21 23:55:07 -0500817 _Fetch(cwd, cwd, path, True, False)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700818 finally:
819 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700821
Mike Frysingeredd3d452020-02-21 23:55:07 -0500822def _Clone(url, cwd, clone_bundle, quiet, verbose):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700823 """Clones a git repository to a new subdirectory of repodir
824 """
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500825 if verbose:
826 print('Cloning git repository', url)
827
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700828 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500829 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700830 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500831 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400832 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700833 raise CloneFailure()
834
Mike Frysinger62285d22020-02-12 08:01:38 -0500835 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700837 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500838 _SetConfig(cwd, 'remote.origin.url', url)
839 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700840 'remote.origin.fetch',
841 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500842 if clone_bundle and _DownloadBundle(url, cwd, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500843 _ImportBundle(cwd)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500844 _Fetch(url, cwd, 'origin', quiet, verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700845
846
847def _Verify(cwd, branch, quiet):
848 """Verify the branch has been signed by a tag.
849 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500850 try:
851 ret = run_git('describe', 'origin/%s' % branch, cwd=cwd)
852 cur = ret.stdout.strip()
853 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400854 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
Mike Frysinger62285d22020-02-12 08:01:38 -0500855 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700856
857 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
858 if m:
859 cur = m.group(1)
860 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400861 print(file=sys.stderr)
862 print("info: Ignoring branch '%s'; using tagged release '%s'"
863 % (branch, cur), file=sys.stderr)
864 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700865
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800866 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500867 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -0500868 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700869 return '%s^0' % cur
870
871
872def _Checkout(cwd, branch, rev, quiet):
873 """Checkout an upstream branch into the repository and track it.
874 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500875 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700876
877 _SetConfig(cwd, 'branch.default.remote', 'origin')
878 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
879
Mike Frysinger62285d22020-02-12 08:01:38 -0500880 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700881
Mike Frysinger62285d22020-02-12 08:01:38 -0500882 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883 if not quiet:
884 cmd.append('-v')
885 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -0500886 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700887
888
889def _FindRepo():
890 """Look for a repo installation, starting at the current directory.
891 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200892 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700893 repo = None
894
Anthony Newnamdf14a702011-01-09 17:31:57 -0800895 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200896 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700897 and curdir != olddir \
898 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200899 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700900 if not os.path.isfile(repo):
901 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200902 olddir = curdir
903 curdir = os.path.dirname(curdir)
904 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700905
906
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700907class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700908 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500909 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700910
911
Mike Frysinger949bc342020-02-18 21:37:00 -0500912def _ExpandAlias(name):
913 """Look up user registered aliases."""
914 # We don't resolve aliases for existing subcommands. This matches git.
915 if name in {'gitc-init', 'help', 'init'}:
916 return name, []
917
918 alias = _GetRepoConfig('alias.%s' % (name,))
919 if alias is None:
920 return name, []
921
922 args = alias.strip().split(' ', 1)
923 name = args[0]
924 if len(args) == 2:
925 args = shlex.split(args[1])
926 else:
927 args = []
928 return name, args
929
930
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700931def _ParseArguments(args):
932 cmd = None
933 opt = _Options()
934 arg = []
935
Sarah Owensa6053d52012-11-01 13:36:50 -0700936 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700937 a = args[i]
938 if a == '-h' or a == '--help':
939 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500940 elif a == '--version':
941 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500942 elif a == '--trace':
943 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944 elif not a.startswith('-'):
945 cmd = a
946 arg = args[i + 1:]
947 break
948 return cmd, opt, arg
949
950
951def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700952 gitc_usage = ""
953 if get_gitc_manifest_dir():
954 gitc_usage = " gitc-init Initialize a GITC Client.\n"
955
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400956 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700957 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700958
959repo is not yet installed. Use "repo init" to install it here.
960
961The most commonly used repo commands are:
962
963 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700964""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700965 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700966
967For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400968""")
969 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700970
971
972def _Help(args):
973 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500974 if args[0] in {'init', 'gitc-init'}:
975 parser = GetParser(gitc_init=args[0] == 'gitc-init')
976 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700977 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700978 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400979 print("error: '%s' is not a bootstrap command.\n"
980 ' For access to online help, install repo ("repo init").'
981 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982 else:
983 _Usage()
984 sys.exit(1)
985
986
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500987def _Version():
988 """Show version information."""
989 print('<repo not installed>')
990 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
991 print(' (from %s)' % (__file__,))
992 print('git %s' % (ParseGitVersion().full,))
993 print('Python %s' % sys.version)
Mike Frysinger5f11eac2020-02-25 15:09:01 -0500994 uname = platform.uname()
995 print('OS %s %s (%s)' % (uname.system, uname.release, uname.version))
996 print('CPU %s (%s)' %
997 (uname.machine, uname.processor if uname.processor else 'unknown'))
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500998 sys.exit(0)
999
1000
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001002 print('error: repo is not installed. Use "repo init" to install it here.',
1003 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001004 sys.exit(1)
1005
1006
1007def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001008 print("""error: command '%s' requires repo to be installed first.
1009 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001010 sys.exit(1)
1011
1012
1013def _RunSelf(wrapper_path):
1014 my_dir = os.path.dirname(wrapper_path)
1015 my_main = os.path.join(my_dir, 'main.py')
1016 my_git = os.path.join(my_dir, '.git')
1017
1018 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -07001019 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020 'project.py',
1021 'subcmds']:
1022 if not os.path.exists(os.path.join(my_dir, name)):
1023 return None, None
1024 return my_main, my_git
1025 return None, None
1026
1027
1028def _SetDefaultsTo(gitdir):
1029 global REPO_URL
1030 global REPO_REV
1031
1032 REPO_URL = gitdir
Mike Frysinger62285d22020-02-12 08:01:38 -05001033 try:
1034 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD')
1035 REPO_REV = ret.stdout.strip()
1036 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001037 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038 sys.exit(1)
1039
1040
1041def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001042 cmd, opt, args = _ParseArguments(orig_args)
1043
Mike Frysinger84094102020-02-11 02:10:28 -05001044 # We run this early as we run some git commands ourselves.
1045 SetGitTrace2ParentSid()
1046
Dan Willemsen745b4ad2015-10-06 15:23:19 -07001047 repo_main, rel_repo_dir = None, None
1048 # Don't use the local repo copy, make sure to switch to the gitc client first.
1049 if cmd != 'gitc-init':
1050 repo_main, rel_repo_dir = _FindRepo()
1051
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001052 wrapper_path = os.path.abspath(__file__)
1053 my_main, my_git = _RunSelf(wrapper_path)
1054
Simran Basi8ce50412015-08-28 14:25:44 -07001055 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -07001056 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001057 print('error: repo cannot be used in the GITC local manifest directory.'
1058 '\nIf you want to work on this GITC client please rerun this '
1059 'command from the corresponding client under /gitc/',
1060 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001061 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001062 if not repo_main:
Mike Frysinger949bc342020-02-18 21:37:00 -05001063 # Only expand aliases here since we'll be parsing the CLI ourselves.
1064 # If we had repo_main, alias expansion would happen in main.py.
1065 cmd, alias_args = _ExpandAlias(cmd)
1066 args = alias_args + args
1067
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 if opt.help:
1069 _Usage()
1070 if cmd == 'help':
1071 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001072 if opt.version or cmd == 'version':
1073 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 if not cmd:
1075 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001076 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 if my_git:
1078 _SetDefaultsTo(my_git)
1079 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001080 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001082 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001083 print("fatal: cloning the git-repo repository failed, will remove "
1084 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001085 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001087 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001088 else:
1089 _NoCommands(cmd)
1090
1091 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001092 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093
David Pursehouse685f0802012-11-14 08:34:39 +09001094 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001095 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001096 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001097 '--wrapper-version=%s' % ver_str,
1098 '--wrapper-path=%s' % wrapper_path,
1099 '--']
1100 me.extend(orig_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001101 exec_command(me)
1102 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1103 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001104
1105
1106if __name__ == '__main__':
1107 main(sys.argv[1:])