blob: 7771c2d8160bdac303ddca6ede802f1c2b896806 [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 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003
Mike Frysinger87fb5a12019-06-13 01:54:46 -04004"""Repo launcher.
5
6This is a standalone tool that people may copy to anywhere in their system.
7It is used to get an initial repo client checkout, and after that it runs the
8copy of repo in the checkout.
9"""
10
Mike Frysingerc92ce5c2019-06-13 01:14:23 -040011from __future__ import print_function
12
Mike Frysinger84094102020-02-11 02:10:28 -050013import datetime
Mike Frysinger3ba716f2019-06-13 01:48:12 -040014import os
15import platform
16import subprocess
17import sys
18
19
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050020# Keep basic logic in sync with repo_trace.py.
21class Trace(object):
22 """Trace helper logic."""
23
24 REPO_TRACE = 'REPO_TRACE'
25
26 def __init__(self):
27 self.set(os.environ.get(self.REPO_TRACE) == '1')
28
29 def set(self, value):
30 self.enabled = bool(value)
31
32 def print(self, *args, **kwargs):
33 if self.enabled:
34 print(*args, **kwargs)
35
36
37trace = Trace()
38
39
Mike Frysinger3ba716f2019-06-13 01:48:12 -040040def exec_command(cmd):
41 """Execute |cmd| or return None on failure."""
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050042 trace.print(':', ' '.join(cmd))
Mike Frysinger3ba716f2019-06-13 01:48:12 -040043 try:
44 if platform.system() == 'Windows':
45 ret = subprocess.call(cmd)
46 sys.exit(ret)
47 else:
48 os.execvp(cmd[0], cmd)
Mike Frysinger72b6dc82020-02-12 17:04:32 -050049 except Exception:
Mike Frysinger3ba716f2019-06-13 01:48:12 -040050 pass
51
52
53def check_python_version():
54 """Make sure the active Python version is recent enough."""
55 def reexec(prog):
56 exec_command([prog] + sys.argv)
57
58 MIN_PYTHON_VERSION = (3, 6)
59
60 ver = sys.version_info
61 major = ver.major
62 minor = ver.minor
63
64 # Abort on very old Python 2 versions.
65 if (major, minor) < (2, 7):
66 print('repo: error: Your Python version is too old. '
67 'Please use Python {}.{} or newer instead.'.format(
68 *MIN_PYTHON_VERSION), file=sys.stderr)
69 sys.exit(1)
70
71 # Try to re-exec the version specific Python 3 if needed.
72 if (major, minor) < MIN_PYTHON_VERSION:
73 # Python makes releases ~once a year, so try our min version +10 to help
74 # bridge the gap. This is the fallback anyways so perf isn't critical.
75 min_major, min_minor = MIN_PYTHON_VERSION
76 for inc in range(0, 10):
77 reexec('python{}.{}'.format(min_major, min_minor + inc))
78
79 # Try the generic Python 3 wrapper, but only if it's new enough. We don't
80 # want to go from (still supported) Python 2.7 to (unsupported) Python 3.5.
81 try:
82 proc = subprocess.Popen(
83 ['python3', '-c', 'import sys; '
84 'print(sys.version_info.major, sys.version_info.minor)'],
85 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
86 (output, _) = proc.communicate()
87 python3_ver = tuple(int(x) for x in output.decode('utf-8').split())
88 except (OSError, subprocess.CalledProcessError):
89 python3_ver = None
90
91 # The python3 version looks like it's new enough, so give it a try.
92 if python3_ver and python3_ver >= MIN_PYTHON_VERSION:
93 reexec('python3')
94
95 # We're still here, so diagnose things for the user.
96 if major < 3:
97 print('repo: warning: Python 2 is no longer supported; '
98 'Please upgrade to Python {}.{}+.'.format(*MIN_PYTHON_VERSION),
99 file=sys.stderr)
100 else:
101 print('repo: error: Python 3 version is too old; '
102 'Please use Python {}.{} or newer.'.format(*MIN_PYTHON_VERSION),
103 file=sys.stderr)
104 sys.exit(1)
105
106
107if __name__ == '__main__':
108 # TODO(vapier): Enable this on Windows once we have Python 3 issues fixed.
109 if platform.system() != 'Windows':
110 check_python_version()
111
112
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700113# repo default configuration
114#
Mark E. Hamilton55536282016-02-03 15:49:43 -0700115REPO_URL = os.environ.get('REPO_URL', None)
116if not REPO_URL:
117 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -0500118REPO_REV = os.environ.get('REPO_REV')
119if not REPO_REV:
120 REPO_REV = 'stable'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700121
122# Copyright (C) 2008 Google Inc.
123#
124# Licensed under the Apache License, Version 2.0 (the "License");
125# you may not use this file except in compliance with the License.
126# You may obtain a copy of the License at
127#
128# http://www.apache.org/licenses/LICENSE-2.0
129#
130# Unless required by applicable law or agreed to in writing, software
131# distributed under the License is distributed on an "AS IS" BASIS,
132# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133# See the License for the specific language governing permissions and
134# limitations under the License.
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136# increment this whenever we make important changes to this script
Mike Frysinger0b57eed2020-02-12 17:24:11 -0500137VERSION = (2, 3)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700138
139# increment this if the MAINTAINER_KEYS block is modified
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500140KEYRING_VERSION = (2, 3)
Mike Frysingere4433652016-09-13 18:06:07 -0400141
142# Each individual key entry is created by using:
143# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700144MAINTAINER_KEYS = """
145
146 Repo Maintainer <repo@android.kernel.org>
147-----BEGIN PGP PUBLIC KEY BLOCK-----
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700148
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
Mike Frysinger9cc1d702020-02-13 18:28:03 -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
214cZ7aFsJF4PtcDrfdejyAxbtsSHI=
215=82Tj
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216-----END PGP PUBLIC KEY BLOCK-----
217"""
218
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700219GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500220# NB: The version of git that the repo launcher requires may be much older than
221# the version of git that the main repo source tree requires. Keeping this at
222# an older version also makes it easier for users to upgrade/rollback as needed.
223#
224# git-1.7 is in (EOL) Ubuntu Precise.
225MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700226repodir = '.repo' # name of repo's private directory
227S_repo = 'repo' # special repo repository
228S_manifests = 'manifests' # special manifest repository
229REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700230GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700231GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
233
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400234import collections
David Jamesbf79c662013-12-26 14:20:13 -0800235import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700238import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700239import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900240
241if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700242 import urllib.request
243 import urllib.error
244else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700245 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900246 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700247 urllib = imp.new_module('urllib')
248 urllib.request = urllib2
249 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250
Conley Owens5e0ee142013-09-26 15:50:49 -0700251
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252home_dot_repo = os.path.expanduser('~/.repoconfig')
253gpg_dir = os.path.join(home_dot_repo, 'gnupg')
254
255extra_args = []
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700256
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900257
Mike Frysingerd8fda902020-02-14 00:24:38 -0500258def GetParser(gitc_init=False):
259 """Setup the CLI parser."""
260 if gitc_init:
261 usage = 'repo gitc-init -u url -c client [options]'
262 else:
263 usage = 'repo init -u url [options]'
264
265 parser = optparse.OptionParser(usage=usage)
266
Mike Frysingerf700ac72020-02-06 00:04:21 -0500267 # Logging.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500268 group = parser.add_option_group('Logging options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500269 group.add_option('-q', '--quiet',
270 action='store_true', default=False,
271 help='be quiet')
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]')
305 group.add_option('--archive', action='store_true',
306 help='checkout an archive instead of a git repository for '
307 'each project. See git archive.')
308 group.add_option('--submodules', action='store_true',
309 help='sync any submodules associated with the manifest repo')
310 group.add_option('-g', '--groups', default='default',
311 help='restrict manifest projects to ones with specified '
312 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
313 metavar='GROUP')
314 group.add_option('-p', '--platform', default='auto',
315 help='restrict manifest projects to ones with a specified '
316 'platform group [auto|all|none|linux|darwin|...]',
317 metavar='PLATFORM')
318 group.add_option('--no-clone-bundle', action='store_true',
319 help='disable use of /clone.bundle on HTTP/HTTPS')
320 group.add_option('--no-tags', action='store_true',
321 help="don't fetch tags in the manifest")
Doug Anderson49cd59b2011-06-13 21:42:06 -0700322
Mike Frysingerf700ac72020-02-06 00:04:21 -0500323 # Tool.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500324 group = parser.add_option_group('repo Version options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500325 group.add_option('--repo-url', metavar='URL',
326 help='repo repository location ($REPO_URL)')
327 group.add_option('--repo-branch', metavar='REVISION',
328 help='repo branch or revision ($REPO_REV)')
329 group.add_option('--no-repo-verify', action='store_true',
330 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700331
Mike Frysingerf700ac72020-02-06 00:04:21 -0500332 # Other.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500333 group = parser.add_option_group('Other options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500334 group.add_option('--config-name',
335 action='store_true', default=False,
336 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700337
Mike Frysingerd8fda902020-02-14 00:24:38 -0500338 # gitc-init specific settings.
339 if gitc_init:
340 group = parser.add_option_group('GITC options')
341 group.add_option('-f', '--manifest-file',
342 help='Optional manifest file to use for this GITC client.')
343 group.add_option('-c', '--gitc-client',
344 help='Name of the gitc_client instance to create or modify.')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700345
Mike Frysingerd8fda902020-02-14 00:24:38 -0500346 return parser
Simran Basi1efc2b42015-08-05 15:04:22 -0700347
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900348
Mike Frysinger62285d22020-02-12 08:01:38 -0500349# This is a poor replacement for subprocess.run until we require Python 3.6+.
350RunResult = collections.namedtuple(
351 'RunResult', ('returncode', 'stdout', 'stderr'))
352
353
354class RunError(Exception):
355 """Error when running a command failed."""
356
357
358def run_command(cmd, **kwargs):
359 """Run |cmd| and return its output."""
360 check = kwargs.pop('check', False)
361 if kwargs.pop('capture_output', False):
362 kwargs.setdefault('stdout', subprocess.PIPE)
363 kwargs.setdefault('stderr', subprocess.PIPE)
364 cmd_input = kwargs.pop('input', None)
365
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500366 def decode(output):
367 """Decode |output| to text."""
368 if output is None:
369 return output
370 try:
371 return output.decode('utf-8')
372 except UnicodeError:
373 print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
374 file=sys.stderr)
375 # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
376 return output.decode('utf-8', 'replace')
377
Mike Frysinger62285d22020-02-12 08:01:38 -0500378 # Run & package the results.
379 proc = subprocess.Popen(cmd, **kwargs)
380 (stdout, stderr) = proc.communicate(input=cmd_input)
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500381 trace.print(':', ' '.join(cmd))
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500382 ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
Mike Frysinger62285d22020-02-12 08:01:38 -0500383
384 # If things failed, print useful debugging output.
385 if check and ret.returncode:
386 print('repo: error: "%s" failed with exit status %s' %
387 (cmd[0], ret.returncode), file=sys.stderr)
388 print(' cwd: %s\n cmd: %r' %
389 (kwargs.get('cwd', os.getcwd()), cmd), file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900390
Mike Frysinger62285d22020-02-12 08:01:38 -0500391 def _print_output(name, output):
392 if output:
393 print(' %s:\n >> %s' % (name, '\n >> '.join(output.splitlines())),
394 file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900395
Mike Frysinger62285d22020-02-12 08:01:38 -0500396 _print_output('stdout', ret.stdout)
397 _print_output('stderr', ret.stderr)
398 raise RunError(ret)
399
400 return ret
401
402
Simran Basi8ce50412015-08-28 14:25:44 -0700403_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700404
405
Simran Basi8ce50412015-08-28 14:25:44 -0700406def get_gitc_manifest_dir():
407 global _gitc_manifest_dir
408 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700409 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700410 try:
411 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
412 for line in gitc_config:
413 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
414 if match:
415 _gitc_manifest_dir = match.group('gitc_manifest_dir')
416 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700417 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700418 return _gitc_manifest_dir
419
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700420
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700421def gitc_parse_clientdir(gitc_fs_path):
422 """Parse a path in the GITC FS and return its client name.
423
424 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
425
426 @returns: The GITC client name
427 """
428 if gitc_fs_path == GITC_FS_ROOT_DIR:
429 return None
430 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
431 manifest_dir = get_gitc_manifest_dir()
432 if manifest_dir == '':
433 return None
434 if manifest_dir[-1] != '/':
435 manifest_dir += '/'
436 if gitc_fs_path == manifest_dir:
437 return None
438 if not gitc_fs_path.startswith(manifest_dir):
439 return None
440 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
441 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
442
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700443
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700444class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700445
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700446 """Indicate the remote clone of repo itself failed.
447 """
448
449
Simran Basi1efc2b42015-08-05 15:04:22 -0700450def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700451 """Installs repo by cloning it over the network.
452 """
Mike Frysingerd8fda902020-02-14 00:24:38 -0500453 parser = GetParser(gitc_init=gitc_init)
454 opt, args = parser.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800455 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500456 parser.print_usage()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700457 sys.exit(1)
458
459 url = opt.repo_url
460 if not url:
461 url = REPO_URL
462 extra_args.append('--repo-url=%s' % url)
463
464 branch = opt.repo_branch
465 if not branch:
466 branch = REPO_REV
467 extra_args.append('--repo-branch=%s' % branch)
468
469 if branch.startswith('refs/heads/'):
470 branch = branch[len('refs/heads/'):]
471 if branch.startswith('refs/'):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400472 print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700473 raise CloneFailure()
474
David Jamesbf79c662013-12-26 14:20:13 -0800475 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700476 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700477 gitc_manifest_dir = get_gitc_manifest_dir()
478 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400479 print('fatal: GITC filesystem is not available. Exiting...',
480 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700481 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700482 gitc_client = opt.gitc_client
483 if not gitc_client:
484 gitc_client = gitc_parse_clientdir(os.getcwd())
485 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400486 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700487 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700488 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700489 if not os.path.exists(client_dir):
490 os.makedirs(client_dir)
491 os.chdir(client_dir)
492 if os.path.exists(repodir):
493 # This GITC Client has already initialized repo so continue.
494 return
495
David Jamesbf79c662013-12-26 14:20:13 -0800496 os.mkdir(repodir)
497 except OSError as e:
498 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400499 print('fatal: cannot make %s directory: %s'
500 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900501 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700502 # name. Instead exit immediately.
503 #
504 sys.exit(1)
505
506 _CheckGitVersion()
507 try:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100508 if opt.no_repo_verify:
509 do_verify = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700510 else:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100511 if NeedSetupGnuPG():
512 do_verify = SetupGnuPG(opt.quiet)
513 else:
514 do_verify = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700515
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700516 dst = os.path.abspath(os.path.join(repodir, S_repo))
Hu xiuyun9711a982015-12-11 11:16:41 +0800517 _Clone(url, dst, opt.quiet, not opt.no_clone_bundle)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100519 if do_verify:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700520 rev = _Verify(dst, branch, opt.quiet)
521 else:
522 rev = 'refs/remotes/origin/%s^0' % branch
523
524 _Checkout(dst, branch, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200525
526 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400527 print("warning: '%s' does not look like a git-repo repository, is "
528 "REPO_URL set correctly?" % url, file=sys.stderr)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200529
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700530 except CloneFailure:
531 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400532 print('fatal: repo init failed; run without --quiet to see why',
533 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700534 raise
535
536
Mike Frysinger62285d22020-02-12 08:01:38 -0500537def run_git(*args, **kwargs):
538 """Run git and return execution details."""
539 kwargs.setdefault('capture_output', True)
540 kwargs.setdefault('check', True)
541 try:
542 return run_command([GIT] + list(args), **kwargs)
543 except OSError as e:
544 print(file=sys.stderr)
545 print('repo: error: "%s" is not available' % GIT, file=sys.stderr)
546 print('repo: error: %s' % e, file=sys.stderr)
547 print(file=sys.stderr)
548 print('Please make sure %s is installed and in your path.' % GIT,
549 file=sys.stderr)
550 sys.exit(1)
551 except RunError:
552 raise CloneFailure()
553
554
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400555# The git version info broken down into components for easy analysis.
556# Similar to Python's sys.version_info.
557GitVersion = collections.namedtuple(
558 'GitVersion', ('major', 'minor', 'micro', 'full'))
559
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900560
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400561def ParseGitVersion(ver_str=None):
562 if ver_str is None:
563 # Load the version ourselves.
Mike Frysinger62285d22020-02-12 08:01:38 -0500564 ver_str = run_git('--version').stdout
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400565
Conley Owensff0a3c82014-01-30 14:46:03 -0800566 if not ver_str.startswith('git version '):
567 return None
568
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400569 full_version = ver_str[len('git version '):].strip()
570 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800571 to_tuple = []
572 for num_str in num_ver_str.split('.')[:3]:
573 if num_str.isdigit():
574 to_tuple.append(int(num_str))
575 else:
576 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400577 to_tuple.append(full_version)
578 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800579
580
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400581def _CheckGitVersion():
Mike Frysinger62285d22020-02-12 08:01:38 -0500582 ver_act = ParseGitVersion()
Conley Owensff0a3c82014-01-30 14:46:03 -0800583 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400584 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700585 raise CloneFailure()
586
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700587 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900588 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400589 print('fatal: git %s or later required' % need, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700590 raise CloneFailure()
591
592
Mike Frysinger84094102020-02-11 02:10:28 -0500593def SetGitTrace2ParentSid(env=None):
594 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
595 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
596 # (1) Be unique (2) be valid filename (3) be fixed length.
597 #
598 # Since we always export this variable, we try to avoid more expensive calls.
599 # e.g. We don't attempt hostname lookups or hashing the results.
600 if env is None:
601 env = os.environ
602
603 KEY = 'GIT_TRACE2_PARENT_SID'
604
605 now = datetime.datetime.utcnow()
606 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
607
608 # If it's already set, then append ourselves.
609 if KEY in env:
610 value = env[KEY] + '/' + value
611
612 _setenv(KEY, value, env=env)
613
614
615def _setenv(key, value, env=None):
616 """Set |key| in the OS environment |env| to |value|."""
617 if env is None:
618 env = os.environ
619 # Environment handling across systems is messy.
620 try:
621 env[key] = value
622 except UnicodeEncodeError:
623 env[key] = value.encode()
624
625
Conley Owensc9129d92012-10-01 16:12:28 -0700626def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700627 if not os.path.isdir(home_dot_repo):
628 return True
629
630 kv = os.path.join(home_dot_repo, 'keyring-version')
631 if not os.path.exists(kv):
632 return True
633
634 kv = open(kv).read()
635 if not kv:
636 return True
637
David Pursehouse685f0802012-11-14 08:34:39 +0900638 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700639 if kv < KEYRING_VERSION:
640 return True
641 return False
642
643
Conley Owensc9129d92012-10-01 16:12:28 -0700644def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800645 try:
646 os.mkdir(home_dot_repo)
647 except OSError as e:
648 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400649 print('fatal: cannot make %s directory: %s'
650 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700651 sys.exit(1)
652
David Jamesbf79c662013-12-26 14:20:13 -0800653 try:
654 os.mkdir(gpg_dir, stat.S_IRWXU)
655 except OSError as e:
656 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400657 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
658 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700659 sys.exit(1)
660
Mike Frysinger19a1f222020-02-14 16:28:13 -0500661 if not quiet:
662 print('repo: Updating release signing keys to keyset ver %s' %
663 ('.'.join(str(x) for x in KEYRING_VERSION),))
664 # NB: We use --homedir (and cwd below) because some environments (Windows) do
665 # not correctly handle full native paths. We avoid the issue by changing to
666 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
667 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
668 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700669 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500670 # gpg can be pretty chatty. Always capture the output and if something goes
671 # wrong, the builtin check failure will dump stdout & stderr for debugging.
672 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
673 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900674 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900675 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700676 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400677 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
678 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
679 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700680 return False
681
Mike Frysinger3164d402019-11-11 05:40:22 -0500682 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
683 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700684 return True
685
686
Mike Frysinger62285d22020-02-12 08:01:38 -0500687def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700688 """Set a git configuration option to the specified value.
689 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500690 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700691
692
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700693def _InitHttp():
694 handlers = []
695
Sarah Owens1f7627f2012-10-31 09:21:55 -0700696 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700697 try:
698 import netrc
699 n = netrc.netrc()
700 for host in n.hosts:
701 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700702 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800703 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900704 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700705 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700706 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
707 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700708
709 if 'http_proxy' in os.environ:
710 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700711 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700712 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700713 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
714 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
715 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700716
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700717
Mike Frysinger62285d22020-02-12 08:01:38 -0500718def _Fetch(url, cwd, src, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700719 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400720 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700721
Mike Frysinger62285d22020-02-12 08:01:38 -0500722 cmd = ['fetch']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700723 if quiet:
724 cmd.append('--quiet')
725 err = subprocess.PIPE
726 else:
727 err = None
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700728 cmd.append(src)
729 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800730 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger62285d22020-02-12 08:01:38 -0500731 run_git(*cmd, stderr=err, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700733
Mike Frysinger62285d22020-02-12 08:01:38 -0500734def _DownloadBundle(url, cwd, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700735 if not url.endswith('/'):
736 url += '/'
737 url += 'clone.bundle'
738
Mike Frysinger62285d22020-02-12 08:01:38 -0500739 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
740 check=False)
741 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700742 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
743 if m:
744 new_url = m.group(1)
745 old_url = m.group(2)
746 if url.startswith(old_url):
747 url = new_url + url[len(old_url):]
748 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700749
750 if not url.startswith('http:') and not url.startswith('https:'):
751 return False
752
Mike Frysinger62285d22020-02-12 08:01:38 -0500753 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700754 try:
755 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700756 r = urllib.request.urlopen(url)
757 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200758 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700759 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400760 print('fatal: Cannot get %s' % url, file=sys.stderr)
761 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700762 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700763 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400764 print('fatal: Cannot get %s' % url, file=sys.stderr)
765 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700766 raise CloneFailure()
767 try:
768 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400769 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700770 while True:
771 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400772 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700773 return True
774 dest.write(buf)
775 finally:
776 r.close()
777 finally:
778 dest.close()
779
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700780
Mike Frysinger62285d22020-02-12 08:01:38 -0500781def _ImportBundle(cwd):
782 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700783 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500784 _Fetch(cwd, cwd, path, True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700785 finally:
786 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700787
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700788
Mike Frysinger62285d22020-02-12 08:01:38 -0500789def _Clone(url, cwd, quiet, clone_bundle):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 """Clones a git repository to a new subdirectory of repodir
791 """
792 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500793 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700794 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500795 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400796 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700797 raise CloneFailure()
798
Mike Frysinger62285d22020-02-12 08:01:38 -0500799 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700800
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700801 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500802 _SetConfig(cwd, 'remote.origin.url', url)
803 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700804 'remote.origin.fetch',
805 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysinger62285d22020-02-12 08:01:38 -0500806 if clone_bundle and _DownloadBundle(url, cwd, quiet):
807 _ImportBundle(cwd)
808 _Fetch(url, cwd, 'origin', quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700809
810
811def _Verify(cwd, branch, quiet):
812 """Verify the branch has been signed by a tag.
813 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500814 try:
815 ret = run_git('describe', 'origin/%s' % branch, cwd=cwd)
816 cur = ret.stdout.strip()
817 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400818 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
Mike Frysinger62285d22020-02-12 08:01:38 -0500819 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
821 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
822 if m:
823 cur = m.group(1)
824 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400825 print(file=sys.stderr)
826 print("info: Ignoring branch '%s'; using tagged release '%s'"
827 % (branch, cur), file=sys.stderr)
828 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700829
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800830 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500831 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -0500832 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700833 return '%s^0' % cur
834
835
836def _Checkout(cwd, branch, rev, quiet):
837 """Checkout an upstream branch into the repository and track it.
838 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500839 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700840
841 _SetConfig(cwd, 'branch.default.remote', 'origin')
842 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
843
Mike Frysinger62285d22020-02-12 08:01:38 -0500844 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700845
Mike Frysinger62285d22020-02-12 08:01:38 -0500846 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700847 if not quiet:
848 cmd.append('-v')
849 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -0500850 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700851
852
853def _FindRepo():
854 """Look for a repo installation, starting at the current directory.
855 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200856 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 repo = None
858
Anthony Newnamdf14a702011-01-09 17:31:57 -0800859 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200860 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700861 and curdir != olddir \
862 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200863 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700864 if not os.path.isfile(repo):
865 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200866 olddir = curdir
867 curdir = os.path.dirname(curdir)
868 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700869
870
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700871class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500873 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700874
875
876def _ParseArguments(args):
877 cmd = None
878 opt = _Options()
879 arg = []
880
Sarah Owensa6053d52012-11-01 13:36:50 -0700881 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 a = args[i]
883 if a == '-h' or a == '--help':
884 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500885 elif a == '--version':
886 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500887 elif a == '--trace':
888 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700889 elif not a.startswith('-'):
890 cmd = a
891 arg = args[i + 1:]
892 break
893 return cmd, opt, arg
894
895
896def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700897 gitc_usage = ""
898 if get_gitc_manifest_dir():
899 gitc_usage = " gitc-init Initialize a GITC Client.\n"
900
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400901 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700902 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700903
904repo is not yet installed. Use "repo init" to install it here.
905
906The most commonly used repo commands are:
907
908 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700909""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700910 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911
912For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400913""")
914 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700915
916
917def _Help(args):
918 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500919 if args[0] in {'init', 'gitc-init'}:
920 parser = GetParser(gitc_init=args[0] == 'gitc-init')
921 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700922 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700923 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400924 print("error: '%s' is not a bootstrap command.\n"
925 ' For access to online help, install repo ("repo init").'
926 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 else:
928 _Usage()
929 sys.exit(1)
930
931
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500932def _Version():
933 """Show version information."""
934 print('<repo not installed>')
935 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
936 print(' (from %s)' % (__file__,))
937 print('git %s' % (ParseGitVersion().full,))
938 print('Python %s' % sys.version)
939 sys.exit(0)
940
941
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400943 print('error: repo is not installed. Use "repo init" to install it here.',
944 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945 sys.exit(1)
946
947
948def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400949 print("""error: command '%s' requires repo to be installed first.
950 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700951 sys.exit(1)
952
953
954def _RunSelf(wrapper_path):
955 my_dir = os.path.dirname(wrapper_path)
956 my_main = os.path.join(my_dir, 'main.py')
957 my_git = os.path.join(my_dir, '.git')
958
959 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -0700960 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700961 'project.py',
962 'subcmds']:
963 if not os.path.exists(os.path.join(my_dir, name)):
964 return None, None
965 return my_main, my_git
966 return None, None
967
968
969def _SetDefaultsTo(gitdir):
970 global REPO_URL
971 global REPO_REV
972
973 REPO_URL = gitdir
Mike Frysinger62285d22020-02-12 08:01:38 -0500974 try:
975 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD')
976 REPO_REV = ret.stdout.strip()
977 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400978 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700979 sys.exit(1)
980
981
982def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700983 cmd, opt, args = _ParseArguments(orig_args)
984
Mike Frysinger84094102020-02-11 02:10:28 -0500985 # We run this early as we run some git commands ourselves.
986 SetGitTrace2ParentSid()
987
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700988 repo_main, rel_repo_dir = None, None
989 # Don't use the local repo copy, make sure to switch to the gitc client first.
990 if cmd != 'gitc-init':
991 repo_main, rel_repo_dir = _FindRepo()
992
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993 wrapper_path = os.path.abspath(__file__)
994 my_main, my_git = _RunSelf(wrapper_path)
995
Simran Basi8ce50412015-08-28 14:25:44 -0700996 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -0700997 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400998 print('error: repo cannot be used in the GITC local manifest directory.'
999 '\nIf you want to work on this GITC client please rerun this '
1000 'command from the corresponding client under /gitc/',
1001 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001002 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001003 if not repo_main:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001004 if opt.help:
1005 _Usage()
1006 if cmd == 'help':
1007 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001008 if opt.version or cmd == 'version':
1009 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001010 if not cmd:
1011 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001012 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001013 if my_git:
1014 _SetDefaultsTo(my_git)
1015 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001016 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001017 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001018 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001019 print("fatal: cloning the git-repo repository failed, will remove "
1020 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001021 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001023 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001024 else:
1025 _NoCommands(cmd)
1026
1027 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001028 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029
David Pursehouse685f0802012-11-14 08:34:39 +09001030 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001031 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001032 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 '--wrapper-version=%s' % ver_str,
1034 '--wrapper-path=%s' % wrapper_path,
1035 '--']
1036 me.extend(orig_args)
1037 me.extend(extra_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001038 exec_command(me)
1039 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1040 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001041
1042
1043if __name__ == '__main__':
1044 main(sys.argv[1:])