- prek hooks: convert all rev = "vX.Y.Z" to commit SHAs with version comments - fly/Dockerfile: digest-pin nginx (1.30.0-alpine), tailscale (v1.94.2), and alloy (v1.16.0); bump from previous tag pins - mise-tasks: pin PEP 723 deps with == (rich 15.0.0, typer 0.25.0, pyyaml 6.0.3, httpx 0.28.1) — PEP 508 doesn't support hashes inline - prek additional_dependencies: pin ansible-lint==26.4.0, ansible-core==2.20.5 - taplo-lint: pass --no-schema (upstream catalog format changed and taplo v0.9.3 can't parse it; we don't validate against TOML schemas) - docs/update-tooling-dependencies: document SHA-pin convention, digest-pin lookup via docker buildx imagetools, and prek clean before re-verifying (cache can grow to several GiB) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
112 lines
3.5 KiB
Text
Executable file
112 lines
3.5 KiB
Text
Executable file
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.12"
|
|
# dependencies = ["httpx==0.28.1", "rich==15.0.0", "typer==0.25.0"]
|
|
# ///
|
|
#MISE description="List unresolved comments on a PR"
|
|
#USAGE arg "<pr_number>" help="Pull request number"
|
|
"""Fetch and display unresolved review comments on a pull request.
|
|
|
|
This script queries the Forge (Gitea) API to find all review comments that
|
|
have not been resolved on a given PR. A comment is considered unresolved
|
|
if its 'resolver' field is null.
|
|
|
|
Usage: mise run pr-comments <pr_number>
|
|
"""
|
|
|
|
from typing import Annotated
|
|
|
|
import httpx
|
|
import typer
|
|
from rich.console import Console
|
|
from rich.text import Text
|
|
|
|
FORGE_API_BASE = "https://forge.eblu.me/api/v1"
|
|
REPO_OWNER = "eblume"
|
|
REPO_NAME = "blumeops"
|
|
|
|
|
|
def get_reviews(client: httpx.Client, pr_number: int) -> list[dict]:
|
|
"""Get all reviews for a pull request."""
|
|
response = client.get(
|
|
f"{FORGE_API_BASE}/repos/{REPO_OWNER}/{REPO_NAME}/pulls/{pr_number}/reviews"
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
|
|
def get_review_comments(client: httpx.Client, pr_number: int, review_id: int) -> list[dict]:
|
|
"""Get all comments for a specific review."""
|
|
response = client.get(
|
|
f"{FORGE_API_BASE}/repos/{REPO_OWNER}/{REPO_NAME}/pulls/{pr_number}/reviews/{review_id}/comments"
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
|
|
app = typer.Typer()
|
|
|
|
|
|
@app.command()
|
|
def main(
|
|
pr_number: Annotated[int, typer.Argument(help="Pull request number")],
|
|
) -> None:
|
|
console = Console()
|
|
|
|
unresolved_comments: list[tuple[dict, dict]] = [] # (review, comment) pairs
|
|
|
|
with httpx.Client() as client:
|
|
# Get all reviews
|
|
try:
|
|
reviews = get_reviews(client, pr_number)
|
|
except httpx.HTTPStatusError as e:
|
|
if e.response.status_code == 404:
|
|
console.print(f"[red]Error:[/red] PR #{pr_number} not found")
|
|
else:
|
|
console.print(f"[red]Error:[/red] API request failed: {e}")
|
|
raise SystemExit(1)
|
|
|
|
# For each review, get comments and filter to unresolved
|
|
for review in reviews:
|
|
try:
|
|
comments = get_review_comments(client, pr_number, review["id"])
|
|
except httpx.HTTPStatusError:
|
|
continue # Skip reviews we can't fetch comments for
|
|
|
|
for comment in comments:
|
|
if comment.get("resolver") is None:
|
|
unresolved_comments.append((review, comment))
|
|
|
|
if not unresolved_comments:
|
|
console.print(f"[green]No unresolved comments on PR #{pr_number}[/green]")
|
|
raise SystemExit(0)
|
|
|
|
# Display unresolved comments
|
|
console.print(f"[bold]Unresolved Comments on PR #{pr_number}[/bold] ({len(unresolved_comments)} comments)")
|
|
console.print("=" * 60)
|
|
console.print()
|
|
|
|
for review, comment in unresolved_comments:
|
|
# Header with file path and reviewer
|
|
header = Text()
|
|
path = comment.get("path", "unknown file")
|
|
reviewer = review.get("user", {}).get("login", "unknown")
|
|
header.append(f"{path}", style="bold cyan")
|
|
header.append(f" (by {reviewer})")
|
|
console.print(header)
|
|
|
|
# Comment body
|
|
body = comment.get("body", "").strip()
|
|
for line in body.split("\n"):
|
|
console.print(f" {line}")
|
|
|
|
# Link to comment
|
|
html_url = comment.get("html_url", "")
|
|
if html_url:
|
|
console.print(f" [dim]{html_url}[/dim]")
|
|
|
|
console.print()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app()
|