github-repository-sync 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-repository-sync
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: Synchronize local repositories for a GitHub organization.
5
5
  Author: Kaizten Analytics
6
6
  License-Expression: LicenseRef-Proprietary
@@ -130,6 +130,7 @@ state files.
130
130
 
131
131
  After repository processing starts, every run prints aggregate counts for:
132
132
 
133
+ - all repositories returned for the GitHub organization;
133
134
  - repositories processed;
134
135
  - repositories already local and missing locally;
135
136
  - repositories cloned and pulled successfully;
@@ -144,6 +145,11 @@ upstream. Each repository is counted at most once in the error total, even if
144
145
  more than one operation failed. Dry runs additionally report the numbers of
145
146
  planned pulls and clones.
146
147
 
148
+ The GitHub organization total is the first summary metric. It includes every
149
+ repository returned by the current authenticated GitHub query, including
150
+ archived and disabled repositories, and is not reduced by `--exclude-archived`
151
+ or `--limit`.
152
+
147
153
  Failures during repository processing produce a nonzero exit status after the
148
154
  summary is printed. Startup failures, such as an invalid root or failed SSH
149
155
  preflight, occur before processing and therefore do not print a summary.
@@ -119,6 +119,7 @@ state files.
119
119
 
120
120
  After repository processing starts, every run prints aggregate counts for:
121
121
 
122
+ - all repositories returned for the GitHub organization;
122
123
  - repositories processed;
123
124
  - repositories already local and missing locally;
124
125
  - repositories cloned and pulled successfully;
@@ -133,6 +134,11 @@ upstream. Each repository is counted at most once in the error total, even if
133
134
  more than one operation failed. Dry runs additionally report the numbers of
134
135
  planned pulls and clones.
135
136
 
137
+ The GitHub organization total is the first summary metric. It includes every
138
+ repository returned by the current authenticated GitHub query, including
139
+ archived and disabled repositories, and is not reduced by `--exclude-archived`
140
+ or `--limit`.
141
+
136
142
  Failures during repository processing produce a nonzero exit status after the
137
143
  summary is printed. Startup failures, such as an invalid root or failed SSH
138
144
  preflight, occur before processing and therefore do not print a summary.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "github-repository-sync"
7
- version = "0.0.2"
7
+ version = "0.0.3"
8
8
  description = "Synchronize local repositories for a GitHub organization."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-repository-sync
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: Synchronize local repositories for a GitHub organization.
5
5
  Author: Kaizten Analytics
6
6
  License-Expression: LicenseRef-Proprietary
@@ -130,6 +130,7 @@ state files.
130
130
 
131
131
  After repository processing starts, every run prints aggregate counts for:
132
132
 
133
+ - all repositories returned for the GitHub organization;
133
134
  - repositories processed;
134
135
  - repositories already local and missing locally;
135
136
  - repositories cloned and pulled successfully;
@@ -144,6 +145,11 @@ upstream. Each repository is counted at most once in the error total, even if
144
145
  more than one operation failed. Dry runs additionally report the numbers of
145
146
  planned pulls and clones.
146
147
 
148
+ The GitHub organization total is the first summary metric. It includes every
149
+ repository returned by the current authenticated GitHub query, including
150
+ archived and disabled repositories, and is not reduced by `--exclude-archived`
151
+ or `--limit`.
152
+
147
153
  Failures during repository processing produce a nonzero exit status after the
148
154
  summary is printed. Startup failures, such as an invalid root or failed SSH
149
155
  preflight, occur before processing and therefore do not print a summary.
@@ -820,21 +820,24 @@ def sync_existing_repository(repo_name: str, repo_path: Path, dry_run: bool) ->
820
820
  )
821
821
 
822
822
 
823
- def print_sync_summary(results: list[RepositoryResult], dry_run: bool) -> None:
823
+ def print_sync_summary(
824
+ results: list[RepositoryResult], dry_run: bool, organization_repository_count: int
825
+ ) -> None:
824
826
  """Print aggregate counts and actionable repository details."""
825
827
  cloned = [result for result in results if result.cloned]
826
828
  needs_push = [result for result in results if result.needs_push]
827
829
 
828
- print("ℹ️ Summary:")
830
+ print("🏁 Summary:")
829
831
  print("-----------")
832
+ print(f"🏢 Repositories in GitHub organization: {organization_repository_count}")
830
833
  print(f"📦 Repositories processed: {len(results)}")
831
- print(f"📁 Repositories already local: {sum(result.was_local for result in results)}")
834
+ print(f"👌 Repositories already local: {sum(result.was_local for result in results)}")
832
835
  print(f"📁 Repositories missing locally: {sum(not result.was_local for result in results)}")
