summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py159
1 files changed, 114 insertions, 45 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 5f8bc2f0..eaca50c9 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -63,9 +63,16 @@ from command import (
63 MirrorSafeCommand, 63 MirrorSafeCommand,
64 WORKER_BATCH_SIZE, 64 WORKER_BATCH_SIZE,
65) 65)
66from error import RepoChangedException, GitError 66from error import (
67 RepoChangedException,
68 GitError,
69 RepoExitError,
70 SyncError,
71 UpdateManifestError,
72 RepoUnhandledExceptionError,
73)
67import platform_utils 74import platform_utils
68from project import SyncBuffer 75from project import SyncBuffer, DeleteWorktreeError
69from progress import Progress, elapsed_str, jobs_str 76from progress import Progress, elapsed_str, jobs_str
70from repo_trace import Trace 77from repo_trace import Trace
71import ssh 78import ssh
@@ -94,6 +101,7 @@ class _FetchOneResult(NamedTuple):
94 """ 101 """
95 102
96 success: bool 103 success: bool
104 errors: List[Exception]
97 project: Project 105 project: Project
98 start: float 106 start: float
99 finish: float 107 finish: float
@@ -110,6 +118,7 @@ class _FetchResult(NamedTuple):
110 118
111 success: bool 119 success: bool
112 projects: Set[str] 120 projects: Set[str]
121 errors: List[Exception]
113 122
114 123
115class _FetchMainResult(NamedTuple): 124class _FetchMainResult(NamedTuple):
@@ -120,6 +129,7 @@ class _FetchMainResult(NamedTuple):
120 """ 129 """
121 130
122 all_projects: List[Project] 131 all_projects: List[Project]
132 errors: List[Exception]
123 133
124 134
125class _CheckoutOneResult(NamedTuple): 135class _CheckoutOneResult(NamedTuple):
@@ -133,11 +143,24 @@ class _CheckoutOneResult(NamedTuple):
133 """ 143 """
134 144
135 success: bool 145 success: bool
146 errors: List[Exception]
136 project: Project 147 project: Project
137 start: float 148 start: float
138 finish: float 149 finish: float
139 150
140 151
152class SuperprojectError(SyncError):
153 """Superproject sync repo."""
154
155
156class SyncFailFastError(SyncError):
157 """Sync exit error when --fail-fast set."""
158
159
160class SmartSyncError(SyncError):
161 """Smart sync exit error."""
162
163
141class Sync(Command, MirrorSafeCommand): 164class Sync(Command, MirrorSafeCommand):
142 COMMON = True 165 COMMON = True
143 MULTI_MANIFEST_SUPPORT = True 166 MULTI_MANIFEST_SUPPORT = True
@@ -588,7 +611,7 @@ later is required to fix a server side protocol bug.
588 file=sys.stderr, 611 file=sys.stderr,
589 ) 612 )
590 if update_result.fatal and opt.use_superproject is not None: 613 if update_result.fatal and opt.use_superproject is not None:
591 sys.exit(1) 614 raise SuperprojectError()
592 if need_unload: 615 if need_unload:
593 m.outer_client.manifest.Unload() 616 m.outer_client.manifest.Unload()
594 617
@@ -621,6 +644,7 @@ later is required to fix a server side protocol bug.
621 self._sync_dict[k] = start 644 self._sync_dict[k] = start
622 success = False 645 success = False
623 remote_fetched = False 646 remote_fetched = False
647 errors = []
624 buf = io.StringIO() 648 buf = io.StringIO()
625 try: 649 try:
626 sync_result = project.Sync_NetworkHalf( 650 sync_result = project.Sync_NetworkHalf(
@@ -644,6 +668,8 @@ later is required to fix a server side protocol bug.
644 ) 668 )
645 success = sync_result.success 669 success = sync_result.success
646 remote_fetched = sync_result.remote_fetched 670 remote_fetched = sync_result.remote_fetched
671 if sync_result.error:
672 errors.append(sync_result.error)
647 673
648 output = buf.getvalue() 674 output = buf.getvalue()
649 if (opt.verbose or not success) and output: 675 if (opt.verbose or not success) and output:
@@ -659,6 +685,7 @@ later is required to fix a server side protocol bug.
659 print(f"Keyboard interrupt while processing {project.name}") 685 print(f"Keyboard interrupt while processing {project.name}")
660 except GitError as e: 686 except GitError as e:
661 print("error.GitError: Cannot fetch %s" % str(e), file=sys.stderr) 687 print("error.GitError: Cannot fetch %s" % str(e), file=sys.stderr)
688 errors.append(e)
662 except Exception as e: 689 except Exception as e:
663 print( 690 print(
664 "error: Cannot fetch %s (%s: %s)" 691 "error: Cannot fetch %s (%s: %s)"
@@ -666,11 +693,14 @@ later is required to fix a server side protocol bug.
666 file=sys.stderr, 693 file=sys.stderr,
667 ) 694 )
668 del self._sync_dict[k] 695 del self._sync_dict[k]
696 errors.append(e)
669 raise 697 raise
670 698
671 finish = time.time() 699 finish = time.time()
672 del self._sync_dict[k] 700 del self._sync_dict[k]
673 return _FetchOneResult(success, project, start, finish, remote_fetched) 701 return _FetchOneResult(
702 success, errors, project, start, finish, remote_fetched
703 )
674 704
675 @classmethod 705 @classmethod
676 def _FetchInitChild(cls, ssh_proxy): 706 def _FetchInitChild(cls, ssh_proxy):
@@ -701,6 +731,7 @@ later is required to fix a server side protocol bug.
701 jobs = opt.jobs_network 731 jobs = opt.jobs_network
702 fetched = set() 732 fetched = set()
703 remote_fetched = set() 733 remote_fetched = set()
734 errors = []
704 pm = Progress( 735 pm = Progress(
705 "Fetching", 736 "Fetching",
706 len(projects), 737 len(projects),
@@ -745,6 +776,8 @@ later is required to fix a server side protocol bug.
745 finish, 776 finish,
746 success, 777 success,
747 ) 778 )
779 if result.errors:
780 errors.extend(result.errors)
748 if result.remote_fetched: 781 if result.remote_fetched:
749 remote_fetched.add(project) 782 remote_fetched.add(project)
750 # Check for any errors before running any more tasks. 783 # Check for any errors before running any more tasks.
@@ -813,7 +846,7 @@ later is required to fix a server side protocol bug.
813 if not self.outer_client.manifest.IsArchive: 846 if not self.outer_client.manifest.IsArchive:
814 self._GCProjects(projects, opt, err_event) 847 self._GCProjects(projects, opt, err_event)
815 848
816 return _FetchResult(ret, fetched) 849 return _FetchResult(ret, fetched, errors)
817 850
818 def _FetchMain( 851 def _FetchMain(
819 self, opt, args, all_projects, err_event, ssh_proxy, manifest 852 self, opt, args, all_projects, err_event, ssh_proxy, manifest
@@ -832,6 +865,7 @@ later is required to fix a server side protocol bug.
832 List of all projects that should be checked out. 865 List of all projects that should be checked out.
833 """ 866 """
834 rp = manifest.repoProject 867 rp = manifest.repoProject
868 errors = []
835 869
836 to_fetch = [] 870 to_fetch = []
837 now = time.time() 871 now = time.time()
@@ -843,6 +877,9 @@ later is required to fix a server side protocol bug.
843 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy) 877 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
844 success = result.success 878 success = result.success
845 fetched = result.projects 879 fetched = result.projects
880 if result.errors:
881 errors.extend(result.errors)
882
846 if not success: 883 if not success:
847 err_event.set() 884 err_event.set()
848 885
@@ -854,8 +891,11 @@ later is required to fix a server side protocol bug.
854 "\nerror: Exited sync due to fetch errors.\n", 891 "\nerror: Exited sync due to fetch errors.\n",
855 file=sys.stderr, 892 file=sys.stderr,
856 ) 893 )
857 sys.exit(1) 894 raise SyncError(
858 return _FetchMainResult([]) 895 "error: Exited sync due to fetch errors.",
896 aggregate_errors=errors,
897 )
898 return _FetchMainResult([], errors)
859 899
860 # Iteratively fetch missing and/or nested unregistered submodules. 900 # Iteratively fetch missing and/or nested unregistered submodules.
861 previously_missing_set = set() 901 previously_missing_set = set()
@@ -883,11 +923,13 @@ later is required to fix a server side protocol bug.
883 result = self._Fetch(missing, opt, err_event, ssh_proxy) 923 result = self._Fetch(missing, opt, err_event, ssh_proxy)
884 success = result.success 924 success = result.success
885 new_fetched = result.projects 925 new_fetched = result.projects
926 if result.errors:
927 errors.extend(result.errors)
886 if not success: 928 if not success:
887 err_event.set() 929 err_event.set()
888 fetched.update(new_fetched) 930 fetched.update(new_fetched)
889 931
890 return _FetchMainResult(all_projects) 932 return _FetchMainResult(all_projects, errors)
891 933
892 def _CheckoutOne(self, detach_head, force_sync, project): 934 def _CheckoutOne(self, detach_head, force_sync, project):
893 """Checkout work tree for one project 935 """Checkout work tree for one project
@@ -905,8 +947,11 @@ later is required to fix a server side protocol bug.
905 project.manifest.manifestProject.config, detach_head=detach_head 947 project.manifest.manifestProject.config, detach_head=detach_head
906 ) 948 )
907 success = False 949 success = False
950 errors = []
908 try: 951 try:
909 project.Sync_LocalHalf(syncbuf, force_sync=force_sync) 952 project.Sync_LocalHalf(
953 syncbuf, force_sync=force_sync, errors=errors
954 )
910 success = syncbuf.Finish() 955 success = syncbuf.Finish()
911 except GitError as e: 956 except GitError as e:
912 print( 957 print(
@@ -914,6 +959,7 @@ later is required to fix a server side protocol bug.
914 % (project.name, str(e)), 959 % (project.name, str(e)),
915 file=sys.stderr, 960 file=sys.stderr,
916 ) 961 )
962 errors.append(e)
917 except Exception as e: 963 except Exception as e:
918 print( 964 print(
919 "error: Cannot checkout %s: %s: %s" 965 "error: Cannot checkout %s: %s: %s"
@@ -925,9 +971,9 @@ later is required to fix a server side protocol bug.
925 if not success: 971 if not success:
926 print("error: Cannot checkout %s" % (project.name), file=sys.stderr) 972 print("error: Cannot checkout %s" % (project.name), file=sys.stderr)
927 finish = time.time() 973 finish = time.time()
928 return _CheckoutOneResult(success, project, start, finish) 974 return _CheckoutOneResult(success, errors, project, start, finish)
929 975
930 def _Checkout(self, all_projects, opt, err_results): 976 def _Checkout(self, all_projects, opt, err_results, checkout_errors):
931 """Checkout projects listed in all_projects 977 """Checkout projects listed in all_projects
932 978
933 Args: 979 Args:
@@ -949,6 +995,10 @@ later is required to fix a server side protocol bug.
949 self.event_log.AddSync( 995 self.event_log.AddSync(
950 project, event_log.TASK_SYNC_LOCAL, start, finish, success 996 project, event_log.TASK_SYNC_LOCAL, start, finish, success
951 ) 997 )
998
999 if result.errors:
1000 checkout_errors.extend(result.errors)
1001
952 # Check for any errors before running any more tasks. 1002 # Check for any errors before running any more tasks.
953 # ...we'll let existing jobs finish, though. 1003 # ...we'll let existing jobs finish, though.
954 if success: 1004 if success:
@@ -1214,10 +1264,9 @@ later is required to fix a server side protocol bug.
1214 revisionId=None, 1264 revisionId=None,
1215 groups=None, 1265 groups=None,
1216 ) 1266 )
1217 if not project.DeleteWorktree( 1267 project.DeleteWorktree(
1218 quiet=opt.quiet, force=opt.force_remove_dirty 1268 quiet=opt.quiet, force=opt.force_remove_dirty
1219 ): 1269 )
1220 return 1
1221 1270
1222 new_project_paths.sort() 1271 new_project_paths.sort()
1223 with open(file_path, "w") as fd: 1272 with open(file_path, "w") as fd:
@@ -1260,7 +1309,7 @@ later is required to fix a server side protocol bug.
1260 file=sys.stderr, 1309 file=sys.stderr,
1261 ) 1310 )
1262 platform_utils.remove(copylinkfile_path) 1311 platform_utils.remove(copylinkfile_path)
1263 return False 1312 raise
1264 1313
1265 need_remove_files = [] 1314 need_remove_files = []
1266 need_remove_files.extend( 1315 need_remove_files.extend(
@@ -1285,12 +1334,10 @@ later is required to fix a server side protocol bug.
1285 1334
1286 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest): 1335 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1287 if not manifest.manifest_server: 1336 if not manifest.manifest_server:
1288 print( 1337 raise SmartSyncError(
1289 "error: cannot smart sync: no manifest server defined in " 1338 "error: cannot smart sync: no manifest server defined in "
1290 "manifest", 1339 "manifest"
1291 file=sys.stderr,
1292 ) 1340 )
1293 sys.exit(1)
1294 1341
1295 manifest_server = manifest.manifest_server 1342 manifest_server = manifest.manifest_server
1296 if not opt.quiet: 1343 if not opt.quiet:
@@ -1368,33 +1415,28 @@ later is required to fix a server side protocol bug.
1368 with open(smart_sync_manifest_path, "w") as f: 1415 with open(smart_sync_manifest_path, "w") as f:
1369 f.write(manifest_str) 1416 f.write(manifest_str)
1370 except IOError as e: 1417 except IOError as e:
1371 print( 1418 raise SmartSyncError(
1372 "error: cannot write manifest to %s:\n%s" 1419 "error: cannot write manifest to %s:\n%s"
1373 % (smart_sync_manifest_path, e), 1420 % (smart_sync_manifest_path, e),
1374 file=sys.stderr, 1421 aggregate_errors=[e],
1375 ) 1422 )
1376 sys.exit(1)
1377 self._ReloadManifest(manifest_name, manifest) 1423 self._ReloadManifest(manifest_name, manifest)
1378 else: 1424 else:
1379 print( 1425 raise SmartSyncError(
1380 "error: manifest server RPC call failed: %s" % manifest_str, 1426 "error: manifest server RPC call failed: %s" % manifest_str
1381 file=sys.stderr,
1382 ) 1427 )
1383 sys.exit(1)
1384 except (socket.error, IOError, xmlrpc.client.Fault) as e: 1428 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1385 print( 1429 raise SmartSyncError(
1386 "error: cannot connect to manifest server %s:\n%s" 1430 "error: cannot connect to manifest server %s:\n%s"
1387 % (manifest.manifest_server, e), 1431 % (manifest.manifest_server, e),
1388 file=sys.stderr, 1432 aggregate_errors=[e],
1389 ) 1433 )
1390 sys.exit(1)
1391 except xmlrpc.client.ProtocolError as e: 1434 except xmlrpc.client.ProtocolError as e:
1392 print( 1435 raise SmartSyncError(
1393 "error: cannot connect to manifest server %s:\n%d %s" 1436 "error: cannot connect to manifest server %s:\n%d %s"
1394 % (manifest.manifest_server, e.errcode, e.errmsg), 1437 % (manifest.manifest_server, e.errcode, e.errmsg),
1395 file=sys.stderr, 1438 aggregate_errors=[e],
1396 ) 1439 )
1397 sys.exit(1)
1398 1440
1399 return manifest_name 1441 return manifest_name
1400 1442
@@ -1436,7 +1478,7 @@ later is required to fix a server side protocol bug.
1436 """ 1478 """
1437 if not opt.local_only: 1479 if not opt.local_only:
1438 start = time.time() 1480 start = time.time()
1439 success = mp.Sync_NetworkHalf( 1481 result = mp.Sync_NetworkHalf(
1440 quiet=opt.quiet, 1482 quiet=opt.quiet,
1441 verbose=opt.verbose, 1483 verbose=opt.verbose,
1442 current_branch_only=self._GetCurrentBranchOnly( 1484 current_branch_only=self._GetCurrentBranchOnly(
@@ -1453,19 +1495,24 @@ later is required to fix a server side protocol bug.
1453 ) 1495 )
1454 finish = time.time() 1496 finish = time.time()
1455 self.event_log.AddSync( 1497 self.event_log.AddSync(
1456 mp, event_log.TASK_SYNC_NETWORK, start, finish, success 1498 mp, event_log.TASK_SYNC_NETWORK, start, finish, result.success
1457 ) 1499 )
1458 1500
1459 if mp.HasChanges: 1501 if mp.HasChanges:
1502 errors = []
1460 syncbuf = SyncBuffer(mp.config) 1503 syncbuf = SyncBuffer(mp.config)
1461 start = time.time() 1504 start = time.time()
1462 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules) 1505 mp.Sync_LocalHalf(
1506 syncbuf, submodules=mp.manifest.HasSubmodules, errors=errors
1507 )
1463 clean = syncbuf.Finish() 1508 clean = syncbuf.Finish()
1464 self.event_log.AddSync( 1509 self.event_log.AddSync(
1465 mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean 1510 mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean
1466 ) 1511 )
1467 if not clean: 1512 if not clean:
1468 sys.exit(1) 1513 raise UpdateManifestError(
1514 aggregate_errors=errors, project=mp.name
1515 )
1469 self._ReloadManifest(manifest_name, mp.manifest) 1516 self._ReloadManifest(manifest_name, mp.manifest)
1470 1517
1471 def ValidateOptions(self, opt, args): 1518 def ValidateOptions(self, opt, args):
@@ -1546,6 +1593,15 @@ later is required to fix a server side protocol bug.
1546 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit) 1593 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1547 1594
1548 def Execute(self, opt, args): 1595 def Execute(self, opt, args):
1596 errors = []
1597 try:
1598 self._ExecuteHelper(opt, args, errors)
1599 except RepoExitError:
1600 raise
1601 except (KeyboardInterrupt, Exception) as e:
1602 raise RepoUnhandledExceptionError(e, aggregate_errors=errors)
1603
1604 def _ExecuteHelper(self, opt, args, errors):
1549 manifest = self.outer_manifest 1605 manifest = self.outer_manifest
1550 if not opt.outer_manifest: 1606 if not opt.outer_manifest:
1551 manifest = self.manifest 1607 manifest = self.manifest
@@ -1695,6 +1751,8 @@ later is required to fix a server side protocol bug.
1695 result = self._FetchMain( 1751 result = self._FetchMain(
1696 opt, args, all_projects, err_event, ssh_proxy, manifest 1752 opt, args, all_projects, err_event, ssh_proxy, manifest
1697 ) 1753 )
1754 if result.errors:
1755 errors.extend(result.errors)
1698 all_projects = result.all_projects 1756 all_projects = result.all_projects
1699 1757
1700 if opt.network_only: 1758 if opt.network_only:
@@ -1712,36 +1770,47 @@ later is required to fix a server side protocol bug.
1712 "`repo sync -l` will update some local checkouts.", 1770 "`repo sync -l` will update some local checkouts.",
1713 file=sys.stderr, 1771 file=sys.stderr,
1714 ) 1772 )
1715 sys.exit(1) 1773 raise SyncFailFastError(aggregate_errors=errors)
1716 1774
1717 for m in self.ManifestList(opt): 1775 for m in self.ManifestList(opt):
1718 if m.IsMirror or m.IsArchive: 1776 if m.IsMirror or m.IsArchive:
1719 # Bail out now, we have no working tree. 1777 # Bail out now, we have no working tree.
1720 continue 1778 continue
1721 1779
1722 if self.UpdateProjectList(opt, m): 1780 try:
1781 self.UpdateProjectList(opt, m)
1782 except Exception as e:
1723 err_event.set() 1783 err_event.set()
1724 err_update_projects = True 1784 err_update_projects = True
1785 errors.append(e)
1786 if isinstance(e, DeleteWorktreeError):
1787 errors.extend(e.aggregate_errors)
1725 if opt.fail_fast: 1788 if opt.fail_fast:
1726 print( 1789 print(
1727 "\nerror: Local checkouts *not* updated.", 1790 "\nerror: Local checkouts *not* updated.",
1728 file=sys.stderr, 1791 file=sys.stderr,
1729 ) 1792 )
1730 sys.exit(1) 1793 raise SyncFailFastError(aggregate_errors=errors)
1731 1794
1732 err_update_linkfiles = not self.UpdateCopyLinkfileList(m) 1795 err_update_linkfiles = False
1733 if err_update_linkfiles: 1796 try:
1797 self.UpdateCopyLinkfileList(m)
1798 except Exception as e:
1799 err_update_linkfiles = True
1800 errors.append(e)
1734 err_event.set() 1801 err_event.set()
1735 if opt.fail_fast: 1802 if opt.fail_fast:
1736 print( 1803 print(
1737 "\nerror: Local update copyfile or linkfile failed.", 1804 "\nerror: Local update copyfile or linkfile failed.",
1738 file=sys.stderr, 1805 file=sys.stderr,
1739 ) 1806 )
1740 sys.exit(1) 1807 raise SyncFailFastError(aggregate_errors=errors)
1741 1808
1742 err_results = [] 1809 err_results = []
1743 # NB: We don't exit here because this is the last step. 1810 # NB: We don't exit here because this is the last step.
1744 err_checkout = not self._Checkout(all_projects, opt, err_results) 1811 err_checkout = not self._Checkout(
1812 all_projects, opt, err_results, errors
1813 )
1745 if err_checkout: 1814 if err_checkout:
1746 err_event.set() 1815 err_event.set()
1747 1816
@@ -1784,7 +1853,7 @@ later is required to fix a server side protocol bug.
1784 "error.", 1853 "error.",
1785 file=sys.stderr, 1854 file=sys.stderr,
1786 ) 1855 )
1787 sys.exit(1) 1856 raise SyncError(aggregate_errors=errors)
1788 1857
1789 # Log the previous sync analysis state from the config. 1858 # Log the previous sync analysis state from the config.
1790 self.git_event_log.LogDataConfigEvents( 1859 self.git_event_log.LogDataConfigEvents(
@@ -1842,7 +1911,7 @@ def _PostRepoFetch(rp, repo_verify=True, verbose=False):
1842 try: 1911 try:
1843 rp.work_git.reset("--keep", new_rev) 1912 rp.work_git.reset("--keep", new_rev)
1844 except GitError as e: 1913 except GitError as e:
1845 sys.exit(str(e)) 1914 raise RepoUnhandledExceptionError(e)
1846 print("info: Restarting repo with latest version", file=sys.stderr) 1915 print("info: Restarting repo with latest version", file=sys.stderr)
1847 raise RepoChangedException(["--repo-upgraded"]) 1916 raise RepoChangedException(["--repo-upgraded"])
1848 else: 1917 else: