--- a/src/aerie/sync.py +++ b/src/aerie/sync.py @@ -54,6 +54,10 @@ def clone_repo(repo: RepoConfig, release: str) -> None: run(["git", "clone", "--branch", release, repo.sync_uri, str(repo.location)]) +def is_empty_dir(path: Path) -> bool: + return path.is_dir() and not any(path.iterdir()) + + def update_repo(repo: RepoConfig, release: str) -> None: ensure_clean_git_tree(repo.location) run(["git", "-C", str(repo.location), "fetch", "--prune", "origin"]) @@ -70,6 +74,9 @@ def sync_repo(repo: RepoConfig, release: str) -> None: clone_repo(repo, release) return if not (repo.location / ".git").exists(): + if is_empty_dir(repo.location): + clone_repo(repo, release) + return raise AerieError(f"{repo.location} exists but is not a git repository") update_repo(repo, release) --- /dev/null +++ b/tests/test_sync.py @@ -0,0 +1,95 @@ +from contextlib import redirect_stdout +from io import StringIO +from pathlib import Path +from tempfile import TemporaryDirectory +import unittest +from unittest.mock import patch + +from aerie.config import AerieError, ProgramConfig, RepoConfig +from aerie.sync import sync_all, sync_repo + + +def repo_config(location: Path) -> RepoConfig: + return RepoConfig( + name="srcux", + main_repo="srcux", + location=location, + auto_sync=True, + sync_type="git", + sync_uri="https://example.test/repo.git", + priority="1", + ) + + +class SyncTests(unittest.TestCase): + def test_sync_repo_clones_into_empty_existing_directory(self): + with TemporaryDirectory() as tmp: + location = Path(tmp) / "baldeagleos" + location.mkdir() + repo = repo_config(location) + + with redirect_stdout(StringIO()), patch("aerie.sync.run") as run: + sync_repo(repo, "develop") + + run.assert_called_once_with( + [ + "git", + "clone", + "--branch", + "develop", + "https://example.test/repo.git", + str(location), + ] + ) + + def test_sync_repo_rejects_non_empty_non_git_directory(self): + with TemporaryDirectory() as tmp: + location = Path(tmp) / "baldeagleos" + location.mkdir() + (location / "README").write_text("not a git checkout\n", encoding="utf-8") + repo = repo_config(location) + + with redirect_stdout(StringIO()), patch("aerie.sync.run") as run: + with self.assertRaisesRegex(AerieError, "exists but is not a git repository"): + sync_repo(repo, "develop") + + run.assert_not_called() + + def test_sync_all_clones_repo_location_created_by_path_check(self): + with TemporaryDirectory() as tmp: + tmp_path = Path(tmp) + location = tmp_path / "repos" / "baldeagleos" + repos_conf = tmp_path / "baldeagleos.conf" + repos_conf.write_text( + f""" +[DEFAULT] +main-repo = srcux + +[srcux] +location = {location} +auto-sync = yes +sync-type = git +sync-uri = https://example.test/repo.git +priority = 1 +""", + encoding="utf-8", + ) + config = ProgramConfig(release="develop", reposconfpath=repos_conf) + + with redirect_stdout(StringIO()), patch("aerie.sync.run") as run: + sync_all(config) + + run.assert_called_once_with( + [ + "git", + "clone", + "--branch", + "develop", + "https://example.test/repo.git", + str(location), + ] + ) + + +if __name__ == "__main__": + unittest.main()