@ -1,4 +1,14 @@
#!/usr/bin/env python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""Repo launcher.
This is a standalone tool that people may copy to anywhere in their system.
It is used to get an initial repo client checkout, and after that it runs the
copy of repo in the checkout.
"""
from __future__ import print_function
# repo default configuration
# repo default configuration
#
#
@ -245,6 +255,7 @@ GITC_CONFIG_FILE = '/gitc/.config'
GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
import collections
import errno
import errno
import optparse
import optparse
import platform
import platform
@ -265,22 +276,15 @@ else:
urllib.error = urllib2
urllib.error = urllib2
def _print(*objects, **kwargs):
sep = kwargs.get('sep', ' ')
end = kwargs.get('end', '\n')
out = kwargs.get('file', sys.stdout)
out.write(sep.join(objects) + end)
# Python version check
# Python version check
ver = sys.version_info
ver = sys.version_info
if (ver[0], ver[1]) < MIN_PYTHON_VERSION:
if (ver[0], ver[1]) < MIN_PYTHON_VERSION:
_ print('error: Python version {} unsupported.\n'
print('error: Python version {} unsupported.\n'
'Please use Python {}.{} instead.'.format(
'Please use Python {}.{} instead.'.format(
sys.version.split(' ')[0],
sys.version.split(' ')[0],
MIN_PYTHON_VERSION[0],
MIN_PYTHON_VERSION[0],
MIN_PYTHON_VERSION[1],
MIN_PYTHON_VERSION[1],
), file=sys.stderr)
), file=sys.stderr)
sys.exit(1)
sys.exit(1)
home_dot_repo = os.path.expanduser('~/.repoconfig')
home_dot_repo = os.path.expanduser('~/.repoconfig')
@ -316,9 +320,19 @@ group.add_option('--mirror',
group.add_option('--reference',
group.add_option('--reference',
dest='reference',
dest='reference',
help='location of mirror directory', metavar='DIR')
help='location of mirror directory', metavar='DIR')
group.add_option('--dissociate',
dest='dissociate', action='store_true',
help='dissociate from reference mirrors after clone')
group.add_option('--depth', type='int', default=None,
group.add_option('--depth', type='int', default=None,
dest='depth',
dest='depth',
help='create a shallow clone with given depth; see git clone')
help='create a shallow clone with given depth; see git clone')
group.add_option('--partial-clone', action='store_true',
dest='partial_clone',
help='perform partial clone (https://git-scm.com/'
'docs/gitrepository-layout#_code_partialclone_code)')
group.add_option('--clone-filter', action='store', default='blob:none',
dest='clone_filter',
help='filter for use with --partial-clone [default: %default]')
group.add_option('--archive',
group.add_option('--archive',
dest='archive', action='store_true',
dest='archive', action='store_true',
help='checkout an archive instead of a git repository for '
help='checkout an archive instead of a git repository for '
@ -443,21 +457,21 @@ def _Init(args, gitc_init=False):
if branch.startswith('refs/heads/'):
if branch.startswith('refs/heads/'):
branch = branch[len('refs/heads/'):]
branch = branch[len('refs/heads/'):]
if branch.startswith('refs/'):
if branch.startswith('refs/'):
_ print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
print("fatal: invalid branch name '%s'" % branch, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
try:
try:
if gitc_init:
if gitc_init:
gitc_manifest_dir = get_gitc_manifest_dir()
gitc_manifest_dir = get_gitc_manifest_dir()
if not gitc_manifest_dir:
if not gitc_manifest_dir:
_ print('fatal: GITC filesystem is not available. Exiting...',
print('fatal: GITC filesystem is not available. Exiting...',
file=sys.stderr)
file=sys.stderr)
sys.exit(1)
sys.exit(1)
gitc_client = opt.gitc_client
gitc_client = opt.gitc_client
if not gitc_client:
if not gitc_client:
gitc_client = gitc_parse_clientdir(os.getcwd())
gitc_client = gitc_parse_clientdir(os.getcwd())
if not gitc_client:
if not gitc_client:
_ print('fatal: GITC client (-c) is required.', file=sys.stderr)
print('fatal: GITC client (-c) is required.', file=sys.stderr)
sys.exit(1)
sys.exit(1)
client_dir = os.path.join(gitc_manifest_dir, gitc_client)
client_dir = os.path.join(gitc_manifest_dir, gitc_client)
if not os.path.exists(client_dir):
if not os.path.exists(client_dir):
@ -470,8 +484,8 @@ def _Init(args, gitc_init=False):
os.mkdir(repodir)
os.mkdir(repodir)
except OSError as e:
except OSError as e:
if e.errno != errno.EEXIST:
if e.errno != errno.EEXIST:
_ print('fatal: cannot make %s directory: %s'
print('fatal: cannot make %s directory: %s'
% (repodir, e.strerror), file=sys.stderr)
% (repodir, e.strerror), file=sys.stderr)
# Don't raise CloneFailure; that would delete the
# Don't raise CloneFailure; that would delete the
# name. Instead exit immediately.
# name. Instead exit immediately.
#
#
@ -487,62 +501,81 @@ def _Init(args, gitc_init=False):
dst = os.path.abspath(os.path.join(repodir, S_repo))
dst = os.path.abspath(os.path.join(repodir, S_repo))
_Clone(url, dst, opt.quiet, not opt.no_clone_bundle)
_Clone(url, dst, opt.quiet, not opt.no_clone_bundle)
if not os.path.isfile('%s/repo' % dst):
_print("warning: '%s' does not look like a git-repo repository, is "
"REPO_URL set correctly?" % url, file=sys.stderr)
if can_verify and not opt.no_repo_verify:
if can_verify and not opt.no_repo_verify:
rev = _Verify(dst, branch, opt.quiet)
rev = _Verify(dst, branch, opt.quiet)
else:
else:
rev = 'refs/remotes/origin/%s^0' % branch
rev = 'refs/remotes/origin/%s^0' % branch
_Checkout(dst, branch, rev, opt.quiet)
_Checkout(dst, branch, rev, opt.quiet)
if not os.path.isfile(os.path.join(dst, 'repo')):
print("warning: '%s' does not look like a git-repo repository, is "
"REPO_URL set correctly?" % url, file=sys.stderr)
except CloneFailure:
except CloneFailure:
if opt.quiet:
if opt.quiet:
_print('fatal: repo init failed; run without --quiet to see why',
print('fatal: repo init failed; run without --quiet to see why',
file=sys.stderr)
file=sys.stderr)
raise
raise
def ParseGitVersion(ver_str):
# The git version info broken down into components for easy analysis.
# Similar to Python's sys.version_info.
GitVersion = collections.namedtuple(
'GitVersion', ('major', 'minor', 'micro', 'full'))
def ParseGitVersion(ver_str=None):
if ver_str is None:
# Load the version ourselves.
ver_str = _GetGitVersion()
if not ver_str.startswith('git version '):
if not ver_str.startswith('git version '):
return None
return None
num_ver_str = ver_str[len('git version '):].strip().split('-')[0]
full_version = ver_str[len('git version '):].strip()
num_ver_str = full_version.split('-')[0]
to_tuple = []
to_tuple = []
for num_str in num_ver_str.split('.')[:3]:
for num_str in num_ver_str.split('.')[:3]:
if num_str.isdigit():
if num_str.isdigit():
to_tuple.append(int(num_str))
to_tuple.append(int(num_str))
else:
else:
to_tuple.append(0)
to_tuple.append(0)
return tuple(to_tuple)
to_tuple.append(full_version)
return GitVersion(*to_tuple)
def _Check GitVersion():
def _Get GitVersion():
cmd = [GIT, '--version']
cmd = [GIT, '--version']
try:
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
except OSError as e:
except OSError as e:
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print("fatal: '%s' is not available" % GIT, file=sys.stderr)
print("fatal: '%s' is not available" % GIT, file=sys.stderr)
_ print('fatal: %s' % e, file=sys.stderr)
print('fatal: %s' % e, file=sys.stderr)
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print('Please make sure %s is installed and in your path.' % GIT,
print('Please make sure %s is installed and in your path.' % GIT,
file=sys.stderr)
file=sys.stderr)
raise CloneFailure()
raise
ver_str = proc.stdout.read().strip()
ver_str = proc.stdout.read().strip()
proc.stdout.close()
proc.stdout.close()
proc.wait()
proc.wait()
return ver_str.decode('utf-8')
def _CheckGitVersion():
try:
ver_act = ParseGitVersion()
except OSError:
raise CloneFailure()
ver_act = ParseGitVersion(ver_str)
if ver_act is None:
if ver_act is None:
_print('error: "%s" unsupported' % ver_str, file=sys.stderr)
print('error: "%s" unsupported' % ver_str, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
if ver_act < MIN_GIT_VERSION:
if ver_act < MIN_GIT_VERSION:
need = '.'.join(map(str, MIN_GIT_VERSION))
need = '.'.join(map(str, MIN_GIT_VERSION))
_ print('fatal: git %s or later required' % need, file=sys.stderr)
print('fatal: git %s or later required' % need, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
@ -569,16 +602,16 @@ def SetupGnuPG(quiet):
os.mkdir(home_dot_repo)
os.mkdir(home_dot_repo)
except OSError as e:
except OSError as e:
if e.errno != errno.EEXIST:
if e.errno != errno.EEXIST:
_ print('fatal: cannot make %s directory: %s'
print('fatal: cannot make %s directory: %s'
% (home_dot_repo, e.strerror), file=sys.stderr)
% (home_dot_repo, e.strerror), file=sys.stderr)
sys.exit(1)
sys.exit(1)
try:
try:
os.mkdir(gpg_dir, stat.S_IRWXU)
os.mkdir(gpg_dir, stat.S_IRWXU)
except OSError as e:
except OSError as e:
if e.errno != errno.EEXIST:
if e.errno != errno.EEXIST:
_ print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
print('fatal: cannot make %s directory: %s' % (gpg_dir, e.strerror),
file=sys.stderr)
file=sys.stderr)
sys.exit(1)
sys.exit(1)
env = os.environ.copy()
env = os.environ.copy()
@ -594,18 +627,18 @@ def SetupGnuPG(quiet):
stdin=subprocess.PIPE)
stdin=subprocess.PIPE)
except OSError as e:
except OSError as e:
if not quiet:
if not quiet:
_ print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
print('warning: gpg (GnuPG) is not available.', file=sys.stderr)
_ print('warning: Installing it is strongly encouraged.', file=sys.stderr)
print('warning: Installing it is strongly encouraged.', file=sys.stderr)
_ print(file=sys.stderr)
print(file=sys.stderr)
return False
return False
proc.stdin.write(MAINTAINER_KEYS)
proc.stdin.write(MAINTAINER_KEYS)
proc.stdin.close()
proc.stdin.close()
if proc.wait() != 0:
if proc.wait() != 0:
_ print('fatal: registering repo maintainer keys failed', file=sys.stderr)
print('fatal: registering repo maintainer keys failed', file=sys.stderr)
sys.exit(1)
sys.exit(1)
_ print()
print()
fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w')
fd = open(os.path.join(home_dot_repo, 'keyring-version'), 'w')
fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
fd.write('.'.join(map(str, KEYRING_VERSION)) + '\n')
@ -632,7 +665,7 @@ def _InitHttp():
p = n.hosts[host]
p = n.hosts[host]
mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'http://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
mgr.add_password(p[1], 'https://%s/' % host, p[0], p[2])
except: # pylint: disable=bare-except
except:
pass
pass
handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
handlers.append(urllib.request.HTTPBasicAuthHandler(mgr))
handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
handlers.append(urllib.request.HTTPDigestAuthHandler(mgr))
@ -648,7 +681,7 @@ def _InitHttp():
def _Fetch(url, local, src, quiet):
def _Fetch(url, local, src, quiet):
if not quiet:
if not quiet:
_ print('Get %s' % url, file=sys.stderr)
print('Get %s' % url, file=sys.stderr)
cmd = [GIT, 'fetch']
cmd = [GIT, 'fetch']
if quiet:
if quiet:
@ -658,7 +691,7 @@ def _Fetch(url, local, src, quiet):
err = None
err = None
cmd.append(src)
cmd.append(src)
cmd.append('+refs/heads/*:refs/remotes/origin/*')
cmd.append('+refs/heads/*:refs/remotes/origin/*')
cmd.append('refs/tags/*:refs/tags/*')
cmd.append('+ refs/tags/*:refs/tags/*')
proc = subprocess.Popen(cmd, cwd=local, stderr=err)
proc = subprocess.Popen(cmd, cwd=local, stderr=err)
if err:
if err:
@ -698,19 +731,19 @@ def _DownloadBundle(url, local, quiet):
except urllib.error.HTTPError as e:
except urllib.error.HTTPError as e:
if e.code in [401, 403, 404, 501]:
if e.code in [401, 403, 404, 501]:
return False
return False
_ print('fatal: Cannot get %s' % url, file=sys.stderr)
print('fatal: Cannot get %s' % url, file=sys.stderr)
_ print('fatal: HTTP error %s' % e.code, file=sys.stderr)
print('fatal: HTTP error %s' % e.code, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
except urllib.error.URLError as e:
except urllib.error.URLError as e:
_ print('fatal: Cannot get %s' % url, file=sys.stderr)
print('fatal: Cannot get %s' % url, file=sys.stderr)
_ print('fatal: error %s' % e.reason, file=sys.stderr)
print('fatal: error %s' % e.reason, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
try:
try:
if not quiet:
if not quiet:
_ print('Get %s' % url, file=sys.stderr)
print('Get %s' % url, file=sys.stderr)
while True:
while True:
buf = r.read(8192)
buf = r.read(8192)
if buf == '' :
if not buf:
return True
return True
dest.write(buf)
dest.write(buf)
finally:
finally:
@ -733,23 +766,23 @@ def _Clone(url, local, quiet, clone_bundle):
try:
try:
os.mkdir(local)
os.mkdir(local)
except OSError as e:
except OSError as e:
_ print('fatal: cannot make %s directory: %s' % (local, e.strerror),
print('fatal: cannot make %s directory: %s' % (local, e.strerror),
file=sys.stderr)
file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
cmd = [GIT, 'init', '--quiet']
cmd = [GIT, 'init', '--quiet']
try:
try:
proc = subprocess.Popen(cmd, cwd=local)
proc = subprocess.Popen(cmd, cwd=local)
except OSError as e:
except OSError as e:
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print("fatal: '%s' is not available" % GIT, file=sys.stderr)
print("fatal: '%s' is not available" % GIT, file=sys.stderr)
_ print('fatal: %s' % e, file=sys.stderr)
print('fatal: %s' % e, file=sys.stderr)
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print('Please make sure %s is installed and in your path.' % GIT,
print('Please make sure %s is installed and in your path.' % GIT,
file=sys.stderr)
file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
if proc.wait() != 0:
if proc.wait() != 0:
_ print('fatal: could not create %s' % local, file=sys.stderr)
print('fatal: could not create %s' % local, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
_InitHttp()
_InitHttp()
@ -777,18 +810,18 @@ def _Verify(cwd, branch, quiet):
proc.stderr.close()
proc.stderr.close()
if proc.wait() != 0 or not cur:
if proc.wait() != 0 or not cur:
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
print("fatal: branch '%s' has not been signed" % branch, file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
m = re.compile(r'^(.*)-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur)
if m:
if m:
cur = m.group(1)
cur = m.group(1)
if not quiet:
if not quiet:
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print("info: Ignoring branch '%s'; using tagged release '%s'"
print("info: Ignoring branch '%s'; using tagged release '%s'"
% (branch, cur), file=sys.stderr)
% (branch, cur), file=sys.stderr)
_ print(file=sys.stderr)
print(file=sys.stderr)
env = os.environ.copy()
env = os.environ.copy()
try:
try:
@ -809,10 +842,10 @@ def _Verify(cwd, branch, quiet):
proc.stderr.close()
proc.stderr.close()
if proc.wait() != 0:
if proc.wait() != 0:
_ print(file=sys.stderr)
print(file=sys.stderr)
_ print(out, file=sys.stderr)
print(out, file=sys.stderr)
_ print(err, file=sys.stderr)
print(err, file=sys.stderr)
_ print(file=sys.stderr)
print(file=sys.stderr)
raise CloneFailure()
raise CloneFailure()
return '%s^0' % cur
return '%s^0' % cur
@ -883,7 +916,7 @@ def _Usage():
if get_gitc_manifest_dir():
if get_gitc_manifest_dir():
gitc_usage = " gitc-init Initialize a GITC Client.\n"
gitc_usage = " gitc-init Initialize a GITC Client.\n"
_ print(
print(
"""usage: repo COMMAND [ARGS]
"""usage: repo COMMAND [ARGS]
repo is not yet installed. Use "repo init" to install it here.
repo is not yet installed. Use "repo init" to install it here.
@ -895,8 +928,8 @@ The most commonly used repo commands are:
""" help Display detailed help on a command
""" help Display detailed help on a command
For access to the full online help, install repo ("repo init").
For access to the full online help, install repo ("repo init").
""", file=sys.stderr )
""")
sys.exit(1 )
sys.exit(0 )
def _Help(args):
def _Help(args):
@ -909,23 +942,23 @@ def _Help(args):
init_optparse.print_help()
init_optparse.print_help()
sys.exit(0)
sys.exit(0)
else:
else:
_ print("error: '%s' is not a bootstrap command.\n"
print("error: '%s' is not a bootstrap command.\n"
' For access to online help, install repo ("repo init").'
' For access to online help, install repo ("repo init").'
% args[0], file=sys.stderr)
% args[0], file=sys.stderr)
else:
else:
_Usage()
_Usage()
sys.exit(1)
sys.exit(1)
def _NotInstalled():
def _NotInstalled():
_ print('error: repo is not installed. Use "repo init" to install it here.',
print('error: repo is not installed. Use "repo init" to install it here.',
file=sys.stderr)
file=sys.stderr)
sys.exit(1)
sys.exit(1)
def _NoCommands(cmd):
def _NoCommands(cmd):
_ print("""error: command '%s' requires repo to be installed first.
print("""error: command '%s' requires repo to be installed first.
Use "repo init" to install it here.""" % cmd, file=sys.stderr)
Use "repo init" to install it here.""" % cmd, file=sys.stderr)
sys.exit(1)
sys.exit(1)
@ -962,7 +995,7 @@ def _SetDefaultsTo(gitdir):
proc.stderr.close()
proc.stderr.close()
if proc.wait() != 0:
if proc.wait() != 0:
_ print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
print('fatal: %s has no current branch' % gitdir, file=sys.stderr)
sys.exit(1)
sys.exit(1)
@ -979,10 +1012,10 @@ def main(orig_args):
cwd = os.getcwd()
cwd = os.getcwd()
if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
if get_gitc_manifest_dir() and cwd.startswith(get_gitc_manifest_dir()):
_ print('error: repo cannot be used in the GITC local manifest directory.'
print('error: repo cannot be used in the GITC local manifest directory.'
'\nIf you want to work on this GITC client please rerun this '
'\nIf you want to work on this GITC client please rerun this '
'command from the corresponding client under /gitc/',
'command from the corresponding client under /gitc/',
file=sys.stderr)
file=sys.stderr)
sys.exit(1)
sys.exit(1)
if not repo_main:
if not repo_main:
if opt.help:
if opt.help:
@ -998,8 +1031,8 @@ def main(orig_args):
_Init(args, gitc_init=(cmd == 'gitc-init'))
_Init(args, gitc_init=(cmd == 'gitc-init'))
except CloneFailure:
except CloneFailure:
path = os.path.join(repodir, S_repo)
path = os.path.join(repodir, S_repo)
_ print("fatal: cloning the git-repo repository failed, will remove "
print("fatal: cloning the git-repo repository failed, will remove "
"'%s' " % path, file=sys.stderr)
"'%s' " % path, file=sys.stderr)
shutil.rmtree(path, ignore_errors=True)
shutil.rmtree(path, ignore_errors=True)
sys.exit(1)
sys.exit(1)
repo_main, rel_repo_dir = _FindRepo()
repo_main, rel_repo_dir = _FindRepo()
@ -1023,14 +1056,10 @@ def main(orig_args):
else:
else:
os.execv(sys.executable, me)
os.execv(sys.executable, me)
except OSError as e:
except OSError as e:
_ print("fatal: unable to start %s" % repo_main, file=sys.stderr)
print("fatal: unable to start %s" % repo_main, file=sys.stderr)
_ print("fatal: %s" % e, file=sys.stderr)
print("fatal: %s" % e, file=sys.stderr)
sys.exit(148)
sys.exit(148)
if __name__ == '__main__':
if __name__ == '__main__':
if ver[0] == 3:
_print('warning: Python 3 support is currently experimental. YMMV.\n'
'Please use Python 2.7 instead.',
file=sys.stderr)
main(sys.argv[1:])
main(sys.argv[1:])