diff options
author | Mike Frysinger <vapier@google.com> | 2019-09-23 19:21:20 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@google.com> | 2020-02-05 21:37:20 +0000 |
commit | 5a03308c5c5f8dbfab05bfe726337dafc10b0c85 (patch) | |
tree | 40078193ca127a493d5512e0d616149342c9dd1c | |
parent | 3ba716f3823c010a9788077d9515c26db5d58f11 (diff) | |
download | git-repo-5a03308c5c5f8dbfab05bfe726337dafc10b0c85.tar.gz |
sync: try to checkout repos across sync failures
Currently our default behavior is:
* Try to sync all repos
* If any errors seen, exit
* Try to garbage collect all repos
* If any errors seen, exit
* Try to update local project list
* If any errors seen, exit
* Try to checkout out all local repos
* If any errors seen, exit
Users find these incomplete syncs confusing, so lets try to complete
as much as possible by default and printing out summaries at the end.
Bug: https://crbug.com/gerrit/11293
Change-Id: Idd17cc9c3bbc574d8a0f08a30225dec7bfe414cb
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/238554
Reviewed-by: Michael Mortensen <mmortensen@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
-rw-r--r-- | subcmds/sync.py | 86 |
1 files changed, 57 insertions, 29 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py index ca4b97b3..808df208 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -368,7 +368,7 @@ later is required to fix a server side protocol bug. | |||
368 | 368 | ||
369 | return success | 369 | return success |
370 | 370 | ||
371 | def _Fetch(self, projects, opt): | 371 | def _Fetch(self, projects, opt, err_event): |
372 | fetched = set() | 372 | fetched = set() |
373 | lock = _threading.Lock() | 373 | lock = _threading.Lock() |
374 | pm = Progress('Fetching projects', len(projects), | 374 | pm = Progress('Fetching projects', len(projects), |
@@ -380,7 +380,6 @@ later is required to fix a server side protocol bug. | |||
380 | 380 | ||
381 | threads = set() | 381 | threads = set() |
382 | sem = _threading.Semaphore(self.jobs) | 382 | sem = _threading.Semaphore(self.jobs) |
383 | err_event = _threading.Event() | ||
384 | for project_list in objdir_project_map.values(): | 383 | for project_list in objdir_project_map.values(): |
385 | # Check for any errors before running any more tasks. | 384 | # Check for any errors before running any more tasks. |
386 | # ...we'll let existing threads finish, though. | 385 | # ...we'll let existing threads finish, though. |
@@ -409,16 +408,11 @@ later is required to fix a server side protocol bug. | |||
409 | for t in threads: | 408 | for t in threads: |
410 | t.join() | 409 | t.join() |
411 | 410 | ||
412 | # If we saw an error, exit with code 1 so that other scripts can check. | ||
413 | if err_event.isSet() and opt.fail_fast: | ||
414 | print('\nerror: Exited sync due to fetch errors', file=sys.stderr) | ||
415 | sys.exit(1) | ||
416 | |||
417 | pm.end() | 411 | pm.end() |
418 | self._fetch_times.Save() | 412 | self._fetch_times.Save() |
419 | 413 | ||
420 | if not self.manifest.IsArchive: | 414 | if not self.manifest.IsArchive: |
421 | self._GCProjects(projects) | 415 | self._GCProjects(projects, opt, err_event) |
422 | 416 | ||
423 | return fetched | 417 | return fetched |
424 | 418 | ||
@@ -504,12 +498,16 @@ later is required to fix a server side protocol bug. | |||
504 | 498 | ||
505 | return success | 499 | return success |
506 | 500 | ||
507 | def _Checkout(self, all_projects, opt): | 501 | def _Checkout(self, all_projects, opt, err_event, err_results): |
508 | """Checkout projects listed in all_projects | 502 | """Checkout projects listed in all_projects |
509 | 503 | ||
510 | Args: | 504 | Args: |
511 | all_projects: List of all projects that should be checked out. | 505 | all_projects: List of all projects that should be checked out. |
512 | opt: Program options returned from optparse. See _Options(). | 506 | opt: Program options returned from optparse. See _Options(). |
507 | err_event: We'll set this event in the case of an error (after printing | ||
508 | out info about the error). | ||
509 | err_results: A list of strings, paths to git repos where checkout | ||
510 | failed. | ||
513 | """ | 511 | """ |
514 | 512 | ||
515 | # Perform checkouts in multiple threads when we are using partial clone. | 513 | # Perform checkouts in multiple threads when we are using partial clone. |
@@ -528,8 +526,6 @@ later is required to fix a server side protocol bug. | |||
528 | 526 | ||
529 | threads = set() | 527 | threads = set() |
530 | sem = _threading.Semaphore(syncjobs) | 528 | sem = _threading.Semaphore(syncjobs) |
531 | err_event = _threading.Event() | ||
532 | err_results = [] | ||
533 | 529 | ||
534 | for project in all_projects: | 530 | for project in all_projects: |
535 | # Check for any errors before running any more tasks. | 531 | # Check for any errors before running any more tasks. |
@@ -560,15 +556,8 @@ later is required to fix a server side protocol bug. | |||
560 | t.join() | 556 | t.join() |
561 | 557 | ||
562 | pm.end() | 558 | pm.end() |
563 | # If we saw an error, exit with code 1 so that other scripts can check. | ||
564 | if err_event.isSet(): | ||
565 | print('\nerror: Exited sync due to checkout errors', file=sys.stderr) | ||
566 | if err_results: | ||
567 | print('Failing repos:\n%s' % '\n'.join(err_results), | ||
568 | file=sys.stderr) | ||
569 | sys.exit(1) | ||
570 | 559 | ||
571 | def _GCProjects(self, projects): | 560 | def _GCProjects(self, projects, opt, err_event): |
572 | gc_gitdirs = {} | 561 | gc_gitdirs = {} |
573 | for project in projects: | 562 | for project in projects: |
574 | if len(project.manifest.GetProjectsWithName(project.name)) > 1: | 563 | if len(project.manifest.GetProjectsWithName(project.name)) > 1: |
@@ -592,7 +581,6 @@ later is required to fix a server side protocol bug. | |||
592 | 581 | ||
593 | threads = set() | 582 | threads = set() |
594 | sem = _threading.Semaphore(jobs) | 583 | sem = _threading.Semaphore(jobs) |
595 | err_event = _threading.Event() | ||
596 | 584 | ||
597 | def GC(bare_git): | 585 | def GC(bare_git): |
598 | try: | 586 | try: |
@@ -607,7 +595,7 @@ later is required to fix a server side protocol bug. | |||
607 | sem.release() | 595 | sem.release() |
608 | 596 | ||
609 | for bare_git in gc_gitdirs.values(): | 597 | for bare_git in gc_gitdirs.values(): |
610 | if err_event.isSet(): | 598 | if err_event.isSet() and opt.fail_fast: |
611 | break | 599 | break |
612 | sem.acquire() | 600 | sem.acquire() |
613 | t = _threading.Thread(target=GC, args=(bare_git,)) | 601 | t = _threading.Thread(target=GC, args=(bare_git,)) |
@@ -618,10 +606,6 @@ later is required to fix a server side protocol bug. | |||
618 | for t in threads: | 606 | for t in threads: |
619 | t.join() | 607 | t.join() |
620 | 608 | ||
621 | if err_event.isSet(): | ||
622 | print('\nerror: Exited sync due to gc errors', file=sys.stderr) | ||
623 | sys.exit(1) | ||
624 | |||
625 | def _ReloadManifest(self, manifest_name=None): | 609 | def _ReloadManifest(self, manifest_name=None): |
626 | if manifest_name: | 610 | if manifest_name: |
627 | # Override calls _Unload already | 611 | # Override calls _Unload already |
@@ -902,6 +886,8 @@ later is required to fix a server side protocol bug. | |||
902 | print('error: failed to remove existing smart sync override manifest: %s' % | 886 | print('error: failed to remove existing smart sync override manifest: %s' % |
903 | e, file=sys.stderr) | 887 | e, file=sys.stderr) |
904 | 888 | ||
889 | err_event = _threading.Event() | ||
890 | |||
905 | rp = self.manifest.repoProject | 891 | rp = self.manifest.repoProject |
906 | rp.PreSync() | 892 | rp.PreSync() |
907 | 893 | ||
@@ -955,6 +941,10 @@ later is required to fix a server side protocol bug. | |||
955 | missing_ok=True, | 941 | missing_ok=True, |
956 | submodules_ok=opt.fetch_submodules) | 942 | submodules_ok=opt.fetch_submodules) |
957 | 943 | ||
944 | err_network_sync = False | ||
945 | err_update_projects = False | ||
946 | err_checkout = False | ||
947 | |||
958 | self._fetch_times = _FetchTimes(self.manifest) | 948 | self._fetch_times = _FetchTimes(self.manifest) |
959 | if not opt.local_only: | 949 | if not opt.local_only: |
960 | to_fetch = [] | 950 | to_fetch = [] |
@@ -964,10 +954,14 @@ later is required to fix a server side protocol bug. | |||
964 | to_fetch.extend(all_projects) | 954 | to_fetch.extend(all_projects) |
965 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) | 955 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) |
966 | 956 | ||
967 | fetched = self._Fetch(to_fetch, opt) | 957 | fetched = self._Fetch(to_fetch, opt, err_event) |
958 | |||
968 | _PostRepoFetch(rp, opt.no_repo_verify) | 959 | _PostRepoFetch(rp, opt.no_repo_verify) |
969 | if opt.network_only: | 960 | if opt.network_only: |
970 | # bail out now; the rest touches the working tree | 961 | # bail out now; the rest touches the working tree |
962 | if err_event.isSet(): | ||
963 | print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr) | ||
964 | sys.exit(1) | ||
971 | return | 965 | return |
972 | 966 | ||
973 | # Iteratively fetch missing and/or nested unregistered submodules | 967 | # Iteratively fetch missing and/or nested unregistered submodules |
@@ -989,22 +983,56 @@ later is required to fix a server side protocol bug. | |||
989 | if previously_missing_set == missing_set: | 983 | if previously_missing_set == missing_set: |
990 | break | 984 | break |
991 | previously_missing_set = missing_set | 985 | previously_missing_set = missing_set |
992 | fetched.update(self._Fetch(missing, opt)) | 986 | fetched.update(self._Fetch(missing, opt, err_event)) |
987 | |||
988 | # If we saw an error, exit with code 1 so that other scripts can check. | ||
989 | if err_event.isSet(): | ||
990 | err_network_sync = True | ||
991 | if opt.fail_fast: | ||
992 | print('\nerror: Exited sync due to fetch errors.\n' | ||
993 | 'Local checkouts *not* updated. Resolve network issues & ' | ||
994 | 'retry.\n' | ||
995 | '`repo sync -l` will update some local checkouts.', | ||
996 | file=sys.stderr) | ||
997 | sys.exit(1) | ||
993 | 998 | ||
994 | if self.manifest.IsMirror or self.manifest.IsArchive: | 999 | if self.manifest.IsMirror or self.manifest.IsArchive: |
995 | # bail out now, we have no working tree | 1000 | # bail out now, we have no working tree |
996 | return | 1001 | return |
997 | 1002 | ||
998 | if self.UpdateProjectList(opt): | 1003 | if self.UpdateProjectList(opt): |
999 | sys.exit(1) | 1004 | err_event.set() |
1005 | err_update_projects = True | ||
1006 | if opt.fail_fast: | ||
1007 | print('\nerror: Local checkouts *not* updated.', file=sys.stderr) | ||
1008 | sys.exit(1) | ||
1000 | 1009 | ||
1001 | self._Checkout(all_projects, opt) | 1010 | err_results = [] |
1011 | self._Checkout(all_projects, opt, err_event, err_results) | ||
1012 | if err_event.isSet(): | ||
1013 | err_checkout = True | ||
1014 | # NB: We don't exit here because this is the last step. | ||
1002 | 1015 | ||
1003 | # If there's a notice that's supposed to print at the end of the sync, print | 1016 | # If there's a notice that's supposed to print at the end of the sync, print |
1004 | # it now... | 1017 | # it now... |
1005 | if self.manifest.notice: | 1018 | if self.manifest.notice: |
1006 | print(self.manifest.notice) | 1019 | print(self.manifest.notice) |
1007 | 1020 | ||
1021 | # If we saw an error, exit with code 1 so that other scripts can check. | ||
1022 | if err_event.isSet(): | ||
1023 | print('\nerror: Unable to fully sync the tree.', file=sys.stderr) | ||
1024 | if err_network_sync: | ||
1025 | print('error: Downloading network changes failed.', file=sys.stderr) | ||
1026 | if err_update_projects: | ||
1027 | print('error: Updating local project lists failed.', file=sys.stderr) | ||
1028 | if err_checkout: | ||
1029 | print('error: Checking out local projects failed.', file=sys.stderr) | ||
1030 | if err_results: | ||
1031 | print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr) | ||
1032 | print('Try re-running with "-j1 --fail-fast" to exit at the first error.', | ||
1033 | file=sys.stderr) | ||
1034 | sys.exit(1) | ||
1035 | |||
1008 | def _PostRepoUpgrade(manifest, quiet=False): | 1036 | def _PostRepoUpgrade(manifest, quiet=False): |
1009 | wrapper = Wrapper() | 1037 | wrapper = Wrapper() |
1010 | if wrapper.NeedSetupGnuPG(): | 1038 | if wrapper.NeedSetupGnuPG(): |