blob: 2f4601a7c3129d9d8993decf58871114dcaf3157 [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 Frysinger587f1622020-03-23 16:49:11 -0400605 print('fatal: git %s or later required; found %s' % (need, ver_act.full),
606 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700607 raise CloneFailure()
608
609
Mike Frysinger84094102020-02-11 02:10:28 -0500610def SetGitTrace2ParentSid(env=None):
611 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
612 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
613 # (1) Be unique (2) be valid filename (3) be fixed length.
614 #
615 # Since we always export this variable, we try to avoid more expensive calls.
616 # e.g. We don't attempt hostname lookups or hashing the results.
617 if env is None:
618 env = os.environ
619
620 KEY = 'GIT_TRACE2_PARENT_SID'
621
622 now = datetime.datetime.utcnow()
623 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
624
625 # If it's already set, then append ourselves.
626 if KEY in env:
627 value = env[KEY] + '/' + value
628
629 _setenv(KEY, value, env=env)
630
631
632def _setenv(key, value, env=None):
633 """Set |key| in the OS environment |env| to |value|."""
634 if env is None:
635 env = os.environ
636 # Environment handling across systems is messy.
637 try:
638 env[key] = value
639 except UnicodeEncodeError:
640 env[key] = value.encode()
641
642
Conley Owensc9129d92012-10-01 16:12:28 -0700643def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700644 if not os.path.isdir(home_dot_repo):
645 return True
646
647 kv = os.path.join(home_dot_repo, 'keyring-version')
648 if not os.path.exists(kv):
649 return True
650
651 kv = open(kv).read()
652 if not kv:
653 return True
654
David Pursehouse685f0802012-11-14 08:34:39 +0900655 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656 if kv < KEYRING_VERSION:
657 return True
658 return False
659
660
Conley Owensc9129d92012-10-01 16:12:28 -0700661def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800662 try:
663 os.mkdir(home_dot_repo)
664 except OSError as e:
665 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400666 print('fatal: cannot make %s directory: %s'
667 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700668 sys.exit(1)
669
David Jamesbf79c662013-12-26 14:20:13 -0800670 try:
671 os.mkdir(gpg_dir, stat.S_IRWXU)
672 except OSError as e:
673 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400674 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
675 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700676 sys.exit(1)
677
Mike Frysinger19a1f222020-02-14 16:28:13 -0500678 if not quiet:
679 print('repo: Updating release signing keys to keyset ver %s' %
680 ('.'.join(str(x) for x in KEYRING_VERSION),))
681 # NB: We use --homedir (and cwd below) because some environments (Windows) do
682 # not correctly handle full native paths. We avoid the issue by changing to
683 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
684 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
685 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700686 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500687 # gpg can be pretty chatty. Always capture the output and if something goes
688 # wrong, the builtin check failure will dump stdout & stderr for debugging.
689 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
690 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900691 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900692 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700693 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400694 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
695 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
696 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700697 return False
698
Mike Frysinger3164d402019-11-11 05:40:22 -0500699 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
700 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700701 return True
702
703
Mike Frysinger62285d22020-02-12 08:01:38 -0500704def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700705 """Set a git configuration option to the specified value.
706 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500707 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700708
709
Mike Frysinger949bc342020-02-18 21:37:00 -0500710def _GetRepoConfig(name):
711 """Read a repo configuration option."""
712 config = os.path.join(home_dot_repo, 'config')
713 if not os.path.exists(config):
714 return None
715
716 cmd = ['config', '--file', config, '--get', name]
717 ret = run_git(*cmd, check=False)
718 if ret.returncode == 0:
719 return ret.stdout
720 elif ret.returncode == 1:
721 return None
722 else:
723 print('repo: error: git %s failed:\n%s' % (' '.join(cmd), ret.stderr),
724 file=sys.stderr)
725 raise RunError()
726
727
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700728def _InitHttp():
729 handlers = []
730
Sarah Owens1f7627f2012-10-31 09:21:55 -0700731 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700732 try:
733 import netrc
734 n = netrc.netrc()
735 for host in n.hosts:
736 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700737 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800738 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900739 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700740 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700741 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
742 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700743
744 if 'http_proxy' in os.environ:
745 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700746 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700747 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700748 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
749 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
750 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700751
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700752
Mike Frysingeredd3d452020-02-21 23:55:07 -0500753def _Fetch(url, cwd, src, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500754 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -0500755 if not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -0500757 err = None
758 if not quiet and sys.stdout.isatty():
759 cmd.append('--progress')
760 elif not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700761 err = subprocess.PIPE
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700762 cmd.append(src)
763 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800764 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger4847e052020-02-22 00:07:35 -0500765 run_git(*cmd, stderr=err, capture_output=False, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700766
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700767
Mike Frysingeredd3d452020-02-21 23:55:07 -0500768def _DownloadBundle(url, cwd, quiet, verbose):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700769 if not url.endswith('/'):
770 url += '/'
771 url += 'clone.bundle'
772
Mike Frysinger62285d22020-02-12 08:01:38 -0500773 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
774 check=False)
775 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700776 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
777 if m:
778 new_url = m.group(1)
779 old_url = m.group(2)
780 if url.startswith(old_url):
781 url = new_url + url[len(old_url):]
782 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700783
784 if not url.startswith('http:') and not url.startswith('https:'):
785 return False
786
Mike Frysinger62285d22020-02-12 08:01:38 -0500787 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700788 try:
789 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700790 r = urllib.request.urlopen(url)
791 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200792 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700793 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400794 print('fatal: Cannot get %s' % url, file=sys.stderr)
795 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700796 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700797 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400798 print('fatal: Cannot get %s' % url, file=sys.stderr)
799 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700800 raise CloneFailure()
801 try:
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500802 if verbose:
803 print('Downloading clone bundle %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700804 while True:
805 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400806 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700807 return True
808 dest.write(buf)
809 finally:
810 r.close()
811 finally:
812 dest.close()
813
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700814
Mike Frysinger62285d22020-02-12 08:01:38 -0500815def _ImportBundle(cwd):
816 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700817 try:
Mike Frysingeredd3d452020-02-21 23:55:07 -0500818 _Fetch(cwd, cwd, path, True, False)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700819 finally:
820 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700822
Mike Frysingeredd3d452020-02-21 23:55:07 -0500823def _Clone(url, cwd, clone_bundle, quiet, verbose):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700824 """Clones a git repository to a new subdirectory of repodir
825 """
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500826 if verbose:
827 print('Cloning git repository', url)
828
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700829 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500830 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700831 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500832 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400833 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834 raise CloneFailure()
835
Mike Frysinger62285d22020-02-12 08:01:38 -0500836 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700837
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700838 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500839 _SetConfig(cwd, 'remote.origin.url', url)
840 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700841 'remote.origin.fetch',
842 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500843 if clone_bundle and _DownloadBundle(url, cwd, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500844 _ImportBundle(cwd)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500845 _Fetch(url, cwd, 'origin', quiet, verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700846
847
848def _Verify(cwd, branch, quiet):
849 """Verify the branch has been signed by a tag.
850 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500851 try:
852 ret = run_git('describe', 'origin/%s' % branch, cwd=cwd)
853 cur = ret.stdout.strip()
854 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400855 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
Mike Frysinger62285d22020-02-12 08:01:38 -0500856 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857
858 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
859 if m:
860 cur = m.group(1)
861 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400862 print(file=sys.stderr)
863 print("info: Ignoring branch '%s'; using tagged release '%s'"
864 % (branch, cur), file=sys.stderr)
865 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700866
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800867 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500868 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -0500869 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870 return '%s^0' % cur
871
872
873def _Checkout(cwd, branch, rev, quiet):
874 """Checkout an upstream branch into the repository and track it.
875 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500876 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700877
878 _SetConfig(cwd, 'branch.default.remote', 'origin')
879 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
880
Mike Frysinger62285d22020-02-12 08:01:38 -0500881 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882
Mike Frysinger62285d22020-02-12 08:01:38 -0500883 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884 if not quiet:
885 cmd.append('-v')
886 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -0500887 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888
889
890def _FindRepo():
891 """Look for a repo installation, starting at the current directory.
892 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200893 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700894 repo = None
895
Anthony Newnamdf14a702011-01-09 17:31:57 -0800896 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200897 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700898 and curdir != olddir \
899 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200900 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 if not os.path.isfile(repo):
902 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200903 olddir = curdir
904 curdir = os.path.dirname(curdir)
905 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700906
907
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700908class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700909 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500910 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911
912
Mike Frysinger949bc342020-02-18 21:37:00 -0500913def _ExpandAlias(name):
914 """Look up user registered aliases."""
915 # We don't resolve aliases for existing subcommands. This matches git.
916 if name in {'gitc-init', 'help', 'init'}:
917 return name, []
918
919 alias = _GetRepoConfig('alias.%s' % (name,))
920 if alias is None:
921 return name, []
922
923 args = alias.strip().split(' ', 1)
924 name = args[0]
925 if len(args) == 2:
926 args = shlex.split(args[1])
927 else:
928 args = []
929 return name, args
930
931
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932def _ParseArguments(args):
933 cmd = None
934 opt = _Options()
935 arg = []
936
Sarah Owensa6053d52012-11-01 13:36:50 -0700937 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938 a = args[i]
939 if a == '-h' or a == '--help':
940 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500941 elif a == '--version':
942 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500943 elif a == '--trace':
944 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945 elif not a.startswith('-'):
946 cmd = a
947 arg = args[i + 1:]
948 break
949 return cmd, opt, arg
950
951
952def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700953 gitc_usage = ""
954 if get_gitc_manifest_dir():
955 gitc_usage = " gitc-init Initialize a GITC Client.\n"
956
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400957 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700958 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700959
960repo is not yet installed. Use "repo init" to install it here.
961
962The most commonly used repo commands are:
963
964 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700965""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700966 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700967
968For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400969""")
970 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700971
972
973def _Help(args):
974 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500975 if args[0] in {'init', 'gitc-init'}:
976 parser = GetParser(gitc_init=args[0] == 'gitc-init')
977 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700978 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700979 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400980 print("error: '%s' is not a bootstrap command.\n"
981 ' For access to online help, install repo ("repo init").'
982 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700983 else:
984 _Usage()
985 sys.exit(1)
986
987
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500988def _Version():
989 """Show version information."""
990 print('<repo not installed>')
991 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
992 print(' (from %s)' % (__file__,))
993 print('git %s' % (ParseGitVersion().full,))
994 print('Python %s' % sys.version)
Mike Frysinger5f11eac2020-02-25 15:09:01 -0500995 uname = platform.uname()
996 print('OS %s %s (%s)' % (uname.system, uname.release, uname.version))
997 print('CPU %s (%s)' %
998 (uname.machine, uname.processor if uname.processor else 'unknown'))
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500999 sys.exit(0)
1000
1001
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001002def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001003 print('error: repo is not installed. Use "repo init" to install it here.',
1004 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005 sys.exit(1)
1006
1007
1008def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001009 print("""error: command '%s' requires repo to be installed first.
1010 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001011 sys.exit(1)
1012
1013
1014def _RunSelf(wrapper_path):
1015 my_dir = os.path.dirname(wrapper_path)
1016 my_main = os.path.join(my_dir, 'main.py')
1017 my_git = os.path.join(my_dir, '.git')
1018
1019 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -07001020 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021 'project.py',
1022 'subcmds']:
1023 if not os.path.exists(os.path.join(my_dir, name)):
1024 return None, None
1025 return my_main, my_git
1026 return None, None
1027
1028
1029def _SetDefaultsTo(gitdir):
1030 global REPO_URL
1031 global REPO_REV
1032
1033 REPO_URL = gitdir
Mike Frysinger62285d22020-02-12 08:01:38 -05001034 try:
1035 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD')
1036 REPO_REV = ret.stdout.strip()
1037 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001038 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001039 sys.exit(1)
1040
1041
1042def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 cmd, opt, args = _ParseArguments(orig_args)
1044
Mike Frysinger84094102020-02-11 02:10:28 -05001045 # We run this early as we run some git commands ourselves.
1046 SetGitTrace2ParentSid()
1047
Dan Willemsen745b4ad2015-10-06 15:23:19 -07001048 repo_main, rel_repo_dir = None, None
1049 # Don't use the local repo copy, make sure to switch to the gitc client first.
1050 if cmd != 'gitc-init':
1051 repo_main, rel_repo_dir = _FindRepo()
1052
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001053 wrapper_path = os.path.abspath(__file__)
1054 my_main, my_git = _RunSelf(wrapper_path)
1055
Simran Basi8ce50412015-08-28 14:25:44 -07001056 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -07001057 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001058 print('error: repo cannot be used in the GITC local manifest directory.'
1059 '\nIf you want to work on this GITC client please rerun this '
1060 'command from the corresponding client under /gitc/',
1061 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001062 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001063 if not repo_main:
Mike Frysinger949bc342020-02-18 21:37:00 -05001064 # Only expand aliases here since we'll be parsing the CLI ourselves.
1065 # If we had repo_main, alias expansion would happen in main.py.
1066 cmd, alias_args = _ExpandAlias(cmd)
1067 args = alias_args + args
1068
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 if opt.help:
1070 _Usage()
1071 if cmd == 'help':
1072 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001073 if opt.version or cmd == 'version':
1074 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 if not cmd:
1076 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001077 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078 if my_git:
1079 _SetDefaultsTo(my_git)
1080 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001081 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001083 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001084 print("fatal: cloning the git-repo repository failed, will remove "
1085 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001086 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001088 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001089 else:
1090 _NoCommands(cmd)
1091
1092 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001093 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
David Pursehouse685f0802012-11-14 08:34:39 +09001095 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001096 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001097 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 '--wrapper-version=%s' % ver_str,
1099 '--wrapper-path=%s' % wrapper_path,
1100 '--']
1101 me.extend(orig_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001102 exec_command(me)
1103 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1104 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001105
1106
1107if __name__ == '__main__':
1108 main(sys.argv[1:])