blob: 77a3f8d3c3848c6cc1453ad4523193ef96a5dfff [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
Mike Frysinger949bc342020-02-18 21:37:00 -050016import shlex
Mike Frysinger3ba716f2019-06-13 01:48:12 -040017import subprocess
18import sys
19
20
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050021# Keep basic logic in sync with repo_trace.py.
22class Trace(object):
23 """Trace helper logic."""
24
25 REPO_TRACE = 'REPO_TRACE'
26
27 def __init__(self):
28 self.set(os.environ.get(self.REPO_TRACE) == '1')
29
30 def set(self, value):
31 self.enabled = bool(value)
32
33 def print(self, *args, **kwargs):
34 if self.enabled:
35 print(*args, **kwargs)
36
37
38trace = Trace()
39
40
Mike Frysinger3ba716f2019-06-13 01:48:12 -040041def exec_command(cmd):
42 """Execute |cmd| or return None on failure."""
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050043 trace.print(':', ' '.join(cmd))
Mike Frysinger3ba716f2019-06-13 01:48:12 -040044 try:
45 if platform.system() == 'Windows':
46 ret = subprocess.call(cmd)
47 sys.exit(ret)
48 else:
49 os.execvp(cmd[0], cmd)
Mike Frysinger72b6dc82020-02-12 17:04:32 -050050 except Exception:
Mike Frysinger3ba716f2019-06-13 01:48:12 -040051 pass
52
53
54def check_python_version():
55 """Make sure the active Python version is recent enough."""
56 def reexec(prog):
57 exec_command([prog] + sys.argv)
58
59 MIN_PYTHON_VERSION = (3, 6)
60
61 ver = sys.version_info
62 major = ver.major
63 minor = ver.minor
64
65 # Abort on very old Python 2 versions.
66 if (major, minor) < (2, 7):
67 print('repo: error: Your Python version is too old. '
68 'Please use Python {}.{} or newer instead.'.format(
69 *MIN_PYTHON_VERSION), file=sys.stderr)
70 sys.exit(1)
71
72 # Try to re-exec the version specific Python 3 if needed.
73 if (major, minor) < MIN_PYTHON_VERSION:
74 # Python makes releases ~once a year, so try our min version +10 to help
75 # bridge the gap. This is the fallback anyways so perf isn't critical.
76 min_major, min_minor = MIN_PYTHON_VERSION
77 for inc in range(0, 10):
78 reexec('python{}.{}'.format(min_major, min_minor + inc))
79
80 # Try the generic Python 3 wrapper, but only if it's new enough. We don't
81 # want to go from (still supported) Python 2.7 to (unsupported) Python 3.5.
82 try:
83 proc = subprocess.Popen(
84 ['python3', '-c', 'import sys; '
85 'print(sys.version_info.major, sys.version_info.minor)'],
86 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
87 (output, _) = proc.communicate()
88 python3_ver = tuple(int(x) for x in output.decode('utf-8').split())
89 except (OSError, subprocess.CalledProcessError):
90 python3_ver = None
91
92 # The python3 version looks like it's new enough, so give it a try.
93 if python3_ver and python3_ver >= MIN_PYTHON_VERSION:
94 reexec('python3')
95
96 # We're still here, so diagnose things for the user.
97 if major < 3:
98 print('repo: warning: Python 2 is no longer supported; '
99 'Please upgrade to Python {}.{}+.'.format(*MIN_PYTHON_VERSION),
100 file=sys.stderr)
101 else:
102 print('repo: error: Python 3 version is too old; '
103 'Please use Python {}.{} or newer.'.format(*MIN_PYTHON_VERSION),
104 file=sys.stderr)
105 sys.exit(1)
106
107
108if __name__ == '__main__':
Mike Frysinger19ec7972020-02-16 12:02:01 -0500109 check_python_version()
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400110
111
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700112# repo default configuration
113#
Mark E. Hamilton55536282016-02-03 15:49:43 -0700114REPO_URL = os.environ.get('REPO_URL', None)
115if not REPO_URL:
116 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -0500117REPO_REV = os.environ.get('REPO_REV')
118if not REPO_REV:
119 REPO_REV = 'stable'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700120
121# Copyright (C) 2008 Google Inc.
122#
123# Licensed under the Apache License, Version 2.0 (the "License");
124# you may not use this file except in compliance with the License.
125# You may obtain a copy of the License at
126#
127# http://www.apache.org/licenses/LICENSE-2.0
128#
129# Unless required by applicable law or agreed to in writing, software
130# distributed under the License is distributed on an "AS IS" BASIS,
131# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132# See the License for the specific language governing permissions and
133# limitations under the License.
134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135# increment this whenever we make important changes to this script
Mike Frysinger19ec7972020-02-16 12:02:01 -0500136VERSION = (2, 4)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137
138# increment this if the MAINTAINER_KEYS block is modified
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500139KEYRING_VERSION = (2, 3)
Mike Frysingere4433652016-09-13 18:06:07 -0400140
141# Each individual key entry is created by using:
142# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700143MAINTAINER_KEYS = """
144
145 Repo Maintainer <repo@android.kernel.org>
146-----BEGIN PGP PUBLIC KEY BLOCK-----
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700147
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
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500182TACbBS+Up3RpfYVfd63c1cDdlru13pQAn3NQy/SN858MkxN+zym86UBgOad2uQIN
183BF5FqOoBEAC8aRtWEtXzeuoQhdFrLTqYs2dy6kl9y+j3DMQYAMs8je582qzUigIO
184ZZxq7T/3WQgghsdw9yPvdzlw9tKdet2TJkR1mtBfSjZQrkKwR0pQP4AD7t/90Whu
185R8Wlu8ysapE2hLxMH5Y2znRQX2LkUYmk0K2ik9AgZEh3AFEg3YLl2pGnSjeSp3ch
186cLX2n/rVZf5LXluZGRG+iov1Ka+8m+UqzohMA1DYNECJW6KPgXsNX++i8/iwZVic
187PWzhRJSQC+QiAZNsKT6HNNKs97YCUVzhjBLnRSxRBPkr0hS/VMWY2V4pbASljWyd
188GYmlDcxheLne0yjes0bJAdvig5rB42FOV0FCM4bDYOVwKfZ7SpzGCYXxtlwe0XNG
189tLW9WA6tICVqNZ/JNiRTBLrsGSkyrEhDPKnIHlHRI5Zux6IHwMVB0lQKHjSop+t6
190oyubqWcPCGGYdz2QGQHNz7huC/Zn0wS4hsoiSwPv6HCq3jNyUkOJ7wZ3ouv60p2I
191kPurgviVaRaPSKTYdKfkcJOtFeqOh1na5IHkXsD9rNctB7tSgfsm0G6qJIVe3ZmJ
1927QAyHBfuLrAWCq5xS8EHDlvxPdAD8EEsa9T32YxcHKIkxr1eSwrUrKb8cPhWq1pp
193Jiylw6G1fZ02VKixqmPC4oFMyg1PO8L2tcQTrnVmZvfFGiaekHKdhQARAQABiQKW
194BBgRAgAgFiEEi7mteT6OYVOvD5pEFlMNXpIPXGUFAl5FqOoCGwICQAkQFlMNXpIP
195XGXBdCAEGQEKAB0WIQSjShO+jna/9GoMAi2i51qCSquWJAUCXkWo6gAKCRCi51qC
196SquWJLzgD/0YEZYS7yKxhP+kk94TcTYMBMSZpU5KFClB77yu4SI1LeXq4ocBT4sp
197EPaOsQiIx//j59J67b7CBe4UeRA6D2n0pw+bCKuc731DFi5X9C1zq3a7E67SQ2yd
198FbYE2fnpVnMqb62g4sTh7JmdxEtXCWBUWL0OEoWouBW1PkFDHx2kYLC7YpZt3+4t
199VtNhSfV8NS6PF8ep3JXHVd2wsC3DQtggeId5GM44o8N0SkwQHNjK8ZD+VZ74ZnhZ
200HeyHskomiOC61LrZWQvxD6VqtfnBQ5GvONO8QuhkiFwMMOnpPVj2k7ngSkd5o27K
2016c53ZESOlR4bAfl0i3RZYC9B5KerGkBE3dTgTzmGjOaahl2eLz4LDPdTwMtS+sAU
2021hPPvZTQeYDdV62bOWUyteMoJu354GgZPQ9eItWYixpNCyOGNcJXl6xk3/OuoP6f
203MciFV8aMxs/7mUR8q1Ei3X9MKu+bbODYj2rC1tMkLj1OaAJkfvRuYrKsQpoUsn4q
204VT9+aciNpU/I7M30watlWo7RfUFI3zaGdMDcMFju1cWt2Un8E3gtscGufzbz1Z5Z
205Gak+tCOWUyuYNWX3noit7Dk6+3JGHGaQettldNu2PLM9SbIXd2EaqK/eEv9BS3dd
206ItkZwzyZXSaQ9UqAceY1AHskJJ5KVXIRLuhP5jBWWo3fnRMyMYt2nwNBAJ9B9TA8
207VlBniwIl5EzCvOFOTGrtewCdHOvr3N3ieypGz1BzyCN9tJMO3G24MwReRal9Fgkr
208BgEEAdpHDwEBB0BhPE/je6OuKgWzJ1mnrUmHhn4IMOHp+58+T5kHU3Oy6YjXBBgR
209AgAgFiEEi7mteT6OYVOvD5pEFlMNXpIPXGUFAl5FqX0CGwIAgQkQFlMNXpIPXGV2
210IAQZFggAHRYhBOH5BA16P22vrIl809O5XaJD5Io5BQJeRal9AAoJENO5XaJD5Io5
211MEkA/3uLmiwANOcgE0zB9zga0T/KkYhYOWFx7zRyDhrTf9spAPwIfSBOAGtwxjLO
212DCce5OaQJl/YuGHvXq2yx5h7T8pdAZ+PAJ4qfIk2LLSidsplTDXOKhOQAuOqUQCf
213cZ7aFsJF4PtcDrfdejyAxbtsSHI=
214=82Tj
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215-----END PGP PUBLIC KEY BLOCK-----
216"""
217
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700218GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500219# NB: The version of git that the repo launcher requires may be much older than
220# the version of git that the main repo source tree requires. Keeping this at
221# an older version also makes it easier for users to upgrade/rollback as needed.
222#
223# git-1.7 is in (EOL) Ubuntu Precise.
224MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700225repodir = '.repo' # name of repo's private directory
226S_repo = 'repo' # special repo repository
227S_manifests = 'manifests' # special manifest repository
228REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700229GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700230GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700231
232
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400233import collections
David Jamesbf79c662013-12-26 14:20:13 -0800234import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700237import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700238import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900239
240if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700241 import urllib.request
242 import urllib.error
243else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700244 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900245 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700246 urllib = imp.new_module('urllib')
247 urllib.request = urllib2
248 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700249
Conley Owens5e0ee142013-09-26 15:50:49 -0700250
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251home_dot_repo = os.path.expanduser('~/.repoconfig')
252gpg_dir = os.path.join(home_dot_repo, 'gnupg')
253
254extra_args = []
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700255
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900256
Mike Frysingerd8fda902020-02-14 00:24:38 -0500257def GetParser(gitc_init=False):
258 """Setup the CLI parser."""
259 if gitc_init:
260 usage = 'repo gitc-init -u url -c client [options]'
261 else:
262 usage = 'repo init -u url [options]'
263
264 parser = optparse.OptionParser(usage=usage)
265
Mike Frysingerf700ac72020-02-06 00:04:21 -0500266 # Logging.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500267 group = parser.add_option_group('Logging options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500268 group.add_option('-q', '--quiet',
269 action='store_true', default=False,
270 help='be quiet')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Mike Frysingerf700ac72020-02-06 00:04:21 -0500272 # Manifest.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500273 group = parser.add_option_group('Manifest options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500274 group.add_option('-u', '--manifest-url',
275 help='manifest repository location', metavar='URL')
276 group.add_option('-b', '--manifest-branch',
277 help='manifest branch or revision', metavar='REVISION')
278 group.add_option('-m', '--manifest-name',
279 help='initial manifest file', metavar='NAME.xml')
Mike Frysingerd8fda902020-02-14 00:24:38 -0500280 cbr_opts = ['--current-branch']
281 # The gitc-init subcommand allocates -c itself, but a lot of init users
282 # want -c, so try to satisfy both as best we can.
283 if not gitc_init:
284 cbr_opts += ['-c']
285 group.add_option(*cbr_opts,
Mike Frysingerf700ac72020-02-06 00:04:21 -0500286 dest='current_branch_only', action='store_true',
287 help='fetch only current manifest branch from server')
288 group.add_option('--mirror', action='store_true',
289 help='create a replica of the remote repositories '
290 'rather than a client working directory')
291 group.add_option('--reference',
292 help='location of mirror directory', metavar='DIR')
293 group.add_option('--dissociate', action='store_true',
294 help='dissociate from reference mirrors after clone')
295 group.add_option('--depth', type='int', default=None,
296 help='create a shallow clone with given depth; '
297 'see git clone')
298 group.add_option('--partial-clone', action='store_true',
299 help='perform partial clone (https://git-scm.com/'
300 'docs/gitrepository-layout#_code_partialclone_code)')
301 group.add_option('--clone-filter', action='store', default='blob:none',
302 help='filter for use with --partial-clone '
303 '[default: %default]')
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500304 group.add_option('--worktree', action='store_true',
305 help=optparse.SUPPRESS_HELP)
Mike Frysingerf700ac72020-02-06 00:04:21 -0500306 group.add_option('--archive', action='store_true',
307 help='checkout an archive instead of a git repository for '
308 'each project. See git archive.')
309 group.add_option('--submodules', action='store_true',
310 help='sync any submodules associated with the manifest repo')
311 group.add_option('-g', '--groups', default='default',
312 help='restrict manifest projects to ones with specified '
313 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
314 metavar='GROUP')
315 group.add_option('-p', '--platform', default='auto',
316 help='restrict manifest projects to ones with a specified '
317 'platform group [auto|all|none|linux|darwin|...]',
318 metavar='PLATFORM')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500319 group.add_option('--no-clone-bundle',
320 dest='clone_bundle', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500321 help='disable use of /clone.bundle on HTTP/HTTPS')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500322 group.add_option('--no-tags',
323 dest='tags', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500324 help="don't fetch tags in the manifest")
Doug Anderson49cd59b2011-06-13 21:42:06 -0700325
Mike Frysingerf700ac72020-02-06 00:04:21 -0500326 # Tool.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500327 group = parser.add_option_group('repo Version options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500328 group.add_option('--repo-url', metavar='URL',
329 help='repo repository location ($REPO_URL)')
330 group.add_option('--repo-branch', metavar='REVISION',
331 help='repo branch or revision ($REPO_REV)')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500332 group.add_option('--no-repo-verify',
333 dest='repo_verify', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500334 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700335
Mike Frysingerf700ac72020-02-06 00:04:21 -0500336 # Other.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500337 group = parser.add_option_group('Other options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500338 group.add_option('--config-name',
339 action='store_true', default=False,
340 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700341
Mike Frysingerd8fda902020-02-14 00:24:38 -0500342 # gitc-init specific settings.
343 if gitc_init:
344 group = parser.add_option_group('GITC options')
345 group.add_option('-f', '--manifest-file',
346 help='Optional manifest file to use for this GITC client.')
347 group.add_option('-c', '--gitc-client',
348 help='Name of the gitc_client instance to create or modify.')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700349
Mike Frysingerd8fda902020-02-14 00:24:38 -0500350 return parser
Simran Basi1efc2b42015-08-05 15:04:22 -0700351
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900352
Mike Frysinger62285d22020-02-12 08:01:38 -0500353# This is a poor replacement for subprocess.run until we require Python 3.6+.
354RunResult = collections.namedtuple(
355 'RunResult', ('returncode', 'stdout', 'stderr'))
356
357
358class RunError(Exception):
359 """Error when running a command failed."""
360
361
362def run_command(cmd, **kwargs):
363 """Run |cmd| and return its output."""
364 check = kwargs.pop('check', False)
365 if kwargs.pop('capture_output', False):
366 kwargs.setdefault('stdout', subprocess.PIPE)
367 kwargs.setdefault('stderr', subprocess.PIPE)
368 cmd_input = kwargs.pop('input', None)
369
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500370 def decode(output):
371 """Decode |output| to text."""
372 if output is None:
373 return output
374 try:
375 return output.decode('utf-8')
376 except UnicodeError:
377 print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
378 file=sys.stderr)
379 # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
380 return output.decode('utf-8', 'replace')
381
Mike Frysinger62285d22020-02-12 08:01:38 -0500382 # Run & package the results.
383 proc = subprocess.Popen(cmd, **kwargs)
384 (stdout, stderr) = proc.communicate(input=cmd_input)
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500385 trace.print(':', ' '.join(cmd))
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500386 ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
Mike Frysinger62285d22020-02-12 08:01:38 -0500387
388 # If things failed, print useful debugging output.
389 if check and ret.returncode:
390 print('repo: error: "%s" failed with exit status %s' %
391 (cmd[0], ret.returncode), file=sys.stderr)
392 print(' cwd: %s\n cmd: %r' %
393 (kwargs.get('cwd', os.getcwd()), cmd), file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900394
Mike Frysinger62285d22020-02-12 08:01:38 -0500395 def _print_output(name, output):
396 if output:
397 print(' %s:\n >> %s' % (name, '\n >> '.join(output.splitlines())),
398 file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900399
Mike Frysinger62285d22020-02-12 08:01:38 -0500400 _print_output('stdout', ret.stdout)
401 _print_output('stderr', ret.stderr)
402 raise RunError(ret)
403
404 return ret
405
406
Simran Basi8ce50412015-08-28 14:25:44 -0700407_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700408
409
Simran Basi8ce50412015-08-28 14:25:44 -0700410def get_gitc_manifest_dir():
411 global _gitc_manifest_dir
412 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700413 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700414 try:
415 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
416 for line in gitc_config:
417 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
418 if match:
419 _gitc_manifest_dir = match.group('gitc_manifest_dir')
420 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700421 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700422 return _gitc_manifest_dir
423
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700424
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700425def gitc_parse_clientdir(gitc_fs_path):
426 """Parse a path in the GITC FS and return its client name.
427
428 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
429
430 @returns: The GITC client name
431 """
432 if gitc_fs_path == GITC_FS_ROOT_DIR:
433 return None
434 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
435 manifest_dir = get_gitc_manifest_dir()
436 if manifest_dir == '':
437 return None
438 if manifest_dir[-1] != '/':
439 manifest_dir += '/'
440 if gitc_fs_path == manifest_dir:
441 return None
442 if not gitc_fs_path.startswith(manifest_dir):
443 return None
444 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
445 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
446
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700447
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700448class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700449
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700450 """Indicate the remote clone of repo itself failed.
451 """
452
453
Simran Basi1efc2b42015-08-05 15:04:22 -0700454def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700455 """Installs repo by cloning it over the network.
456 """
Mike Frysingerd8fda902020-02-14 00:24:38 -0500457 parser = GetParser(gitc_init=gitc_init)
458 opt, args = parser.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800459 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500460 parser.print_usage()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700461 sys.exit(1)
462
463 url = opt.repo_url
464 if not url:
465 url = REPO_URL
466 extra_args.append('--repo-url=%s' % url)
467
468 branch = opt.repo_branch
469 if not branch:
470 branch = REPO_REV
471 extra_args.append('--repo-branch=%s' % branch)
472
473 if branch.startswith('refs/heads/'):
474 branch = branch[len('refs/heads/'):]
475 if branch.startswith('refs/'):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400476 print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700477 raise CloneFailure()
478
David Jamesbf79c662013-12-26 14:20:13 -0800479 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700480 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700481 gitc_manifest_dir = get_gitc_manifest_dir()
482 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400483 print('fatal: GITC filesystem is not available. Exiting...',
484 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700485 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700486 gitc_client = opt.gitc_client
487 if not gitc_client:
488 gitc_client = gitc_parse_clientdir(os.getcwd())
489 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400490 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700491 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700492 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700493 if not os.path.exists(client_dir):
494 os.makedirs(client_dir)
495 os.chdir(client_dir)
496 if os.path.exists(repodir):
497 # This GITC Client has already initialized repo so continue.
498 return
499
David Jamesbf79c662013-12-26 14:20:13 -0800500 os.mkdir(repodir)
501 except OSError as e:
502 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400503 print('fatal: cannot make %s directory: %s'
504 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900505 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700506 # name. Instead exit immediately.
507 #
508 sys.exit(1)
509
510 _CheckGitVersion()
511 try:
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500512 if not opt.repo_verify:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100513 do_verify = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700514 else:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100515 if NeedSetupGnuPG():
516 do_verify = SetupGnuPG(opt.quiet)
517 else:
518 do_verify = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700519
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700520 dst = os.path.abspath(os.path.join(repodir, S_repo))
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500521 _Clone(url, dst, opt.quiet, opt.clone_bundle)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700522
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100523 if do_verify:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700524 rev = _Verify(dst, branch, opt.quiet)
525 else:
526 rev = 'refs/remotes/origin/%s^0' % branch
527
528 _Checkout(dst, branch, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200529
530 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400531 print("warning: '%s' does not look like a git-repo repository, is "
532 "REPO_URL set correctly?" % url, file=sys.stderr)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200533
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700534 except CloneFailure:
535 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400536 print('fatal: repo init failed; run without --quiet to see why',
537 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700538 raise
539
540
Mike Frysinger62285d22020-02-12 08:01:38 -0500541def run_git(*args, **kwargs):
542 """Run git and return execution details."""
543 kwargs.setdefault('capture_output', True)
544 kwargs.setdefault('check', True)
545 try:
546 return run_command([GIT] + list(args), **kwargs)
547 except OSError as e:
548 print(file=sys.stderr)
549 print('repo: error: "%s" is not available' % GIT, file=sys.stderr)
550 print('repo: error: %s' % e, file=sys.stderr)
551 print(file=sys.stderr)
552 print('Please make sure %s is installed and in your path.' % GIT,
553 file=sys.stderr)
554 sys.exit(1)
555 except RunError:
556 raise CloneFailure()
557
558
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400559# The git version info broken down into components for easy analysis.
560# Similar to Python's sys.version_info.
561GitVersion = collections.namedtuple(
562 'GitVersion', ('major', 'minor', 'micro', 'full'))
563
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900564
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400565def ParseGitVersion(ver_str=None):
566 if ver_str is None:
567 # Load the version ourselves.
Mike Frysinger62285d22020-02-12 08:01:38 -0500568 ver_str = run_git('--version').stdout
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400569
Conley Owensff0a3c82014-01-30 14:46:03 -0800570 if not ver_str.startswith('git version '):
571 return None
572
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400573 full_version = ver_str[len('git version '):].strip()
574 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800575 to_tuple = []
576 for num_str in num_ver_str.split('.')[:3]:
577 if num_str.isdigit():
578 to_tuple.append(int(num_str))
579 else:
580 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400581 to_tuple.append(full_version)
582 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800583
584
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400585def _CheckGitVersion():
Mike Frysinger62285d22020-02-12 08:01:38 -0500586 ver_act = ParseGitVersion()
Conley Owensff0a3c82014-01-30 14:46:03 -0800587 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400588 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700589 raise CloneFailure()
590
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700591 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900592 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400593 print('fatal: git %s or later required' % need, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700594 raise CloneFailure()
595
596
Mike Frysinger84094102020-02-11 02:10:28 -0500597def SetGitTrace2ParentSid(env=None):
598 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
599 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
600 # (1) Be unique (2) be valid filename (3) be fixed length.
601 #
602 # Since we always export this variable, we try to avoid more expensive calls.
603 # e.g. We don't attempt hostname lookups or hashing the results.
604 if env is None:
605 env = os.environ
606
607 KEY = 'GIT_TRACE2_PARENT_SID'
608
609 now = datetime.datetime.utcnow()
610 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
611
612 # If it's already set, then append ourselves.
613 if KEY in env:
614 value = env[KEY] + '/' + value
615
616 _setenv(KEY, value, env=env)
617
618
619def _setenv(key, value, env=None):
620 """Set |key| in the OS environment |env| to |value|."""
621 if env is None:
622 env = os.environ
623 # Environment handling across systems is messy.
624 try:
625 env[key] = value
626 except UnicodeEncodeError:
627 env[key] = value.encode()
628
629
Conley Owensc9129d92012-10-01 16:12:28 -0700630def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700631 if not os.path.isdir(home_dot_repo):
632 return True
633
634 kv = os.path.join(home_dot_repo, 'keyring-version')
635 if not os.path.exists(kv):
636 return True
637
638 kv = open(kv).read()
639 if not kv:
640 return True
641
David Pursehouse685f0802012-11-14 08:34:39 +0900642 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700643 if kv < KEYRING_VERSION:
644 return True
645 return False
646
647
Conley Owensc9129d92012-10-01 16:12:28 -0700648def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800649 try:
650 os.mkdir(home_dot_repo)
651 except OSError as e:
652 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400653 print('fatal: cannot make %s directory: %s'
654 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700655 sys.exit(1)
656
David Jamesbf79c662013-12-26 14:20:13 -0800657 try:
658 os.mkdir(gpg_dir, stat.S_IRWXU)
659 except OSError as e:
660 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400661 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
662 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700663 sys.exit(1)
664
Mike Frysinger19a1f222020-02-14 16:28:13 -0500665 if not quiet:
666 print('repo: Updating release signing keys to keyset ver %s' %
667 ('.'.join(str(x) for x in KEYRING_VERSION),))
668 # NB: We use --homedir (and cwd below) because some environments (Windows) do
669 # not correctly handle full native paths. We avoid the issue by changing to
670 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
671 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
672 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700673 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500674 # gpg can be pretty chatty. Always capture the output and if something goes
675 # wrong, the builtin check failure will dump stdout & stderr for debugging.
676 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
677 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900678 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900679 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700680 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400681 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
682 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
683 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700684 return False
685
Mike Frysinger3164d402019-11-11 05:40:22 -0500686 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
687 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700688 return True
689
690
Mike Frysinger62285d22020-02-12 08:01:38 -0500691def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700692 """Set a git configuration option to the specified value.
693 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500694 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700695
696
Mike Frysinger949bc342020-02-18 21:37:00 -0500697def _GetRepoConfig(name):
698 """Read a repo configuration option."""
699 config = os.path.join(home_dot_repo, 'config')
700 if not os.path.exists(config):
701 return None
702
703 cmd = ['config', '--file', config, '--get', name]
704 ret = run_git(*cmd, check=False)
705 if ret.returncode == 0:
706 return ret.stdout
707 elif ret.returncode == 1:
708 return None
709 else:
710 print('repo: error: git %s failed:\n%s' % (' '.join(cmd), ret.stderr),
711 file=sys.stderr)
712 raise RunError()
713
714
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700715def _InitHttp():
716 handlers = []
717
Sarah Owens1f7627f2012-10-31 09:21:55 -0700718 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700719 try:
720 import netrc
721 n = netrc.netrc()
722 for host in n.hosts:
723 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700724 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800725 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900726 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700727 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700728 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
729 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700730
731 if 'http_proxy' in os.environ:
732 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700733 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700734 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700735 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
736 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
737 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700738
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700739
Mike Frysinger62285d22020-02-12 08:01:38 -0500740def _Fetch(url, cwd, src, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700741 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400742 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700743
Mike Frysinger62285d22020-02-12 08:01:38 -0500744 cmd = ['fetch']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 if quiet:
746 cmd.append('--quiet')
747 err = subprocess.PIPE
748 else:
749 err = None
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700750 cmd.append(src)
751 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800752 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger62285d22020-02-12 08:01:38 -0500753 run_git(*cmd, stderr=err, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700755
Mike Frysinger62285d22020-02-12 08:01:38 -0500756def _DownloadBundle(url, cwd, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700757 if not url.endswith('/'):
758 url += '/'
759 url += 'clone.bundle'
760
Mike Frysinger62285d22020-02-12 08:01:38 -0500761 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
762 check=False)
763 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700764 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
765 if m:
766 new_url = m.group(1)
767 old_url = m.group(2)
768 if url.startswith(old_url):
769 url = new_url + url[len(old_url):]
770 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700771
772 if not url.startswith('http:') and not url.startswith('https:'):
773 return False
774
Mike Frysinger62285d22020-02-12 08:01:38 -0500775 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700776 try:
777 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700778 r = urllib.request.urlopen(url)
779 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200780 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700781 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400782 print('fatal: Cannot get %s' % url, file=sys.stderr)
783 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700784 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700785 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400786 print('fatal: Cannot get %s' % url, file=sys.stderr)
787 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700788 raise CloneFailure()
789 try:
790 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400791 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700792 while True:
793 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400794 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700795 return True
796 dest.write(buf)
797 finally:
798 r.close()
799 finally:
800 dest.close()
801
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700802
Mike Frysinger62285d22020-02-12 08:01:38 -0500803def _ImportBundle(cwd):
804 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700805 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500806 _Fetch(cwd, cwd, path, True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700807 finally:
808 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700809
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700810
Mike Frysinger62285d22020-02-12 08:01:38 -0500811def _Clone(url, cwd, quiet, clone_bundle):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812 """Clones a git repository to a new subdirectory of repodir
813 """
814 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500815 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700816 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500817 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400818 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819 raise CloneFailure()
820
Mike Frysinger62285d22020-02-12 08:01:38 -0500821 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700823 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500824 _SetConfig(cwd, 'remote.origin.url', url)
825 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700826 'remote.origin.fetch',
827 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysinger62285d22020-02-12 08:01:38 -0500828 if clone_bundle and _DownloadBundle(url, cwd, quiet):
829 _ImportBundle(cwd)
830 _Fetch(url, cwd, 'origin', quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831
832
833def _Verify(cwd, branch, quiet):
834 """Verify the branch has been signed by a tag.
835 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500836 try:
837 ret = run_git('describe', 'origin/%s' % branch, cwd=cwd)
838 cur = ret.stdout.strip()
839 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400840 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
Mike Frysinger62285d22020-02-12 08:01:38 -0500841 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700842
843 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
844 if m:
845 cur = m.group(1)
846 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400847 print(file=sys.stderr)
848 print("info: Ignoring branch '%s'; using tagged release '%s'"
849 % (branch, cur), file=sys.stderr)
850 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700851
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800852 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500853 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -0500854 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855 return '%s^0' % cur
856
857
858def _Checkout(cwd, branch, rev, quiet):
859 """Checkout an upstream branch into the repository and track it.
860 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500861 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700862
863 _SetConfig(cwd, 'branch.default.remote', 'origin')
864 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
865
Mike Frysinger62285d22020-02-12 08:01:38 -0500866 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700867
Mike Frysinger62285d22020-02-12 08:01:38 -0500868 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700869 if not quiet:
870 cmd.append('-v')
871 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -0500872 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873
874
875def _FindRepo():
876 """Look for a repo installation, starting at the current directory.
877 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200878 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879 repo = None
880
Anthony Newnamdf14a702011-01-09 17:31:57 -0800881 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200882 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700883 and curdir != olddir \
884 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200885 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886 if not os.path.isfile(repo):
887 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200888 olddir = curdir
889 curdir = os.path.dirname(curdir)
890 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700891
892
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700893class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700894 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500895 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700896
897
Mike Frysinger949bc342020-02-18 21:37:00 -0500898def _ExpandAlias(name):
899 """Look up user registered aliases."""
900 # We don't resolve aliases for existing subcommands. This matches git.
901 if name in {'gitc-init', 'help', 'init'}:
902 return name, []
903
904 alias = _GetRepoConfig('alias.%s' % (name,))
905 if alias is None:
906 return name, []
907
908 args = alias.strip().split(' ', 1)
909 name = args[0]
910 if len(args) == 2:
911 args = shlex.split(args[1])
912 else:
913 args = []
914 return name, args
915
916
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917def _ParseArguments(args):
918 cmd = None
919 opt = _Options()
920 arg = []
921
Sarah Owensa6053d52012-11-01 13:36:50 -0700922 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700923 a = args[i]
924 if a == '-h' or a == '--help':
925 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500926 elif a == '--version':
927 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -0500928 elif a == '--trace':
929 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 elif not a.startswith('-'):
931 cmd = a
932 arg = args[i + 1:]
933 break
934 return cmd, opt, arg
935
936
937def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700938 gitc_usage = ""
939 if get_gitc_manifest_dir():
940 gitc_usage = " gitc-init Initialize a GITC Client.\n"
941
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400942 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700943 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944
945repo is not yet installed. Use "repo init" to install it here.
946
947The most commonly used repo commands are:
948
949 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700950""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700951 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
953For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400954""")
955 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700956
957
958def _Help(args):
959 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -0500960 if args[0] in {'init', 'gitc-init'}:
961 parser = GetParser(gitc_init=args[0] == 'gitc-init')
962 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700963 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400965 print("error: '%s' is not a bootstrap command.\n"
966 ' For access to online help, install repo ("repo init").'
967 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968 else:
969 _Usage()
970 sys.exit(1)
971
972
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500973def _Version():
974 """Show version information."""
975 print('<repo not installed>')
976 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
977 print(' (from %s)' % (__file__,))
978 print('git %s' % (ParseGitVersion().full,))
979 print('Python %s' % sys.version)
980 sys.exit(0)
981
982
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700983def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400984 print('error: repo is not installed. Use "repo init" to install it here.',
985 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986 sys.exit(1)
987
988
989def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400990 print("""error: command '%s' requires repo to be installed first.
991 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700992 sys.exit(1)
993
994
995def _RunSelf(wrapper_path):
996 my_dir = os.path.dirname(wrapper_path)
997 my_main = os.path.join(my_dir, 'main.py')
998 my_git = os.path.join(my_dir, '.git')
999
1000 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -07001001 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001002 'project.py',
1003 'subcmds']:
1004 if not os.path.exists(os.path.join(my_dir, name)):
1005 return None, None
1006 return my_main, my_git
1007 return None, None
1008
1009
1010def _SetDefaultsTo(gitdir):
1011 global REPO_URL
1012 global REPO_REV
1013
1014 REPO_URL = gitdir
Mike Frysinger62285d22020-02-12 08:01:38 -05001015 try:
1016 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD')
1017 REPO_REV = ret.stdout.strip()
1018 except CloneFailure:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001019 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020 sys.exit(1)
1021
1022
1023def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001024 cmd, opt, args = _ParseArguments(orig_args)
1025
Mike Frysinger84094102020-02-11 02:10:28 -05001026 # We run this early as we run some git commands ourselves.
1027 SetGitTrace2ParentSid()
1028
Dan Willemsen745b4ad2015-10-06 15:23:19 -07001029 repo_main, rel_repo_dir = None, None
1030 # Don't use the local repo copy, make sure to switch to the gitc client first.
1031 if cmd != 'gitc-init':
1032 repo_main, rel_repo_dir = _FindRepo()
1033
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001034 wrapper_path = os.path.abspath(__file__)
1035 my_main, my_git = _RunSelf(wrapper_path)
1036
Simran Basi8ce50412015-08-28 14:25:44 -07001037 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -07001038 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001039 print('error: repo cannot be used in the GITC local manifest directory.'
1040 '\nIf you want to work on this GITC client please rerun this '
1041 'command from the corresponding client under /gitc/',
1042 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001043 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001044 if not repo_main:
Mike Frysinger949bc342020-02-18 21:37:00 -05001045 # Only expand aliases here since we'll be parsing the CLI ourselves.
1046 # If we had repo_main, alias expansion would happen in main.py.
1047 cmd, alias_args = _ExpandAlias(cmd)
1048 args = alias_args + args
1049
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050 if opt.help:
1051 _Usage()
1052 if cmd == 'help':
1053 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001054 if opt.version or cmd == 'version':
1055 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001056 if not cmd:
1057 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001058 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059 if my_git:
1060 _SetDefaultsTo(my_git)
1061 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001062 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001064 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001065 print("fatal: cloning the git-repo repository failed, will remove "
1066 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001067 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001069 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070 else:
1071 _NoCommands(cmd)
1072
1073 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001074 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075
David Pursehouse685f0802012-11-14 08:34:39 +09001076 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001077 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001078 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079 '--wrapper-version=%s' % ver_str,
1080 '--wrapper-path=%s' % wrapper_path,
1081 '--']
1082 me.extend(orig_args)
1083 me.extend(extra_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001084 exec_command(me)
1085 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1086 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087
1088
1089if __name__ == '__main__':
1090 main(sys.argv[1:])