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
30 changes: 28 additions & 2 deletions src/evalopt_graph/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from collections.abc import Callable, Iterable
from dataclasses import asdict, dataclass
from typing import Any
from urllib.parse import urlsplit

# gate name -> key in profile["commands"]
GATE_TO_CMD_KEY = {"tests": "test", "lint": "lint", "typecheck": "typecheck", "build": "build"}
Expand Down Expand Up @@ -144,11 +145,36 @@ def is_git_repo(repo_path: str) -> bool:
return code == 0 and out.strip() == "true"


_SCP_REMOTE_RE = re.compile(r"^(?:[^@/:\s]+@)?(?P<host>[^/:\s]+):\S+$")


def _remote_hostname(remote_url: str) -> str | None:
"""Return a normalized hostname for URL and Git scp-style remotes."""
candidate = remote_url.strip()
if not candidate:
return None
try:
if "://" in candidate:
parsed = urlsplit(candidate)
if parsed.scheme.lower() not in {"git", "http", "https", "ssh"}:
return None
hostname = parsed.hostname
else:
match = _SCP_REMOTE_RE.fullmatch(candidate)
hostname = match.group("host") if match else None
except ValueError:
return None
return hostname.removesuffix(".").lower() if hostname else None


def is_github_repo(repo_path: str) -> bool:
"""True if the repo has a github.com remote or a .github directory (best-effort, read-only)."""
code, out = _git(repo_path, "remote", "-v")
if code == 0 and "github.com" in out.lower():
return True
if code == 0:
for line in out.splitlines():
fields = line.split()
if len(fields) >= 2 and _remote_hostname(fields[1]) == "github.com":
return True
return os.path.isdir(os.path.join(repo_path, ".github"))


Expand Down
57 changes: 57 additions & 0 deletions tests/test_quality_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import subprocess
import sys

from evalopt_graph import MockProvider, evaluate, passes_quality_gate
Expand All @@ -10,6 +11,7 @@
all_required_pass,
detect_test_weakening,
failing_gates,
is_github_repo,
run_gates,
)

Expand Down Expand Up @@ -90,6 +92,61 @@ def test_run_gates_real_subprocess_fail():
assert results[0].exit_code == 3


def _set_remote(tmp_path, remote_url: str):
repo = tmp_path / "repo"
if not repo.exists():
subprocess.run(["git", "init", "-q", str(repo)], check=True)
subprocess.run(["git", "-C", str(repo), "remote", "add", "origin", remote_url], check=True)
else:
subprocess.run(["git", "-C", str(repo), "remote", "set-url", "origin", remote_url], check=True)
return repo


def test_is_github_repo_accepts_exact_github_remote_hosts(tmp_path):
repo = _set_remote(tmp_path, "https://github.com/owner/repo.git")
assert is_github_repo(str(repo)) is True

_set_remote(tmp_path, "git@github.com:owner/repo.git")
assert is_github_repo(str(repo)) is True

_set_remote(tmp_path, "ssh://git@github.com/owner/repo.git")
assert is_github_repo(str(repo)) is True

_set_remote(tmp_path, "https://GitHub.com./owner/repo.git")
assert is_github_repo(str(repo)) is True


def test_is_github_repo_rejects_github_text_outside_exact_host(tmp_path):
repo = _set_remote(tmp_path, "https://evil.example/github.com/owner/repo.git")
assert is_github_repo(str(repo)) is False

_set_remote(tmp_path, "https://github.com.evil.example/owner/repo.git")
assert is_github_repo(str(repo)) is False

_set_remote(tmp_path, "https://github.com@evil.example/owner/repo.git")
assert is_github_repo(str(repo)) is False

_set_remote(tmp_path, "git@github.com.evil.example:owner/repo.git")
assert is_github_repo(str(repo)) is False

_set_remote(tmp_path, "https://github.com.../owner/repo.git")
assert is_github_repo(str(repo)) is False

_set_remote(tmp_path, "file:///tmp/github.com/owner/repo.git")
assert is_github_repo(str(repo)) is False

_set_remote(tmp_path, "file://github.com/owner/repo.git")
assert is_github_repo(str(repo)) is False


def test_is_github_repo_preserves_dot_github_fallback(tmp_path):
repo = tmp_path / "not-a-git-repo"
repo.mkdir()
assert is_github_repo(str(repo)) is False
(repo / ".github").mkdir(parents=True)
assert is_github_repo(str(repo)) is True


# ---------------- anti-gaming: test-weakening detector ----------------


Expand Down