mirror of
https://github.com/safishamsi/graphify.git
synced 2026-07-14 03:17:28 +00:00
fix(sln): keep Visual Studio solution-folder node ids relative (#1789)
A solution folder is a virtual grouping, not a file: VS writes its name as both the display name and the "path" (name == path, no real file). extract_sln resolved it to an absolute filesystem path anyway and keyed the node id off that. The CLI id-relativization pass only remaps ids of real files in the scan set, so a virtual folder never matched and its absolute id (with the local username) survived into a committed graph.json. Detect solution folders (name == path) and key their id/source_file off the folder name only; real project files still resolve as before. Adds a regression test asserting the folder node id is relative. The earlier fix (0.9.13) covered .csproj/.sln file nodes but missed the virtual folders, so #1789 was closed prematurely; this completes it. Reported and diagnosed by @fremat79. Co-Authored-By: fremat79 <fremat79@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases)
|
||||
|
||||
## 0.9.14 (2026-07-12)
|
||||
|
||||
- Fix: Visual Studio *solution folder* nodes no longer embed the absolute scan path (including the local username) in their `id` and `source_file` (#1789, thanks @fremat79). A solution folder is a virtual grouping, not a file — VS writes its name as both the display name and the "path" — but `extract_sln` resolved it to an absolute filesystem path anyway and keyed the node id off that. The CLI's id-relativization pass only remaps ids of real files in the scan set, so a virtual folder never matched and its absolute id survived into a committed `graph.json` (e.g. `id=/Users/<name>/proj/Plugins` instead of `id=plugins`). Solution folders are now detected (name == path) and keyed off the folder name only; real project files still resolve as before. (The earlier fix covered `.csproj`/`.sln` file nodes but missed the virtual folders — this completes it.)
|
||||
|
||||
## 0.9.13 (2026-07-12)
|
||||
|
||||
- Fix: the query log is now opt-in (off by default) (#1797, thanks @adam-pond-agent). `querylog` wrote every `query`/`path`/`explain` question and corpus path (and full responses if `GRAPHIFY_QUERY_LOG_RESPONSES`) to a default-on, unbounded, fail-silent plaintext file at `~/.cache/graphify-queries.log` — outside any repo's .gitignore/retention, and undocumented, which contradicts graphify's on-device / no-telemetry posture. Logging is now OFF unless you opt in with `GRAPHIFY_QUERY_LOG_ENABLE=1` (default path) or `GRAPHIFY_QUERY_LOG=<path>`; `GRAPHIFY_QUERY_LOG_DISABLE=1` still forces it off. All the query-log env vars are now documented in the README.
|
||||
|
||||
@@ -34,10 +34,21 @@ def extract_sln(path: Path) -> dict:
|
||||
proj_path = m.group(2).replace("\\", "/")
|
||||
proj_guid = m.group(3).strip("{}")
|
||||
|
||||
try:
|
||||
abs_proj = str((path.parent / proj_path).resolve())
|
||||
except Exception:
|
||||
abs_proj = proj_path
|
||||
# A solution folder is a VIRTUAL grouping, not a file: Visual Studio writes
|
||||
# its name as both the display name and the "path" (proj_name == proj_path,
|
||||
# no real file). Resolving it to an absolute path and keying the node id off
|
||||
# that leaked the absolute scan path (incl. the OS username) into graph.json,
|
||||
# because the CLI's id-relativization only remaps ids of real files in the
|
||||
# scan set — a virtual folder never matches, so its absolute id survived
|
||||
# (#1789). Use the folder name itself (relative, no filesystem resolution).
|
||||
is_solution_folder = proj_path == proj_name
|
||||
if is_solution_folder:
|
||||
abs_proj = proj_name
|
||||
else:
|
||||
try:
|
||||
abs_proj = str((path.parent / proj_path).resolve())
|
||||
except Exception:
|
||||
abs_proj = proj_path
|
||||
proj_nid = _make_id(abs_proj)
|
||||
if proj_nid and proj_nid not in seen_ids:
|
||||
seen_ids.add(proj_nid)
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "graphifyy"
|
||||
version = "0.9.13"
|
||||
version = "0.9.14"
|
||||
description = "AI coding assistant skill (Claude Code, CodeBuddy, Codex, OpenCode, Kilo Code, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Devin CLI, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph"
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE" }
|
||||
|
||||
@@ -45,6 +45,35 @@ def test_sln_project_dependency():
|
||||
assert "imports" in _relations(r)
|
||||
|
||||
|
||||
def test_sln_solution_folder_ids_are_relative(tmp_path):
|
||||
"""Solution folders are virtual groupings, not files. Their node ids must be
|
||||
derived from the folder name only — never the resolved absolute scan path,
|
||||
which would leak the local username into a committed graph.json (#1789)."""
|
||||
sln = tmp_path / "App.sln"
|
||||
sln.write_text(
|
||||
'Microsoft Visual Studio Solution File, Format Version 12.00\n'
|
||||
# a solution folder: type GUID 2150E333-... , name == path, no real file
|
||||
'Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", '
|
||||
'"{11111111-1111-1111-1111-111111111111}"\n'
|
||||
'EndProject\n'
|
||||
# a real project resolves to an absolute path as before
|
||||
'Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "App\\App.csproj", '
|
||||
'"{22222222-2222-2222-2222-222222222222}"\n'
|
||||
'EndProject\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
r = extract_sln(sln)
|
||||
assert "error" not in r
|
||||
# The virtual solution folder must be keyed off its name, with no trace of the
|
||||
# absolute scan path. (Real-file nodes — the .sln and .csproj — legitimately
|
||||
# carry absolute ids here; the CLI's id-relativization pass remaps those, but
|
||||
# never the virtual folder, which is why the leak had to be fixed at source.)
|
||||
folder = next(n for n in r["nodes"] if n["label"] == "Plugins")
|
||||
assert folder["id"] == "plugins"
|
||||
assert folder["source_file"] == "Plugins"
|
||||
assert str(tmp_path) not in folder["id"]
|
||||
|
||||
|
||||
# ── .slnx ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_slnx_extracts_projects():
|
||||
|
||||
Reference in New Issue
Block a user