Skip to content

Commit a64149a

Browse files
committed
sync: Record and propagate errors from deferred actions
Failures in deferred sync actions were not recorded because `_Later.Run` discarded the `GitError` exception. Record the specific error using `syncbuf.fail()` and propagate it for proper error aggregation and reporting. Bug: 438178765 Change-Id: Iad59e389f9677bd6b8d873ee1ea2aa6ce44c86fa Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/498141 Tested-by: Gavin Mak <[email protected]> Reviewed-by: Scott Lee <[email protected]>
1 parent 3e6acf2 commit a64149a

File tree

3 files changed

+25
-16
lines changed

3 files changed

+25
-16
lines changed

project.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,18 +1539,14 @@ def Sync_LocalHalf(
15391539
force_checkout=False,
15401540
force_rebase=False,
15411541
submodules=False,
1542-
errors=None,
15431542
verbose=False,
15441543
):
15451544
"""Perform only the local IO portion of the sync process.
15461545
15471546
Network access is not required.
15481547
"""
1549-
if errors is None:
1550-
errors = []
15511548

15521549
def fail(error: Exception):
1553-
errors.append(error)
15541550
syncbuf.fail(self, error)
15551551

15561552
if not os.path.exists(self.gitdir):
@@ -4031,7 +4027,8 @@ def Run(self, syncbuf):
40314027
if not self.quiet:
40324028
out.nl()
40334029
return True
4034-
except GitError:
4030+
except GitError as e:
4031+
syncbuf.fail(self.project, e)
40354032
out.nl()
40364033
return False
40374034

@@ -4047,7 +4044,12 @@ def __init__(self, config):
40474044
class SyncBuffer:
40484045
def __init__(self, config, detach_head=False):
40494046
self._messages = []
4050-
self._failures = []
4047+
4048+
# Failures that have not yet been printed. Cleared after printing.
4049+
self._pending_failures = []
4050+
# A persistent record of all failures during the buffer's lifetime.
4051+
self._all_failures = []
4052+
40514053
self._later_queue1 = []
40524054
self._later_queue2 = []
40534055

@@ -4062,7 +4064,9 @@ def info(self, project, fmt, *args):
40624064
self._messages.append(_InfoMessage(project, fmt % args))
40634065

40644066
def fail(self, project, err=None):
4065-
self._failures.append(_Failure(project, err))
4067+
failure = _Failure(project, err)
4068+
self._pending_failures.append(failure)
4069+
self._all_failures.append(failure)
40664070
self._MarkUnclean()
40674071

40684072
def later1(self, project, what, quiet):
@@ -4082,6 +4086,11 @@ def Recently(self):
40824086
self.recent_clean = True
40834087
return recent_clean
40844088

4089+
@property
4090+
def errors(self):
4091+
"""Returns a list of all exceptions accumulated in the buffer."""
4092+
return [f.why for f in self._all_failures if f.why]
4093+
40854094
def _MarkUnclean(self):
40864095
self.clean = False
40874096
self.recent_clean = False
@@ -4100,18 +4109,18 @@ def _RunQueue(self, queue):
41004109
return True
41014110

41024111
def _PrintMessages(self):
4103-
if self._messages or self._failures:
4112+
if self._messages or self._pending_failures:
41044113
if os.isatty(2):
41054114
self.out.write(progress.CSI_ERASE_LINE)
41064115
self.out.write("\r")
41074116

41084117
for m in self._messages:
41094118
m.Print(self)
4110-
for m in self._failures:
4119+
for m in self._pending_failures:
41114120
m.Print(self)
41124121

41134122
self._messages = []
4114-
self._failures = []
4123+
self._pending_failures = []
41154124

41164125

41174126
class MetaProject(Project):

subcmds/sync.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,10 +1092,10 @@ def _CheckoutOne(
10921092
force_sync=force_sync,
10931093
force_checkout=force_checkout,
10941094
force_rebase=force_rebase,
1095-
errors=errors,
10961095
verbose=verbose,
10971096
)
10981097
success = syncbuf.Finish()
1098+
errors.extend(syncbuf.errors)
10991099
except KeyboardInterrupt:
11001100
logger.error("Keyboard interrupt while processing %s", project.name)
11011101
except GitError as e:
@@ -1753,10 +1753,10 @@ def _UpdateManifestProject(self, opt, mp, manifest_name, errors):
17531753
mp.Sync_LocalHalf(
17541754
syncbuf,
17551755
submodules=mp.manifest.HasSubmodules,
1756-
errors=errors,
17571756
verbose=opt.verbose,
17581757
)
17591758
clean = syncbuf.Finish()
1759+
errors.extend(syncbuf.errors)
17601760
self.event_log.AddSync(
17611761
mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean
17621762
)
@@ -2284,19 +2284,17 @@ def _SyncOneProject(cls, opt, project_index, project) -> _SyncResult:
22842284
project.manifest.manifestProject.config,
22852285
detach_head=opt.detach_head,
22862286
)
2287-
local_half_errors = []
22882287
project.Sync_LocalHalf(
22892288
syncbuf,
22902289
force_sync=opt.force_sync,
22912290
force_checkout=opt.force_checkout,
22922291
force_rebase=opt.rebase,
2293-
errors=local_half_errors,
22942292
verbose=opt.verbose,
22952293
)
22962294
checkout_success = syncbuf.Finish()
2297-
if local_half_errors:
2295+
if syncbuf.errors:
22982296
checkout_error = SyncError(
2299-
aggregate_errors=local_half_errors
2297+
aggregate_errors=syncbuf.errors
23002298
)
23012299
except KeyboardInterrupt:
23022300
logger.error(

tests/test_subcmds_sync.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,7 @@ def test_worker_successful_sync(self):
801801
with mock.patch("subcmds.sync.SyncBuffer") as mock_sync_buffer:
802802
mock_sync_buf_instance = mock.MagicMock()
803803
mock_sync_buf_instance.Finish.return_value = True
804+
mock_sync_buf_instance.errors = []
804805
mock_sync_buffer.return_value = mock_sync_buf_instance
805806

806807
result_obj = self.cmd._SyncProjectList(opt, [0])
@@ -909,6 +910,7 @@ def test_worker_local_only(self):
909910
with mock.patch("subcmds.sync.SyncBuffer") as mock_sync_buffer:
910911
mock_sync_buf_instance = mock.MagicMock()
911912
mock_sync_buf_instance.Finish.return_value = True
913+
mock_sync_buf_instance.errors = []
912914
mock_sync_buffer.return_value = mock_sync_buf_instance
913915

914916
result_obj = self.cmd._SyncProjectList(opt, [0])

0 commit comments

Comments
 (0)