mirror of
https://github.com/himanshudongre/smriti.git
synced 2026-08-28 05:14:59 +00:00
Make doctor runtime checks external-project aware
This commit is contained in:
parent
2bbb7d27bc
commit
c43310caa1
3 changed files with 224 additions and 33 deletions
|
|
@ -763,6 +763,8 @@ def format_doctor(report: dict) -> str:
|
|||
"""Readable diagnostics for backend/runtime consistency."""
|
||||
backend = report.get("backend") or {}
|
||||
local = report.get("local") or {}
|
||||
source = report.get("source") or local
|
||||
cwd = report.get("cwd") or {}
|
||||
cli = report.get("cli") or {}
|
||||
checks = report.get("checks") or {}
|
||||
hints = report.get("hints") or []
|
||||
|
|
@ -819,14 +821,36 @@ def format_doctor(report: dict) -> str:
|
|||
parts.append("PATH matches executable: unknown")
|
||||
parts.append("")
|
||||
|
||||
parts.append("## Local repo")
|
||||
parts.append(f"Branch: `{local.get('branch') or 'unknown'}`")
|
||||
parts.append(f"HEAD: `{local.get('git_sha_short') or local.get('git_sha') or 'unknown'}`")
|
||||
parts.append("## Smriti source")
|
||||
if source.get("git_root"):
|
||||
parts.append(f"Repo: `{source.get('git_root')}`")
|
||||
parts.append(f"Branch: `{source.get('branch') or 'unknown'}`")
|
||||
source_head = source.get("git_sha_short") or source.get("git_sha") or "unknown"
|
||||
parts.append(f"HEAD: `{source_head}`")
|
||||
else:
|
||||
parts.append("Repo: not detected (installed package or non-git install)")
|
||||
parts.append("HEAD: not available")
|
||||
parts.append("")
|
||||
|
||||
parts.append("## Current directory")
|
||||
parts.append(f"Path: `{cwd.get('path') or 'unknown'}`")
|
||||
if cwd.get("git_root"):
|
||||
parts.append(f"Git repo: `{cwd.get('git_root')}`")
|
||||
parts.append(f"Branch: `{cwd.get('branch') or 'unknown'}`")
|
||||
cwd_head = cwd.get("git_sha_short") or cwd.get("git_sha") or "unknown"
|
||||
parts.append(f"HEAD: `{cwd_head}`")
|
||||
if source.get("git_root") and cwd.get("git_root") != source.get("git_root"):
|
||||
parts.append(
|
||||
"Runtime match is checked against the Smriti source above, "
|
||||
"not this project repo."
|
||||
)
|
||||
else:
|
||||
parts.append("Git repo: none detected")
|
||||
parts.append("")
|
||||
|
||||
parts.append("## Checks")
|
||||
runtime_match = checks.get("runtime_match") or "unknown"
|
||||
parts.append(f"- runtime match: {runtime_match}")
|
||||
parts.append(f"- runtime match: {str(runtime_match).replace('_', ' ')}")
|
||||
missing = checks.get("missing_capabilities")
|
||||
if missing is None:
|
||||
parts.append("- missing capabilities: unknown (backend unreachable)")
|
||||
|
|
|
|||
|
|
@ -123,11 +123,14 @@ EXPECTED_HEALTH_CAPABILITIES = {
|
|||
}
|
||||
|
||||
|
||||
def _git_output(*args: str) -> str | None:
|
||||
def _git_output_at(cwd: str | os.PathLike[str] | None, *args: str) -> str | None:
|
||||
"""Return trimmed git output for diagnostics, or None outside a git repo."""
|
||||
cmd = ["git", *args]
|
||||
if cwd is not None:
|
||||
cmd = ["git", "-C", str(cwd), *args]
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", *args],
|
||||
cmd,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
|
|
@ -137,6 +140,65 @@ def _git_output(*args: str) -> str | None:
|
|||
return result.stdout.strip() or None
|
||||
|
||||
|
||||
def _git_output(*args: str) -> str | None:
|
||||
"""Return trimmed git output for the current directory."""
|
||||
return _git_output_at(None, *args)
|
||||
|
||||
|
||||
def _git_context(cwd: str | os.PathLike[str] | None = None) -> dict:
|
||||
root = _git_output_at(cwd, "rev-parse", "--show-toplevel")
|
||||
sha = _git_output_at(cwd, "rev-parse", "HEAD")
|
||||
short = _git_output_at(cwd, "rev-parse", "--short", "HEAD")
|
||||
branch = (
|
||||
_git_output_at(cwd, "branch", "--show-current")
|
||||
or _git_output_at(cwd, "rev-parse", "--abbrev-ref", "HEAD")
|
||||
)
|
||||
return {
|
||||
"git_root": root,
|
||||
"git_sha": sha,
|
||||
"git_sha_short": short,
|
||||
"branch": branch,
|
||||
}
|
||||
|
||||
|
||||
def _is_smriti_source_root(path: str | None) -> bool:
|
||||
if not path:
|
||||
return False
|
||||
root = Path(path)
|
||||
return (
|
||||
(root / "cli" / "smriti_cli" / "main.py").exists()
|
||||
and (root / "backend" / "app" / "main.py").exists()
|
||||
)
|
||||
|
||||
|
||||
def _build_cwd_info() -> dict:
|
||||
info = _git_context()
|
||||
info["path"] = os.getcwd()
|
||||
info["is_smriti_source"] = _is_smriti_source_root(info.get("git_root"))
|
||||
return info
|
||||
|
||||
|
||||
def _build_smriti_source_info() -> dict:
|
||||
"""Detect the Smriti source checkout backing this CLI, if available.
|
||||
|
||||
`smriti doctor` is often run from a user's project repo. Runtime freshness
|
||||
must compare the backend to Smriti's own source checkout, not the caller's
|
||||
app repo. Editable installs have `__file__` inside the Smriti checkout; a
|
||||
wheel/global install may not, in which case the git comparison is simply
|
||||
unavailable rather than a mismatch.
|
||||
"""
|
||||
module_dir = Path(__file__).resolve().parent
|
||||
info = _git_context(module_dir)
|
||||
info["path"] = str(module_dir)
|
||||
info["is_smriti_source"] = _is_smriti_source_root(info.get("git_root"))
|
||||
if not info["is_smriti_source"]:
|
||||
info["git_root"] = None
|
||||
info["git_sha"] = None
|
||||
info["git_sha_short"] = None
|
||||
info["branch"] = None
|
||||
return info
|
||||
|
||||
|
||||
def _git_sha_matches(backend_sha: str | None, local_sha: str | None) -> bool | None:
|
||||
if not backend_sha or not local_sha:
|
||||
return None
|
||||
|
|
@ -268,12 +330,11 @@ def _background_provider_check(providers: dict) -> str:
|
|||
|
||||
def _build_doctor_report(client: SmritiClient) -> dict:
|
||||
"""Build a small diagnostics report without attempting repairs."""
|
||||
local_sha = _git_output("rev-parse", "HEAD")
|
||||
local_branch = (
|
||||
_git_output("branch", "--show-current")
|
||||
or _git_output("rev-parse", "--abbrev-ref", "HEAD")
|
||||
)
|
||||
local_short = _git_output("rev-parse", "--short", "HEAD")
|
||||
source_info = _build_smriti_source_info()
|
||||
cwd_info = _build_cwd_info()
|
||||
if not source_info.get("git_sha") and cwd_info.get("is_smriti_source"):
|
||||
source_info = dict(cwd_info)
|
||||
source_sha = source_info.get("git_sha")
|
||||
|
||||
report = {
|
||||
"api_url": client.base_url,
|
||||
|
|
@ -284,11 +345,11 @@ def _build_doctor_report(client: SmritiClient) -> dict:
|
|||
"capabilities": [],
|
||||
"error": None,
|
||||
},
|
||||
"local": {
|
||||
"git_sha": local_sha,
|
||||
"git_sha_short": local_short,
|
||||
"branch": local_branch,
|
||||
},
|
||||
# Backward-compatible alias: historically "local" meant cwd. It now
|
||||
# means the local Smriti source/install used for runtime comparison.
|
||||
"local": source_info,
|
||||
"source": source_info,
|
||||
"cwd": cwd_info,
|
||||
"cli": _build_cli_info(),
|
||||
"checks": {
|
||||
"runtime_match": "unknown",
|
||||
|
|
@ -322,7 +383,7 @@ def _build_doctor_report(client: SmritiClient) -> dict:
|
|||
database = health.get("database") or {}
|
||||
providers = health.get("providers") or {}
|
||||
missing = sorted(EXPECTED_HEALTH_CAPABILITIES - set(capabilities))
|
||||
match = _git_sha_matches(backend_sha, local_sha)
|
||||
match = _git_sha_matches(backend_sha, source_sha)
|
||||
|
||||
report["backend"].update({
|
||||
"reachable": True,
|
||||
|
|
@ -354,14 +415,16 @@ def _build_doctor_report(client: SmritiClient) -> dict:
|
|||
elif match is False:
|
||||
report["checks"]["runtime_match"] = "mismatch"
|
||||
report["hints"].append(
|
||||
"Backend git_sha differs from local HEAD; restart the backend "
|
||||
"after syncing code, or check out the commit the backend is running."
|
||||
"Backend git_sha differs from the local Smriti source HEAD; "
|
||||
"restart the backend after syncing code, or check out the commit "
|
||||
"the backend is running."
|
||||
)
|
||||
else:
|
||||
report["checks"]["runtime_match"] = "unknown"
|
||||
report["hints"].append(
|
||||
"Could not compare backend git_sha with local HEAD."
|
||||
)
|
||||
report["checks"]["runtime_match"] = "not_applicable"
|
||||
if source_info.get("git_root"):
|
||||
report["hints"].append(
|
||||
"Could not compare backend git_sha with the local Smriti source checkout."
|
||||
)
|
||||
|
||||
if health.get("status") != "ok":
|
||||
report["hints"].append(
|
||||
|
|
|
|||
|
|
@ -17,15 +17,43 @@ def _client(health: dict | None = None) -> MagicMock:
|
|||
return client
|
||||
|
||||
|
||||
def _patch_git(monkeypatch: pytest.MonkeyPatch, *, sha: str = "c470947abcdef", branch: str = "main") -> None:
|
||||
def _patch_git(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
*,
|
||||
sha: str = "c470947abcdef",
|
||||
branch: str = "main",
|
||||
cwd_sha: str | None = None,
|
||||
cwd_branch: str = "main",
|
||||
source_detected: bool = True,
|
||||
) -> None:
|
||||
short = sha[:7]
|
||||
values = {
|
||||
("rev-parse", "HEAD"): sha,
|
||||
("rev-parse", "--short", "HEAD"): short,
|
||||
("branch", "--show-current"): branch,
|
||||
("rev-parse", "--abbrev-ref", "HEAD"): branch,
|
||||
}
|
||||
monkeypatch.setattr(cli_main, "_git_output", lambda *args: values.get(args))
|
||||
cwd_sha = cwd_sha if cwd_sha is not None else sha
|
||||
cwd_short = cwd_sha[:7] if cwd_sha else None
|
||||
|
||||
monkeypatch.setattr(
|
||||
cli_main,
|
||||
"_build_smriti_source_info",
|
||||
lambda: {
|
||||
"path": "/repo/cli/smriti_cli",
|
||||
"git_root": "/repo" if source_detected else None,
|
||||
"git_sha": sha if source_detected else None,
|
||||
"git_sha_short": short if source_detected else None,
|
||||
"branch": branch if source_detected else None,
|
||||
"is_smriti_source": source_detected,
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
cli_main,
|
||||
"_build_cwd_info",
|
||||
lambda: {
|
||||
"path": "/work/project",
|
||||
"git_root": "/work/project" if cwd_sha else None,
|
||||
"git_sha": cwd_sha,
|
||||
"git_sha_short": cwd_short,
|
||||
"branch": cwd_branch if cwd_sha else None,
|
||||
"is_smriti_source": False,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _patch_cli(
|
||||
|
|
@ -110,10 +138,84 @@ def test_doctor_report_flags_mismatch_and_missing_capability(
|
|||
|
||||
assert report["checks"]["runtime_match"] == "mismatch"
|
||||
assert report["checks"]["missing_capabilities"] == ["worktree_binding"]
|
||||
assert any("differs from local HEAD" in hint for hint in report["hints"])
|
||||
assert any(
|
||||
"differs from the local Smriti source HEAD" in hint
|
||||
for hint in report["hints"]
|
||||
)
|
||||
assert any("missing capabilities" in hint for hint in report["hints"])
|
||||
|
||||
|
||||
def test_doctor_report_ignores_external_project_git_head(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
):
|
||||
_patch_git(monkeypatch, sha="c470947abcdef", cwd_sha="deadbeefeedface")
|
||||
_patch_cli(monkeypatch)
|
||||
client = _client(_health(git_sha="c470947"))
|
||||
|
||||
report = cli_main._build_doctor_report(client)
|
||||
|
||||
assert report["checks"]["runtime_match"] == "ok"
|
||||
assert report["source"]["git_sha_short"] == "c470947"
|
||||
assert report["cwd"]["git_sha_short"] == "deadbee"
|
||||
assert not any(
|
||||
"differs from the local Smriti source HEAD" in hint
|
||||
for hint in report["hints"]
|
||||
)
|
||||
|
||||
|
||||
def test_doctor_report_does_not_mismatch_without_source_repo(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
):
|
||||
_patch_git(
|
||||
monkeypatch,
|
||||
sha="ignored",
|
||||
cwd_sha="deadbeefeedface",
|
||||
source_detected=False,
|
||||
)
|
||||
_patch_cli(monkeypatch)
|
||||
client = _client(_health(git_sha="c470947"))
|
||||
|
||||
report = cli_main._build_doctor_report(client)
|
||||
|
||||
assert report["checks"]["runtime_match"] == "not_applicable"
|
||||
assert report["source"]["git_sha"] is None
|
||||
assert report["cwd"]["git_sha_short"] == "deadbee"
|
||||
assert not any(
|
||||
"differs from the local Smriti source HEAD" in hint
|
||||
for hint in report["hints"]
|
||||
)
|
||||
|
||||
|
||||
def test_doctor_report_uses_cwd_when_it_is_smriti_source(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
):
|
||||
_patch_git(
|
||||
monkeypatch,
|
||||
sha="ignored",
|
||||
cwd_sha="c470947abcdef",
|
||||
source_detected=False,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
cli_main,
|
||||
"_build_cwd_info",
|
||||
lambda: {
|
||||
"path": "/repo",
|
||||
"git_root": "/repo",
|
||||
"git_sha": "c470947abcdef",
|
||||
"git_sha_short": "c470947",
|
||||
"branch": "main",
|
||||
"is_smriti_source": True,
|
||||
},
|
||||
)
|
||||
_patch_cli(monkeypatch)
|
||||
client = _client(_health(git_sha="c470947"))
|
||||
|
||||
report = cli_main._build_doctor_report(client)
|
||||
|
||||
assert report["checks"]["runtime_match"] == "ok"
|
||||
assert report["source"]["git_root"] == "/repo"
|
||||
|
||||
|
||||
def test_doctor_report_flags_cli_path_mismatch(monkeypatch: pytest.MonkeyPatch):
|
||||
_patch_git(monkeypatch)
|
||||
_patch_cli(
|
||||
|
|
@ -205,3 +307,5 @@ def test_cmd_doctor_prints_activation_details(
|
|||
assert "Background intelligence: ready (`openai` / `gpt-4o-mini`)" in out
|
||||
assert "## CLI" in out
|
||||
assert "PATH matches executable: yes" in out
|
||||
assert "## Smriti source" in out
|
||||
assert "## Current directory" in out
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue