diff options
author | Gavin Mak <gavinmak@google.com> | 2025-08-13 22:48:36 -0700 |
---|---|---|
committer | Gavin Mak <gavinmak@google.com> | 2025-08-13 23:17:56 -0700 |
commit | a64149a7a77814132629bbb4c07d922c2222df25 (patch) | |
tree | be39644559d35dab72d9f7d19e9115c0ccf3e1fd /project.py | |
parent | 3e6acf2778b533aedb2a1f6f6e3a3159e0b8c86d (diff) | |
download | git-repo-a64149a7a77814132629bbb4c07d922c2222df25.tar.gz |
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 <gavinmak@google.com>
Reviewed-by: Scott Lee <ddoman@google.com>
Diffstat (limited to 'project.py')
-rw-r--r-- | project.py | 29 |
1 files changed, 19 insertions, 10 deletions
@@ -1539,18 +1539,14 @@ class Project: | |||
1539 | force_checkout=False, | 1539 | force_checkout=False, |
1540 | force_rebase=False, | 1540 | force_rebase=False, |
1541 | submodules=False, | 1541 | submodules=False, |
1542 | errors=None, | ||
1543 | verbose=False, | 1542 | verbose=False, |
1544 | ): | 1543 | ): |
1545 | """Perform only the local IO portion of the sync process. | 1544 | """Perform only the local IO portion of the sync process. |
1546 | 1545 | ||
1547 | Network access is not required. | 1546 | Network access is not required. |
1548 | """ | 1547 | """ |
1549 | if errors is None: | ||
1550 | errors = [] | ||
1551 | 1548 | ||
1552 | def fail(error: Exception): | 1549 | def fail(error: Exception): |
1553 | errors.append(error) | ||
1554 | syncbuf.fail(self, error) | 1550 | syncbuf.fail(self, error) |
1555 | 1551 | ||
1556 | if not os.path.exists(self.gitdir): | 1552 | if not os.path.exists(self.gitdir): |
@@ -4031,7 +4027,8 @@ class _Later: | |||
4031 | if not self.quiet: | 4027 | if not self.quiet: |
4032 | out.nl() | 4028 | out.nl() |
4033 | return True | 4029 | return True |
4034 | except GitError: | 4030 | except GitError as e: |
4031 | syncbuf.fail(self.project, e) | ||
4035 | out.nl() | 4032 | out.nl() |
4036 | return False | 4033 | return False |
4037 | 4034 | ||
@@ -4047,7 +4044,12 @@ class _SyncColoring(Coloring): | |||
4047 | class SyncBuffer: | 4044 | class SyncBuffer: |
4048 | def __init__(self, config, detach_head=False): | 4045 | def __init__(self, config, detach_head=False): |
4049 | self._messages = [] | 4046 | 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 | |||
4051 | self._later_queue1 = [] | 4053 | self._later_queue1 = [] |
4052 | self._later_queue2 = [] | 4054 | self._later_queue2 = [] |
4053 | 4055 | ||
@@ -4062,7 +4064,9 @@ class SyncBuffer: | |||
4062 | self._messages.append(_InfoMessage(project, fmt % args)) | 4064 | self._messages.append(_InfoMessage(project, fmt % args)) |
4063 | 4065 | ||
4064 | def fail(self, project, err=None): | 4066 | 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) | ||
4066 | self._MarkUnclean() | 4070 | self._MarkUnclean() |
4067 | 4071 | ||
4068 | def later1(self, project, what, quiet): | 4072 | def later1(self, project, what, quiet): |
@@ -4082,6 +4086,11 @@ class SyncBuffer: | |||
4082 | self.recent_clean = True | 4086 | self.recent_clean = True |
4083 | return recent_clean | 4087 | return recent_clean |
4084 | 4088 | ||
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 | |||
4085 | def _MarkUnclean(self): | 4094 | def _MarkUnclean(self): |
4086 | self.clean = False | 4095 | self.clean = False |
4087 | self.recent_clean = False | 4096 | self.recent_clean = False |
@@ -4100,18 +4109,18 @@ class SyncBuffer: | |||
4100 | return True | 4109 | return True |
4101 | 4110 | ||
4102 | def _PrintMessages(self): | 4111 | def _PrintMessages(self): |
4103 | if self._messages or self._failures: | 4112 | if self._messages or self._pending_failures: |
4104 | if os.isatty(2): | 4113 | if os.isatty(2): |
4105 | self.out.write(progress.CSI_ERASE_LINE) | 4114 | self.out.write(progress.CSI_ERASE_LINE) |
4106 | self.out.write("\r") | 4115 | self.out.write("\r") |
4107 | 4116 | ||
4108 | for m in self._messages: | 4117 | for m in self._messages: |
4109 | m.Print(self) | 4118 | m.Print(self) |
4110 | for m in self._failures: | 4119 | for m in self._pending_failures: |
4111 | m.Print(self) | 4120 | m.Print(self) |
4112 | 4121 | ||
4113 | self._messages = [] | 4122 | self._messages = [] |
4114 | self._failures = [] | 4123 | self._pending_failures = [] |
4115 | 4124 | ||
4116 | 4125 | ||
4117 | class MetaProject(Project): | 4126 | class MetaProject(Project): |