0bc734c020
Applies `ruff format` to the accumulated formatting debt on this branch. Formatting-only — no behavioral changes. Required for CI/lint's format gate (`nox -s format -- --check`), which the branch was failing on 288 tracked files that drifted from ruff's canonical style. In-progress WIP files are intentionally excluded so this commit stays a clean formatting-only diff. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
191 lines
7.6 KiB
Python
191 lines
7.6 KiB
Python
"""Shared per-fingerprint operator-status comment substrate.
|
|
|
|
Houses the dedup substrate every dispatcher uses to post operator-
|
|
visibility comments on PR / issue timelines without re-implementing
|
|
the fingerprinting logic in two places. The reviewer dispatcher
|
|
(:mod:`_review_post`) and the implementer dispatcher
|
|
(``tools/dispatch_implementer.py`` via :mod:`_review_post`) both
|
|
key off this module; their public posters (``post_operator_status_comment``,
|
|
``post_implementer_status_comment``) are thin per-pipeline wrappers
|
|
that supply the marker family + body template.
|
|
|
|
Public surface:
|
|
|
|
- ``_sanitize_status_detail`` — normalise + cap free-form reason
|
|
strings before they enter the fingerprint or the body.
|
|
- ``_compute_status_fingerprint`` — sha256-derived 12-hex-char
|
|
identifier for ``(action, sanitised(reason))``.
|
|
- ``_has_existing_operator_status`` — generic "marker prefix +
|
|
fingerprint already on this PR" predicate (parameterised on the
|
|
pipeline's marker prefix).
|
|
- ``OPERATOR_STATUS_MARKER_PREFIX`` /
|
|
``IMPLEMENTER_STATUS_MARKER_PREFIX`` (and their suffix
|
|
counterparts) — the on-wire marker namespaces. Two-key namespacing
|
|
protects each pipeline's idempotency from cross-talk if a future
|
|
audit enumerates "every status comment one dispatcher kind posted
|
|
on a given PR".
|
|
- ``operator_status_marker`` / ``implementer_status_marker`` —
|
|
public marker constructors so external callers don't have to
|
|
string-concat the prefix + fingerprint themselves.
|
|
|
|
The module is import-safe and self-contained; no cycle risk against
|
|
:mod:`_review_post` or any dispatcher driver. Extracting it here
|
|
prevents :mod:`_review_post` from drifting into a junk drawer of
|
|
"reviewer-specific posters AND a shared dedup substrate AND the
|
|
implementer's marker family" — the previous shape paid the cohesion
|
|
price for backwards compatibility.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
from typing import Any
|
|
|
|
# ─── Reviewer-side marker family ────────────────────────────────────────────
|
|
#
|
|
# Format: ``<!-- pr-review-dispatcher:operator-status:fp=ABCDEF123456 -->``
|
|
#
|
|
# Stable wire string: lands in PR comment bodies; mutating it would
|
|
# break fingerprint compatibility with previously-posted comments.
|
|
_OPERATOR_STATUS_MARKER_PREFIX = "<!-- pr-review-dispatcher:operator-status:fp="
|
|
_OPERATOR_STATUS_MARKER_SUFFIX = " -->"
|
|
OPERATOR_STATUS_MARKER_PREFIX = _OPERATOR_STATUS_MARKER_PREFIX
|
|
OPERATOR_STATUS_MARKER_SUFFIX = _OPERATOR_STATUS_MARKER_SUFFIX
|
|
|
|
|
|
def _operator_status_marker(fingerprint: str) -> str:
|
|
"""Build the full HTML-comment marker for ``fingerprint``."""
|
|
return (
|
|
f"{_OPERATOR_STATUS_MARKER_PREFIX}{fingerprint}{_OPERATOR_STATUS_MARKER_SUFFIX}"
|
|
)
|
|
|
|
|
|
def operator_status_marker(fingerprint: str) -> str:
|
|
"""Public form of :func:`_operator_status_marker`. Tests and any
|
|
external caller that needs to construct or recognise a marker
|
|
should import this rather than the underscore-prefixed helper.
|
|
"""
|
|
return _operator_status_marker(fingerprint)
|
|
|
|
|
|
# ─── Implementer-side marker family (Phase 5b) ──────────────────────────────
|
|
#
|
|
# Distinct prefix from the reviewer namespace so the dedup logic
|
|
# keyed off either prefix cannot mis-match a comment from the other
|
|
# pipeline.
|
|
_IMPLEMENTER_STATUS_MARKER_PREFIX = "<!-- pr-implementer-dispatcher:operator-status:fp="
|
|
_IMPLEMENTER_STATUS_MARKER_SUFFIX = " -->"
|
|
IMPLEMENTER_STATUS_MARKER_PREFIX = _IMPLEMENTER_STATUS_MARKER_PREFIX
|
|
IMPLEMENTER_STATUS_MARKER_SUFFIX = _IMPLEMENTER_STATUS_MARKER_SUFFIX
|
|
|
|
|
|
def implementer_status_marker(fingerprint: str) -> str:
|
|
"""Public form of the implementer-side fingerprint marker. Mirrors
|
|
:func:`operator_status_marker` on the reviewer side.
|
|
"""
|
|
return (
|
|
f"{_IMPLEMENTER_STATUS_MARKER_PREFIX}"
|
|
f"{fingerprint}"
|
|
f"{_IMPLEMENTER_STATUS_MARKER_SUFFIX}"
|
|
)
|
|
|
|
|
|
# ─── Detail sanitisation + fingerprinting ───────────────────────────────────
|
|
|
|
|
|
# Cap on the action_reason / detail string we surface in the
|
|
# operator-status body. Large reasons (a verbose Forgejo error blob,
|
|
# a 5 KB Python traceback excerpt) hurt readability and inflate the
|
|
# fingerprint surface area.
|
|
_STATUS_DETAIL_MAX_CHARS = 600
|
|
|
|
|
|
def _sanitize_status_detail(detail: str) -> str:
|
|
"""Normalise + cap the action_reason string for operator display.
|
|
|
|
- Collapse runs of whitespace (newlines, tabs) into single spaces
|
|
so a multi-line traceback doesn't span the operator comment.
|
|
- Strip control characters (other than tab/newline) for log
|
|
hygiene.
|
|
- Cap at :data:`_STATUS_DETAIL_MAX_CHARS` characters with an
|
|
ellipsis. Without the cap a runaway worker error message
|
|
could blow past the comment-body limit.
|
|
|
|
Used both for fingerprint computation (so stylistic whitespace
|
|
differences don't fork a new fingerprint) and for the rendered
|
|
body. Returns the empty string when ``detail`` is empty.
|
|
"""
|
|
if not detail:
|
|
return ""
|
|
cleaned = "".join(
|
|
ch
|
|
for ch in detail
|
|
if ch == "\n" or ch == "\t" or (0x20 <= ord(ch) < 0x7F) or ord(ch) >= 0xA0
|
|
)
|
|
cleaned = re.sub(r"\s+", " ", cleaned).strip()
|
|
if len(cleaned) > _STATUS_DETAIL_MAX_CHARS:
|
|
cleaned = cleaned[: _STATUS_DETAIL_MAX_CHARS - 1] + "\u2026"
|
|
return cleaned
|
|
|
|
|
|
def _compute_status_fingerprint(action: str, detail: str) -> str:
|
|
"""Compute the 12-hex-char idempotency fingerprint for a status
|
|
comment.
|
|
|
|
Built from ``sha256(f"{action}|{sanitised_detail}")[:12]`` so two
|
|
cycles producing the same outcome surface the same fingerprint
|
|
and skip the duplicate post; distinct outcomes get distinct
|
|
fingerprints and distinct posts. The ``action`` argument is the
|
|
pipeline-specific verb (``failed`` / ``stale`` / ``rebase-failed``
|
|
/ ``timeout`` / etc.); the ``detail`` argument is whatever
|
|
free-form reason string the dispatcher captured.
|
|
"""
|
|
canonical = f"{action}|{_sanitize_status_detail(detail)}"
|
|
return hashlib.sha256(canonical.encode("utf-8", errors="replace")).hexdigest()[:12]
|
|
|
|
|
|
def _has_existing_operator_status(
|
|
pr_comments: list[dict[str, Any]] | None,
|
|
fingerprint: str,
|
|
*,
|
|
marker_prefix: str,
|
|
) -> bool:
|
|
"""Return True iff any prefetched PR comment carries the marker
|
|
for ``fingerprint``. Used to short-circuit the operator-status
|
|
POST so identical-outcome cycles don't double-post.
|
|
|
|
``marker_prefix`` is required (no default) so that a third
|
|
pipeline cannot accidentally key against the reviewer's namespace.
|
|
Reviewer callers pass :data:`_OPERATOR_STATUS_MARKER_PREFIX`;
|
|
implementer callers pass :data:`_IMPLEMENTER_STATUS_MARKER_PREFIX`;
|
|
each pipeline's idempotency stays scoped to its own namespace.
|
|
|
|
Tolerates malformed entries: any comment whose ``body`` is not a
|
|
string is skipped silently.
|
|
"""
|
|
if not pr_comments:
|
|
return False
|
|
needle = f"{marker_prefix}{fingerprint}"
|
|
for comment in pr_comments:
|
|
if not isinstance(comment, dict):
|
|
continue
|
|
body = comment.get("body")
|
|
if isinstance(body, str) and needle in body:
|
|
return True
|
|
return False
|
|
|
|
|
|
__all__ = (
|
|
"IMPLEMENTER_STATUS_MARKER_PREFIX",
|
|
"IMPLEMENTER_STATUS_MARKER_SUFFIX",
|
|
"OPERATOR_STATUS_MARKER_PREFIX",
|
|
"OPERATOR_STATUS_MARKER_SUFFIX",
|
|
"_compute_status_fingerprint",
|
|
"_has_existing_operator_status",
|
|
"_operator_status_marker",
|
|
"_sanitize_status_detail",
|
|
"implementer_status_marker",
|
|
"operator_status_marker",
|
|
)
|