blob: 86d0ec214c5325b109935c89e2917584f9a33e7c [file] [log] [blame]
David Pursehouse8898e2f2012-11-14 07:51:03 +09001#!/usr/bin/env python
Mike Frysingerf6013762019-06-13 02:30:51 -04002# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003
Mike Frysinger87fb5a12019-06-13 01:54:46 -04004"""Repo launcher.
5
6This is a standalone tool that people may copy to anywhere in their system.
7It is used to get an initial repo client checkout, and after that it runs the
8copy of repo in the checkout.
9"""
10
Mike Frysingerc92ce5c2019-06-13 01:14:23 -040011from __future__ import print_function
12
Mike Frysinger84094102020-02-11 02:10:28 -050013import datetime
Mike Frysinger3ba716f2019-06-13 01:48:12 -040014import os
15import platform
16import subprocess
17import sys
18
19
20def exec_command(cmd):
21 """Execute |cmd| or return None on failure."""
22 try:
23 if platform.system() == 'Windows':
24 ret = subprocess.call(cmd)
25 sys.exit(ret)
26 else:
27 os.execvp(cmd[0], cmd)
28 except:
29 pass
30
31
32def check_python_version():
33 """Make sure the active Python version is recent enough."""
34 def reexec(prog):
35 exec_command([prog] + sys.argv)
36
37 MIN_PYTHON_VERSION = (3, 6)
38
39 ver = sys.version_info
40 major = ver.major
41 minor = ver.minor
42
43 # Abort on very old Python 2 versions.
44 if (major, minor) < (2, 7):
45 print('repo: error: Your Python version is too old. '
46 'Please use Python {}.{} or newer instead.'.format(
47 *MIN_PYTHON_VERSION), file=sys.stderr)
48 sys.exit(1)
49
50 # Try to re-exec the version specific Python 3 if needed.
51 if (major, minor) < MIN_PYTHON_VERSION:
52 # Python makes releases ~once a year, so try our min version +10 to help
53 # bridge the gap. This is the fallback anyways so perf isn't critical.
54 min_major, min_minor = MIN_PYTHON_VERSION
55 for inc in range(0, 10):
56 reexec('python{}.{}'.format(min_major, min_minor + inc))
57
58 # Try the generic Python 3 wrapper, but only if it's new enough. We don't
59 # want to go from (still supported) Python 2.7 to (unsupported) Python 3.5.
60 try:
61 proc = subprocess.Popen(
62 ['python3', '-c', 'import sys; '
63 'print(sys.version_info.major, sys.version_info.minor)'],
64 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
65 (output, _) = proc.communicate()
66 python3_ver = tuple(int(x) for x in output.decode('utf-8').split())
67 except (OSError, subprocess.CalledProcessError):
68 python3_ver = None
69
70 # The python3 version looks like it's new enough, so give it a try.
71 if python3_ver and python3_ver >= MIN_PYTHON_VERSION:
72 reexec('python3')
73
74 # We're still here, so diagnose things for the user.
75 if major < 3:
76 print('repo: warning: Python 2 is no longer supported; '
77 'Please upgrade to Python {}.{}+.'.format(*MIN_PYTHON_VERSION),
78 file=sys.stderr)
79 else:
80 print('repo: error: Python 3 version is too old; '
81 'Please use Python {}.{} or newer.'.format(*MIN_PYTHON_VERSION),
82 file=sys.stderr)
83 sys.exit(1)
84
85
86if __name__ == '__main__':
87 # TODO(vapier): Enable this on Windows once we have Python 3 issues fixed.
88 if platform.system() != 'Windows':
89 check_python_version()
90
91
Mark E. Hamilton4088eb42016-02-10 10:44:30 -070092# repo default configuration
93#
Mark E. Hamilton55536282016-02-03 15:49:43 -070094import os
95REPO_URL = os.environ.get('REPO_URL', None)
96if not REPO_URL:
97 REPO_URL = 'https://gerrit.googlesource.com/git-repo'
Mike Frysinger563f1a62020-02-05 23:52:07 -050098REPO_REV = os.environ.get('REPO_REV')
99if not REPO_REV:
100 REPO_REV = 'stable'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700101
102# Copyright (C) 2008 Google Inc.
103#
104# Licensed under the Apache License, Version 2.0 (the "License");
105# you may not use this file except in compliance with the License.
106# You may obtain a copy of the License at
107#
108# http://www.apache.org/licenses/LICENSE-2.0
109#
110# Unless required by applicable law or agreed to in writing, software
111# distributed under the License is distributed on an "AS IS" BASIS,
112# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
113# See the License for the specific language governing permissions and
114# limitations under the License.
115
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700116# increment this whenever we make important changes to this script
Mike Frysingeree451f02020-02-04 00:00:08 -0500117VERSION = (2, 0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700118
119# increment this if the MAINTAINER_KEYS block is modified
Mike Frysingerf5525fb2020-02-04 00:01:00 -0500120KEYRING_VERSION = (2, 0)
Mike Frysingere4433652016-09-13 18:06:07 -0400121
122# Each individual key entry is created by using:
123# gpg --armor --export keyid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700124MAINTAINER_KEYS = """
125
126 Repo Maintainer <repo@android.kernel.org>
127-----BEGIN PGP PUBLIC KEY BLOCK-----
128Version: GnuPG v1.4.2.2 (GNU/Linux)
129
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
165=CMiZ
166-----END PGP PUBLIC KEY BLOCK-----
167"""
168
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700169GIT = 'git' # our git command
Mike Frysinger82caef62020-02-11 18:51:08 -0500170# NB: The version of git that the repo launcher requires may be much older than
171# the version of git that the main repo source tree requires. Keeping this at
172# an older version also makes it easier for users to upgrade/rollback as needed.
173#
174# git-1.7 is in (EOL) Ubuntu Precise.
175MIN_GIT_VERSION = (1, 7, 2) # minimum supported git version
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700176repodir = '.repo' # name of repo's private directory
177S_repo = 'repo' # special repo repository
178S_manifests = 'manifests' # special manifest repository
179REPO_MAIN = S_repo + '/main.py' # main script
Simran Basi8ce50412015-08-28 14:25:44 -0700180GITC_CONFIG_FILE = '/gitc/.config'
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700181GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700182
183
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400184import collections
David Jamesbf79c662013-12-26 14:20:13 -0800185import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700186import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187import re
Mitchel Humpheryseb5acc92014-03-12 10:48:15 -0700188import shutil
Sarah Owens60798a32012-10-25 17:53:09 -0700189import stat
David Pursehouse59bbb582013-05-17 10:49:33 +0900190
191if sys.version_info[0] == 3:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700192 import urllib.request
193 import urllib.error
194else:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700195 import imp
David Pursehouse59bbb582013-05-17 10:49:33 +0900196 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -0700197 urllib = imp.new_module('urllib')
198 urllib.request = urllib2
199 urllib.error = urllib2
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700200
Conley Owens5e0ee142013-09-26 15:50:49 -0700201
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700202home_dot_repo = os.path.expanduser('~/.repoconfig')
203gpg_dir = os.path.join(home_dot_repo, 'gnupg')
204
205extra_args = []
206init_optparse = optparse.OptionParser(usage="repo init -u url [options]")
207
Mike Frysingerf700ac72020-02-06 00:04:21 -0500208def _InitParser():
209 """Setup the init subcommand parser."""
210 # Logging.
211 group = init_optparse.add_option_group('Logging options')
212 group.add_option('-q', '--quiet',
213 action='store_true', default=False,
214 help='be quiet')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215
Mike Frysingerf700ac72020-02-06 00:04:21 -0500216 # Manifest.
217 group = init_optparse.add_option_group('Manifest options')
218 group.add_option('-u', '--manifest-url',
219 help='manifest repository location', metavar='URL')
220 group.add_option('-b', '--manifest-branch',
221 help='manifest branch or revision', metavar='REVISION')
222 group.add_option('-m', '--manifest-name',
223 help='initial manifest file', metavar='NAME.xml')
224 group.add_option('--current-branch',
225 dest='current_branch_only', action='store_true',
226 help='fetch only current manifest branch from server')
227 group.add_option('--mirror', action='store_true',
228 help='create a replica of the remote repositories '
229 'rather than a client working directory')
230 group.add_option('--reference',
231 help='location of mirror directory', metavar='DIR')
232 group.add_option('--dissociate', action='store_true',
233 help='dissociate from reference mirrors after clone')
234 group.add_option('--depth', type='int', default=None,
235 help='create a shallow clone with given depth; '
236 'see git clone')
237 group.add_option('--partial-clone', action='store_true',
238 help='perform partial clone (https://git-scm.com/'
239 'docs/gitrepository-layout#_code_partialclone_code)')
240 group.add_option('--clone-filter', action='store', default='blob:none',
241 help='filter for use with --partial-clone '
242 '[default: %default]')
243 group.add_option('--archive', action='store_true',
244 help='checkout an archive instead of a git repository for '
245 'each project. See git archive.')
246 group.add_option('--submodules', action='store_true',
247 help='sync any submodules associated with the manifest repo')
248 group.add_option('-g', '--groups', default='default',
249 help='restrict manifest projects to ones with specified '
250 'group(s) [default|all|G1,G2,G3|G4,-G5,-G6]',
251 metavar='GROUP')
252 group.add_option('-p', '--platform', default='auto',
253 help='restrict manifest projects to ones with a specified '
254 'platform group [auto|all|none|linux|darwin|...]',
255 metavar='PLATFORM')
256 group.add_option('--no-clone-bundle', action='store_true',
257 help='disable use of /clone.bundle on HTTP/HTTPS')
258 group.add_option('--no-tags', action='store_true',
259 help="don't fetch tags in the manifest")
Doug Anderson49cd59b2011-06-13 21:42:06 -0700260
Mike Frysingerf700ac72020-02-06 00:04:21 -0500261 # Tool.
262 group = init_optparse.add_option_group('repo Version options')
263 group.add_option('--repo-url', metavar='URL',
264 help='repo repository location ($REPO_URL)')
265 group.add_option('--repo-branch', metavar='REVISION',
266 help='repo branch or revision ($REPO_REV)')
267 group.add_option('--no-repo-verify', action='store_true',
268 help='do not verify repo source code')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269
Mike Frysingerf700ac72020-02-06 00:04:21 -0500270 # Other.
271 group = init_optparse.add_option_group('Other options')
272 group.add_option('--config-name',
273 action='store_true', default=False,
274 help='Always prompt for name/e-mail')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700275
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700276
277def _GitcInitOptions(init_optparse_arg):
278 init_optparse_arg.set_usage("repo gitc-init -u url -c client [options]")
279 g = init_optparse_arg.add_option_group('GITC options')
Simran Basi1efc2b42015-08-05 15:04:22 -0700280 g.add_option('-f', '--manifest-file',
281 dest='manifest_file',
282 help='Optional manifest file to use for this GITC client.')
283 g.add_option('-c', '--gitc-client',
284 dest='gitc_client',
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700285 help='The name of the gitc_client instance to create or modify.')
Simran Basi1efc2b42015-08-05 15:04:22 -0700286
Simran Basi8ce50412015-08-28 14:25:44 -0700287_gitc_manifest_dir = None
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700288
289
Simran Basi8ce50412015-08-28 14:25:44 -0700290def get_gitc_manifest_dir():
291 global _gitc_manifest_dir
292 if _gitc_manifest_dir is None:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700293 _gitc_manifest_dir = ''
Simran Basi8ce50412015-08-28 14:25:44 -0700294 try:
295 with open(GITC_CONFIG_FILE, 'r') as gitc_config:
296 for line in gitc_config:
297 match = re.match('gitc_dir=(?P<gitc_manifest_dir>.*)', line)
298 if match:
299 _gitc_manifest_dir = match.group('gitc_manifest_dir')
300 except IOError:
Dan Willemsen2487cb72015-08-31 15:45:06 -0700301 pass
Simran Basi8ce50412015-08-28 14:25:44 -0700302 return _gitc_manifest_dir
303
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700304
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700305def gitc_parse_clientdir(gitc_fs_path):
306 """Parse a path in the GITC FS and return its client name.
307
308 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
309
310 @returns: The GITC client name
311 """
312 if gitc_fs_path == GITC_FS_ROOT_DIR:
313 return None
314 if not gitc_fs_path.startswith(GITC_FS_ROOT_DIR):
315 manifest_dir = get_gitc_manifest_dir()
316 if manifest_dir == '':
317 return None
318 if manifest_dir[-1] != '/':
319 manifest_dir += '/'
320 if gitc_fs_path == manifest_dir:
321 return None
322 if not gitc_fs_path.startswith(manifest_dir):
323 return None
324 return gitc_fs_path.split(manifest_dir)[1].split('/')[0]
325 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
326
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700327
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700328class CloneFailure(Exception):
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700329
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700330 """Indicate the remote clone of repo itself failed.
331 """
332
333
Simran Basi1efc2b42015-08-05 15:04:22 -0700334def _Init(args, gitc_init=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700335 """Installs repo by cloning it over the network.
336 """
Simran Basi1efc2b42015-08-05 15:04:22 -0700337 if gitc_init:
338 _GitcInitOptions(init_optparse)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700339 opt, args = init_optparse.parse_args(args)
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800340 if args:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700341 init_optparse.print_usage()
342 sys.exit(1)
343
344 url = opt.repo_url
345 if not url:
346 url = REPO_URL
347 extra_args.append('--repo-url=%s' % url)
348
349 branch = opt.repo_branch
350 if not branch:
351 branch = REPO_REV
352 extra_args.append('--repo-branch=%s' % branch)
353
354 if branch.startswith('refs/heads/'):
355 branch = branch[len('refs/heads/'):]
356 if branch.startswith('refs/'):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400357 print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700358 raise CloneFailure()
359
David Jamesbf79c662013-12-26 14:20:13 -0800360 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700361 if gitc_init:
Simran Basi8ce50412015-08-28 14:25:44 -0700362 gitc_manifest_dir = get_gitc_manifest_dir()
363 if not gitc_manifest_dir:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400364 print('fatal: GITC filesystem is not available. Exiting...',
365 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700366 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700367 gitc_client = opt.gitc_client
368 if not gitc_client:
369 gitc_client = gitc_parse_clientdir(os.getcwd())
370 if not gitc_client:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400371 print('fatal: GITC client (-c) is required.', file=sys.stderr)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700372 sys.exit(1)
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700373 client_dir = os.path.join(gitc_manifest_dir, gitc_client)
Simran Basi1efc2b42015-08-05 15:04:22 -0700374 if not os.path.exists(client_dir):
375 os.makedirs(client_dir)
376 os.chdir(client_dir)
377 if os.path.exists(repodir):
378 # This GITC Client has already initialized repo so continue.
379 return
380
David Jamesbf79c662013-12-26 14:20:13 -0800381 os.mkdir(repodir)
382 except OSError as e:
383 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400384 print('fatal: cannot make %s directory: %s'
385 % (repodir, e.strerror), file=sys.stderr)
David Pursehouse3794a782012-11-15 06:17:30 +0900386 # Don't raise CloneFailure; that would delete the
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700387 # name. Instead exit immediately.
388 #
389 sys.exit(1)
390
391 _CheckGitVersion()
392 try:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100393 if opt.no_repo_verify:
394 do_verify = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700395 else:
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100396 if NeedSetupGnuPG():
397 do_verify = SetupGnuPG(opt.quiet)
398 else:
399 do_verify = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700400
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700401 dst = os.path.abspath(os.path.join(repodir, S_repo))
Hu xiuyun9711a982015-12-11 11:16:41 +0800402 _Clone(url, dst, opt.quiet, not opt.no_clone_bundle)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700403
Sebastian Schuberth8f997b32020-01-20 11:42:48 +0100404 if do_verify:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700405 rev = _Verify(dst, branch, opt.quiet)
406 else:
407 rev = 'refs/remotes/origin/%s^0' % branch
408
409 _Checkout(dst, branch, rev, opt.quiet)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200410
411 if not os.path.isfile(os.path.join(dst, 'repo')):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400412 print("warning: '%s' does not look like a git-repo repository, is "
413 "REPO_URL set correctly?" % url, file=sys.stderr)
Sebastian Schuberth993dcac2018-07-13 10:25:52 +0200414
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700415 except CloneFailure:
416 if opt.quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400417 print('fatal: repo init failed; run without --quiet to see why',
418 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700419 raise
420
421
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400422# The git version info broken down into components for easy analysis.
423# Similar to Python's sys.version_info.
424GitVersion = collections.namedtuple(
425 'GitVersion', ('major', 'minor', 'micro', 'full'))
426
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400427def ParseGitVersion(ver_str=None):
428 if ver_str is None:
429 # Load the version ourselves.
430 ver_str = _GetGitVersion()
431
Conley Owensff0a3c82014-01-30 14:46:03 -0800432 if not ver_str.startswith('git version '):
433 return None
434
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400435 full_version = ver_str[len('git version '):].strip()
436 num_ver_str = full_version.split('-')[0]
Conley Owensff0a3c82014-01-30 14:46:03 -0800437 to_tuple = []
438 for num_str in num_ver_str.split('.')[:3]:
439 if num_str.isdigit():
440 to_tuple.append(int(num_str))
441 else:
442 to_tuple.append(0)
Mike Frysinger6db1b9e2019-07-10 15:32:36 -0400443 to_tuple.append(full_version)
444 return GitVersion(*to_tuple)
Conley Owensff0a3c82014-01-30 14:46:03 -0800445
446
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400447def _GetGitVersion():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700448 cmd = [GIT, '--version']
Shawn O. Pearce4fd38ec2012-06-05 07:55:07 -0700449 try:
450 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700451 except OSError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400452 print(file=sys.stderr)
453 print("fatal: '%s' is not available" % GIT, file=sys.stderr)
454 print('fatal: %s' % e, file=sys.stderr)
455 print(file=sys.stderr)
456 print('Please make sure %s is installed and in your path.' % GIT,
457 file=sys.stderr)
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400458 raise
Shawn O. Pearce4fd38ec2012-06-05 07:55:07 -0700459
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700460 ver_str = proc.stdout.read().strip()
461 proc.stdout.close()
Shawn O. Pearce16191342008-10-28 08:33:34 -0700462 proc.wait()
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400463 return ver_str.decode('utf-8')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700464
Mike Frysingerf88b2fe2019-07-10 15:37:43 -0400465
466def _CheckGitVersion():
467 try:
468 ver_act = ParseGitVersion()
469 except OSError:
470 raise CloneFailure()
471
Conley Owensff0a3c82014-01-30 14:46:03 -0800472 if ver_act is None:
Mike Frysinger4c263b52019-09-11 04:04:16 -0400473 print('fatal: unable to detect git version', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700474 raise CloneFailure()
475
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700476 if ver_act < MIN_GIT_VERSION:
David Pursehouse685f0802012-11-14 08:34:39 +0900477 need = '.'.join(map(str, MIN_GIT_VERSION))
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400478 print('fatal: git %s or later required' % need, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700479 raise CloneFailure()
480
481
Mike Frysinger84094102020-02-11 02:10:28 -0500482def SetGitTrace2ParentSid(env=None):
483 """Set up GIT_TRACE2_PARENT_SID for git tracing."""
484 # We roughly follow the format git itself uses in trace2/tr2_sid.c.
485 # (1) Be unique (2) be valid filename (3) be fixed length.
486 #
487 # Since we always export this variable, we try to avoid more expensive calls.
488 # e.g. We don't attempt hostname lookups or hashing the results.
489 if env is None:
490 env = os.environ
491
492 KEY = 'GIT_TRACE2_PARENT_SID'
493
494 now = datetime.datetime.utcnow()
495 value = 'repo-%s-P%08x' % (now.strftime('%Y%m%dT%H%M%SZ'), os.getpid())
496
497 # If it's already set, then append ourselves.
498 if KEY in env:
499 value = env[KEY] + '/' + value
500
501 _setenv(KEY, value, env=env)
502
503
504def _setenv(key, value, env=None):
505 """Set |key| in the OS environment |env| to |value|."""
506 if env is None:
507 env = os.environ
508 # Environment handling across systems is messy.
509 try:
510 env[key] = value
511 except UnicodeEncodeError:
512 env[key] = value.encode()
513
514
Conley Owensc9129d92012-10-01 16:12:28 -0700515def NeedSetupGnuPG():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700516 if not os.path.isdir(home_dot_repo):
517 return True
518
519 kv = os.path.join(home_dot_repo, 'keyring-version')
520 if not os.path.exists(kv):
521 return True
522
523 kv = open(kv).read()
524 if not kv:
525 return True
526
David Pursehouse685f0802012-11-14 08:34:39 +0900527 kv = tuple(map(int, kv.split('.')))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700528 if kv < KEYRING_VERSION:
529 return True
530 return False
531
532
Conley Owensc9129d92012-10-01 16:12:28 -0700533def SetupGnuPG(quiet):
David Jamesbf79c662013-12-26 14:20:13 -0800534 try:
535 os.mkdir(home_dot_repo)
536 except OSError as e:
537 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400538 print('fatal: cannot make %s directory: %s'
539 % (home_dot_repo, e.strerror), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700540 sys.exit(1)
541
David Jamesbf79c662013-12-26 14:20:13 -0800542 try:
543 os.mkdir(gpg_dir, stat.S_IRWXU)
544 except OSError as e:
545 if e.errno != errno.EEXIST:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400546 print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
547 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700548 sys.exit(1)
549
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800550 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500551 _setenv('GNUPGHOME', gpg_dir, env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700552
553 cmd = ['gpg', '--import']
554 try:
555 proc = subprocess.Popen(cmd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700556 env=env,
557 stdin=subprocess.PIPE)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700558 except OSError as e:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700559 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400560 print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
561 print('warning: Installing it is strongly encouraged.', file=sys.stderr)
562 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700563 return False
564
Mike Frysinger9100f7f2019-09-11 03:58:36 -0400565 proc.stdin.write(MAINTAINER_KEYS.encode('utf-8'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700566 proc.stdin.close()
567
568 if proc.wait() != 0:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400569 print('fatal: registering repo maintainer keys failed', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700570 sys.exit(1)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400571 print()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700572
Mike Frysinger3164d402019-11-11 05:40:22 -0500573 with open(os.path.join(home_dot_repo, 'keyring-version'), 'w') as fd:
574 fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 return True
576
577
578def _SetConfig(local, name, value):
579 """Set a git configuration option to the specified value.
580 """
581 cmd = [GIT, 'config', name, value]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700582 if subprocess.Popen(cmd, cwd=local).wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700583 raise CloneFailure()
584
585
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700586def _InitHttp():
587 handlers = []
588
Sarah Owens1f7627f2012-10-31 09:21:55 -0700589 mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700590 try:
591 import netrc
592 n = netrc.netrc()
593 for host in n.hosts:
594 p = n.hosts[host]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700595 mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
Xiaodong Xuae0a36c2012-01-31 11:10:09 +0800596 mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
David Pursehouse65b0ba52018-06-24 16:21:51 +0900597 except:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700598 pass
Sarah Owens1f7627f2012-10-31 09:21:55 -0700599 handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
600 handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700601
602 if 'http_proxy' in os.environ:
603 url = os.environ['http_proxy']
Sarah Owens1f7627f2012-10-31 09:21:55 -0700604 handlers.append(urllib.request.ProxyHandler({'http': url, 'https': url}))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700605 if 'REPO_CURL_VERBOSE' in os.environ:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700606 handlers.append(urllib.request.HTTPHandler(debuglevel=1))
607 handlers.append(urllib.request.HTTPSHandler(debuglevel=1))
608 urllib.request.install_opener(urllib.request.build_opener(*handlers))
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700609
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700610
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700611def _Fetch(url, local, src, quiet):
612 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400613 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700614
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700615 cmd = [GIT, 'fetch']
616 if quiet:
617 cmd.append('--quiet')
618 err = subprocess.PIPE
619 else:
620 err = None
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700621 cmd.append(src)
622 cmd.append('+refs/heads/*:refs/remotes/origin/*')
Xin Li6e538442018-12-10 11:33:16 -0800623 cmd.append('+refs/tags/*:refs/tags/*')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700624
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700625 proc = subprocess.Popen(cmd, cwd=local, stderr=err)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700626 if err:
627 proc.stderr.read()
628 proc.stderr.close()
629 if proc.wait() != 0:
630 raise CloneFailure()
631
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700632
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700633def _DownloadBundle(url, local, quiet):
634 if not url.endswith('/'):
635 url += '/'
636 url += 'clone.bundle'
637
638 proc = subprocess.Popen(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700639 [GIT, 'config', '--get-regexp', 'url.*.insteadof'],
640 cwd=local,
641 stdout=subprocess.PIPE)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700642 for line in proc.stdout:
Mike Frysinger9100f7f2019-09-11 03:58:36 -0400643 line = line.decode('utf-8')
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700644 m = re.compile(r'^url\.(.*)\.insteadof (.*)$').match(line)
645 if m:
646 new_url = m.group(1)
647 old_url = m.group(2)
648 if url.startswith(old_url):
649 url = new_url + url[len(old_url):]
650 break
651 proc.stdout.close()
652 proc.wait()
653
654 if not url.startswith('http:') and not url.startswith('https:'):
655 return False
656
657 dest = open(os.path.join(local, '.git', 'clone.bundle'), 'w+b')
658 try:
659 try:
Sarah Owens1f7627f2012-10-31 09:21:55 -0700660 r = urllib.request.urlopen(url)
661 except urllib.error.HTTPError as e:
John Törnblomd3ddcdb2015-08-12 20:12:51 +0200662 if e.code in [401, 403, 404, 501]:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700663 return False
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400664 print('fatal: Cannot get %s' % url, file=sys.stderr)
665 print('fatal: HTTP error %s' % e.code, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700666 raise CloneFailure()
Sarah Owens1f7627f2012-10-31 09:21:55 -0700667 except urllib.error.URLError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400668 print('fatal: Cannot get %s' % url, file=sys.stderr)
669 print('fatal: error %s' % e.reason, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700670 raise CloneFailure()
671 try:
672 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400673 print('Get %s' % url, file=sys.stderr)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700674 while True:
675 buf = r.read(8192)
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400676 if not buf:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700677 return True
678 dest.write(buf)
679 finally:
680 r.close()
681 finally:
682 dest.close()
683
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700684
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700685def _ImportBundle(local):
686 path = os.path.join(local, '.git', 'clone.bundle')
687 try:
688 _Fetch(local, local, path, True)
689 finally:
690 os.remove(path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700691
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700692
Hu xiuyun9711a982015-12-11 11:16:41 +0800693def _Clone(url, local, quiet, clone_bundle):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700694 """Clones a git repository to a new subdirectory of repodir
695 """
696 try:
697 os.mkdir(local)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700698 except OSError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400699 print('fatal: cannot make %s directory: %s' % (local, e.strerror),
700 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700701 raise CloneFailure()
702
703 cmd = [GIT, 'init', '--quiet']
704 try:
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700705 proc = subprocess.Popen(cmd, cwd=local)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700706 except OSError as e:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400707 print(file=sys.stderr)
708 print("fatal: '%s' is not available" % GIT, file=sys.stderr)
709 print('fatal: %s' % e, file=sys.stderr)
710 print(file=sys.stderr)
711 print('Please make sure %s is installed and in your path.' % GIT,
712 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700713 raise CloneFailure()
714 if proc.wait() != 0:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400715 print('fatal: could not create %s' % local, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700716 raise CloneFailure()
717
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700718 _InitHttp()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700719 _SetConfig(local, 'remote.origin.url', url)
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700720 _SetConfig(local,
721 'remote.origin.fetch',
722 '+refs/heads/*:refs/remotes/origin/*')
Hu xiuyun9711a982015-12-11 11:16:41 +0800723 if clone_bundle and _DownloadBundle(url, local, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700724 _ImportBundle(local)
Dan Willemsenfee390e2015-09-02 12:36:30 -0700725 _Fetch(url, local, 'origin', quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700726
727
728def _Verify(cwd, branch, quiet):
729 """Verify the branch has been signed by a tag.
730 """
731 cmd = [GIT, 'describe', 'origin/%s' % branch]
732 proc = subprocess.Popen(cmd,
733 stdout=subprocess.PIPE,
734 stderr=subprocess.PIPE,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700735 cwd=cwd)
Mike Frysinger9100f7f2019-09-11 03:58:36 -0400736 cur = proc.stdout.read().strip().decode('utf-8')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700737 proc.stdout.close()
738
739 proc.stderr.read()
740 proc.stderr.close()
741
742 if proc.wait() != 0 or not cur:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400743 print(file=sys.stderr)
744 print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 raise CloneFailure()
746
747 m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
748 if m:
749 cur = m.group(1)
750 if not quiet:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400751 print(file=sys.stderr)
752 print("info: Ignoring branch '%s'; using tagged release '%s'"
753 % (branch, cur), file=sys.stderr)
754 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800756 env = os.environ.copy()
Mike Frysinger84094102020-02-11 02:10:28 -0500757 _setenv('GNUPGHOME', gpg_dir, env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758
759 cmd = [GIT, 'tag', '-v', cur]
760 proc = subprocess.Popen(cmd,
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700761 stdout=subprocess.PIPE,
762 stderr=subprocess.PIPE,
763 cwd=cwd,
764 env=env)
Mike Frysinger9100f7f2019-09-11 03:58:36 -0400765 out = proc.stdout.read().decode('utf-8')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700766 proc.stdout.close()
767
Mike Frysinger9100f7f2019-09-11 03:58:36 -0400768 err = proc.stderr.read().decode('utf-8')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700769 proc.stderr.close()
770
771 if proc.wait() != 0:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400772 print(file=sys.stderr)
773 print(out, file=sys.stderr)
774 print(err, file=sys.stderr)
775 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 raise CloneFailure()
777 return '%s^0' % cur
778
779
780def _Checkout(cwd, branch, rev, quiet):
781 """Checkout an upstream branch into the repository and track it.
782 """
783 cmd = [GIT, 'update-ref', 'refs/heads/default', rev]
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700784 if subprocess.Popen(cmd, cwd=cwd).wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700785 raise CloneFailure()
786
787 _SetConfig(cwd, 'branch.default.remote', 'origin')
788 _SetConfig(cwd, 'branch.default.merge', 'refs/heads/%s' % branch)
789
790 cmd = [GIT, 'symbolic-ref', 'HEAD', 'refs/heads/default']
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700791 if subprocess.Popen(cmd, cwd=cwd).wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700792 raise CloneFailure()
793
794 cmd = [GIT, 'read-tree', '--reset', '-u']
795 if not quiet:
796 cmd.append('-v')
797 cmd.append('HEAD')
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700798 if subprocess.Popen(cmd, cwd=cwd).wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700799 raise CloneFailure()
800
801
802def _FindRepo():
803 """Look for a repo installation, starting at the current directory.
804 """
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200805 curdir = os.getcwd()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700806 repo = None
807
Anthony Newnamdf14a702011-01-09 17:31:57 -0800808 olddir = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200809 while curdir != '/' \
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700810 and curdir != olddir \
811 and not repo:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200812 repo = os.path.join(curdir, repodir, REPO_MAIN)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700813 if not os.path.isfile(repo):
814 repo = None
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200815 olddir = curdir
816 curdir = os.path.dirname(curdir)
817 return (repo, os.path.join(curdir, repodir))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700818
819
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700820class _Options(object):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821 help = False
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500822 version = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700823
824
825def _ParseArguments(args):
826 cmd = None
827 opt = _Options()
828 arg = []
829
Sarah Owensa6053d52012-11-01 13:36:50 -0700830 for i in range(len(args)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831 a = args[i]
832 if a == '-h' or a == '--help':
833 opt.help = True
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500834 elif a == '--version':
835 opt.version = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 elif not a.startswith('-'):
837 cmd = a
838 arg = args[i + 1:]
839 break
840 return cmd, opt, arg
841
842
843def _Usage():
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700844 gitc_usage = ""
845 if get_gitc_manifest_dir():
846 gitc_usage = " gitc-init Initialize a GITC Client.\n"
847
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400848 print(
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700849 """usage: repo COMMAND [ARGS]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700850
851repo is not yet installed. Use "repo init" to install it here.
852
853The most commonly used repo commands are:
854
855 init Install repo in the current working directory
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700856""" + gitc_usage +
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700857 """ help Display detailed help on a command
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700858
859For access to the full online help, install repo ("repo init").
Mike Frysinger35159ab2019-06-13 00:07:13 -0400860""")
861 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700862
863
864def _Help(args):
865 if args:
866 if args[0] == 'init':
867 init_optparse.print_help()
Trond Norbyed3fd5372011-01-03 11:35:15 +0100868 sys.exit(0)
Dan Willemsen9ff2ece2015-08-31 15:45:06 -0700869 elif args[0] == 'gitc-init':
870 _GitcInitOptions(init_optparse)
871 init_optparse.print_help()
872 sys.exit(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873 else:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400874 print("error: '%s' is not a bootstrap command.\n"
875 ' For access to online help, install repo ("repo init").'
876 % args[0], file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700877 else:
878 _Usage()
879 sys.exit(1)
880
881
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500882def _Version():
883 """Show version information."""
884 print('<repo not installed>')
885 print('repo launcher version %s' % ('.'.join(str(x) for x in VERSION),))
886 print(' (from %s)' % (__file__,))
887 print('git %s' % (ParseGitVersion().full,))
888 print('Python %s' % sys.version)
889 sys.exit(0)
890
891
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700892def _NotInstalled():
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400893 print('error: repo is not installed. Use "repo init" to install it here.',
894 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700895 sys.exit(1)
896
897
898def _NoCommands(cmd):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400899 print("""error: command '%s' requires repo to be installed first.
900 Use "repo init" to install it here.""" % cmd, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 sys.exit(1)
902
903
904def _RunSelf(wrapper_path):
905 my_dir = os.path.dirname(wrapper_path)
906 my_main = os.path.join(my_dir, 'main.py')
907 my_git = os.path.join(my_dir, '.git')
908
909 if os.path.isfile(my_main) and os.path.isdir(my_git):
Shawn O. Pearcec8a300f2009-05-18 13:19:57 -0700910 for name in ['git_config.py',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911 'project.py',
912 'subcmds']:
913 if not os.path.exists(os.path.join(my_dir, name)):
914 return None, None
915 return my_main, my_git
916 return None, None
917
918
919def _SetDefaultsTo(gitdir):
920 global REPO_URL
921 global REPO_REV
922
923 REPO_URL = gitdir
924 proc = subprocess.Popen([GIT,
925 '--git-dir=%s' % gitdir,
926 'symbolic-ref',
927 'HEAD'],
Mark E. Hamilton4088eb42016-02-10 10:44:30 -0700928 stdout=subprocess.PIPE,
929 stderr=subprocess.PIPE)
Mike Frysinger9100f7f2019-09-11 03:58:36 -0400930 REPO_REV = proc.stdout.read().strip().decode('utf-8')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700931 proc.stdout.close()
932
933 proc.stderr.read()
934 proc.stderr.close()
935
936 if proc.wait() != 0:
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400937 print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938 sys.exit(1)
939
940
941def main(orig_args):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 cmd, opt, args = _ParseArguments(orig_args)
943
Mike Frysinger84094102020-02-11 02:10:28 -0500944 # We run this early as we run some git commands ourselves.
945 SetGitTrace2ParentSid()
946
Dan Willemsen745b4ad2015-10-06 15:23:19 -0700947 repo_main, rel_repo_dir = None, None
948 # Don't use the local repo copy, make sure to switch to the gitc client first.
949 if cmd != 'gitc-init':
950 repo_main, rel_repo_dir = _FindRepo()
951
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952 wrapper_path = os.path.abspath(__file__)
953 my_main, my_git = _RunSelf(wrapper_path)
954
Simran Basi8ce50412015-08-28 14:25:44 -0700955 cwd = os.getcwd()
Dan Willemsen2487cb72015-08-31 15:45:06 -0700956 if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400957 print('error: repo cannot be used in the GITC local manifest directory.'
958 '\nIf you want to work on this GITC client please rerun this '
959 'command from the corresponding client under /gitc/',
960 file=sys.stderr)
Simran Basi8ce50412015-08-28 14:25:44 -0700961 sys.exit(1)
Mike Frysingerf700ac72020-02-06 00:04:21 -0500962 _InitParser()
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200963 if not repo_main:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 if opt.help:
965 _Usage()
966 if cmd == 'help':
967 _Help(args)
Mike Frysinger8ddff5c2020-02-09 15:00:25 -0500968 if opt.version or cmd == 'version':
969 _Version()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700970 if not cmd:
971 _NotInstalled()
Simran Basi1efc2b42015-08-05 15:04:22 -0700972 if cmd == 'init' or cmd == 'gitc-init':
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973 if my_git:
974 _SetDefaultsTo(my_git)
975 try:
Simran Basi1efc2b42015-08-05 15:04:22 -0700976 _Init(args, gitc_init=(cmd == 'gitc-init'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700977 except CloneFailure:
Sebastian Schuberth27226e72016-10-28 14:27:43 +0200978 path = os.path.join(repodir, S_repo)
Mike Frysingerc92ce5c2019-06-13 01:14:23 -0400979 print("fatal: cloning the git-repo repository failed, will remove "
980 "'%s' " % path, file=sys.stderr)
Sebastian Schuberth27226e72016-10-28 14:27:43 +0200981 shutil.rmtree(path, ignore_errors=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982 sys.exit(1)
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200983 repo_main, rel_repo_dir = _FindRepo()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700984 else:
985 _NoCommands(cmd)
986
987 if my_main:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200988 repo_main = my_main
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700989
David Pursehouse685f0802012-11-14 08:34:39 +0900990 ver_str = '.'.join(map(str, VERSION))
anatoly techtonik3a2a59e2013-09-21 19:29:10 +0300991 me = [sys.executable, repo_main,
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200992 '--repo-dir=%s' % rel_repo_dir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993 '--wrapper-version=%s' % ver_str,
994 '--wrapper-path=%s' % wrapper_path,
995 '--']
996 me.extend(orig_args)
997 me.extend(extra_args)
Mike Frysinger3ba716f2019-06-13 01:48:12 -0400998 exec_command(me)
999 print("fatal: unable to start %s" % repo_main, file=sys.stderr)
1000 sys.exit(148)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001
1002
1003if __name__ == '__main__':
1004 main(sys.argv[1:])