fix(tools): decode pr_overlap git output with errors=replace

One PR diff containing a byte that is not valid UTF-8 made git() and
merge() raise, which aborted the hourly run and dropped the overlap
comment on every PR. Both subprocess calls now pass errors="replace",
matching check_conventions.py. A test commits a Latin-1 byte and checks
the export is still found.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
This commit is contained in:
GalaxyGamingFR 2026-10-11 02:38:52 -07:00
parent 9a1f6f44ac
commit 15f34d3043
2 changed files with 22 additions and 2 deletions

View File

@ -16,7 +16,7 @@ MARKER = "<!-- pr-overlap -->"
def git(*args):
return subprocess.run(["git", *args], capture_output=True, text=True, encoding="utf-8", check=True).stdout
return subprocess.run(["git", *args], capture_output=True, text=True, encoding="utf-8", errors="replace", check=True).stdout
def gh(*args):
@ -49,7 +49,7 @@ def fetch(base, prs):
def merge(*args):
result = subprocess.run(["git", "merge-tree", "--write-tree", "--name-only", "--no-messages", *args],
capture_output=True, text=True, encoding="utf-8")
capture_output=True, text=True, encoding="utf-8", errors="replace")
lines = result.stdout.splitlines()
if result.returncode not in (0, 1) or not lines:
raise RuntimeError(f"git merge-tree {' '.join(args)} failed: {result.stderr.strip()}")

View File

@ -1,5 +1,6 @@
from contextlib import contextmanager
import json
import os
from pathlib import Path
import subprocess
import sys
@ -74,6 +75,25 @@ class EncodingTests(unittest.TestCase):
with self.assertRaisesRegex(RuntimeError, UNICODE):
pr_overlap.merge("main", "topic")
def test_git_diff_survives_invalid_utf8(self):
with TemporaryDirectory() as directory:
repo = Path(directory)
source = repo / "core" / "libs" / "prx" / "x" / "a.cpp"
source.parent.mkdir(parents=True)
ident = ["-c", "user.name=t", "-c", "user.email=t@example.test"]
for args in (["init", "-q"], ["commit", "-q", "--allow-empty", "-m", "base"]):
subprocess.run(["git", *ident, *args], cwd=repo, check=True, capture_output=True)
source.write_bytes(b"int APS5_VABI f() {} // caf\xe9\n")
for args in (["add", "."], ["commit", "-q", "-m", "add"]):
subprocess.run(["git", *ident, *args], cwd=repo, check=True, capture_output=True)
old = os.getcwd()
os.chdir(repo)
try:
diff = pr_overlap.git("diff", "-U0", "--no-color", "HEAD~1", "HEAD", "--", "core/libs/prx/*.cpp")
finally:
os.chdir(old)
self.assertEqual(pr_overlap.exports(diff), {"f"})
def test_report_is_written_as_utf8(self):
with TemporaryDirectory() as directory:
path = Path(directory) / "1.md"