feat(python/robomp): enforced commit message repair failures

- Make commit message repair mandatory once broken escapes are detected.
- Implement a failure handler that halts execution and provides manual correction instructions when git operations fail during the rewrite process.
- Ensure that partial states are avoided by refusing the push instead of allowing it with uncorrected messages.
This commit is contained in:
can1357
2026-07-02 22:58:03 +02:00
parent 4d80a7bb19
commit b3c7646b4c
+23 -12
View File
@@ -668,11 +668,24 @@ def _repair_commit_message_escapes(bindings: ToolBindings, args: Mapping[str, An
Rebuilds ``origin/<base>..HEAD`` with ``git commit-tree``, preserving
every tree, parent topology, identity, and date — only messages change.
Safe against already-pushed commits: the push transport uses
``--force-with-lease``. Best effort: any git failure leaves the branch
untouched (the push proceeds with the ugly message rather than being
blocked on cosmetics), and the branch ref only moves via a compare-and-
swap ``update-ref`` at the very end.
``--force-with-lease``. Once a broken message is detected the repair is
mandatory — a git failure mid-rewrite refuses the push (the branch ref
itself only ever moves via the compare-and-swap ``update-ref`` at the
very end, so a refusal never leaves partial state).
"""
def fail(step: str, proc: subprocess.CompletedProcess[str]) -> NoReturn:
err = (proc.stderr or proc.stdout).strip() or f"exit {proc.returncode}"
msg = (
f"refusing to push: commit messages contain literal `\\n` escapes and the "
f"automatic repair failed at `{step}`: {err}\n"
"Reword the affected commits yourself (`git rebase -i origin/"
+ bindings.repo.default_branch
+ "`, real newlines via `git commit -F <file>` or multiple `-m` flags) and retry."
)
_audit(bindings, tool_name, args, error=msg)
_raise_command(msg)
base = bindings.repo.default_branch
rev_list = _run_repo_command(bindings, ["git", "rev-list", "--reverse", f"origin/{base}..HEAD"])
if rev_list.returncode != 0:
@@ -685,6 +698,8 @@ def _repair_commit_message_escapes(bindings: ToolBindings, args: Mapping[str, An
for sha in shas:
show = _run_repo_command(bindings, ["git", "log", "-1", "--format=%B", sha])
if show.returncode != 0:
if repaired:
fail("git log", show)
return
message = show.stdout
fixed = _repair_message_escapes(message)
@@ -703,10 +718,10 @@ def _repair_commit_message_escapes(bindings: ToolBindings, args: Mapping[str, An
["git", "log", "-1", "--format=%T%x1f%P%x1f%an%x1f%ae%x1f%aI%x1f%cn%x1f%ce%x1f%cI", sha],
)
if meta.returncode != 0:
return
fail("git log", meta)
fields = meta.stdout.strip("\n").split("\x1f")
if len(fields) != 8:
return
fail("git log", meta)
tree, parents_raw, a_name, a_email, a_date, c_name, c_email, c_date = fields
parents_old = parents_raw.split()
parents_new = [rewritten.get(p, p) for p in parents_old]
@@ -730,7 +745,7 @@ def _repair_commit_message_escapes(bindings: ToolBindings, args: Mapping[str, An
},
)
if made.returncode != 0 or not made.stdout.strip():
return
fail("git commit-tree", made)
rewritten[sha] = made.stdout.strip()
old_head, new_head = shas[-1], rewritten[shas[-1]]
@@ -739,11 +754,7 @@ def _repair_commit_message_escapes(bindings: ToolBindings, args: Mapping[str, An
["git", "update-ref", "-m", "robomp: repaired commit message escapes", "HEAD", new_head, old_head],
)
if update.returncode != 0:
log.warning(
"commit message escape repair failed at update-ref",
extra={"issue": bindings.issue_key, "err": (update.stderr or update.stdout).strip()},
)
return
fail("git update-ref", update)
_audit(bindings, tool_name, args, result={"repaired_commit_messages": [sha[:12] for sha in repaired]})
log.info(
"repaired commit message escapes",