fix(review): language-tagged suggestion fence for Gitea syntax highlighting
Gitea 1.26.x has no GitHub-style 'Apply suggestion' button — a ```suggestion fence is just an unknown-language code block, so chroma does not highlight it and there is no apply control. Switch inline_comment_body to wrap the suggested fix in a fence tagged with the file's language (new _lang_for_path helper, .java→java, .ts→typescript, .py→python, ...), so Gitea syntax-highlights the code. No capability lost (there was never an apply button on this Gitea version). Correct the docstrings/skills/README that wrongly claimed an apply-button was rendered. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -37,13 +37,14 @@ ai_review.review_pr() (same core the CI-step uses)
|
||||
7. post review → POST .../pulls/{i}/reviews (event: COMMENT) as pragent-bot
|
||||
- prose summary → review body intro
|
||||
- anchored findings → inline line comments, body wraps `suggestion` in a
|
||||
```suggestion fence (Gitea renders an apply-button); reference → 📎 ref link
|
||||
language-tagged fenced code block (Gitea syntax-highlights it; Gitea
|
||||
1.26.x has no apply-suggestion button); reference → 📎 ref link
|
||||
- unanchored findings → summary-body bullets
|
||||
- summary body carries the <!-- pragent:sha=... --> marker for dedupe
|
||||
```
|
||||
|
||||
Fail-open. No duplicate per commit (dedupe). Inline comments + apply-able
|
||||
suggestions where the line anchors cleanly. Repo-local focus via
|
||||
Fail-open. No duplicate per commit (dedupe). Inline comments + syntax-highlighted
|
||||
suggested-fix blocks where the line anchors cleanly. Repo-local focus via
|
||||
`.pr-review.json`. Prior reviews fed as context so re-pushes synthesize instead
|
||||
of repeating (light version of framework §6.1).
|
||||
|
||||
|
||||
+47
-11
@@ -5,8 +5,9 @@ Runs as a Gitea Actions step OR is called by the central webhook server
|
||||
(`webhook_server.py`). Fetches a PR diff, asks glm-5.2:cloud (via the on-network
|
||||
headroom proxy, Anthropic /v1/messages format) to review it, and posts the
|
||||
findings back as `pragent-bot` — as a **review summary** plus **inline line
|
||||
comments** with apply-able ```suggestion blocks where the model could produce
|
||||
them and the line anchors cleanly to the post-change file.
|
||||
comments** with a fenced suggested-fix block (tagged with the file's language so
|
||||
Gitea syntax-highlights it) where the model could produce one and the line
|
||||
anchors cleanly to the post-change file.
|
||||
|
||||
Features (pilot v2):
|
||||
- **Dedupe / persistence:** Gitea itself is the source of truth. Before
|
||||
@@ -22,9 +23,12 @@ Features (pilot v2):
|
||||
findings with `path`/`line`. We parse the diff hunks to learn which
|
||||
`(path, new_line)` pairs are valid post-change anchors and post each
|
||||
anchored finding as a positional review comment; the `suggestion` field, if
|
||||
non-empty, is wrapped in a ```suggestion fence so Gitea renders an
|
||||
apply-button. Findings that don't anchor (bad line, unchanged file, etc.)
|
||||
are folded into the summary body as plain bullets.
|
||||
non-empty, is wrapped in a fenced code block tagged with the file's language
|
||||
(via `_lang_for_path`) so Gitea syntax-highlights it. Gitea 1.26.x has no
|
||||
GitHub-style "Apply suggestion" button, so a language-tagged block is used
|
||||
for highlighting instead of a ```suggestion fence. Findings that don't
|
||||
anchor (bad line, unchanged file, etc.) are folded into the summary body as
|
||||
plain bullets.
|
||||
|
||||
Fail-open by design: any error becomes a short "review failed" review comment,
|
||||
and review_pr never raises. Stdlib only — no pip install.
|
||||
@@ -431,19 +435,51 @@ def split_findings(findings: list[dict], anchors: dict[str, set[int]]) -> tuple[
|
||||
return anchored, unanchored
|
||||
|
||||
|
||||
def _lang_for_path(path: str) -> str:
|
||||
"""Map a file extension to a chroma language tag for fenced code blocks.
|
||||
|
||||
Used so the suggested-fix block is syntax-highlighted in Gitea. Gitea 1.26.x
|
||||
has no GitHub-style "Apply suggestion" button (the ```suggestion fence is
|
||||
just an unknown-language code block → plain monospace, no apply), so we tag
|
||||
the block with the file's real language for highlighting instead.
|
||||
"""
|
||||
ext = path.rsplit(".", 1)[-1].lower() if "." in path else ""
|
||||
return {
|
||||
"java": "java", "kt": "kotlin", "scala": "scala", "groovy": "groovy",
|
||||
"ts": "typescript", "tsx": "tsx", "js": "javascript", "jsx": "jsx",
|
||||
"mjs": "javascript", "cjs": "javascript",
|
||||
"py": "python", "pyi": "python",
|
||||
"go": "go", "rs": "rust", "rb": "ruby", "php": "php",
|
||||
"c": "c", "h": "c", "cpp": "cpp", "cc": "cpp", "hpp": "cpp",
|
||||
"cs": "csharp", "swift": "swift", "m": "objc",
|
||||
"sh": "bash", "bash": "bash", "zsh": "bash",
|
||||
"yml": "yaml", "yaml": "yaml", "json": "json", "jsonc": "json",
|
||||
"toml": "toml", "ini": "ini", "cfg": "ini",
|
||||
"html": "html", "htm": "html", "css": "css", "scss": "scss",
|
||||
"xml": "xml", "svg": "xml", "sql": "sql",
|
||||
"md": "markdown", "dockerfile": "dockerfile",
|
||||
}.get(ext, "")
|
||||
|
||||
|
||||
def inline_comment_body(f: dict) -> str:
|
||||
"""Render one finding as a positional review-comment body.
|
||||
|
||||
Includes a ```suggestion fence only if the model produced non-empty
|
||||
replacement code. Gitea renders that as an apply-able suggestion. Appends a
|
||||
`📎 ref:` link when the finding carries a `reference` URL.
|
||||
Includes a fenced suggested-fix block only if the model produced non-empty
|
||||
replacement code. The fence is tagged with the file's language (via
|
||||
`_lang_for_path`) so Gitea syntax-highlights it — Gitea 1.26.x has no
|
||||
GitHub-style "Apply suggestion" button (```suggestion is just an
|
||||
unknown-language block there → plain monospace), so a language-tagged block
|
||||
is strictly more readable and loses nothing. Appends a `📎 ref:` link when
|
||||
the finding carries a `reference` URL.
|
||||
"""
|
||||
sev = f["severity"].upper()
|
||||
body = f"**[{sev}]** {f['problem']}"
|
||||
if f["fix"]:
|
||||
body += f"\n\nFix: {f['fix']}"
|
||||
if f["suggestion"]:
|
||||
body += f"\n\n```suggestion\n{f['suggestion']}\n```"
|
||||
lang = _lang_for_path(f.get("path", ""))
|
||||
fence = f"```{lang}" if lang else "```"
|
||||
body += f"\n\n{fence}\n{f['suggestion']}\n```"
|
||||
ref = f.get("reference", "")
|
||||
if ref:
|
||||
body += f"\n\n📎 ref: {ref}"
|
||||
@@ -632,8 +668,8 @@ def post_inline_review(
|
||||
honored here and silently leave the comment unpositioned (Gitea then renders
|
||||
a file-level comment on EVERY diff line of the file, which is the flood we
|
||||
hit). `f["line"]` is already a validated post-change (RIGHT-side) line from
|
||||
`split_findings`, so it maps directly to `new_position`. The body carries the
|
||||
```suggestion fence when the model produced replacement code.
|
||||
`split_findings`, so it maps directly to `new_position`. The body carries a
|
||||
language-tagged fenced code block when the model produced replacement code.
|
||||
"""
|
||||
comments = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user