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
22 changes: 17 additions & 5 deletions src/local_coding_slm/safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,8 @@ def git_is_ignored(root: Path, relpath: str) -> bool | None:
return None


def git_tracked_text_files(root: Path) -> list[tuple[str, str]]:
def git_tracked_text_files(root: Path) -> list[tuple[str, str]] | None:
"""Return tracked text, or None when git cannot list the tree."""
try:
proc = subprocess.run(
["git", "ls-files", "-z"],
Expand All @@ -356,9 +357,9 @@ def git_tracked_text_files(root: Path) -> list[tuple[str, str]]:
capture_output=True,
)
except OSError:
return []
return None
if proc.returncode != 0:
return []
return None
out: list[tuple[str, str]] = []
for rel in proc.stdout.split(b"\0"):
if not rel:
Expand Down Expand Up @@ -391,6 +392,18 @@ def git_tracked_text_files(root: Path) -> list[tuple[str, str]]:
return out


def _committed_ip_result(
repo: Path,
tracked: Iterable[tuple[str, str]] | None,
) -> CheckResult:
if tracked is not None:
return classify_tracked_ipv4(list(tracked))
files = git_tracked_text_files(repo)
if files is None:
return CheckResult("committed_ips", "fail", "could not list tracked files")
return classify_tracked_ipv4(files)


def _listen_check(
url: str,
*,
Expand Down Expand Up @@ -439,8 +452,7 @@ def run_checks(
ignored = env_ignored if env_ignored is not None else git_is_ignored(repo, ".env")
results.append(classify_env_ignored(ignored))

files = list(tracked) if tracked is not None else git_tracked_text_files(repo)
results.append(classify_tracked_ipv4(files))
results.append(_committed_ip_result(repo, tracked))
return results


Expand Down
27 changes: 27 additions & 0 deletions tests/test_safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,33 @@ def test_safe_deployment_no_fail(self) -> None:
)
self.assertEqual(worst_status(results), "pass")

def test_git_ls_files_failure_fails_closed(self) -> None:
"""A failed git listing is not an empty tree with no addresses."""
import subprocess
from pathlib import Path

safe = {
"environ": {
"OLLAMA_BASE_URL": "http://127.0.0.1:11434",
"OLLAMA_FAST_MODEL": "qwen3.5:9b",
"OLLAMA_STRONG_MODEL": "devstral-small-2",
},
"listen_rows": [("127.0.0.1", 11434)],
"env_ignored": True,
"env_example_text": "OLLAMA_BASE_URL=http://127.0.0.1:11434\n",
"root": Path("/tmp/not-a-repo"),
}
failed = subprocess.CompletedProcess(["git", "ls-files", "-z"], 1, b"", b"fatal")
with patch("local_coding_slm.safety.subprocess.run", return_value=failed):
results = run_checks(**safe)
ips = next(item for item in results if item.name == "committed_ips")
self.assertEqual(ips.status, "fail")
self.assertIn("could not list", ips.message)
with patch("local_coding_slm.safety.subprocess.run", side_effect=OSError("git missing")):
raised = run_checks(**safe)
missing = next(item for item in raised if item.name == "committed_ips")
self.assertEqual(missing.status, "fail")

def test_unofficial_library_tag_fails_run_checks(self) -> None:
results = run_checks(
environ={
Expand Down
Loading