Skip to content

Commit 6e2a7e5

Browse files
committed
fix: --all-starred now clones repos without --repositories
1 parent aba048a commit 6e2a7e5

File tree

2 files changed

+167
-8
lines changed

2 files changed

+167
-8
lines changed

github_backup/github_backup.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ def get_github_host(args):
561561

562562

563563
def read_file_contents(file_uri):
564-
return open(file_uri[len(FILE_URI_PREFIX) :], "rt").readline().strip()
564+
return open(file_uri[len(FILE_URI_PREFIX):], "rt").readline().strip()
565565

566566

567567
def get_github_repo_url(args, repository):
@@ -1672,9 +1672,10 @@ def backup_repositories(args, output_directory, repositories):
16721672
repo_url = get_github_repo_url(args, repository)
16731673

16741674
include_gists = args.include_gists or args.include_starred_gists
1675+
include_starred = args.all_starred and repository.get("is_starred")
16751676
if (args.include_repository or args.include_everything) or (
16761677
include_gists and repository.get("is_gist")
1677-
):
1678+
) or include_starred:
16781679
repo_name = (
16791680
repository.get("name")
16801681
if not repository.get("is_gist")
@@ -2023,12 +2024,9 @@ def fetch_repository(
20232024
):
20242025
if bare_clone:
20252026
if os.path.exists(local_dir):
2026-
clone_exists = (
2027-
subprocess.check_output(
2028-
["git", "rev-parse", "--is-bare-repository"], cwd=local_dir
2029-
)
2030-
== b"true\n"
2031-
)
2027+
clone_exists = subprocess.check_output(
2028+
["git", "rev-parse", "--is-bare-repository"], cwd=local_dir
2029+
) == b"true\n"
20322030
else:
20332031
clone_exists = False
20342032
else:

tests/test_all_starred.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""Tests for --all-starred flag behavior (issue #225)."""
2+
3+
import pytest
4+
from unittest.mock import Mock, patch
5+
6+
from github_backup import github_backup
7+
8+
9+
class TestAllStarredCloning:
10+
"""Test suite for --all-starred repository cloning behavior.
11+
12+
Issue #225: --all-starred should clone starred repos without requiring --repositories.
13+
"""
14+
15+
def _create_mock_args(self, **overrides):
16+
"""Create a mock args object with sensible defaults."""
17+
args = Mock()
18+
args.user = "testuser"
19+
args.output_directory = "/tmp/backup"
20+
args.include_repository = False
21+
args.include_everything = False
22+
args.include_gists = False
23+
args.include_starred_gists = False
24+
args.all_starred = False
25+
args.skip_existing = False
26+
args.bare_clone = False
27+
args.lfs_clone = False
28+
args.no_prune = False
29+
args.include_wiki = False
30+
args.include_issues = False
31+
args.include_issue_comments = False
32+
args.include_issue_events = False
33+
args.include_pulls = False
34+
args.include_pull_comments = False
35+
args.include_pull_commits = False
36+
args.include_pull_details = False
37+
args.include_labels = False
38+
args.include_hooks = False
39+
args.include_milestones = False
40+
args.include_releases = False
41+
args.include_assets = False
42+
args.include_attachments = False
43+
args.incremental = False
44+
args.incremental_by_files = False
45+
args.github_host = None
46+
args.prefer_ssh = False
47+
args.token_classic = None
48+
args.token_fine = None
49+
args.username = None
50+
args.password = None
51+
args.as_app = False
52+
args.osx_keychain_item_name = None
53+
args.osx_keychain_item_account = None
54+
55+
for key, value in overrides.items():
56+
setattr(args, key, value)
57+
58+
return args
59+
60+
@patch('github_backup.github_backup.fetch_repository')
61+
@patch('github_backup.github_backup.get_github_repo_url')
62+
def test_all_starred_clones_without_repositories_flag(self, mock_get_url, mock_fetch):
63+
"""--all-starred should clone starred repos without --repositories flag.
64+
65+
This is the core fix for issue #225.
66+
"""
67+
args = self._create_mock_args(all_starred=True)
68+
mock_get_url.return_value = "https://github.com/otheruser/awesome-project.git"
69+
70+
# A starred repository (is_starred flag set by retrieve_repositories)
71+
starred_repo = {
72+
"name": "awesome-project",
73+
"full_name": "otheruser/awesome-project",
74+
"owner": {"login": "otheruser"},
75+
"private": False,
76+
"fork": False,
77+
"has_wiki": False,
78+
"is_starred": True, # This flag is set for starred repos
79+
}
80+
81+
with patch('github_backup.github_backup.mkdir_p'):
82+
github_backup.backup_repositories(args, "/tmp/backup", [starred_repo])
83+
84+
# fetch_repository should be called for the starred repo
85+
assert mock_fetch.called, "--all-starred should trigger repository cloning"
86+
mock_fetch.assert_called_once()
87+
call_args = mock_fetch.call_args
88+
assert call_args[0][0] == "awesome-project" # repo name
89+
90+
@patch('github_backup.github_backup.fetch_repository')
91+
@patch('github_backup.github_backup.get_github_repo_url')
92+
def test_starred_repo_not_cloned_without_all_starred_flag(self, mock_get_url, mock_fetch):
93+
"""Starred repos should NOT be cloned if --all-starred is not set."""
94+
args = self._create_mock_args(all_starred=False)
95+
mock_get_url.return_value = "https://github.com/otheruser/awesome-project.git"
96+
97+
starred_repo = {
98+
"name": "awesome-project",
99+
"full_name": "otheruser/awesome-project",
100+
"owner": {"login": "otheruser"},
101+
"private": False,
102+
"fork": False,
103+
"has_wiki": False,
104+
"is_starred": True,
105+
}
106+
107+
with patch('github_backup.github_backup.mkdir_p'):
108+
github_backup.backup_repositories(args, "/tmp/backup", [starred_repo])
109+
110+
# fetch_repository should NOT be called
111+
assert not mock_fetch.called, "Starred repos should not be cloned without --all-starred"
112+
113+
@patch('github_backup.github_backup.fetch_repository')
114+
@patch('github_backup.github_backup.get_github_repo_url')
115+
def test_non_starred_repo_not_cloned_with_only_all_starred(self, mock_get_url, mock_fetch):
116+
"""Non-starred repos should NOT be cloned when only --all-starred is set."""
117+
args = self._create_mock_args(all_starred=True)
118+
mock_get_url.return_value = "https://github.com/testuser/my-project.git"
119+
120+
# A regular (non-starred) repository
121+
regular_repo = {
122+
"name": "my-project",
123+
"full_name": "testuser/my-project",
124+
"owner": {"login": "testuser"},
125+
"private": False,
126+
"fork": False,
127+
"has_wiki": False,
128+
# No is_starred flag
129+
}
130+
131+
with patch('github_backup.github_backup.mkdir_p'):
132+
github_backup.backup_repositories(args, "/tmp/backup", [regular_repo])
133+
134+
# fetch_repository should NOT be called for non-starred repos
135+
assert not mock_fetch.called, "Non-starred repos should not be cloned with only --all-starred"
136+
137+
@patch('github_backup.github_backup.fetch_repository')
138+
@patch('github_backup.github_backup.get_github_repo_url')
139+
def test_repositories_flag_still_works(self, mock_get_url, mock_fetch):
140+
"""--repositories flag should still clone repos as before."""
141+
args = self._create_mock_args(include_repository=True)
142+
mock_get_url.return_value = "https://github.com/testuser/my-project.git"
143+
144+
regular_repo = {
145+
"name": "my-project",
146+
"full_name": "testuser/my-project",
147+
"owner": {"login": "testuser"},
148+
"private": False,
149+
"fork": False,
150+
"has_wiki": False,
151+
}
152+
153+
with patch('github_backup.github_backup.mkdir_p'):
154+
github_backup.backup_repositories(args, "/tmp/backup", [regular_repo])
155+
156+
# fetch_repository should be called
157+
assert mock_fetch.called, "--repositories should trigger repository cloning"
158+
159+
160+
if __name__ == "__main__":
161+
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)