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:
safishamsi
2026-07-12 14:31:50 +01:00
co-authored by fremat79 Claude Opus 4.8
parent eec7a01838
commit 373bc8efd8
4 changed files with 49 additions and 5 deletions
+15 -4
View File
@@ -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)