833
- print(f"📦 Repositories cloned: {len(cloned)}")
836
+ print(f"⮺ Repositories cloned: {len(cloned)}")
834
837
  for result in cloned:
835
838
  print(f"- {result.repo_name}: {result.path}")
836
839
  print(f"📥 Repositories pulled: {sum(result.pulled for result in results)}")
837
- print(f"� Repositories needing push/local attention: {len(needs_push)}")
840
+ print(f"⚠️ Repositories needing push/local attention: {len(needs_push)}")
838
841
  for result in needs_push:
839
842
  print(f"- {result.repo_name}: {result.path}")
840
843
  print(f"📁 Repositories skipped because dirty: {sum(result.skipped_dirty for result in results)}")
@@ -926,6 +929,7 @@ def main() -> int:
926
929
  org_repos = fetch_org_repositories(args.github_api, args.org, args.token)
927
930
  source = "GitHub API"
928
931
 
932
+ organization_repository_count = len(org_repos)
929
933
  active_repos = [
930
934
  repo
931
935
  for repo in org_repos
@@ -1037,7 +1041,7 @@ def main() -> int:
1037
1041
 
1038
1042
  print("\n")
1039
1043
 
1040
- print_sync_summary(results, args.dry_run)
1044
+ print_sync_summary(results, args.dry_run, organization_repository_count)
1041
1045
  return 1 if state_write_error or any(result.errors for result in results) else 0
1042
1046
 
1043
1047
 
@@ -557,9 +557,12 @@ class SyncOrgRepositoriesTests(unittest.TestCase):
557
557
  stdout = io.StringIO()
558
558
 
559
559
  with redirect_stdout(stdout):
560
- sync.print_sync_summary(results, dry_run=True)
560
+ sync.print_sync_summary(results, dry_run=True, organization_repository_count=8)
561
561
 
562
562
  output = stdout.getvalue()
563
+ output_lines = output.splitlines()
564
+ self.assertEqual(output_lines[2], "🏢 Repositories in GitHub organization: 8")
565
+ self.assertEqual(output_lines[3], "📦 Repositories processed: 3")
563
566
  self.assertIn("Repositories processed: 3", output)
564
567
  self.assertIn("Repositories already local: 1", output)
565
568
  self.assertIn("Repositories missing locally: 2", output)
@@ -657,6 +660,7 @@ class SyncOrgRepositoriesTests(unittest.TestCase):
657
660
  saved_state = save_state.call_args.args[1]
658
661
  self.assertGreater(saved_state["missing"].last_changed_at, old_clone_time)
659
662
  output = stdout.getvalue()
663
+ self.assertIn("Repositories in GitHub organization: 3", output)
660
664
  self.assertIn("[1/2] Processing local", output)
661
665
  self.assertIn("[2/2] Processing missing", output)
662
666
  self.assertIn("Repositories processed: 2", output)
@@ -777,6 +781,7 @@ class SyncOrgRepositoriesTests(unittest.TestCase):
777
781
  )
778
782
  output = stdout.getvalue()
779
783
  self.assertIn("Selected 2 of 3 repositories with --limit=2", output)
784
+ self.assertIn("Repositories in GitHub organization: 3", output)
780
785
  self.assertIn("1 repositories missing locally", output)
781
786
  self.assertIn("[1/2] Processing unknown", output)
782
787
  self.assertIn("[2/2] Processing new", output)
@@ -789,6 +794,7 @@ class SyncOrgRepositoriesTests(unittest.TestCase):
789
794
  def test_main_returns_nonzero_when_state_cannot_be_saved(self):
790
795
  with tempfile.TemporaryDirectory() as directory:
791
796
  stderr = io.StringIO()
797
+ stdout = io.StringIO()
792
798
  with (
793
799
  mock.patch.object(
794
800
  sys,
@@ -810,13 +816,14 @@ class SyncOrgRepositoriesTests(unittest.TestCase):
810
816
  mock.patch.object(
811
817
  sync, "save_repository_state", side_effect=OSError("read-only")
812
818
  ),
813
- redirect_stdout(io.StringIO()),
819
+ redirect_stdout(stdout),
814
820
  redirect_stderr(stderr),
815
821
  ):
816
822
  return_code = sync.main()
817
823
 
818
824
  self.assertEqual(return_code, 1)
819
825
  self.assertIn("could not save repository state", stderr.getvalue())
826
+ self.assertIn("Repositories in GitHub organization: 0", stdout.getvalue())
820
827
 
821
828
  def test_main_reports_missing_root(self):
822
829
  missing = Path("/tmp/github-repository-sync-definitely-missing")