blob: 18649b898b129556d454345bf4092ac037d800fb [file] [log] [blame]
David Pursehouse8898e2f2012-11-14 07:51:03 +09001#!/usr/bin/env python
Mike Frysingerf6013762019-06-13 02:30:51 -04002# -*- coding:utf-8 -*-
Mike Frysingerf241f8c2020-02-20 17:08:43 -05003#
4# Copyright (C) 2008 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017
Mike Frysinger87fb5a12019-06-13 01:54:46 -040018"""Repo launcher.
19
20This is a standalone tool that people may copy to anywhere in their system.
21It is used to get an initial repo client checkout, and after that it runs the
22copy of repo in the checkout.
23"""
24
Mike Frysingerc92ce5c2019-06-13 01:14:23 -040025from __future__ import print_function
26
Mike Frysinger84094102020-02-11 02:10:28 -050027import datetime
Mike Frysinger3ba716f2019-06-13 01:48:12 -040028import os
29import platform
Mike Frysinger949bc342020-02-18 21:37:00 -050030import shlex
Mike Frysinger3ba716f2019-06-13 01:48:12 -040031import subprocess
32import sys
33
34
Mike Frysinger47692012021-01-04 21:55:26 -050035# These should never be newer than the main.py version since this needs to be a
36# bit more flexible with older systems. See that file for more details on the
37# versions we select.
38MIN_PYTHON_VERSION_SOFT = (3, 6)
39MIN_PYTHON_VERSION_HARD = (3, 5)
40
41
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050042# Keep basic logic in sync with repo_trace.py.
43class Trace(object):
44 """Trace helper logic."""
45
46 REPO_TRACE = 'REPO_TRACE'
47
48 def __init__(self):
49 self.set(os.environ.get(self.REPO_TRACE) == '1')
50
51 def set(self, value):
52 self.enabled = bool(value)
53
54 def print(self, *args, **kwargs):
55 if self.enabled:
56 print(*args, **kwargs)
57
58
59trace = Trace()
60
61
Mike Frysinger3ba716f2019-06-13 01:48:12 -040062def exec_command(cmd):
63 """Execute |cmd| or return None on failure."""
Mike Frysinger6fb0cb52020-02-12 09:39:23 -050064 trace.print(':', ' '.join(cmd))
Mike Frysinger3ba716f2019-06-13 01:48:12 -040065 try:
66 if platform.system() == 'Windows':
67 ret = subprocess.call(cmd)
68 sys.exit(ret)
69 else:
70 os.execvp(cmd[0], cmd)
Mike Frysinger72b6dc82020-02-12 17:04:32 -050071 except Exception:
Mike Frysinger3ba716f2019-06-13 01:48:12 -040072 pass
73
74
75def check_python_version():
76 """Make sure the active Python version is recent enough."""
77 def reexec(prog):
78 exec_command([prog] + sys.argv)
79
Mike Frysinger3ba716f2019-06-13 01:48:12 -040080 ver = sys.version_info
81 major = ver.major
82 minor = ver.minor
83
84 # Abort on very old Python 2 versions.
85 if (major, minor) < (2, 7):
86 print('repo: error: Your Python version is too old. '
87 'Please use Python {}.{} or newer instead.'.format(
Mike Frysinger47692012021-01-04 21:55:26 -050088 *MIN_PYTHON_VERSION_SOFT), file=sys.stderr)
Mike Frysinger3ba716f2019-06-13 01:48:12 -040089 sys.exit(1)
90
91 # Try to re-exec the version specific Python 3 if needed.
Mike Frysinger47692012021-01-04 21:55:26 -050092 if (major, minor) < MIN_PYTHON_VERSION_SOFT:
Mike Frysinger3ba716f2019-06-13 01:48:12 -040093 # Python makes releases ~once a year, so try our min version +10 to help
94 # bridge the gap. This is the fallback anyways so perf isn't critical.
Mike Frysinger47692012021-01-04 21:55:26 -050095 min_major, min_minor = MIN_PYTHON_VERSION_SOFT
Mike Frysinger3ba716f2019-06-13 01:48:12 -040096 for inc in range(0, 10):
97 reexec('python{}.{}'.format(min_major, min_minor + inc))
98
Mike Frysinger47692012021-01-04 21:55:26 -050099 # Fallback to older versions if possible.
100 for inc in range(MIN_PYTHON_VERSION_SOFT[1] - MIN_PYTHON_VERSION_HARD[1], 0, -1):
101 # Don't downgrade, and don't reexec ourselves (which would infinite loop).
102 if (min_major, min_minor - inc) <= (major, minor):
103 break
104 reexec('python{}.{}'.format(min_major, min_minor - inc))
105
Mike Frysingerce9b6c42021-01-04 22:07:19 -0500106 # Try the generic Python 3 wrapper, but only if it's new enough. If it
107 # isn't, we want to just give up below and make the user resolve things.
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400108 try:
109 proc = subprocess.Popen(
110 ['python3', '-c', 'import sys; '
111 'print(sys.version_info.major, sys.version_info.minor)'],
112 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
113 (output, _) = proc.communicate()
114 python3_ver = tuple(int(x) for x in output.decode('utf-8').split())
115 except (OSError, subprocess.CalledProcessError):
116 python3_ver = None
117
Mike Frysinger47692012021-01-04 21:55:26 -0500118 # If the python3 version looks like it's new enough, give it a try.
119 if (python3_ver and python3_ver >= MIN_PYTHON_VERSION_HARD
Craig Northway0e8828c2021-05-05 08:49:26 -0700120 and python3_ver != (major, minor)):
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400121 reexec('python3')
122
123 # We're still here, so diagnose things for the user.
124 if major < 3:
Mike Frysingerce9b6c42021-01-04 22:07:19 -0500125 print('repo: error: Python 2 is no longer supported; '
Mike Frysinger47692012021-01-04 21:55:26 -0500126 'Please upgrade to Python {}.{}+.'.format(*MIN_PYTHON_VERSION_HARD),
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400127 file=sys.stderr)
Mike Frysingerce9b6c42021-01-04 22:07:19 -0500128 sys.exit(1)
Mike Frysinger47692012021-01-04 21:55:26 -0500129 elif (major, minor) < MIN_PYTHON_VERSION_HARD:
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400130 print('repo: error: Python 3 version is too old; '
Mike Frysinger47692012021-01-04 21:55:26 -0500131 'Please use Python {}.{} or newer.'.format(*MIN_PYTHON_VERSION_HARD),
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400132 file=sys.stderr)
133 sys.exit(1)
134
135
136if __name__ == '__main__':
Mike Frysinger19ec7972020-02-16 12:02:01 -0500137 check_python_version()
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400138
139
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700140# repo default configuration
141#
Mark E. Hamilton55536282016-02-03 15:49:43 -0700142REPO_URL = os.environ.get('REPO_URL', None)
143if not REPO_URL:
144 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -0500145REPO_REV = os.environ.get('REPO_REV')
146if not REPO_REV:
147 REPO_REV = 'stable'
Mike Frysingera1cd7702021-04-20 23:38:04 -0400148# URL to file bug reports for repo tool issues.
149BUG_URL = 'https://bugs.chromium.org/p/gerrit/issues/entry?template=Repo+tool+issue'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700150
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700151# increment this whenever we make important changes to this script
Josip Sokcevica24185e2022-11-22 16:27:24 -0800152VERSION = (2, 30)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700153
154# increment this if the MAINTAINER_KEYS block is modified
Mike Frysinger9cc1d702020-02-13 18:28:03 -0500155KEYRING_VERSION = (2, 3)
Mike Frysingere4433652016-09-13 18:06:07 -0400156
157# Each individual key entry is created by using:
158# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159MAINTAINER_KEYS = """
160
161 Repo Maintainer <repo@android.kernel.org>
162-----BEGIN PGP PUBLIC KEY BLOCK-----
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700163
164mQGiBEj3ugERBACrLJh/ZPyVSKeClMuznFIrsQ+hpNnmJGw1a9GXKYKk8qHPhAZf
165WKtrBqAVMNRLhL85oSlekRz98u41H5si5zcuv+IXJDF5MJYcB8f22wAy15lUqPWi
166VCkk1l8qqLiuW0fo+ZkPY5qOgrvc0HW1SmdH649uNwqCbcKb6CxaTxzhOwCgj3AP
167xI1WfzLqdJjsm1Nq98L0cLcD/iNsILCuw44PRds3J75YP0pze7YF/6WFMB6QSFGu
168aUX1FsTTztKNXGms8i5b2l1B8JaLRWq/jOnZzyl1zrUJhkc0JgyZW5oNLGyWGhKD
169Fxp5YpHuIuMImopWEMFIRQNrvlg+YVK8t3FpdI1RY0LYqha8pPzANhEYgSfoVzOb
170fbfbA/4ioOrxy8ifSoga7ITyZMA+XbW8bx33WXutO9N7SPKS/AK2JpasSEVLZcON
171ae5hvAEGVXKxVPDjJBmIc2cOe7kOKSi3OxLzBqrjS2rnjiP4o0ekhZIe4+ocwVOg
172e0PLlH5avCqihGRhpoqDRsmpzSHzJIxtoeb+GgGEX8KkUsVAhbQpUmVwbyBNYWlu
173dGFpbmVyIDxyZXBvQGFuZHJvaWQua2VybmVsLm9yZz6IYAQTEQIAIAUCSPe6AQIb
174AwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEBZTDV6SD1xl1GEAn0x/OKQpy7qI
1756G73NJviU0IUMtftAKCFMUhGb/0bZvQ8Rm3QCUpWHyEIu7kEDQRI97ogEBAA2wI6
1765fs9y/rMwD6dkD/vK9v4C9mOn1IL5JCPYMJBVSci+9ED4ChzYvfq7wOcj9qIvaE0
177GwCt2ar7Q56me5J+byhSb32Rqsw/r3Vo5cZMH80N4cjesGuSXOGyEWTe4HYoxnHv
178gF4EKI2LK7xfTUcxMtlyn52sUpkfKsCpUhFvdmbAiJE+jCkQZr1Z8u2KphV79Ou+
179P1N5IXY/XWOlq48Qf4MWCYlJFrB07xjUjLKMPDNDnm58L5byDrP/eHysKexpbakL
180xCmYyfT6DV1SWLblpd2hie0sL3YejdtuBMYMS2rI7Yxb8kGuqkz+9l1qhwJtei94
1815MaretDy/d/JH/pRYkRf7L+ke7dpzrP+aJmcz9P1e6gq4NJsWejaALVASBiioqNf
182QmtqSVzF1wkR5avZkFHuYvj6V/t1RrOZTXxkSk18KFMJRBZrdHFCWbc5qrVxUB6e
183N5pja0NFIUCigLBV1c6I2DwiuboMNh18VtJJh+nwWeez/RueN4ig59gRTtkcc0PR
18435tX2DR8+xCCFVW/NcJ4PSePYzCuuLvp1vEDHnj41R52Fz51hgddT4rBsp0nL+5I
185socSOIIezw8T9vVzMY4ArCKFAVu2IVyBcahTfBS8q5EM63mONU6UVJEozfGljiMw
186xuQ7JwKcw0AUEKTKG7aBgBaTAgT8TOevpvlw91cAAwUP/jRkyVi/0WAb0qlEaq/S
187ouWxX1faR+vU3b+Y2/DGjtXQMzG0qpetaTHC/AxxHpgt/dCkWI6ljYDnxgPLwG0a
188Oasm94BjZc6vZwf1opFZUKsjOAAxRxNZyjUJKe4UZVuMTk6zo27Nt3LMnc0FO47v
189FcOjRyquvgNOS818irVHUf12waDx8gszKxQTTtFxU5/ePB2jZmhP6oXSe4K/LG5T
190+WBRPDrHiGPhCzJRzm9BP0lTnGCAj3o9W90STZa65RK7IaYpC8TB35JTBEbrrNCp
191w6lzd74LnNEp5eMlKDnXzUAgAH0yzCQeMl7t33QCdYx2hRs2wtTQSjGfAiNmj/WW
192Vl5Jn+2jCDnRLenKHwVRFsBX2e0BiRWt/i9Y8fjorLCXVj4z+7yW6DawdLkJorEo
193p3v5ILwfC7hVx4jHSnOgZ65L9s8EQdVr1ckN9243yta7rNgwfcqb60ILMFF1BRk/
1940V7wCL+68UwwiQDvyMOQuqkysKLSDCLb7BFcyA7j6KG+5hpsREstFX2wK1yKeraz
1955xGrFy8tfAaeBMIQ17gvFSp/suc9DYO0ICK2BISzq+F+ZiAKsjMYOBNdH/h0zobQ
196HTHs37+/QLMomGEGKZMWi0dShU2J5mNRQu3Hhxl3hHDVbt5CeJBb26aQcQrFz69W
197zE3GNvmJosh6leayjtI9P2A6iEkEGBECAAkFAkj3uiACGwwACgkQFlMNXpIPXGWp
Mike Frysinger9cc1d702020-02-13 18:28:03 -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
229cZ7aFsJF4PtcDrfdejyAxbtsSHI=
230=82Tj
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700231-----END PGP PUBLIC KEY BLOCK-----
232"""
233
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700234GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500235# NB: The version of git that the repo launcher requires may be much older than
236# the version of git that the main repo source tree requires. Keeping this at
237# an older version also makes it easier for users to upgrade/rollback as needed.
238#
239# git-1.7 is in (EOL) Ubuntu Precise.
240MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700241repodir = '.repo' # name of repo's private directory
242S_repo = 'repo' # special repo repository
243S_manifests = 'manifests' # special manifest repository
244REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700245GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700246GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247
248
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400249import collections
David Jamesbf79c662013-12-26 14:20:13 -0800250import errno
Mike Frysingere5670c82021-01-07 22:14:25 -0500251import json
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700253import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700254import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700255import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900256
257if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700258 import urllib.request
259 import urllib.error
260else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700261 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900262 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700263 urllib = imp.new_module('urllib')
264 urllib.request = urllib2
265 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266
Conley Owens5e0ee142013-09-26 15:50:49 -0700267
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268home_dot_repo = os.path.expanduser('~/.repoconfig')
269gpg_dir = os.path.join(home_dot_repo, 'gnupg')
270
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900271
Mike Frysingerd8fda902020-02-14 00:24:38 -0500272def GetParser(gitc_init=False):
273 """Setup the CLI parser."""
274 if gitc_init:
Mike Frysinger401c6f02021-02-18 15:20:15 -0500275 usage = 'repo gitc-init -c client [options] [-u] url'
Mike Frysingerd8fda902020-02-14 00:24:38 -0500276 else:
Mike Frysinger401c6f02021-02-18 15:20:15 -0500277 usage = 'repo init [options] [-u] url'
Mike Frysingerd8fda902020-02-14 00:24:38 -0500278
279 parser = optparse.OptionParser(usage=usage)
Mike Frysinger9a734a32021-04-08 19:14:15 -0400280 InitParser(parser, gitc_init=gitc_init)
281 return parser
Mike Frysingerd8fda902020-02-14 00:24:38 -0500282
Mike Frysinger9a734a32021-04-08 19:14:15 -0400283
284def InitParser(parser, gitc_init=False):
285 """Setup the CLI parser."""
Mike Frysinger9180a072021-04-13 14:57:40 -0400286 # NB: Keep in sync with command.py:_CommonOptions().
287
Mike Frysingerf700ac72020-02-06 00:04:21 -0500288 # Logging.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500289 group = parser.add_option_group('Logging options')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500290 group.add_option('-v', '--verbose',
291 dest='output_mode', action='store_true',
292 help='show all output')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500293 group.add_option('-q', '--quiet',
Mike Frysingeredd3d452020-02-21 23:55:07 -0500294 dest='output_mode', action='store_false',
295 help='only show errors')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700296
Mike Frysingerf700ac72020-02-06 00:04:21 -0500297 # Manifest.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500298 group = parser.add_option_group('Manifest options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500299 group.add_option('-u', '--manifest-url',
300 help='manifest repository location', metavar='URL')
Mike Frysinger23882b32021-02-23 15:43:07 -0500301 group.add_option('-b', '--manifest-branch', metavar='REVISION',
302 help='manifest branch or revision (use HEAD for default)')
Mike Frysingerd2b086b2021-04-21 00:25:24 -0400303 group.add_option('-m', '--manifest-name', default='default.xml',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500304 help='initial manifest file', metavar='NAME.xml')
Mike Frysingera1051d82021-04-08 23:04:36 -0400305 group.add_option('-g', '--groups', default='default',
306 help='restrict manifest projects to ones with specified '
307 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
308 metavar='GROUP')
309 group.add_option('-p', '--platform', default='auto',
310 help='restrict manifest projects to ones with a specified '
311 'platform group [auto|all|none|linux|darwin|...]',
312 metavar='PLATFORM')
313 group.add_option('--submodules', action='store_true',
314 help='sync any submodules associated with the manifest repo')
Jack Neusc474c9c2021-07-26 23:08:54 +0000315 group.add_option('--standalone-manifest', action='store_true',
316 help='download the manifest as a static file '
317 'rather then create a git checkout of '
318 'the manifest repo')
LaMont Jonesb750b482022-08-18 07:20:57 -0400319 group.add_option('--manifest-depth', type='int', default=0, metavar='DEPTH',
320 help='create a shallow clone of the manifest repo with '
321 'given depth (0 for full clone); see git clone '
322 '(default: %default)')
Mike Frysingera1051d82021-04-08 23:04:36 -0400323
324 # Options that only affect manifest project, and not any of the projects
325 # specified in the manifest itself.
326 group = parser.add_option_group('Manifest (only) checkout options')
Mike Frysingerd8fda902020-02-14 00:24:38 -0500327 cbr_opts = ['--current-branch']
328 # The gitc-init subcommand allocates -c itself, but a lot of init users
329 # want -c, so try to satisfy both as best we can.
330 if not gitc_init:
331 cbr_opts += ['-c']
Mike Frysinger39cb17f2022-08-18 07:28:42 -0400332 group.add_option(*cbr_opts, default=True,
Mike Frysingerf700ac72020-02-06 00:04:21 -0500333 dest='current_branch_only', action='store_true',
Mike Frysinger39cb17f2022-08-18 07:28:42 -0400334 help='fetch only current manifest branch from server (default)')
Mike Frysinger73561142021-05-03 01:10:09 -0400335 group.add_option('--no-current-branch',
336 dest='current_branch_only', action='store_false',
337 help='fetch all manifest branches from server')
Mike Frysingerd68ed632021-05-03 01:21:35 -0400338 group.add_option('--tags',
339 action='store_true',
340 help='fetch tags in the manifest')
Mike Frysingera1051d82021-04-08 23:04:36 -0400341 group.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400342 dest='tags', action='store_false',
Mike Frysingera1051d82021-04-08 23:04:36 -0400343 help="don't fetch tags in the manifest")
344
345 # These are fundamentally different ways of structuring the checkout.
346 group = parser.add_option_group('Checkout modes')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500347 group.add_option('--mirror', action='store_true',
348 help='create a replica of the remote repositories '
349 'rather than a client working directory')
Mike Frysingera1051d82021-04-08 23:04:36 -0400350 group.add_option('--archive', action='store_true',
351 help='checkout an archive instead of a git repository for '
352 'each project. See git archive.')
353 group.add_option('--worktree', action='store_true',
354 help='use git-worktree to manage projects')
355
356 # These are fundamentally different ways of structuring the checkout.
357 group = parser.add_option_group('Project checkout optimizations')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500358 group.add_option('--reference',
359 help='location of mirror directory', metavar='DIR')
360 group.add_option('--dissociate', action='store_true',
361 help='dissociate from reference mirrors after clone')
362 group.add_option('--depth', type='int', default=None,
363 help='create a shallow clone with given depth; '
364 'see git clone')
365 group.add_option('--partial-clone', action='store_true',
366 help='perform partial clone (https://git-scm.com/'
367 'docs/gitrepository-layout#_code_partialclone_code)')
Raman Tenneti6a2f4fb2021-04-02 10:55:33 -0700368 group.add_option('--no-partial-clone', action='store_false',
369 help='disable use of partial clone (https://git-scm.com/'
370 'docs/gitrepository-layout#_code_partialclone_code)')
Raman Tennetif32f2432021-04-12 20:57:25 -0700371 group.add_option('--partial-clone-exclude', action='store',
372 help='exclude the specified projects (a comma-delimited '
373 'project names) from partial clone (https://git-scm.com'
374 '/docs/gitrepository-layout#_code_partialclone_code)')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500375 group.add_option('--clone-filter', action='store', default='blob:none',
376 help='filter for use with --partial-clone '
377 '[default: %default]')
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800378 group.add_option('--use-superproject', action='store_true', default=None,
Raman Tenneti62517292021-11-01 14:49:16 -0700379 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800380 group.add_option('--no-use-superproject', action='store_false',
381 dest='use_superproject',
382 help='disable use of manifest superprojects')
Xin Lid79a4bc2020-05-20 16:03:45 -0700383 group.add_option('--clone-bundle', action='store_true',
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800384 help='enable use of /clone.bundle on HTTP/HTTPS '
385 '(default if not --partial-clone)')
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500386 group.add_option('--no-clone-bundle',
Xin Lid79a4bc2020-05-20 16:03:45 -0700387 dest='clone_bundle', action='store_false',
388 help='disable use of /clone.bundle on HTTP/HTTPS (default if --partial-clone)')
XD Trol630876f2022-01-17 23:29:04 +0800389 group.add_option('--git-lfs', action='store_true',
390 help='enable Git LFS support')
391 group.add_option('--no-git-lfs',
392 dest='git_lfs', action='store_false',
393 help='disable Git LFS support')
Doug Anderson49cd59b2011-06-13 21:42:06 -0700394
Mike Frysingerf700ac72020-02-06 00:04:21 -0500395 # Tool.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500396 group = parser.add_option_group('repo Version options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500397 group.add_option('--repo-url', metavar='URL',
398 help='repo repository location ($REPO_URL)')
Mike Frysinger58ac1672020-03-14 14:35:26 -0400399 group.add_option('--repo-rev', metavar='REV',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500400 help='repo branch or revision ($REPO_REV)')
Mike Frysinger58ac1672020-03-14 14:35:26 -0400401 group.add_option('--repo-branch', dest='repo_rev',
402 help=optparse.SUPPRESS_HELP)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500403 group.add_option('--no-repo-verify',
404 dest='repo_verify', default=True, action='store_false',
Mike Frysingerf700ac72020-02-06 00:04:21 -0500405 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700406
Mike Frysingerf700ac72020-02-06 00:04:21 -0500407 # Other.
Mike Frysingerd8fda902020-02-14 00:24:38 -0500408 group = parser.add_option_group('Other options')
Mike Frysingerf700ac72020-02-06 00:04:21 -0500409 group.add_option('--config-name',
410 action='store_true', default=False,
411 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700412
Mike Frysingerd8fda902020-02-14 00:24:38 -0500413 # gitc-init specific settings.
414 if gitc_init:
415 group = parser.add_option_group('GITC options')
416 group.add_option('-f', '--manifest-file',
417 help='Optional manifest file to use for this GITC client.')
418 group.add_option('-c', '--gitc-client',
419 help='Name of the gitc_client instance to create or modify.')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700420
Mike Frysingerd8fda902020-02-14 00:24:38 -0500421 return parser
Simran Basi1efc2b42015-08-05 15:04:22 -0700422
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900423
Mike Frysinger62285d22020-02-12 08:01:38 -0500424# This is a poor replacement for subprocess.run until we require Python 3.6+.
425RunResult = collections.namedtuple(
426 'RunResult', ('returncode', 'stdout', 'stderr'))
427
428
429class RunError(Exception):
430 """Error when running a command failed."""
431
432
433def run_command(cmd, **kwargs):
434 """Run |cmd| and return its output."""
435 check = kwargs.pop('check', False)
436 if kwargs.pop('capture_output', False):
437 kwargs.setdefault('stdout', subprocess.PIPE)
438 kwargs.setdefault('stderr', subprocess.PIPE)
439 cmd_input = kwargs.pop('input', None)
440
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500441 def decode(output):
442 """Decode |output| to text."""
443 if output is None:
444 return output
445 try:
446 return output.decode('utf-8')
447 except UnicodeError:
448 print('repo: warning: Invalid UTF-8 output:\ncmd: %r\n%r' % (cmd, output),
449 file=sys.stderr)
450 # TODO(vapier): Once we require Python 3, use 'backslashreplace'.
451 return output.decode('utf-8', 'replace')
452
Mike Frysinger62285d22020-02-12 08:01:38 -0500453 # Run & package the results.
454 proc = subprocess.Popen(cmd, **kwargs)
455 (stdout, stderr) = proc.communicate(input=cmd_input)
Mike Frysinger71928c12020-02-21 23:45:08 -0500456 dbg = ': ' + ' '.join(cmd)
457 if cmd_input is not None:
458 dbg += ' 0<|'
459 if stdout == subprocess.PIPE:
460 dbg += ' 1>|'
461 if stderr == subprocess.PIPE:
462 dbg += ' 2>|'
463 elif stderr == subprocess.STDOUT:
464 dbg += ' 2>&1'
465 trace.print(dbg)
Mike Frysinger6a784ff2020-02-14 23:38:28 -0500466 ret = RunResult(proc.returncode, decode(stdout), decode(stderr))
Mike Frysinger62285d22020-02-12 08:01:38 -0500467
468 # If things failed, print useful debugging output.
469 if check and ret.returncode:
470 print('repo: error: "%s" failed with exit status %s' %
471 (cmd[0], ret.returncode), file=sys.stderr)
472 print(' cwd: %s\n cmd: %r' %
473 (kwargs.get('cwd', os.getcwd()), cmd), file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900474
Mike Frysinger62285d22020-02-12 08:01:38 -0500475 def _print_output(name, output):
476 if output:
477 print(' %s:\n >> %s' % (name, '\n >> '.join(output.splitlines())),
478 file=sys.stderr)
David Pursehousec19cc5c2020-02-14 09:18:15 +0900479
Mike Frysinger62285d22020-02-12 08:01:38 -0500480 _print_output('stdout', ret.stdout)
481 _print_output('stderr', ret.stderr)
482 raise RunError(ret)
483
484 return ret
485
486
Simran Basi8ce50412015-08-28 14:25:44 -0700487_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700488
489
Simran Basi8ce50412015-08-28 14:25:44 -0700490def get_gitc_manifest_dir():
491 global _gitc_manifest_dir
492 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700493 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700494 try:
495 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
496 for line in gitc_config:
497 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
498 if match:
499 _gitc_manifest_dir = match.group('gitc_manifest_dir')
500 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700501 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700502 return _gitc_manifest_dir
503
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700504
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700505def gitc_parse_clientdir(gitc_fs_path):
506 """Parse a path in the GITC FS and return its client name.
507
Mike Frysinger0a849b62020-12-11 03:26:42 -0500508 Args:
509 gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700510
Mike Frysinger0a849b62020-12-11 03:26:42 -0500511 Returns:
512 The GITC client name.
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700513 """
514 if gitc_fs_path == GITC_FS_ROOT_DIR:
515 return None
516 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
517 manifest_dir = get_gitc_manifest_dir()
518 if manifest_dir == '':
519 return None
520 if manifest_dir[-1] != '/':
521 manifest_dir += '/'
522 if gitc_fs_path == manifest_dir:
523 return None
524 if not gitc_fs_path.startswith(manifest_dir):
525 return None
526 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
527 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
528
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700529
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700530class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700531
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700532 """Indicate the remote clone of repo itself failed.
533 """
534
535
Mike Frysinger3599cc32020-02-29 02:53:41 -0500536def check_repo_verify(repo_verify, quiet=False):
537 """Check the --repo-verify state."""
538 if not repo_verify:
539 print('repo: warning: verification of repo code has been disabled;\n'
540 'repo will not be able to verify the integrity of itself.\n',
541 file=sys.stderr)
542 return False
543
544 if NeedSetupGnuPG():
545 return SetupGnuPG(quiet)
546
547 return True
548
549
550def check_repo_rev(dst, rev, repo_verify=True, quiet=False):
551 """Check that |rev| is valid."""
552 do_verify = check_repo_verify(repo_verify, quiet=quiet)
553 remote_ref, local_rev = resolve_repo_rev(dst, rev)
554 if not quiet and not remote_ref.startswith('refs/heads/'):
555 print('warning: repo is not tracking a remote branch, so it will not '
556 'receive updates', file=sys.stderr)
557 if do_verify:
558 rev = verify_rev(dst, remote_ref, local_rev, quiet)
559 else:
560 rev = local_rev
561 return (remote_ref, rev)
562
563
Simran Basi1efc2b42015-08-05 15:04:22 -0700564def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700565 """Installs repo by cloning it over the network.
566 """
Mike Frysingerd8fda902020-02-14 00:24:38 -0500567 parser = GetParser(gitc_init=gitc_init)
568 opt, args = parser.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800569 if args:
Mike Frysinger401c6f02021-02-18 15:20:15 -0500570 if not opt.manifest_url:
571 opt.manifest_url = args.pop(0)
572 if args:
573 parser.print_usage()
574 sys.exit(1)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500575 opt.quiet = opt.output_mode is False
576 opt.verbose = opt.output_mode is True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700577
Xin Lid79a4bc2020-05-20 16:03:45 -0700578 if opt.clone_bundle is None:
579 opt.clone_bundle = False if opt.partial_clone else True
580
Mike Frysingere1111f52020-03-14 16:28:31 -0400581 url = opt.repo_url or REPO_URL
Mike Frysingercfc81112020-02-29 02:56:32 -0500582 rev = opt.repo_rev or REPO_REV
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700583
David Jamesbf79c662013-12-26 14:20:13 -0800584 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700585 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700586 gitc_manifest_dir = get_gitc_manifest_dir()
587 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400588 print('fatal: GITC filesystem is not available. Exiting...',
589 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700590 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700591 gitc_client = opt.gitc_client
592 if not gitc_client:
593 gitc_client = gitc_parse_clientdir(os.getcwd())
594 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400595 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700596 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700597 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700598 if not os.path.exists(client_dir):
599 os.makedirs(client_dir)
600 os.chdir(client_dir)
601 if os.path.exists(repodir):
602 # This GITC Client has already initialized repo so continue.
603 return
604
David Jamesbf79c662013-12-26 14:20:13 -0800605 os.mkdir(repodir)
606 except OSError as e:
607 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400608 print('fatal: cannot make %s directory: %s'
609 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900610 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700611 # name. Instead exit immediately.
612 #
613 sys.exit(1)
614
615 _CheckGitVersion()
616 try:
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500617 if not opt.quiet:
618 print('Downloading Repo source from', url)
Mike Frysingera010a9f2022-08-19 05:16:46 -0400619 dst_final = os.path.abspath(os.path.join(repodir, S_repo))
620 dst = dst_final + '.tmp'
621 shutil.rmtree(dst, ignore_errors=True)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500622 _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700623
Mike Frysinger3599cc32020-02-29 02:53:41 -0500624 remote_ref, rev = check_repo_rev(dst, rev, opt.repo_verify, quiet=opt.quiet)
Mike Frysingercfc81112020-02-29 02:56:32 -0500625 _Checkout(dst, remote_ref, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200626
627 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerd75ca2e2022-08-19 05:17:49 -0400628 print("fatal: '%s' does not look like a git-repo repository, is "
629 "--repo-url set correctly?" % url, file=sys.stderr)
630 raise CloneFailure()
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200631
Mike Frysingera010a9f2022-08-19 05:16:46 -0400632 os.rename(dst, dst_final)
633
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700634 except CloneFailure:
Mike Frysinger4aa85842022-01-25 02:10:28 -0500635 print('fatal: double check your --repo-rev setting.', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700636 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400637 print('fatal: repo init failed; run without --quiet to see why',
638 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700639 raise
640
641
Mike Frysinger62285d22020-02-12 08:01:38 -0500642def run_git(*args, **kwargs):
643 """Run git and return execution details."""
644 kwargs.setdefault('capture_output', True)
645 kwargs.setdefault('check', True)
646 try:
647 return run_command([GIT] + list(args), **kwargs)
648 except OSError as e:
649 print(file=sys.stderr)
650 print('repo: error: "%s" is not available' % GIT, file=sys.stderr)
651 print('repo: error: %s' % e, file=sys.stderr)
652 print(file=sys.stderr)
653 print('Please make sure %s is installed and in your path.' % GIT,
654 file=sys.stderr)
655 sys.exit(1)
656 except RunError:
657 raise CloneFailure()
658
659
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400660# The git version info broken down into components for easy analysis.
661# Similar to Python's sys.version_info.
662GitVersion = collections.namedtuple(
663 'GitVersion', ('major', 'minor', 'micro', 'full'))
664
David Pursehouse31b9b4b2020-02-13 08:20:14 +0900665
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400666def ParseGitVersion(ver_str=None):
667 if ver_str is None:
668 # Load the version ourselves.
Mike Frysinger62285d22020-02-12 08:01:38 -0500669 ver_str = run_git('--version').stdout
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400670
Conley Owensff0a3c82014-01-30 14:46:03 -0800671 if not ver_str.startswith('git version '):
672 return None
673
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400674 full_version = ver_str[len('git version '):].strip()
675 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800676 to_tuple = []
677 for num_str in num_ver_str.split('.')[:3]:
678 if num_str.isdigit():
679 to_tuple.append(int(num_str))
680 else:
681 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400682 to_tuple.append(full_version)
683 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800684
685
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400686def _CheckGitVersion():
Mike Frysinger62285d22020-02-12 08:01:38 -0500687 ver_act = ParseGitVersion()
Conley Owensff0a3c82014-01-30 14:46:03 -0800688 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400689 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700690 raise CloneFailure()
691
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700692 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900693 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysinger587f1622020-03-23 16:49:11 -0400694 print('fatal: git %s or later required; found %s' % (need, ver_act.full),
695 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700696 raise CloneFailure()
697
698
Mike Frysinger84094102020-02-11 02:10:28 -0500699def SetGitTrace2ParentSid(env=None):
700 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
701 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
702 # (1) Be unique (2) be valid filename (3) be fixed length.
703 #
704 # Since we always export this variable, we try to avoid more expensive calls.
705 # e.g. We don't attempt hostname lookups or hashing the results.
706 if env is None:
707 env = os.environ
708
709 KEY = 'GIT_TRACE2_PARENT_SID'
710
711 now = datetime.datetime.utcnow()
712 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
713
714 # If it's already set, then append ourselves.
715 if KEY in env:
716 value = env[KEY] + '/' + value
717
718 _setenv(KEY, value, env=env)
719
720
721def _setenv(key, value, env=None):
722 """Set |key| in the OS environment |env| to |value|."""
723 if env is None:
724 env = os.environ
725 # Environment handling across systems is messy.
726 try:
727 env[key] = value
728 except UnicodeEncodeError:
729 env[key] = value.encode()
730
731
Conley Owensc9129d92012-10-01 16:12:28 -0700732def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700733 if not os.path.isdir(home_dot_repo):
734 return True
735
736 kv = os.path.join(home_dot_repo, 'keyring-version')
737 if not os.path.exists(kv):
738 return True
739
740 kv = open(kv).read()
741 if not kv:
742 return True
743
David Pursehouse685f0802012-11-14 08:34:39 +0900744 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 if kv < KEYRING_VERSION:
746 return True
747 return False
748
749
Conley Owensc9129d92012-10-01 16:12:28 -0700750def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800751 try:
752 os.mkdir(home_dot_repo)
753 except OSError as e:
754 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400755 print('fatal: cannot make %s directory: %s'
756 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 sys.exit(1)
758
David Jamesbf79c662013-12-26 14:20:13 -0800759 try:
760 os.mkdir(gpg_dir, stat.S_IRWXU)
761 except OSError as e:
762 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400763 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
764 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 sys.exit(1)
766
Mike Frysinger19a1f222020-02-14 16:28:13 -0500767 if not quiet:
768 print('repo: Updating release signing keys to keyset ver %s' %
769 ('.'.join(str(x) for x in KEYRING_VERSION),))
770 # NB: We use --homedir (and cwd below) because some environments (Windows) do
771 # not correctly handle full native paths. We avoid the issue by changing to
772 # the right dir with cwd=gpg_dir before executing gpg, and then telling gpg to
773 # use the cwd (.) as its homedir which leaves the path resolution logic to it.
774 cmd = ['gpg', '--homedir', '.', '--import']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775 try:
Mike Frysinger19a1f222020-02-14 16:28:13 -0500776 # gpg can be pretty chatty. Always capture the output and if something goes
777 # wrong, the builtin check failure will dump stdout & stderr for debugging.
778 run_command(cmd, stdin=subprocess.PIPE, capture_output=True,
779 cwd=gpg_dir, check=True,
David Pursehousec19cc5c2020-02-14 09:18:15 +0900780 input=MAINTAINER_KEYS.encode('utf-8'))
David Pursehouse22dbfb92020-02-13 08:20:55 +0900781 except OSError:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700782 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400783 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
784 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
785 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700786 return False
787
Mike Frysinger3164d402019-11-11 05:40:22 -0500788 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
789 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 return True
791
792
Mike Frysinger62285d22020-02-12 08:01:38 -0500793def _SetConfig(cwd, name, value):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700794 """Set a git configuration option to the specified value.
795 """
Mike Frysinger62285d22020-02-12 08:01:38 -0500796 run_git('config', name, value, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700797
798
Mike Frysinger949bc342020-02-18 21:37:00 -0500799def _GetRepoConfig(name):
800 """Read a repo configuration option."""
801 config = os.path.join(home_dot_repo, 'config')
802 if not os.path.exists(config):
803 return None
804
805 cmd = ['config', '--file', config, '--get', name]
806 ret = run_git(*cmd, check=False)
807 if ret.returncode == 0:
808 return ret.stdout
809 elif ret.returncode == 1:
810 return None
811 else:
812 print('repo: error: git %s failed:\n%s' % (' '.join(cmd), ret.stderr),
813 file=sys.stderr)
814 raise RunError()
815
816
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700817def _InitHttp():
818 handlers = []
819
Sarah Owens1f7627f2012-10-31 09:21:55 -0700820 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700821 try:
822 import netrc
823 n = netrc.netrc()
824 for host in n.hosts:
825 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700826 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800827 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse58a8b5c2020-02-13 08:21:28 +0900828 except Exception:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700829 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700830 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
831 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700832
833 if 'http_proxy' in os.environ:
834 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700835 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700836 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700837 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
838 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
839 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700840
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700841
Mike Frysingeredd3d452020-02-21 23:55:07 -0500842def _Fetch(url, cwd, src, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500843 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -0500844 if not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700845 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -0500846 err = None
847 if not quiet and sys.stdout.isatty():
848 cmd.append('--progress')
849 elif not verbose:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700850 err = subprocess.PIPE
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700851 cmd.append(src)
852 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800853 cmd.append('+refs/tags/*:refs/tags/*')
Mike Frysinger4847e052020-02-22 00:07:35 -0500854 run_git(*cmd, stderr=err, capture_output=False, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700856
Mike Frysingeredd3d452020-02-21 23:55:07 -0500857def _DownloadBundle(url, cwd, quiet, verbose):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700858 if not url.endswith('/'):
859 url += '/'
860 url += 'clone.bundle'
861
Mike Frysinger62285d22020-02-12 08:01:38 -0500862 ret = run_git('config', '--get-regexp', 'url.*.insteadof', cwd=cwd,
863 check=False)
864 for line in ret.stdout.splitlines():
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700865 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
866 if m:
867 new_url = m.group(1)
868 old_url = m.group(2)
869 if url.startswith(old_url):
870 url = new_url + url[len(old_url):]
871 break
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700872
873 if not url.startswith('http:') and not url.startswith('https:'):
874 return False
875
Mike Frysinger62285d22020-02-12 08:01:38 -0500876 dest = open(os.path.join(cwd, '.git', 'clone.bundle'), 'w+b')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700877 try:
878 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700879 r = urllib.request.urlopen(url)
880 except urllib.error.HTTPError as e:
Craig Northway0e8828c2021-05-05 08:49:26 -0700881 if e.code not in [400, 401, 403, 404, 501]:
882 print('warning: Cannot get %s' % url, file=sys.stderr)
883 print('warning: HTTP error %s' % e.code, file=sys.stderr)
884 return False
Sarah Owens1f7627f2012-10-31 09:21:55 -0700885 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400886 print('fatal: Cannot get %s' % url, file=sys.stderr)
887 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700888 raise CloneFailure()
889 try:
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500890 if verbose:
891 print('Downloading clone bundle %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700892 while True:
893 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400894 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700895 return True
896 dest.write(buf)
897 finally:
898 r.close()
899 finally:
900 dest.close()
901
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700902
Mike Frysinger62285d22020-02-12 08:01:38 -0500903def _ImportBundle(cwd):
904 path = os.path.join(cwd, '.git', 'clone.bundle')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700905 try:
Mike Frysingeredd3d452020-02-21 23:55:07 -0500906 _Fetch(cwd, cwd, path, True, False)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700907 finally:
908 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700909
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700910
Mike Frysingeredd3d452020-02-21 23:55:07 -0500911def _Clone(url, cwd, clone_bundle, quiet, verbose):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700912 """Clones a git repository to a new subdirectory of repodir
913 """
Mike Frysingerdcbfadf2020-02-22 00:04:39 -0500914 if verbose:
915 print('Cloning git repository', url)
916
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917 try:
Mike Frysinger62285d22020-02-12 08:01:38 -0500918 os.mkdir(cwd)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700919 except OSError as e:
Mike Frysinger62285d22020-02-12 08:01:38 -0500920 print('fatal: cannot make %s directory: %s' % (cwd, e.strerror),
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400921 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 raise CloneFailure()
923
Mike Frysinger62285d22020-02-12 08:01:38 -0500924 run_git('init', '--quiet', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700925
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700926 _InitHttp()
Mike Frysinger62285d22020-02-12 08:01:38 -0500927 _SetConfig(cwd, 'remote.origin.url', url)
928 _SetConfig(cwd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700929 'remote.origin.fetch',
930 '+refs/heads/*:refs/remotes/origin/*')
Mike Frysingeredd3d452020-02-21 23:55:07 -0500931 if clone_bundle and _DownloadBundle(url, cwd, quiet, verbose):
Mike Frysinger62285d22020-02-12 08:01:38 -0500932 _ImportBundle(cwd)
Mike Frysingeredd3d452020-02-21 23:55:07 -0500933 _Fetch(url, cwd, 'origin', quiet, verbose)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700934
935
Mike Frysingercfc81112020-02-29 02:56:32 -0500936def resolve_repo_rev(cwd, committish):
937 """Figure out what REPO_REV represents.
938
939 We support:
940 * refs/heads/xxx: Branch.
941 * refs/tags/xxx: Tag.
942 * xxx: Branch or tag or commit.
943
944 Args:
945 cwd: The git checkout to run in.
946 committish: The REPO_REV argument to resolve.
947
948 Returns:
949 A tuple of (remote ref, commit) as makes sense for the committish.
950 For branches, this will look like ('refs/heads/stable', <revision>).
951 For tags, this will look like ('refs/tags/v1.0', <revision>).
952 For commits, this will be (<revision>, <revision>).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700953 """
Mike Frysingercfc81112020-02-29 02:56:32 -0500954 def resolve(committish):
955 ret = run_git('rev-parse', '--verify', '%s^{commit}' % (committish,),
956 cwd=cwd, check=False)
957 return None if ret.returncode else ret.stdout.strip()
958
959 # An explicit branch.
960 if committish.startswith('refs/heads/'):
961 remote_ref = committish
962 committish = committish[len('refs/heads/'):]
963 rev = resolve('refs/remotes/origin/%s' % committish)
964 if rev is None:
965 print('repo: error: unknown branch "%s"' % (committish,),
966 file=sys.stderr)
967 raise CloneFailure()
968 return (remote_ref, rev)
969
970 # An explicit tag.
971 if committish.startswith('refs/tags/'):
972 remote_ref = committish
973 committish = committish[len('refs/tags/'):]
974 rev = resolve(remote_ref)
975 if rev is None:
976 print('repo: error: unknown tag "%s"' % (committish,),
977 file=sys.stderr)
978 raise CloneFailure()
979 return (remote_ref, rev)
980
981 # See if it's a short branch name.
982 rev = resolve('refs/remotes/origin/%s' % committish)
983 if rev:
984 return ('refs/heads/%s' % (committish,), rev)
985
986 # See if it's a tag.
987 rev = resolve('refs/tags/%s' % committish)
988 if rev:
989 return ('refs/tags/%s' % (committish,), rev)
990
991 # See if it's a commit.
992 rev = resolve(committish)
993 if rev and rev.lower().startswith(committish.lower()):
994 return (rev, rev)
995
996 # Give up!
997 print('repo: error: unable to resolve "%s"' % (committish,), file=sys.stderr)
998 raise CloneFailure()
999
1000
Mike Frysinger3599cc32020-02-29 02:53:41 -05001001def verify_rev(cwd, remote_ref, rev, quiet):
Mike Frysingercfc81112020-02-29 02:56:32 -05001002 """Verify the commit has been signed by a tag."""
1003 ret = run_git('describe', rev, cwd=cwd)
1004 cur = ret.stdout.strip()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005
1006 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
1007 if m:
1008 cur = m.group(1)
1009 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001010 print(file=sys.stderr)
Mike Frysingercfc81112020-02-29 02:56:32 -05001011 print("warning: '%s' is not signed; falling back to signed release '%s'"
1012 % (remote_ref, cur), file=sys.stderr)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001013 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001014
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001015 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -05001016 _setenv('GNUPGHOME', gpg_dir, env)
Mike Frysinger62285d22020-02-12 08:01:38 -05001017 run_git('tag', '-v', cur, cwd=cwd, env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018 return '%s^0' % cur
1019
1020
Mike Frysingercfc81112020-02-29 02:56:32 -05001021def _Checkout(cwd, remote_ref, rev, quiet):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022 """Checkout an upstream branch into the repository and track it.
1023 """
Mike Frysinger62285d22020-02-12 08:01:38 -05001024 run_git('update-ref', 'refs/heads/default', rev, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001025
1026 _SetConfig(cwd, 'branch.default.remote', 'origin')
Mike Frysingercfc81112020-02-29 02:56:32 -05001027 _SetConfig(cwd, 'branch.default.merge', remote_ref)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028
Mike Frysinger62285d22020-02-12 08:01:38 -05001029 run_git('symbolic-ref', 'HEAD', 'refs/heads/default', cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001030
Mike Frysinger62285d22020-02-12 08:01:38 -05001031 cmd = ['read-tree', '--reset', '-u']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001032 if not quiet:
1033 cmd.append('-v')
1034 cmd.append('HEAD')
Mike Frysinger62285d22020-02-12 08:01:38 -05001035 run_git(*cmd, cwd=cwd)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036
1037
1038def _FindRepo():
1039 """Look for a repo installation, starting at the current directory.
1040 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001041 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001042 repo = None
1043
Anthony Newnamdf14a702011-01-09 17:31:57 -08001044 olddir = None
Jonathan Nieder6248e0f2020-10-28 11:27:41 -07001045 while curdir != olddir and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001046 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047 if not os.path.isfile(repo):
1048 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001049 olddir = curdir
1050 curdir = os.path.dirname(curdir)
1051 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001052
1053
Mark E. Hamilton4088eb42016-02-10 10:44:30 -07001054class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001055 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001056 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057
1058
Mike Frysinger949bc342020-02-18 21:37:00 -05001059def _ExpandAlias(name):
1060 """Look up user registered aliases."""
1061 # We don't resolve aliases for existing subcommands. This matches git.
1062 if name in {'gitc-init', 'help', 'init'}:
1063 return name, []
1064
1065 alias = _GetRepoConfig('alias.%s' % (name,))
1066 if alias is None:
1067 return name, []
1068
1069 args = alias.strip().split(' ', 1)
1070 name = args[0]
1071 if len(args) == 2:
1072 args = shlex.split(args[1])
1073 else:
1074 args = []
1075 return name, args
1076
1077
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078def _ParseArguments(args):
1079 cmd = None
1080 opt = _Options()
1081 arg = []
1082
Sarah Owensa6053d52012-11-01 13:36:50 -07001083 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 a = args[i]
1085 if a == '-h' or a == '--help':
1086 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001087 elif a == '--version':
1088 opt.version = True
Mike Frysinger6fb0cb52020-02-12 09:39:23 -05001089 elif a == '--trace':
1090 trace.set(True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 elif not a.startswith('-'):
1092 cmd = a
1093 arg = args[i + 1:]
1094 break
1095 return cmd, opt, arg
1096
1097
Mike Frysingere5670c82021-01-07 22:14:25 -05001098class Requirements(object):
1099 """Helper for checking repo's system requirements."""
1100
1101 REQUIREMENTS_NAME = 'requirements.json'
1102
1103 def __init__(self, requirements):
1104 """Initialize.
1105
1106 Args:
1107 requirements: A dictionary of settings.
1108 """
1109 self.requirements = requirements
1110
1111 @classmethod
1112 def from_dir(cls, path):
1113 return cls.from_file(os.path.join(path, cls.REQUIREMENTS_NAME))
1114
1115 @classmethod
1116 def from_file(cls, path):
1117 try:
1118 with open(path, 'rb') as f:
1119 data = f.read()
1120 except EnvironmentError:
1121 # NB: EnvironmentError is used for Python 2 & 3 compatibility.
1122 # If we couldn't open the file, assume it's an old source tree.
1123 return None
1124
1125 return cls.from_data(data)
1126
1127 @classmethod
1128 def from_data(cls, data):
1129 comment_line = re.compile(br'^ *#')
1130 strip_data = b''.join(x for x in data.splitlines() if not comment_line.match(x))
1131 try:
1132 json_data = json.loads(strip_data)
1133 except Exception: # pylint: disable=broad-except
1134 # If we couldn't parse it, assume it's incompatible.
1135 return None
1136
1137 return cls(json_data)
1138
1139 def _get_soft_ver(self, pkg):
1140 """Return the soft version for |pkg| if it exists."""
1141 return self.requirements.get(pkg, {}).get('soft', ())
1142
1143 def _get_hard_ver(self, pkg):
1144 """Return the hard version for |pkg| if it exists."""
1145 return self.requirements.get(pkg, {}).get('hard', ())
1146
1147 @staticmethod
1148 def _format_ver(ver):
1149 """Return a dotted version from |ver|."""
1150 return '.'.join(str(x) for x in ver)
1151
1152 def assert_ver(self, pkg, curr_ver):
1153 """Verify |pkg|'s |curr_ver| is new enough."""
1154 curr_ver = tuple(curr_ver)
1155 soft_ver = tuple(self._get_soft_ver(pkg))
1156 hard_ver = tuple(self._get_hard_ver(pkg))
1157 if curr_ver < hard_ver:
1158 print('repo: error: Your version of "%s" (%s) is unsupported; '
1159 'Please upgrade to at least version %s to continue.' %
1160 (pkg, self._format_ver(curr_ver), self._format_ver(soft_ver)),
1161 file=sys.stderr)
1162 sys.exit(1)
1163
1164 if curr_ver < soft_ver:
1165 print('repo: warning: Your version of "%s" (%s) is no longer supported; '
1166 'Please upgrade to at least version %s to avoid breakage.' %
1167 (pkg, self._format_ver(curr_ver), self._format_ver(soft_ver)),
1168 file=sys.stderr)
1169
1170 def assert_all(self):
1171 """Assert all of the requirements are satisified."""
1172 # See if we need a repo launcher upgrade first.
1173 self.assert_ver('repo', VERSION)
1174
1175 # Check python before we try to import the repo code.
1176 self.assert_ver('python', sys.version_info)
1177
1178 # Check git while we're at it.
1179 self.assert_ver('git', ParseGitVersion())
1180
1181
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001182def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -07001183 gitc_usage = ""
1184 if get_gitc_manifest_dir():
1185 gitc_usage = " gitc-init Initialize a GITC Client.\n"
1186
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001187 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -07001188 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001189
1190repo is not yet installed. Use "repo init" to install it here.
1191
1192The most commonly used repo commands are:
1193
1194 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -07001195""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -07001196 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001197
1198For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -04001199""")
Mike Frysingera1cd7702021-04-20 23:38:04 -04001200 print('Bug reports:', BUG_URL)
Mike Frysinger35159ab2019-06-13 00:07:13 -04001201 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001202
1203
1204def _Help(args):
1205 if args:
Mike Frysingerd8fda902020-02-14 00:24:38 -05001206 if args[0] in {'init', 'gitc-init'}:
1207 parser = GetParser(gitc_init=args[0] == 'gitc-init')
1208 parser.print_help()
Dan Willemsen9ff2ece2015-08-31 15:45:06 -07001209 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001210 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001211 print("error: '%s' is not a bootstrap command.\n"
1212 ' For access to online help, install repo ("repo init").'
1213 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001214 else:
1215 _Usage()
1216 sys.exit(1)
1217
1218
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001219def _Version():
1220 """Show version information."""
1221 print('<repo not installed>')
1222 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
1223 print(' (from %s)' % (__file__,))
1224 print('git %s' % (ParseGitVersion().full,))
1225 print('Python %s' % sys.version)
Mike Frysinger5f11eac2020-02-25 15:09:01 -05001226 uname = platform.uname()
Mike Frysingere257d562020-03-23 16:55:02 -04001227 if sys.version_info.major < 3:
1228 # Python 3 returns a named tuple, but Python 2 is simpler.
1229 print(uname)
1230 else:
1231 print('OS %s %s (%s)' % (uname.system, uname.release, uname.version))
1232 print('CPU %s (%s)' %
1233 (uname.machine, uname.processor if uname.processor else 'unknown'))
Mike Frysingera1cd7702021-04-20 23:38:04 -04001234 print('Bug reports:', BUG_URL)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001235 sys.exit(0)
1236
1237
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001238def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001239 print('error: repo is not installed. Use "repo init" to install it here.',
1240 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001241 sys.exit(1)
1242
1243
1244def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001245 print("""error: command '%s' requires repo to be installed first.
1246 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001247 sys.exit(1)
1248
1249
1250def _RunSelf(wrapper_path):
1251 my_dir = os.path.dirname(wrapper_path)
1252 my_main = os.path.join(my_dir, 'main.py')
1253 my_git = os.path.join(my_dir, '.git')
1254
1255 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -07001256 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001257 'project.py',
1258 'subcmds']:
1259 if not os.path.exists(os.path.join(my_dir, name)):
1260 return None, None
1261 return my_main, my_git
1262 return None, None
1263
1264
1265def _SetDefaultsTo(gitdir):
1266 global REPO_URL
1267 global REPO_REV
1268
1269 REPO_URL = gitdir
Mike Frysingercdb344c2020-03-24 02:43:46 -04001270 ret = run_git('--git-dir=%s' % gitdir, 'symbolic-ref', 'HEAD', check=False)
1271 if ret.returncode:
1272 # If we're not tracking a branch (bisect/etc...), then fall back to commit.
1273 print('repo: warning: %s has no current branch; using HEAD' % gitdir,
1274 file=sys.stderr)
1275 try:
1276 ret = run_git('rev-parse', 'HEAD', cwd=gitdir)
1277 except CloneFailure:
1278 print('fatal: %s has invalid HEAD' % gitdir, file=sys.stderr)
1279 sys.exit(1)
1280
1281 REPO_REV = ret.stdout.strip()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001282
1283
1284def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001285 cmd, opt, args = _ParseArguments(orig_args)
1286
Mike Frysinger84094102020-02-11 02:10:28 -05001287 # We run this early as we run some git commands ourselves.
1288 SetGitTrace2ParentSid()
1289
Dan Willemsen745b4ad2015-10-06 15:23:19 -07001290 repo_main, rel_repo_dir = None, None
1291 # Don't use the local repo copy, make sure to switch to the gitc client first.
1292 if cmd != 'gitc-init':
1293 repo_main, rel_repo_dir = _FindRepo()
1294
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001295 wrapper_path = os.path.abspath(__file__)
1296 my_main, my_git = _RunSelf(wrapper_path)
1297
Simran Basi8ce50412015-08-28 14:25:44 -07001298 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -07001299 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001300 print('error: repo cannot be used in the GITC local manifest directory.'
1301 '\nIf you want to work on this GITC client please rerun this '
1302 'command from the corresponding client under /gitc/',
1303 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -07001304 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001305 if not repo_main:
Mike Frysinger949bc342020-02-18 21:37:00 -05001306 # Only expand aliases here since we'll be parsing the CLI ourselves.
1307 # If we had repo_main, alias expansion would happen in main.py.
1308 cmd, alias_args = _ExpandAlias(cmd)
1309 args = alias_args + args
1310
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001311 if opt.help:
1312 _Usage()
1313 if cmd == 'help':
1314 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -05001315 if opt.version or cmd == 'version':
1316 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001317 if not cmd:
1318 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -07001319 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001320 if my_git:
1321 _SetDefaultsTo(my_git)
1322 try:
Simran Basi1efc2b42015-08-05 15:04:22 -07001323 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001324 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001325 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -04001326 print("fatal: cloning the git-repo repository failed, will remove "
1327 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +02001328 shutil.rmtree(path, ignore_errors=True)
Mike Frysingera010a9f2022-08-19 05:16:46 -04001329 shutil.rmtree(path + '.tmp', ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001330 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001331 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001332 else:
1333 _NoCommands(cmd)
1334
1335 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001336 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001337
Konrad Weihmann433977e2020-04-17 21:39:32 +02001338 if not repo_main:
1339 print("fatal: unable to find repo entry point", file=sys.stderr)
1340 sys.exit(1)
1341
Mike Frysingere5670c82021-01-07 22:14:25 -05001342 reqs = Requirements.from_dir(os.path.dirname(repo_main))
1343 if reqs:
1344 reqs.assert_all()
1345
David Pursehouse685f0802012-11-14 08:34:39 +09001346 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +03001347 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +02001348 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001349 '--wrapper-version=%s' % ver_str,
1350 '--wrapper-path=%s' % wrapper_path,
1351 '--']
1352 me.extend(orig_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -04001353 exec_command(me)
1354 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1355 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001356
1357
1358if __name__ == '__main__':
1359 main(sys.argv[1:])