Compare commits

..

4 Commits

Author SHA1 Message Date
ef99ec07b4 superproject: Display status messages during repo init/sync.
Superproject objects accept the optional argument “quiet”.
The following progress messages are displayed if quiet is false.

Displayed the following message whenever we find we have to make a new
folder (aka new remote), because if you started with repo init android
and later do googleplex-android that is when it will be slow.

"<location>: Performing initial setup for superproject; this might take
several minutes.".

After fetch completion, added the following notification:
"<location>: Initial setup for superproject completed."

Tested the code with the following commands.

$ ./run_tests -v

Tested the sync code by using repo_dev alias and pointing to this CL.

$ repo_dev init -u persistent-https://googleplex-android.git.corp.google.com/platform/manifest -b rvc-dev  --partial-clone --clone-filter=blob:limit=10M --repo-rev=main  --use-superproject

Bug: [google internal] b/181178282
Bug: https://crbug.com/gerrit/13707
Change-Id: Ia7fb85c6fb934faaa90c48fc0c55e7f41055f48a
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/299122
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Raman Tenneti <rtenneti@google.com>
2021-03-04 20:07:52 +00:00
934cb0a849 tests: fix duplicate method from copy & paste error
Change-Id: Ib748c61b1e65aee6dff8b97a9753d14c470a827f
Reported-by: Daniel Kutik <daniel.kutik@lavawerk.com>
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/299002
Reviewed-by: Daniel Kutik <daniel.kutik@lavawerk.com>
Reviewed-by: Ian Kasprzak <iankaz@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
2021-03-04 16:17:11 +00:00
3c0931285c project: fix variable typo
Bug: https://crbug.com/gerrit/11293
Reported-by: Daniel Kutik <daniel.kutik@lavawerk.com>
Change-Id: I37bac58aa1dc9ecc10e29253d14ff9e6fb42427c
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/298942
Reviewed-by: Ian Kasprzak <iankaz@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
2021-03-03 16:45:21 +00:00
5413397204 manifest: relax include name rules for user-specified path
Allow the user to specify relative or absolute or any other funky
path that they want when using `repo init` or `repo sync`.  Our
goal is to restrict the paths in the remote manifest git repo we
cloned from the network, not protect the user from themselves.

Bug: https://crbug.com/gerrit/14156
Change-Id: I1ccfb2a6bd1dce2bd765e261bef0bbf0f8a9beb6
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/298823
Reviewed-by: Jonathan Nieder <jrn@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
2021-03-02 03:18:57 +00:00
8 changed files with 85 additions and 24 deletions

View File

@ -22,12 +22,12 @@ class ManifestParseError(Exception):
""" """
class ManifestInvalidRevisionError(Exception): class ManifestInvalidRevisionError(ManifestParseError):
"""The revision value in a project is incorrect. """The revision value in a project is incorrect.
""" """
class ManifestInvalidPathError(Exception): class ManifestInvalidPathError(ManifestParseError):
"""A path used in <copyfile> or <linkfile> is incorrect. """A path used in <copyfile> or <linkfile> is incorrect.
""" """

View File

