mirror of
https://github.com/safishamsi/graphify.git
synced 2026-08-28 09:17:02 +00:00
fix(windows): stop a PowerShell BOM breaking hook rebuilds (#3028)
Windows PowerShell 5.1 writes a UTF-8 BOM, so the .graphify_root marker it produced carried a leading BOM that Python's utf-8 decode kept as U+FEFF; .strip() left it in, breaking the post-commit rebuild (WinError 123) and silently mis-rooting _infer_merge_root. Write the marker BOM-less from PowerShell (WriteAllText + UTF8Encoding $false) and read the marker with utf-8-sig at the reader sites so already-broken installs recover too.
This commit is contained in:
committed by
safishamsi
parent
5687fe1bcc
commit
3c448660cd
+1
-1
@@ -439,7 +439,7 @@ def _infer_merge_root(graph_path: Path) -> str | None:
|
||||
try:
|
||||
marker = parent / ".graphify_root"
|
||||
if marker.exists():
|
||||
recorded = marker.read_text(encoding="utf-8").strip()
|
||||
recorded = marker.read_text(encoding="utf-8-sig").strip()
|
||||
if recorded:
|
||||
return str(Path(recorded).resolve())
|
||||
except OSError:
|
||||
|
||||
+2
-2
@@ -165,7 +165,7 @@ try:
|
||||
_out = os.environ.get('GRAPHIFY_OUT', 'graphify-out')
|
||||
_saved = Path(_out) / '.graphify_root'
|
||||
if _saved.exists():
|
||||
_txt = _saved.read_text(encoding='utf-8').strip()
|
||||
_txt = _saved.read_text(encoding='utf-8-sig').strip()
|
||||
if _txt:
|
||||
_root = Path(_txt)
|
||||
_rebuild_code(_root, changed_paths=changed, force=_force)
|
||||
@@ -214,7 +214,7 @@ try:
|
||||
_out = os.environ.get('GRAPHIFY_OUT', 'graphify-out')
|
||||
_saved = Path(_out) / '.graphify_root'
|
||||
if _saved.exists():
|
||||
_txt = _saved.read_text(encoding='utf-8').strip()
|
||||
_txt = _saved.read_text(encoding='utf-8-sig').strip()
|
||||
if _txt:
|
||||
_root = Path(_txt)
|
||||
_rebuild_code(_root, force=_force)
|
||||
|
||||
+1
-1
@@ -691,7 +691,7 @@ def _resolve_source_path(src: str, graph_path: Path) -> Path | None:
|
||||
out_dir = gp.parent
|
||||
candidates: list[Path] = []
|
||||
try:
|
||||
recorded = (out_dir / ".graphify_root").read_text(encoding="utf-8").strip()
|
||||
recorded = (out_dir / ".graphify_root").read_text(encoding="utf-8-sig").strip()
|
||||
if recorded:
|
||||
candidates.append(Path(recorded))
|
||||
except (OSError, ValueError):
|
||||
|
||||
@@ -116,10 +116,15 @@ if (-not $GRAPHIFY_PYTHON) {
|
||||
$GRAPHIFY_PYTHON = Find-GraphifyPython
|
||||
}
|
||||
|
||||
# Save interpreter path — all subsequent steps read this
|
||||
$GRAPHIFY_PYTHON | Out-File -FilePath graphify-out\.graphify_python -Encoding utf8 -NoNewline
|
||||
# Save interpreter path — all subsequent steps read this.
|
||||
# `Out-File -Encoding utf8` always writes a BOM on Windows PowerShell 5.1 (utf8NoBOM
|
||||
# only exists from PowerShell 6), and that BOM rides into the saved path, so the hook
|
||||
# rebuild fails with WinError 123 (#3028). WriteAllText with an explicit BOM-less
|
||||
# encoding writes the bytes POSIX writes, and adds no trailing newline.
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
[System.IO.File]::WriteAllText((Join-Path $PWD 'graphify-out\.graphify_python'), [string]$GRAPHIFY_PYTHON, $Utf8NoBom)
|
||||
# Save scan root so `graphify update` (no args) knows where to look next time
|
||||
(Resolve-Path INPUT_PATH).Path | Out-File -FilePath graphify-out\.graphify_root -Encoding utf8 -NoNewline
|
||||
[System.IO.File]::WriteAllText((Join-Path $PWD 'graphify-out\.graphify_root'), (Resolve-Path INPUT_PATH).Path, $Utf8NoBom)
|
||||
```
|
||||
|
||||
If the import succeeds, print nothing and move straight to Step 2.
|
||||
|
||||
+1
-1
@@ -369,7 +369,7 @@ class _StoredSourcePaths:
|
||||
root_marker = out / ".graphify_root"
|
||||
if root_marker.exists():
|
||||
try:
|
||||
saved_root = Path(root_marker.read_text(encoding="utf-8").strip())
|
||||
saved_root = Path(root_marker.read_text(encoding="utf-8-sig").strip())
|
||||
if saved_root.is_absolute():
|
||||
# #2603: the marker holds the SCAN root, but stored
|
||||
# source_file values are relative to the BUILD's cwd
|
||||
|
||||
@@ -264,3 +264,27 @@ def test_prune_reextracted_absolute_node_not_deleted(tmp_path):
|
||||
G = build_merge([new_chunk], graph_path, prune_sources=["mod.py"], dedup=False)
|
||||
labels = {d["label"] for _, d in G.nodes(data=True)}
|
||||
assert "gone" in labels, "re-extracted file wrongly pruned across mismatched forms (#2012/#1796)"
|
||||
|
||||
|
||||
def test_graphify_root_marker_with_a_utf8_bom_still_resolves(tmp_path):
|
||||
"""A marker written by Windows PowerShell 5.1 carries a UTF-8 BOM (#3028).
|
||||
|
||||
`Out-File -Encoding utf8` on 5.1 always prepends EF BB BF — there is no
|
||||
BOM-less utf8 there — and `str.strip()` does not remove U+FEFF, so the BOM
|
||||
survived into the recorded path. Worse than an error: `C:\...` is no
|
||||
longer drive-qualified, so `Path.resolve()` treated it as relative and silently
|
||||
joined it onto the cwd. Reading with `utf-8-sig` drops an optional BOM and
|
||||
leaves a BOM-less file untouched, so existing broken checkouts heal in place.
|
||||
"""
|
||||
out = tmp_path / "out"
|
||||
out.mkdir()
|
||||
graph_path = out / "graph.json"
|
||||
real_root = tmp_path / "elsewhere" / "repo"
|
||||
real_root.mkdir(parents=True)
|
||||
(out / ".graphify_root").write_bytes(
|
||||
b"\xef\xbb\xbf" + str(real_root).encode("utf-8")
|
||||
)
|
||||
|
||||
resolved = _infer_merge_root(graph_path)
|
||||
assert resolved == str(real_root.resolve())
|
||||
assert "" not in (resolved or "")
|
||||
|
||||
+24
-1
@@ -320,7 +320,9 @@ def test_rebuild_bodies_read_graphify_root(name, body):
|
||||
# The recovered root is what gets rebuilt, not a hardcoded cwd.
|
||||
assert "_rebuild_code(_root" in body, f"{name} does not pass the recovered root"
|
||||
# Quote-safe inside the shell-double-quoted launcher: single quotes only.
|
||||
assert "read_text(encoding='utf-8')" in body, f"{name} root read is not single-quoted"
|
||||
# `utf-8-sig` rather than `utf-8` so a Windows PowerShell 5.1 BOM cannot ride
|
||||
# into the path (#3028); the codec is a no-op on a BOM-less marker.
|
||||
assert "read_text(encoding='utf-8-sig')" in body, f"{name} root read is not single-quoted"
|
||||
|
||||
|
||||
def test_rebuild_bodies_with_graphify_root_are_valid_python():
|
||||
@@ -1144,3 +1146,24 @@ def test_both_hooks_configured(tmp_path):
|
||||
for name in ("post-commit", "post-checkout"):
|
||||
hook_text = (repo / ".git" / "hooks" / name).read_text()
|
||||
assert 'export GRAPHIFY_VIZ_NODE_LIMIT="${GRAPHIFY_VIZ_NODE_LIMIT:-42}"' in hook_text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,body",
|
||||
[("post-commit", _REBUILD_BODY_COMMIT), ("post-checkout", _REBUILD_BODY_CHECKOUT)],
|
||||
)
|
||||
def test_rebuild_bodies_tolerate_a_bom_in_graphify_root(name, body):
|
||||
"""The rebuild must survive a marker written by Windows PowerShell 5.1 (#3028).
|
||||
|
||||
`Out-File -Encoding utf8` on 5.1 always writes a UTF-8 BOM, so the path the
|
||||
hook reads back begins with U+FEFF. `strip()` does not remove it, and it rode
|
||||
straight into a Windows path API: every post-commit rebuild died with
|
||||
`WinError 123` while `hook install` / `hook status` still reported success.
|
||||
`utf-8-sig` drops an optional BOM and is a no-op on a clean file.
|
||||
"""
|
||||
assert "encoding='utf-8-sig'" in body, (
|
||||
f"{name} rebuild body must read .graphify_root BOM-tolerantly"
|
||||
)
|
||||
assert "encoding='utf-8')" not in body, (
|
||||
f"{name} rebuild body still has a BOM-intolerant read"
|
||||
)
|
||||
|
||||
@@ -1093,3 +1093,25 @@ def test_semantic_cache_calls_pass_prompt_file_for_every_split_host():
|
||||
)
|
||||
# The placeholder is inert unless the body tells the agent what to substitute.
|
||||
assert "SPEC_PATH below is the **absolute** path" in a.content, a.path
|
||||
|
||||
|
||||
def test_windows_skill_writes_marker_files_without_a_bom():
|
||||
"""The Windows bootstrap must not write the sidecar markers with a BOM (#3028).
|
||||
|
||||
`Out-File -Encoding utf8` always emits EF BB BF on Windows PowerShell 5.1 --
|
||||
`utf8NoBOM` only exists from PowerShell 6 -- and `-NoNewline` does nothing about
|
||||
it. The BOM then rode into `.graphify_python` / `.graphify_root`, so every
|
||||
post-commit rebuild died with WinError 123 while `hook install` and `hook status`
|
||||
both still reported success. The readers now decode with `utf-8-sig`; this keeps
|
||||
the writer from producing the BOM in the first place.
|
||||
"""
|
||||
core, _ = _platform_artifacts("windows")
|
||||
for marker in (".graphify_python", ".graphify_root"):
|
||||
assert f"Out-File -FilePath graphify-out\{marker} -Encoding utf8" not in core, (
|
||||
f"the windows render still writes {marker} with a BOM-emitting Out-File"
|
||||
)
|
||||
assert f"WriteAllText((Join-Path $PWD 'graphify-out\{marker}')" in core, (
|
||||
f"{marker} must be written through WriteAllText with a BOM-less encoding"
|
||||
)
|
||||
assert "New-Object System.Text.UTF8Encoding $false" in core, \
|
||||
"the BOM-less encoding object must be constructed in the windows render"
|
||||
|
||||
@@ -116,10 +116,15 @@ if (-not $GRAPHIFY_PYTHON) {
|
||||
$GRAPHIFY_PYTHON = Find-GraphifyPython
|
||||
}
|
||||
|
||||
# Save interpreter path — all subsequent steps read this
|
||||
$GRAPHIFY_PYTHON | Out-File -FilePath graphify-out\.graphify_python -Encoding utf8 -NoNewline
|
||||
# Save interpreter path — all subsequent steps read this.
|
||||
# `Out-File -Encoding utf8` always writes a BOM on Windows PowerShell 5.1 (utf8NoBOM
|
||||
# only exists from PowerShell 6), and that BOM rides into the saved path, so the hook
|
||||
# rebuild fails with WinError 123 (#3028). WriteAllText with an explicit BOM-less
|
||||
# encoding writes the bytes POSIX writes, and adds no trailing newline.
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
[System.IO.File]::WriteAllText((Join-Path $PWD 'graphify-out\.graphify_python'), [string]$GRAPHIFY_PYTHON, $Utf8NoBom)
|
||||
# Save scan root so `graphify update` (no args) knows where to look next time
|
||||
(Resolve-Path INPUT_PATH).Path | Out-File -FilePath graphify-out\.graphify_root -Encoding utf8 -NoNewline
|
||||
[System.IO.File]::WriteAllText((Join-Path $PWD 'graphify-out\.graphify_root'), (Resolve-Path INPUT_PATH).Path, $Utf8NoBom)
|
||||
```
|
||||
|
||||
If the import succeeds, print nothing and move straight to Step 2.
|
||||
|
||||
@@ -50,10 +50,15 @@ if (-not $GRAPHIFY_PYTHON) {
|
||||
$GRAPHIFY_PYTHON = Find-GraphifyPython
|
||||
}
|
||||
|
||||
# Save interpreter path — all subsequent steps read this
|
||||
$GRAPHIFY_PYTHON | Out-File -FilePath graphify-out\.graphify_python -Encoding utf8 -NoNewline
|
||||
# Save interpreter path — all subsequent steps read this.
|
||||
# `Out-File -Encoding utf8` always writes a BOM on Windows PowerShell 5.1 (utf8NoBOM
|
||||
# only exists from PowerShell 6), and that BOM rides into the saved path, so the hook
|
||||
# rebuild fails with WinError 123 (#3028). WriteAllText with an explicit BOM-less
|
||||
# encoding writes the bytes POSIX writes, and adds no trailing newline.
|
||||
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
[System.IO.File]::WriteAllText((Join-Path $PWD 'graphify-out\.graphify_python'), [string]$GRAPHIFY_PYTHON, $Utf8NoBom)
|
||||
# Save scan root so `graphify update` (no args) knows where to look next time
|
||||
(Resolve-Path INPUT_PATH).Path | Out-File -FilePath graphify-out\.graphify_root -Encoding utf8 -NoNewline
|
||||
[System.IO.File]::WriteAllText((Join-Path $PWD 'graphify-out\.graphify_root'), (Resolve-Path INPUT_PATH).Path, $Utf8NoBom)
|
||||
```
|
||||
|
||||
If the import succeeds, print nothing and move straight to Step 2.
|
||||
|
||||
Reference in New Issue
Block a user