sync: use namedtuples for internal return values

Replace tuple returns with namedtuples, to simplify adding new fields.

Extend the Sync_NetworkHalf return value to:
 - success: True if successful (the former return value)
 - remote_fetched: True if we called `git fetch`

Change-Id: If63c24c2f849523f77fa19c05bbf23a5e9a20ba9
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/344534
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: LaMont Jones <lamontjones@google.com>
This commit is contained in:
LaMont Jones 2022-09-01 15:15:04 +00:00
parent aefa4d3a29
commit 1eddca8476
3 changed files with 103 additions and 21 deletions

View File

@ -26,6 +26,7 @@ import sys
import tarfile import tarfile
import tempfile import tempfile
import time import time
from typing import NamedTuple
import urllib.parse import urllib.parse
from color import Coloring from color import Coloring
@ -45,6 +46,14 @@ from repo_trace import IsTrace, Trace
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
class SyncNetworkHalfResult(NamedTuple):
"""Sync_NetworkHalf return value."""
# True if successful.
success: bool
# Did we query the remote? False when optimized_fetch is True and we have the
# commit already present.
remote_fetched: bool
# Maximum sleep time allowed during retries. # Maximum sleep time allowed during retries.
MAXIMUM_RETRY_SLEEP_SEC = 3600.0 MAXIMUM_RETRY_SLEEP_SEC = 3600.0
# +-10% random jitter is added to each Fetches retry sleep duration. # +-10% random jitter is added to each Fetches retry sleep duration.
@ -1133,7 +1142,7 @@ class Project(object):
if archive and not isinstance(self, MetaProject): if archive and not isinstance(self, MetaProject):
if self.remote.url.startswith(('http://', 'https://')): if self.remote.url.startswith(('http://', 'https://')):
_error("%s: Cannot fetch archives from http/https remotes.", self.name) _error("%s: Cannot fetch archives from http/https remotes.", self.name)
return False return SyncNetworkHalfResult(False, False)
name = self.relpath.replace('\\', '/') name = self.relpath.replace('\\', '/')
name = name.replace('/', '_') name = name.replace('/', '_')
@ -1144,19 +1153,19 @@ class Project(object):
self._FetchArchive(tarpath, cwd=topdir) self._FetchArchive(tarpath, cwd=topdir)
except GitError as e: except GitError as e:
_error('%s', e) _error('%s', e)
return False return SyncNetworkHalfResult(False, False)
# From now on, we only need absolute tarpath # From now on, we only need absolute tarpath
tarpath = os.path.join(topdir, tarpath) tarpath = os.path.join(topdir, tarpath)
if not self._ExtractArchive(tarpath, path=topdir): if not self._ExtractArchive(tarpath, path=topdir):
return False return SyncNetworkHalfResult(False, True)
try: try:
platform_utils.remove(tarpath) platform_utils.remove(tarpath)
except OSError as e: except OSError as e:
_warn("Cannot remove archive %s: %s", tarpath, str(e)) _warn("Cannot remove archive %s: %s", tarpath, str(e))
self._CopyAndLinkFiles() self._CopyAndLinkFiles()
return True return SyncNetworkHalfResult(True, True)
# If the shared object dir already exists, don't try to rebootstrap with a # If the shared object dir already exists, don't try to rebootstrap with a
# clone bundle download. We should have the majority of objects already. # clone bundle download. We should have the majority of objects already.
@ -1220,9 +1229,11 @@ class Project(object):
depth = self.manifest.manifestProject.depth depth = self.manifest.manifestProject.depth
# See if we can skip the network fetch entirely. # See if we can skip the network fetch entirely.
remote_fetched = False
if not (optimized_fetch and if not (optimized_fetch and
(ID_RE.match(self.revisionExpr) and (ID_RE.match(self.revisionExpr) and
self._CheckForImmutableRevision())): self._CheckForImmutableRevision())):
remote_fetched = True
if not self._RemoteFetch( if not self._RemoteFetch(
initial=is_new, initial=is_new,
quiet=quiet, verbose=verbose, output_redir=output_redir, quiet=quiet, verbose=verbose, output_redir=output_redir,
@ -1231,7 +1242,7 @@ class Project(object):
submodules=submodules, force_sync=force_sync, submodules=submodules, force_sync=force_sync,
ssh_proxy=ssh_proxy, ssh_proxy=ssh_proxy,
clone_filter=clone_filter, retry_fetches=retry_fetches): clone_filter=clone_filter, retry_fetches=retry_fetches):
return False return SyncNetworkHalfResult(False, remote_fetched)
mp = self.manifest.manifestProject mp = self.manifest.manifestProject
dissociate = mp.dissociate dissociate = mp.dissociate
@ -1244,7 +1255,7 @@ class Project(object):
if p.stdout and output_redir: if p.stdout and output_redir:
output_redir.write(p.stdout) output_redir.write(p.stdout)
if p.Wait() != 0: if p.Wait() != 0:
return False return SyncNetworkHalfResult(False, remote_fetched)
platform_utils.remove(alternates_file) platform_utils.remove(alternates_file)
if self.worktree: if self.worktree:
@ -1253,7 +1264,7 @@ class Project(object):
self._InitMirrorHead() self._InitMirrorHead()
platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'), platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'),
missing_ok=True) missing_ok=True)
return True return SyncNetworkHalfResult(True, remote_fetched)
def PostRepoUpgrade(self): def PostRepoUpgrade(self):
self._InitHooks() self._InitHooks()
@ -3836,7 +3847,7 @@ class ManifestProject(MetaProject):
is_new=is_new, quiet=not verbose, verbose=verbose, is_new=is_new, quiet=not verbose, verbose=verbose,
clone_bundle=clone_bundle, current_branch_only=current_branch_only, clone_bundle=clone_bundle, current_branch_only=current_branch_only,
tags=tags, submodules=submodules, clone_filter=clone_filter, tags=tags, submodules=submodules, clone_filter=clone_filter,
partial_clone_exclude=self.manifest.PartialCloneExclude): partial_clone_exclude=self.manifest.PartialCloneExclude).success:
r = self.GetRemote() r = self.GetRemote()
print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr) print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)

