Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions src/cloudai/_core/installables/git_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,15 @@ def container_mount(self) -> str:

def check_submodules_state(self, repo_path: Path) -> tuple[bool, str]:
"""Check if submodules state in the cloned repo matches self.init_submodules."""
result = subprocess.run(
["git", "submodule", "status", "--recursive"],
cwd=str(repo_path),
capture_output=True,
text=True,
)
try:
result = subprocess.run(
["git", "submodule", "status", "--recursive"],
cwd=str(repo_path),
capture_output=True,
text=True,
)
except OSError as e:
return False, f"Failed to get submodule status: {e}"
if result.returncode != 0:
return False, f"Failed to get submodule status: {result.stderr}"
output = [line for line in result.stdout.splitlines() if line.strip()]
Expand All @@ -92,9 +95,12 @@ def ensure_submodules_state(self, repo_path: Path) -> tuple[bool, str]:
return False, submodules_are_ok_msg

cmd = ["update", "--init", "--recursive"] if self.init_submodules else ["deinit", "--all", "--force"]
result = subprocess.run(["git", "submodule", *cmd], cwd=str(repo_path), capture_output=True, text=True)
action = "initialize" if self.init_submodules else "deinitialize"
try:
result = subprocess.run(["git", "submodule", *cmd], cwd=str(repo_path), capture_output=True, text=True)
except OSError as e:
return False, f"Failed to {action} submodules: {e}"
if result.returncode != 0:
action = "initialize" if self.init_submodules else "deinitialize"
return False, f"Failed to {action} submodules: {result.stderr}"

return True, ""
Expand Down Expand Up @@ -182,14 +188,20 @@ def _clone_repository(self, installer: "BaseInstaller", path: Path) -> InstallSt
clone_cmd.extend([self.url, str(path)])

logging.debug(f"Running git clone command: {' '.join(clone_cmd)}")
result = subprocess.run(clone_cmd, capture_output=True, text=True)
try:
result = subprocess.run(clone_cmd, capture_output=True, text=True)
except OSError as e:
return InstallStatusResult(False, f"Failed to clone repository: {e}")
if result.returncode != 0:
return InstallStatusResult(False, f"Failed to clone repository: {result.stderr}")
return InstallStatusResult(True)

def _checkout_commit(self, commit_hash: str, path: Path) -> InstallStatusResult:
logging.debug(f"Checking out specific commit in {path}: {commit_hash}")
result = subprocess.run(["git", "checkout", commit_hash], cwd=str(path), capture_output=True, text=True)
try:
result = subprocess.run(["git", "checkout", commit_hash], cwd=str(path), capture_output=True, text=True)
except OSError as e:
return InstallStatusResult(False, f"Failed to checkout commit {commit_hash}: {e}")
if result.returncode != 0:
return InstallStatusResult(False, f"Failed to checkout commit {commit_hash}: {result.stderr}")
return InstallStatusResult(True)
Expand Down
47 changes: 47 additions & 0 deletions tests/core/installables/test_git_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ def test_check_submodules_state_status_failure(git_unmocked: GitRepo):
assert message == "Failed to get submodule status: err"


def test_check_submodules_state_oserror(git_unmocked: GitRepo):
with patch("subprocess.run", side_effect=OSError("No such file or directory")):
result, message = git_unmocked.check_submodules_state(Path("/repo"))

assert result is False
assert "Failed to get submodule status" in message


@pytest.mark.parametrize(
("init_submodules", "stdout", "expected_command"),
[
Expand Down Expand Up @@ -216,6 +224,28 @@ def test_ensure_submodules_state_status_fails(git_unmocked: GitRepo):
assert mock_run.call_count == 1


@pytest.mark.parametrize(
("init_submodules", "expected_message"),
[
(True, "Failed to initialize submodules"),
(False, "Failed to deinitialize submodules"),
],
)
def test_ensure_submodules_state_reconcile_oserror(git_unmocked: GitRepo, init_submodules: bool, expected_message: str):
git_unmocked.init_submodules = init_submodules
stdout = "-0123456789abcdef path/to/submodule\n" if init_submodules else " 0123456789abcdef path/to/submodule\n"

with patch("subprocess.run") as mock_run:
mock_run.side_effect = [
CompletedProcess(args=[], returncode=0, stdout=stdout, stderr=""),
OSError("No such file or directory"),
]
result, message = git_unmocked.ensure_submodules_state(Path("/repo"))

assert result is False
assert expected_message in message


def test_repo_exists_with_correct_commit(installer: BaseInstaller, git: GitRepo):
repo_path = installer.system.install_path / git.repo_name
repo_path.mkdir()
Expand Down Expand Up @@ -254,6 +284,14 @@ def test_error_cloning_repo(installer: BaseInstaller, git: GitRepo):
assert res.message == "Failed to clone repository: err"


def test_clone_repository_oserror(installer: BaseInstaller, git: GitRepo):
repo_path = installer.system.install_path / git.repo_name
with patch("subprocess.run", side_effect=OSError("No such file or directory")):
res = git._clone_repository(installer, repo_path)
assert not res.success
assert "Failed to clone repository" in res.message


def test_commit_checked_out(installer: BaseInstaller, git: GitRepo):
repo_path = installer.system.install_path / git.repo_name
repo_path.mkdir()
Expand All @@ -276,6 +314,15 @@ def test_error_checking_out_commit(installer: BaseInstaller, git: GitRepo):
assert res.message == f"Failed to checkout commit {git.commit}: err"


def test_checkout_commit_oserror(installer: BaseInstaller, git: GitRepo):
repo_path = installer.system.install_path / git.repo_name
repo_path.mkdir()
with patch("subprocess.run", side_effect=OSError("No such file or directory")):
res = git._checkout_commit(git.commit, repo_path)
assert not res.success
assert f"Failed to checkout commit {git.commit}" in res.message


def test_checkout_failure_cleans_up_repo(installer: BaseInstaller, git: GitRepo):
repo_path = installer.system.install_path / git.repo_name
with (
Expand Down
Loading