@ -41,7 +41,8 @@ class Superproject(object):
lookup of commit ids for all projects. It contains _project_commit_ids which lookup of commit ids for all projects. It contains _project_commit_ids which
is a dictionary with project/commit id entries. is a dictionary with project/commit id entries.
""" """
def __init__(self, manifest, repodir, superproject_dir='exp-superproject'): def __init__(self, manifest, repodir, superproject_dir='exp-superproject',
quiet=False):
"""Initializes superproject. """Initializes superproject.
Args: Args:
@ -49,9 +50,11 @@ class Superproject(object):
repodir: Path to the .repo/ dir for holding all internal checkout state. repodir: Path to the .repo/ dir for holding all internal checkout state.
It must be in the top directory of the repo client checkout. It must be in the top directory of the repo client checkout.
superproject_dir: Relative path under |repodir| to checkout superproject. superproject_dir: Relative path under |repodir| to checkout superproject.
quiet: If True then only print the progress messages.
""" """
self._project_commit_ids = None self._project_commit_ids = None
self._manifest = manifest self._manifest = manifest
self._quiet = quiet
self._branch = self._GetBranch() self._branch = self._GetBranch()
self._repodir = os.path.abspath(repodir) self._repodir = os.path.abspath(repodir)
self._superproject_dir = superproject_dir self._superproject_dir = superproject_dir
@ -89,6 +92,9 @@ class Superproject(object):
""" """
if not os.path.exists(self._superproject_path): if not os.path.exists(self._superproject_path):
os.mkdir(self._superproject_path) os.mkdir(self._superproject_path)
if not self._quiet and not os.path.exists(self._work_git):
print('%s: Performing initial setup for superproject; this might take '
'several minutes.' % self._work_git)
cmd = ['init', '--bare', self._work_git_name] cmd = ['init', '--bare', self._work_git_name]
p = GitCommand(None, p = GitCommand(None,
cmd, cmd,
@ -183,6 +189,8 @@ class Superproject(object):
return False return False
if not self._Fetch(url): if not self._Fetch(url):
return False return False
if not self._quiet:
print('%s: Initial setup for superproject completed.' % self._work_git)
return True return True
def _GetAllProjectsCommitIds(self): def _GetAllProjectsCommitIds(self):

View File

@ -624,16 +624,22 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
b = b[len(R_HEADS):] b = b[len(R_HEADS):]
self.branch = b self.branch = b
# The manifestFile was specified by the user which is why we allow include
# paths to point anywhere.
nodes = [] nodes = []
nodes.append(self._ParseManifestXml(self.manifestFile, nodes.append(self._ParseManifestXml(
self.manifestProject.worktree)) self.manifestFile, self.manifestProject.worktree,
restrict_includes=False))
if self._load_local_manifests and self.local_manifests: if self._load_local_manifests and self.local_manifests:
try: try:
for local_file in sorted(platform_utils.listdir(self.local_manifests)): for local_file in sorted(platform_utils.listdir(self.local_manifests)):
if local_file.endswith('.xml'): if local_file.endswith('.xml'):
local = os.path.join(self.local_manifests, local_file) local = os.path.join(self.local_manifests, local_file)
nodes.append(self._ParseManifestXml(local, self.repodir)) # Since local manifests are entirely managed by the user, allow
# them to point anywhere the user wants.
nodes.append(self._ParseManifestXml(
local, self.repodir, restrict_includes=False))
except OSError: except OSError:
pass pass
@ -651,7 +657,19 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
self._loaded = True self._loaded = True
def _ParseManifestXml(self, path, include_root, parent_groups=''): def _ParseManifestXml(self, path, include_root, parent_groups='',
restrict_includes=True):
"""Parse a manifest XML and return the computed nodes.
Args:
path: The XML file to read & parse.
include_root: The path to interpret include "name"s relative to.
parent_groups: The groups to apply to this projects.
restrict_includes: Whether to constrain the "name" attribute of includes.
Returns:
List of XML nodes.
"""
try: try:
root = xml.dom.minidom.parse(path) root = xml.dom.minidom.parse(path)
except (OSError, xml.parsers.expat.ExpatError) as e: except (OSError, xml.parsers.expat.ExpatError) as e:
@ -670,10 +688,11 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
for node in manifest.childNodes: for node in manifest.childNodes:
if node.nodeName == 'include': if node.nodeName == 'include':
name = self._reqatt(node, 'name') name = self._reqatt(node, 'name')
msg = self._CheckLocalPath(name) if restrict_includes:
if msg: msg = self._CheckLocalPath(name)
raise ManifestInvalidPathError( if msg:
'<include> invalid "name": %s: %s' % (name, msg)) raise ManifestInvalidPathError(
'<include> invalid "name": %s: %s' % (name, msg))
include_groups = '' include_groups = ''
if parent_groups: if parent_groups:
include_groups = parent_groups include_groups = parent_groups
@ -681,13 +700,13 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
include_groups = node.getAttribute('groups') + ',' + include_groups include_groups = node.getAttribute('groups') + ',' + include_groups
fp = os.path.join(include_root, name) fp = os.path.join(include_root, name)
if not os.path.isfile(fp): if not os.path.isfile(fp):
raise ManifestParseError("include %s doesn't exist or isn't a file" raise ManifestParseError("include [%s/]%s doesn't exist or isn't a file"
% (name,)) % (include_root, name))
try: try:
nodes.extend(self._ParseManifestXml(fp, include_root, include_groups)) nodes.extend(self._ParseManifestXml(fp, include_root, include_groups))
# should isolate this to the exact exception, but that's # should isolate this to the exact exception, but that's
# tricky. actual parsing implementation may vary. # tricky. actual parsing implementation may vary.
except (KeyboardInterrupt, RuntimeError, SystemExit): except (KeyboardInterrupt, RuntimeError, SystemExit, ManifestParseError):
raise raise
except Exception as e: except Exception as e:
raise ManifestParseError( raise ManifestParseError(

View File

@ -1150,7 +1150,7 @@ class Project(object):
p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir), p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
merge_output=bool(output_redir)) merge_output=bool(output_redir))
if p.stdout and output_redir: if p.stdout and output_redir:
buf.write(p.stdout) output_redir.write(p.stdout)
if p.Wait() != 0: if p.Wait() != 0:
return False return False
platform_utils.remove(alternates_file) platform_utils.remove(alternates_file)

View File

@ -185,10 +185,15 @@ to update the working directory files.
return {'REPO_MANIFEST_URL': 'manifest_url', return {'REPO_MANIFEST_URL': 'manifest_url',
'REPO_MIRROR_LOCATION': 'reference'} 'REPO_MIRROR_LOCATION': 'reference'}
def _CloneSuperproject(self): def _CloneSuperproject(self, opt):
"""Clone the superproject based on the superproject's url and branch.""" """Clone the superproject based on the superproject's url and branch.
Args:
opt: Program options returned from optparse. See _Options().
"""
superproject = git_superproject.Superproject(self.manifest, superproject = git_superproject.Superproject(self.manifest,
self.repodir) self.repodir,
quiet=opt.quiet)
if not superproject.Sync(): if not superproject.Sync():
print('error: git update of superproject failed', file=sys.stderr) print('error: git update of superproject failed', file=sys.stderr)
sys.exit(1) sys.exit(1)
@ -553,7 +558,7 @@ to update the working directory files.
self._LinkManifest(opt.manifest_name) self._LinkManifest(opt.manifest_name)
if self.manifest.manifestProject.config.GetBoolean('repo.superproject'): if self.manifest.manifestProject.config.GetBoolean('repo.superproject'):
self._CloneSuperproject() self._CloneSuperproject(opt)
if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror:
if opt.config_name or self._ShouldConfigureUser(opt): if opt.config_name or self._ShouldConfigureUser(opt):

View File

@ -292,7 +292,8 @@ later is required to fix a server side protocol bug.
Returns path to the overriding manifest file. Returns path to the overriding manifest file.
""" """
superproject = git_superproject.Superproject(self.manifest, superproject = git_superproject.Superproject(self.manifest,
self.repodir) self.repodir,
quiet=opt.quiet)
all_projects = self.GetProjects(args, all_projects = self.GetProjects(args,
missing_ok=True, missing_ok=True,
submodules_ok=opt.fetch_submodules) submodules_ok=opt.fetch_submodules)

View File

@ -298,8 +298,8 @@ class IncludeElementTests(ManifestParseTestCase):
# Check level2 proj group not removed. # Check level2 proj group not removed.
self.assertIn('l2g1', proj.groups) self.assertIn('l2g1', proj.groups)
def test_bad_name_checks(self): def test_allow_bad_name_from_user(self):
"""Check handling of bad name attribute.""" """Check handling of bad name attribute from the user's input."""
def parse(name): def parse(name):
manifest = self.getXmlManifest(f""" manifest = self.getXmlManifest(f"""
<manifest> <manifest>
@ -307,6 +307,34 @@ class IncludeElementTests(ManifestParseTestCase):
<default remote="default-remote" revision="refs/heads/main" /> <default remote="default-remote" revision="refs/heads/main" />
<include name="{name}" /> <include name="{name}" />
</manifest> </manifest>
""")
# Force the manifest to be parsed.
manifest.ToXml()
# Setup target of the include.
target = os.path.join(self.tempdir, 'target.xml')
with open(target, 'w') as fp:
fp.write('<manifest></manifest>')
# Include with absolute path.
parse(os.path.abspath(target))
# Include with relative path.
parse(os.path.relpath(target, self.manifest_dir))
def test_bad_name_checks(self):
"""Check handling of bad name attribute."""
def parse(name):
# Setup target of the include.
with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp:
fp.write(f'<manifest><include name="{name}"/></manifest>')
manifest = self.getXmlManifest("""
<manifest>
<remote name="default-remote" fetch="http://localhost" />
<default remote="default-remote" revision="refs/heads/main" />
<include name="target.xml" />
</manifest>
""") """)
# Force the manifest to be parsed. # Force the manifest to be parsed.
manifest.ToXml() manifest.ToXml()

View File

@ -305,8 +305,8 @@ class Requirements(RepoWrapperTestCase):
reqs = self.wrapper.Requirements({'python': {'hard': sys.version_info}}) reqs = self.wrapper.Requirements({'python': {'hard': sys.version_info}})
reqs.assert_all() reqs.assert_all()
def test_assert_all_old_repo(self): def test_assert_all_old_python(self):
"""Check assert_all rejects old repo.""" """Check assert_all rejects old python."""
reqs = self.wrapper.Requirements({'python': {'hard': [99999, 0]}}) reqs = self.wrapper.Requirements({'python': {'hard': [99999, 0]}})
with self.assertRaises(SystemExit): with self.assertRaises(SystemExit):
reqs.assert_all() reqs.assert_all()