View File

@ -51,7 +51,7 @@ need to be performed by an end-user.
_PostRepoUpgrade(self.manifest) _PostRepoUpgrade(self.manifest)
else: else:
if not rp.Sync_NetworkHalf(): if not rp.Sync_NetworkHalf().success:
print("error: can't update repo", file=sys.stderr) print("error: can't update repo", file=sys.stderr)
sys.exit(1) sys.exit(1)

View File

@ -26,6 +26,7 @@ import socket
import sys import sys
import tempfile import tempfile
import time import time
from typing import NamedTuple
import urllib.error import urllib.error
import urllib.parse import urllib.parse
import urllib.request import urllib.request
@ -71,6 +72,58 @@ REPO_BACKUP_OBJECTS = 'REPO_BACKUP_OBJECTS'
_BACKUP_OBJECTS = os.environ.get(REPO_BACKUP_OBJECTS) != '0' _BACKUP_OBJECTS = os.environ.get(REPO_BACKUP_OBJECTS) != '0'
class _FetchOneResult(NamedTuple):
"""_FetchOne return value.
Attributes:
success (bool): True if successful.
project (Project): The fetched project.
start (float): The starting time.time().
finish (float): The ending time.time().
remote_fetched (bool): True if the remote was actually queried.
"""
success: bool
project: Project
start: float
finish: float
remote_fetched: bool
class _FetchResult(NamedTuple):
"""_Fetch return value.
Attributes:
success (bool): True if successful.
projects (set[str]): The names of the git directories of fetched projects.
"""
success: bool
projects: set[str]
class _FetchMainResult(NamedTuple):
"""_FetchMain return value.
Attributes:
all_projects (list[Project]): The fetched projects.
"""
all_projects: list[Project]
class _CheckoutOneResult(NamedTuple):
"""_CheckoutOne return value.
Attributes:
success (bool): True if successful.
project (Project): The project.
start (float): The starting time.time().
finish (float): The ending time.time().
"""
success: bool
project: Project
start: float
finish: float
class Sync(Command, MirrorSafeCommand): class Sync(Command, MirrorSafeCommand):
COMMON = True COMMON = True
MULTI_MANIFEST_SUPPORT = True MULTI_MANIFEST_SUPPORT = True
@ -412,7 +465,7 @@ later is required to fix a server side protocol bug.
success = False success = False
buf = io.StringIO() buf = io.StringIO()
try: try:
success = project.Sync_NetworkHalf( sync_result = project.Sync_NetworkHalf(
quiet=opt.quiet, quiet=opt.quiet,
verbose=opt.verbose, verbose=opt.verbose,
output_redir=buf, output_redir=buf,
@ -426,6 +479,7 @@ later is required to fix a server side protocol bug.
ssh_proxy=self.ssh_proxy, ssh_proxy=self.ssh_proxy,
clone_filter=project.manifest.CloneFilter, clone_filter=project.manifest.CloneFilter,
partial_clone_exclude=project.manifest.PartialCloneExclude) partial_clone_exclude=project.manifest.PartialCloneExclude)
success = sync_result.success
output = buf.getvalue() output = buf.getvalue()
if (opt.verbose or not success) and output: if (opt.verbose or not success) and output:
@ -443,7 +497,8 @@ later is required to fix a server side protocol bug.
raise raise
finish = time.time() finish = time.time()
return (success, project, start, finish) return _FetchOneResult(success, project, start, finish,
sync_result.remote_fetched)
@classmethod @classmethod
def _FetchInitChild(cls, ssh_proxy): def _FetchInitChild(cls, ssh_proxy):
@ -454,6 +509,7 @@ later is required to fix a server side protocol bug.
jobs = opt.jobs_network jobs = opt.jobs_network
fetched = set() fetched = set()
remote_fetched = set()
pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet) pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
objdir_project_map = dict() objdir_project_map = dict()
@ -464,10 +520,16 @@ later is required to fix a server side protocol bug.
def _ProcessResults(results_sets): def _ProcessResults(results_sets):
ret = True ret = True
for results in results_sets: for results in results_sets:
for (success, project, start, finish) in results: for result in results:
success = result.success
project = result.project
start = result.start
finish = result.finish
self._fetch_times.Set(project, finish - start) self._fetch_times.Set(project, finish - start)
self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK, self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
start, finish, success) start, finish, success)
if result.remote_fetched:
remote_fetched.add(project)
# Check for any errors before running any more tasks. # Check for any errors before running any more tasks.
# ...we'll let existing jobs finish, though. # ...we'll let existing jobs finish, though.
if not success: if not success:
@ -525,7 +587,7 @@ later is required to fix a server side protocol bug.
if not self.outer_client.manifest.IsArchive: if not self.outer_client.manifest.IsArchive:
self._GCProjects(projects, opt, err_event) self._GCProjects(projects, opt, err_event)
return (ret, fetched) return _FetchResult(ret, fetched)
def _FetchMain(self, opt, args, all_projects, err_event, def _FetchMain(self, opt, args, all_projects, err_event,
ssh_proxy, manifest): ssh_proxy, manifest):
@ -551,7 +613,9 @@ later is required to fix a server side protocol bug.
to_fetch.extend(all_projects) to_fetch.extend(all_projects)
to_fetch.sort(key=self._fetch_times.Get, reverse=True) to_fetch.sort(key=self._fetch_times.Get, reverse=True)
success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy) result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
success = result.success
fetched = result.projects
if not success: if not success:
err_event.set() err_event.set()
@ -561,7 +625,7 @@ later is required to fix a server side protocol bug.
if err_event.is_set(): if err_event.is_set():
print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr) print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
sys.exit(1) sys.exit(1)
return return _FetchMainResult([])
# Iteratively fetch missing and/or nested unregistered submodules # Iteratively fetch missing and/or nested unregistered submodules
previously_missing_set = set() previously_missing_set = set()
@ -584,12 +648,14 @@ later is required to fix a server side protocol bug.
if previously_missing_set == missing_set: if previously_missing_set == missing_set:
break break
previously_missing_set = missing_set previously_missing_set = missing_set
success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy) result = self._Fetch(missing, opt, err_event, ssh_proxy)
success = result.success
new_fetched = result.projects
if not success: if not success:
err_event.set() err_event.set()
fetched.update(new_fetched) fetched.update(new_fetched)
return all_projects return _FetchMainResult(all_projects)
def _CheckoutOne(self, detach_head, force_sync, project): def _CheckoutOne(self, detach_head, force_sync, project):
"""Checkout work tree for one project """Checkout work tree for one project
@ -621,7 +687,7 @@ later is required to fix a server side protocol bug.
if not success: if not success:
print('error: Cannot checkout %s' % (project.name), file=sys.stderr) print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
finish = time.time() finish = time.time()
return (success, project, start, finish) return _CheckoutOneResult(success, project, start, finish)
def _Checkout(self, all_projects, opt, err_results): def _Checkout(self, all_projects, opt, err_results):
"""Checkout projects listed in all_projects """Checkout projects listed in all_projects
@ -636,7 +702,11 @@ later is required to fix a server side protocol bug.
def _ProcessResults(pool, pm, results): def _ProcessResults(pool, pm, results):
ret = True ret = True
for (success, project, start, finish) in results: for result in results:
success = result.success
project = result.project
start = result.start
finish = result.finish
self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL, self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
start, finish, success) start, finish, success)
# Check for any errors before running any more tasks. # Check for any errors before running any more tasks.
@ -1208,8 +1278,9 @@ later is required to fix a server side protocol bug.
with ssh.ProxyManager(manager) as ssh_proxy: with ssh.ProxyManager(manager) as ssh_proxy:
# Initialize the socket dir once in the parent. # Initialize the socket dir once in the parent.
ssh_proxy.sock() ssh_proxy.sock()
all_projects = self._FetchMain(opt, args, all_projects, err_event, result = self._FetchMain(opt, args, all_projects, err_event,
ssh_proxy, manifest) ssh_proxy, manifest)
all_projects = result.all_projects
if opt.network_only: if opt.network_only:
return return