blob: 77e80284ee34c964dbda8ff903186803f4e0cc8b [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__':
Mike Frysinger19ec7972020-02-16 12:02:01 -0500108 check_python_version()
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400109
110
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700111# repo default configuration
112#
Mark E. Hamilton55536282016-02-03 15:49:43 -0700113REPO_URL = os.environ.get('REPO_URL', None)
114if not REPO_URL:
115 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -0500116REPO_REV = os.environ.get('REPO_REV')
117if not REPO_REV:
118 REPO_REV = 'stable'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700119
120# Copyright (C) 2008 Google Inc.
121#
122# Licensed under the Apache License, Version 2.0 (the "License");
123# you may not use this file except in compliance with the License.
124# You may obtain a copy of the License at
125#
126# http://www.apache.org/licenses/LICENSE-2.0
127#
128# Unless required by applicable law or agreed to in writing, software
129# distributed under the License is distributed on an "AS IS" BASIS,
130# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131# See the License for the specific language governing permissions and
132# limitations under the License.
133
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134# increment this whenever we make important changes to this script
Mike Frysinger19ec7972020-02-16 12:02:01 -0500135VERSION = (2, 4)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136
137# increment this if the MAINTAINER_KEYS block is modified
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500138KEYRING_VERSION = (2, 3)
Mike Frysingere4433652016-09-13 18:06:07 -0400139
140# Each individual key entry is created by using:
141# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700142MAINTAINER_KEYS = """
143
144 Repo Maintainer <repo@android.kernel.org>
145-----BEGIN PGP PUBLIC KEY BLOCK-----
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700146
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
Mike Frysinger9cc1d702020-02-13 18:28:03 -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
212cZ7aFsJF4PtcDrfdejyAxbtsSHI=
213=82Tj
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700214-----END PGP PUBLIC KEY BLOCK-----
215"""
216
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700217GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500218# NB: The version of git that the repo launcher requires may be much older than
219# the version of git that the main repo source tree requires. Keeping this at
220# an older version also makes it easier for users to upgrade/rollback as needed.
221#
222# git-1.7 is in (EOL) Ubuntu Precise.
223MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700224repodir = '.repo' # name of repo's private directory
225S_repo = 'repo' # special repo repository
226S_manifests = 'manifests' # special manifest repository
227REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700228GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700229GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
231
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400232import collections
David Jamesbf79c662013-12-26 14:20:13 -0800233import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700236import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700237import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900238
239if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700240 import urllib.request
241 import urllib.error
242else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700243 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900244 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700245 urllib = imp.new_module('urllib')
246 urllib.request = urllib2
247 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248
Conley Owens5e0ee142013-09-26 15:50:49 -0700249
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250home_dot_repo = os.path.expanduser('~/.repoconfig')
251gpg_dir = os.path.join(home_dot_repo, 'gnupg')
252
253extra_args = []
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700254
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900255
Mike Frysingerd8fda902020-02-14 00:24:38 -0500256def GetParser(gitc_init=False):
257 """Setup the CLI parser."""
258 if gitc_init:
259 usage = 'repo gitc-init -u url -c client [options]'
260 else:
261 usage = 'repo init -u url [options]'
262
263 parser = optparse.OptionParser(usage=usage)
264
Mike Frysingerf700ac72020-02-06 00:04:21 -0500265 # Logging.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500266 group = parser.add_option_group('Logging options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500267 group.add_option('-q', '--quiet',
268 action='store_true', default=False,
269 help='be quiet')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Mike Frysingerf700ac72020-02-06 00:04:21 -0500271 # Manifest.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500272 group = parser.add_option_group('Manifest options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500273 group.add_option('-u', '--manifest-url',
274 help='manifest repository location', metavar='URL')
275 group.add_option('-b', '--manifest-branch',
276 help='manifest branch or revision', metavar='REVISION')
277 group.add_option('-m', '--manifest-name',
278 help='initial manifest file', metavar='NAME.xml')
Mike Frysingerd8fda902020-02-14 00:24:38 -0500279 cbr_opts = ['--current-branch']
280 # The gitc-init subcommand allocates -c itself, but a lot of init users
281 # want -c, so try to satisfy both as best we can.
282 if not gitc_init:
283 cbr_opts += ['-c']
284 group.add_option(*cbr_opts,
Mike Frysingerf700ac72020-02-06 00:04:21 -0500285 dest='current_branch_only', action='store_true',
286 help='fetch only current manifest branch from server')
287 group.add_option('--mirror', action='store_true',
288 help='create a replica of the remote repositories '
289 'rather than a client working directory')
290 group.add_option('--reference',
291 help='location of mirror directory', metavar='DIR')
292 group.add_option('--dissociate', action='store_true',
293 help='dissociate from reference mirrors after clone')
294 group.add_option('--depth', type='int', default=None,
295 help='create a shallow clone with given depth; '
296 'see git clone')
297 group.add_option('--partial-clone', action='store_true',
298 help='perform partial clone (https://git-scm.com/'
299 'docs/gitrepository-layout#_code_partialclone_code)')
300 group.add_option('--clone-filter', action='store', default='blob:none',
301 help='filter for use with --partial-clone '
302 '[default: %default]')
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500303 group.add_option('--worktree', action='store_true',
304 help=optparse.SUPPRESS_HELP)
Mike Frysingerf700ac72020-02-06 00:04:21 -0500305 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')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500318 group.add_option('--no-clone-bundle',
319 dest='clone_bundle', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500320 help='disable use of /clone.bundle on HTTP/HTTPS')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500321 group.add_option('--no-tags',
322 dest='tags', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500323 help="don't fetch tags in the manifest")
Doug Anderson49cd59b2011-06-13 21:42:06 -0700324
Mike Frysingerf700ac72020-02-06 00:04:21 -0500325 # Tool.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500326 group = parser.add_option_group('repo Version options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500327 group.add_option('--repo-url', metavar='URL',
328 help='repo repository location ($REPO_URL)')
329 group.add_option('--repo-branch', metavar='REVISION',
330 help='repo branch or revision ($REPO_REV)')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500331 group.add_option('--no-repo-verify',
332 dest='repo_verify', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500333 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700334
Mike Frysingerf700ac72020-02-06 00:04:21 -0500335 # Other.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500336 group = parser.add_option_group('Other options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500337 group.add_option('--config-name',
338 action='store_true', default=False,
339 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700340
Mike Frysingerd8fda902020-02-14 00:24:38 -0500341 # gitc-init specific settings.
342 if gitc_init:
343 group = parser.add_option_group('GITC options')
344 group.add_option('-f', '--manifest-file',
345 help='Optional manifest file to use for this GITC client.')
346 group.add_option('-c', '--gitc-client',
347 help='Name of the gitc_client instance to create or modify.')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700348
Mike Frysingerd8fda902020-02-14 00:24:38 -0500349 return parser
Simran Basi1efc2b42015-08-05 15:04:22 -0700350
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900351
Mike Frysinger62285d22020-02-12 08:01:38 -0500352# This is a poor replacement for subprocess.run until we require Python 3.6+.
353RunResult = collections.namedtuple(
354 'RunResult', ('returncode', 'stdout', 'stderr'))
355
356
357class RunError(Exception):
358 """Error when running a command failed."""
359
360
361def run_command(cmd, **kwargs):
362 """Run |cmd| and return its output."""
363 check = kwargs.pop('check', False)
364 if kwargs.pop('capture_output', False):
365 kwargs.setdefault('stdout', subprocess.PIPE)
366 kwargs.setdefault('stderr', subprocess.PIPE)
367 cmd_input = kwargs.pop('input', None)
368
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500369 def decode(output):
370 """Decode |output| to text."""
371 if output is None:
372 return output
373 try:
374 return output.decode('utf-8')
375 except UnicodeError:
376 print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
377 file=sys.stderr)
378 # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
379 return output.decode('utf-8', 'replace')
380
Mike Frysinger62285d22020-02-12 08:01:38 -0500381 # Run & package the results.
382 proc = subprocess.Popen(cmd, **kwargs)
383 (stdout, stderr) = proc.communicate(input=cmd_input)
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500384 trace.print(':', ' '.join(cmd))
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500385 ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
Mike Frysinger62285d22020-02-12 08:01:38 -0500386
387 # If things failed, print useful debugging output.
388 if check and ret.returncode:
389 print('repo: error: "%s" failed with exit status %s' %
390 (cmd[0], ret.returncode), file=sys.stderr)
391 print(' cwd: %s\n cmd: %r' %
392 (kwargs.get('cwd', os.getcwd()), cmd), file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900393
Mike Frysinger62285d22020-02-12 08:01:38 -0500394 def _print_output(name, output):
395 if output:
396 print(' %s:\n >> %s' % (name, '\n >> '.join(output.splitlines())),
397 file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900398
Mike Frysinger62285d22020-02-12 08:01:38 -0500399 _print_output('stdout', ret.stdout)
400 _print_output('stderr', ret.stderr)
401 raise RunError(ret)
402
403 return ret
404
405
Simran Basi8ce50412015-08-28 14:25:44 -0700406_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700407
408
Simran Basi8ce50412015-08-28 14:25:44 -0700409def get_gitc_manifest_dir():
410 global _gitc_manifest_dir
411 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700412 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700413 try:
414 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
415 for line in gitc_config:
416 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
417 if match:
418 _gitc_manifest_dir = match.group('gitc_manifest_dir')
419 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700420 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700421 return _gitc_manifest_dir
422
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700423
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700424def gitc_parse_clientdir(gitc_fs_path):
425 """Parse a path in the GITC FS and return its client name.
426
427 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
428
429 @returns: The GITC client name
430 """
431 if gitc_fs_path == GITC_FS_ROOT_DIR:
432 return None
433 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
434 manifest_dir = get_gitc_manifest_dir()
435 if manifest_dir == '':
436 return None
437 if manifest_dir[-1] != '/':
438 manifest_dir += '/'
439 if gitc_fs_path == manifest_dir:
440 return None
441 if not gitc_fs_path.startswith(manifest_dir):
442 return None
443 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
444 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
445
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700446
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700447class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700448
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700449 """Indicate the remote clone of repo itself failed.
450 """
451
452
Simran Basi1efc2b42015-08-05 15:04:22 -0700453def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700454 """Installs repo by cloning it over the network.
455 """
Mike Frysingerd8fda902020-02-14 00:24:38 -0500456 parser = GetParser(gitc_init=gitc_init)
457 opt, args = parser.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800458 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500459 parser.print_usage()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700460 sys.exit(1)
461
462 url = opt.repo_url
463 if not url:
464 url = REPO_URL
465 extra_args.append('--repo-url=%s' % url)
466
467 branch = opt.repo_branch
468 if not branch:
469 branch = REPO_REV
470 extra_args.append('--repo-branch=%s' % branch)
471
472 if branch.startswith('refs/heads/'):
473 branch = branch[len('refs/heads/'):]
474 if branch.startswith('refs/'):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400475 print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700476 raise CloneFailure()
477
David Jamesbf79c662013-12-26 14:20:13 -0800478 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700479 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700480 gitc_manifest_dir = get_gitc_manifest_dir()
481 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400482 print('fatal: GITC filesystem is not available. Exiting...',
483 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700484 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700485 gitc_client = opt.gitc_client
486 if not gitc_client:
487 gitc_client = gitc_parse_clientdir(os.getcwd())
488 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400489 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700490 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700491 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700492 if not os.path.exists(client_dir):
493 os.makedirs(client_dir)
494 os.chdir(client_dir)
495 if os.path.exists(repodir):
496 # This GITC Client has already initialized repo so continue.
497 return
498
David Jamesbf79c662013-12-26 14:20:13 -0800499 os.mkdir(repodir)
500 except OSError as e:
501 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400502 print('fatal: cannot make %s directory: %s'
503 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900504 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700505 # name. Instead exit immediately.
506 #
507 sys.exit(1)
508
509 _CheckGitVersion()
510 try:
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500511 if not opt.repo_verify:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100512 do_verify = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700513 else:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100514 if NeedSetupGnuPG():
515 do_verify = SetupGnuPG(opt.quiet)
516 else:
517 do_verify = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700519 dst = os.path.abspath(os.path.join(repodir, S_repo))
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500520 _Clone(url, dst, opt.quiet, opt.clone_bundle)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700521
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100522 if do_verify:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700523 rev = _Verify(dst, branch, opt.quiet)
524 else:
525 rev = 'refs/remotes/origin/%s^0' % branch
526
527 _Checkout(dst, branch, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200528
529 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400530 print("warning: '%s' does not look like a git-repo repository, is "
531 "REPO_URL set correctly?" % url, file=sys.stderr)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200532
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700533 except CloneFailure:
534 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400535 print('fatal: repo init failed; run without --quiet to see why',
536 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700537 raise
538
539
Mike Frysinger62285d22020-02-12 08:01:38 -0500540def run_git(*args, **kwargs):
541 """Run git and return execution details."""
542 kwargs.setdefault('capture_output', True)
543 kwargs.setdefault('check', True)
544 try:
545 return run_command([GIT] + list(args), **kwargs)
546 except OSError as e:
547 print(file=sys.stderr)
548 print('repo: error: "%s" is not available' % GIT, file=sys.stderr)
549 print('repo: error: %s' % e, file=sys.stderr)
550 print(file=sys.stderr)
551 print('Please make sure %s is installed and in your path.' % GIT,
552 file=sys.stderr)
553 sys.exit(1)
554 except RunError:
555 raise CloneFailure()
556
557
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400558# The git version info broken down into components for easy analysis.
559# Similar to Python's sys.version_info.
560GitVersion = collections.namedtuple(
561 'GitVersion', ('major', 'minor', 'micro', 'full'))
562
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900563
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400564def ParseGitVersion(ver_str=None):
565 if ver_str is None:
566 # Load the version ourselves.
Mike Frysinger62285d22020-02-12 08:01:38 -0500567 ver_str = run_git('--version').stdout
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400568
Conley Owensff0a3c82014-01-30 14:46:03 -0800569 if not ver_str.startswith('git version '):
570 return None
571
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400572 full_version = ver_str[len('git version '):].strip()
573 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800574 to_tuple = []
575 for num_str in num_ver_str.split('.')[:3]:
576 if num_str.isdigit():
577 to_tuple.append(int(num_str))
578 else:
579 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400580 to_tuple.append(full_version)
581 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800582
583
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400584def _CheckGitVersion():
Mike Frysinger62285d22020-02-12 08:01:38 -0500585 ver_act = ParseGitVersion()
Conley Owensff0a3c82014-01-30 14:46:03 -0800586 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400587 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700588 raise CloneFailure()
589
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700590 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900591 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400592 print('fatal: git %s or later required' % need, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700593 raise CloneFailure()
594
595
Mike Frysinger84094102020-02-11 02:10:28 -0500596def SetGitTrace2ParentSid(env=None):
597 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
598 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
599 # (1) Be unique (2) be valid filename (3) be fixed length.
600 #
601 # Since we always export this variable, we try to avoid more expensive calls.
602 # e.g. We don't attempt hostname lookups or hashing the results.
603 if env is None:
604 env = os.environ
605
606 KEY = 'GIT_TRACE2_PARENT_SID'
607
608 now = datetime.datetime.utcnow()
609 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
610
611 # If it's already set, then append ourselves.
612 if KEY in env:
613 value = env[KEY] + '/' + value
614
615 _setenv(KEY, value, env=env)
616
617
618def _setenv(key, value, env=None):
619 """Set |key| in the OS environment |env| to |value|."""
620 if env is None:
621 env = os.environ
622 # Environment handling across systems is messy.
623 try:
624 env[key] = value
625 except UnicodeEncodeError:
626 env[key] = value.encode()
627
628
Conley Owensc9129d92012-10-01 16:12:28 -0700629def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700630 if not os.path.isdir(home_dot_repo):
631 return True
632
633 kv = os.path.join(home_dot_repo, 'keyring-version')
634 if not os.path.exists(kv):
635 return True
636
637 kv = open(kv).read()
638 if not kv:
639 return True
640
David Pursehouse685f0802012-11-14 08:34:39 +0900641 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642 if kv < KEYRING_VERSION:
643 return True
644 return False
645
646
Conley Owensc9129d92012-10-01 16:12:28 -0700647def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800648 try:
649 os.mkdir(home_dot_repo)
650 except OSError as e:
651 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400652 print('fatal: cannot make %s directory: %s'
653 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700654 sys.exit(1)
655
David Jamesbf79c662013-12-26 14:20:13 -0800656 try:
657 os.mkdir(gpg_dir, stat.S_IRWXU)
658 except OSError as e:
659 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400660 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
661 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700662 sys.exit(1)
663
Mike Frysinger19a1f222020-02-14 16:28:13 -0500664 if not quiet:
665 print('repo: Updating release signing keys to keyset ver %s' %
666 ('.'.join(str(x) for x in KEYRING_VERSION),))
667 # NB: We use --homedir (and cwd below) because some environments (Windows) do
668 # not correctly handle full native paths. We avoid the issue by changing to
669 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
670 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
671 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700672 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500673 # gpg can be pretty chatty. Always capture the output and if something goes
674 # wrong, the builtin check failure will dump stdout & stderr for debugging.
675 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
676 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900677 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900678 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700679 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400680 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
681 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
682 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700683 return False
684
Mike Frysinger3164d402019-11-11 05:40:22 -0500685 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
686 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700687 return True
688
689
Mike Frysinger62285d22020-02-12 08:01:38 -0500690def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700691 """Set a git configuration option to the specified value.
692 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500693 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700694
695
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700696def _InitHttp():
697 handlers = []
698
Sarah Owens1f7627f2012-10-31 09:21:55 -0700699 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700700 try:
701 import netrc
702 n = netrc.netrc()
703 for host in n.hosts:
704 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700705 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800706 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900707 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700708 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700709 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
710 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700711
712 if 'http_proxy' in os.environ:
713 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700714 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700715 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700716 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
717 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
718 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700719
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700720
Mike Frysinger62285d22020-02-12 08:01:38 -0500721def _Fetch(url, cwd, src, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700722 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400723 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700724
Mike Frysinger62285d22020-02-12 08:01:38 -0500725 cmd = ['fetch']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700726 if quiet:
727 cmd.append('--quiet')
728 err = subprocess.PIPE
729 else:
730 err = None
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700731 cmd.append(src)
732 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800733 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger62285d22020-02-12 08:01:38 -0500734 run_git(*cmd, stderr=err, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700735
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700736
Mike Frysinger62285d22020-02-12 08:01:38 -0500737def _DownloadBundle(url, cwd, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700738 if not url.endswith('/'):
739 url += '/'
740 url += 'clone.bundle'
741
Mike Frysinger62285d22020-02-12 08:01:38 -0500742 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
743 check=False)
744 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700745 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
746 if m:
747 new_url = m.group(1)
748 old_url = m.group(2)
749 if url.startswith(old_url):
750 url = new_url + url[len(old_url):]
751 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700752
753 if not url.startswith('http:') and not url.startswith('https:'):
754 return False
755
Mike Frysinger62285d22020-02-12 08:01:38 -0500756 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700757 try:
758 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700759 r = urllib.request.urlopen(url)
760 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200761 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700762 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400763 print('fatal: Cannot get %s' % url, file=sys.stderr)
764 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700765 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700766 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400767 print('fatal: Cannot get %s' % url, file=sys.stderr)
768 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700769 raise CloneFailure()
770 try:
771 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400772 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700773 while True:
774 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400775 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700776 return True
777 dest.write(buf)
778 finally:
779 r.close()
780 finally:
781 dest.close()
782
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700783
Mike Frysinger62285d22020-02-12 08:01:38 -0500784def _ImportBundle(cwd):
785 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700786 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500787 _Fetch(cwd, cwd, path, True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700788 finally:
789 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700791
Mike Frysinger62285d22020-02-12 08:01:38 -0500792def _Clone(url, cwd, quiet, clone_bundle):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700793 """Clones a git repository to a new subdirectory of repodir
794 """
795 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500796 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700797 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500798 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400799 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700800 raise CloneFailure()
801
Mike Frysinger62285d22020-02-12 08:01:38 -0500802 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700803
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700804 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500805 _SetConfig(cwd, 'remote.origin.url', url)
806 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700807 'remote.origin.fetch',
808 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysinger62285d22020-02-12 08:01:38 -0500809 if clone_bundle and _DownloadBundle(url, cwd, quiet):
810 _ImportBundle(cwd)
811 _Fetch(url, cwd, 'origin', quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812
813
814def _Verify(cwd, branch, quiet):
815 """Verify the branch has been signed by a tag.
816 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500817 try:
818 ret = run_git('describe', 'origin/%s' % branch, cwd=cwd)
819 cur = ret.stdout.strip()
820 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400821 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
Mike Frysinger62285d22020-02-12 08:01:38 -0500822 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700823
824 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
825 if m:
826 cur = m.group(1)
827 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400828 print(file=sys.stderr)
829 print("info: Ignoring branch '%s'; using tagged release '%s'"
830 % (branch, cur), file=sys.stderr)
831 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700832
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800833 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500834 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -0500835 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 return '%s^0' % cur
837
838
839def _Checkout(cwd, branch, rev, quiet):
840 """Checkout an upstream branch into the repository and track it.
841 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500842 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700843
844 _SetConfig(cwd, 'branch.default.remote', 'origin')
845 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
846
Mike Frysinger62285d22020-02-12 08:01:38 -0500847 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700848
Mike Frysinger62285d22020-02-12 08:01:38 -0500849 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700850 if not quiet:
851 cmd.append('-v')
852 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -0500853 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700854
855
856def _FindRepo():
857 """Look for a repo installation, starting at the current directory.
858 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200859 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700860 repo = None
861
Anthony Newnamdf14a702011-01-09 17:31:57 -0800862 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200863 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700864 and curdir != olddir \
865 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200866 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700867 if not os.path.isfile(repo):
868 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200869 olddir = curdir
870 curdir = os.path.dirname(curdir)
871 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872
873
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700874class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700875 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500876 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700877
878
879def _ParseArguments(args):
880 cmd = None
881 opt = _Options()
882 arg = []
883
Sarah Owensa6053d52012-11-01 13:36:50 -0700884 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700885 a = args[i]
886 if a == '-h' or a == '--help':
887 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500888 elif a == '--version':
889 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500890 elif a == '--trace':
891 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700892 elif not a.startswith('-'):
893 cmd = a
894 arg = args[i + 1:]
895 break
896 return cmd, opt, arg
897
898
899def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700900 gitc_usage = ""
901 if get_gitc_manifest_dir():
902 gitc_usage = " gitc-init Initialize a GITC Client.\n"
903
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400904 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700905 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700906
907repo is not yet installed. Use "repo init" to install it here.
908
909The most commonly used repo commands are:
910
911 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700912""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700913 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914
915For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400916""")
917 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700918
919
920def _Help(args):
921 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500922 if args[0] in {'init', 'gitc-init'}:
923 parser = GetParser(gitc_init=args[0] == 'gitc-init')
924 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700925 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400927 print("error: '%s' is not a bootstrap command.\n"
928 ' For access to online help, install repo ("repo init").'
929 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 else:
931 _Usage()
932 sys.exit(1)
933
934
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500935def _Version():
936 """Show version information."""
937 print('<repo not installed>')
938 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
939 print(' (from %s)' % (__file__,))
940 print('git %s' % (ParseGitVersion().full,))
941 print('Python %s' % sys.version)
942 sys.exit(0)
943
944
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400946 print('error: repo is not installed. Use "repo init" to install it here.',
947 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700948 sys.exit(1)
949
950
951def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400952 print("""error: command '%s' requires repo to be installed first.
953 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954 sys.exit(1)
955
956
957def _RunSelf(wrapper_path):
958 my_dir = os.path.dirname(wrapper_path)
959 my_main = os.path.join(my_dir, 'main.py')
960 my_git = os.path.join(my_dir, '.git')
961
962 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -0700963 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 'project.py',
965 'subcmds']:
966 if not os.path.exists(os.path.join(my_dir, name)):
967 return None, None
968 return my_main, my_git
969 return None, None
970
971
972def _SetDefaultsTo(gitdir):
973 global REPO_URL
974 global REPO_REV
975
976 REPO_URL = gitdir
Mike Frysinger62285d22020-02-12 08:01:38 -0500977 try:
978 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD')
979 REPO_REV = ret.stdout.strip()
980 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400981 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982 sys.exit(1)
983
984
985def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986 cmd, opt, args = _ParseArguments(orig_args)
987
Mike Frysinger84094102020-02-11 02:10:28 -0500988 # We run this early as we run some git commands ourselves.
989 SetGitTrace2ParentSid()
990
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700991 repo_main, rel_repo_dir = None, None
992 # Don't use the local repo copy, make sure to switch to the gitc client first.
993 if cmd != 'gitc-init':
994 repo_main, rel_repo_dir = _FindRepo()
995
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700996 wrapper_path = os.path.abspath(__file__)
997 my_main, my_git = _RunSelf(wrapper_path)
998
Simran Basi8ce50412015-08-28 14:25:44 -0700999 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -07001000 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001001 print('error: repo cannot be used in the GITC local manifest directory.'
1002 '\nIf you want to work on this GITC client please rerun this '
1003 'command from the corresponding client under /gitc/',
1004 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001005 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001006 if not repo_main:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001007 if opt.help:
1008 _Usage()
1009 if cmd == 'help':
1010 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001011 if opt.version or cmd == 'version':
1012 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001013 if not cmd:
1014 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001015 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016 if my_git:
1017 _SetDefaultsTo(my_git)
1018 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001019 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001021 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001022 print("fatal: cloning the git-repo repository failed, will remove "
1023 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001024 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001025 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001026 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001027 else:
1028 _NoCommands(cmd)
1029
1030 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001031 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001032
David Pursehouse685f0802012-11-14 08:34:39 +09001033 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001034 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001035 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 '--wrapper-version=%s' % ver_str,
1037 '--wrapper-path=%s' % wrapper_path,
1038 '--']
1039 me.extend(orig_args)
1040 me.extend(extra_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001041 exec_command(me)
1042 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1043 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044
1045
1046if __name__ == '__main__':
1047 main(sys.argv[1:])