mirror of
https://github.com/safishamsi/graphify.git
synced 2026-07-23 15:51:44 +00:00
The PR always wrote gitignore=not no_gitignore into .graphify_build.json, so a flag-less `graphify extract` after `--no-gitignore` reset it to True and the git-ignored code silently disappeared again — the exact #1971 complaint. Write False only when the flag is set (None = leave as-is, mirroring #1886 excludes), and honor the persisted value for the run when the flag is absent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
110 lines
4.6 KiB
Python
110 lines
4.6 KiB
Python
"""`graphify extract --code-only` indexes code without an LLM key (#1734).
|
|
|
|
A mixed repo (code + docs) with no API key configured used to hard-fail on the
|
|
doc/paper/image files. `--code-only` skips the semantic pass so the code graph
|
|
still builds, and the no-key error now points users at the flag.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
PYTHON = sys.executable
|
|
_KEY_VARS = ("GEMINI_API_KEY", "GOOGLE_API_KEY", "OPENAI_API_KEY", "OPENAI_BASE_URL",
|
|
"ANTHROPIC_API_KEY", "MOONSHOT_API_KEY", "DEEPSEEK_API_KEY")
|
|
|
|
|
|
def _mixed_repo(tmp_path: Path) -> Path:
|
|
repo = tmp_path / "repo"
|
|
repo.mkdir()
|
|
(repo / "app.py").write_text("def hello():\n return 1\n")
|
|
(repo / "README.md").write_text("# Design\n\nHow it works.\n")
|
|
(repo / "NOTES.txt").write_text("Architecture notes and rationale.\n")
|
|
return repo
|
|
|
|
|
|
def _run(repo: Path, *extra: str):
|
|
env = {k: v for k, v in os.environ.items() if k not in _KEY_VARS}
|
|
env["GRAPHIFY_OUT"] = str(repo / "graphify-out")
|
|
return subprocess.run(
|
|
[PYTHON, "-m", "graphify", "extract", ".", *extra],
|
|
cwd=repo, capture_output=True, text=True, env=env,
|
|
)
|
|
|
|
|
|
def test_code_only_succeeds_without_key(tmp_path):
|
|
repo = _mixed_repo(tmp_path)
|
|
r = _run(repo, "--code-only")
|
|
assert r.returncode == 0, f"--code-only should succeed with no key: {r.stderr}"
|
|
out = r.stdout + r.stderr
|
|
assert "--code-only: skipping" in out
|
|
graph = repo / "graphify-out" / "graph.json"
|
|
assert graph.exists(), "code graph must still be written"
|
|
import json
|
|
g = json.loads(graph.read_text())
|
|
labels = [n.get("label") for n in g["nodes"]]
|
|
assert any(str(l).startswith("hello") for l in labels), "code was indexed"
|
|
|
|
|
|
def test_mixed_repo_without_key_errors_and_points_at_code_only(tmp_path):
|
|
repo = _mixed_repo(tmp_path)
|
|
r = _run(repo) # no --code-only, no key
|
|
assert r.returncode != 0, "mixed repo with no key should still error without the flag"
|
|
assert "--code-only" in r.stderr, "the no-key error must point users at --code-only"
|
|
|
|
|
|
def test_no_gitignore_indexes_vcs_ignored_code_but_keeps_graphifyignore(tmp_path):
|
|
repo = tmp_path / "repo"
|
|
generated = repo / "proj" / "deep" / "generated"
|
|
generated.mkdir(parents=True)
|
|
(repo / ".git" / "info").mkdir(parents=True)
|
|
(repo / ".git" / "info" / "exclude").write_text("local/\n")
|
|
(repo / "proj" / ".gitignore").write_text("generated/\n")
|
|
(repo / "proj" / ".graphifyignore").write_text("hidden/\n")
|
|
(generated / "Gen.cs").write_text("namespace N { public class Gen {} }\n")
|
|
local = repo / "local"
|
|
local.mkdir()
|
|
(local / "Local.cs").write_text("namespace N { public class Local {} }\n")
|
|
hidden = repo / "proj" / "hidden"
|
|
hidden.mkdir()
|
|
(hidden / "Hidden.cs").write_text("namespace N { public class Hidden {} }\n")
|
|
|
|
result = _run(repo, "--no-gitignore", "--no-cluster")
|
|
|
|
assert result.returncode == 0, result.stderr
|
|
graph = json.loads((repo / "graphify-out" / "graph.json").read_text())
|
|
sources = {Path(str(node.get("source_file", ""))).as_posix() for node in graph["nodes"]}
|
|
assert any(source.endswith("proj/deep/generated/Gen.cs") for source in sources)
|
|
assert any(source.endswith("local/Local.cs") for source in sources)
|
|
assert not any(source.endswith("proj/hidden/Hidden.cs") for source in sources)
|
|
|
|
|
|
def test_no_gitignore_setting_persists_across_flagless_extract(tmp_path):
|
|
"""#1971 persistence: once --no-gitignore is set, a later flag-less
|
|
`graphify extract` must NOT clobber it back to honoring .gitignore (which
|
|
would make the git-ignored code silently disappear again)."""
|
|
repo = tmp_path / "repo"
|
|
gen = repo / "generated"
|
|
gen.mkdir(parents=True)
|
|
(repo / ".gitignore").write_text("generated/\n")
|
|
(repo / "app.py").write_text("def hello():\n return 1\n")
|
|
(gen / "Gen.py").write_text("def gen():\n return 2\n")
|
|
|
|
def _sources():
|
|
g = json.loads((repo / "graphify-out" / "graph.json").read_text())
|
|
return {Path(str(n.get("source_file", ""))).as_posix() for n in g["nodes"]}
|
|
|
|
r1 = _run(repo, "--no-gitignore", "--code-only", "--no-cluster")
|
|
assert r1.returncode == 0, r1.stderr
|
|
assert any(s.endswith("generated/Gen.py") for s in _sources())
|
|
|
|
# A plain flag-less re-extract must keep the git-ignored file (setting persisted).
|
|
r2 = _run(repo, "--code-only", "--no-cluster")
|
|
assert r2.returncode == 0, r2.stderr
|
|
assert any(s.endswith("generated/Gen.py") for s in _sources()), (
|
|
"flag-less re-extract clobbered the persisted --no-gitignore setting (#1971)"
|
|
)
|