"""Standard issue body template for Forgejo issues created via Pipeline.""" from __future__ import annotations ISSUE_TEMPLATE = """\ ## Summary Briefly describe the issue in 1 to 3 sentences. ## Problem Explain what is wrong, missing, confusing, or broken. ## Affected area Example: - UI page: - Backend service: - API: - Database: - Auth: - CI/CD: - Docs: ## Affected files Known or suspected files: - `path/to/file` - `path/to/other-file` ## Affected routes or endpoints Known or suspected routes: - `GET /example` - `POST /api/example` ## Steps to reproduce 1. 2. 3. ## Expected behavior Describe what should happen. ## Actual behavior Describe what actually happens. ## Error output, logs, or screenshots Paste relevant logs only. Redact secrets. ```text Paste logs here ```\ """ def _section(heading: str, body: str) -> str: return f"## {heading}\n{body.strip()}" def _code_block(content: str, lang: str = "text") -> str: return f"```{lang}\n{content.strip()}\n```" def render_error_report( *, summary: str, problem: str, affected_area: str | None = None, affected_files: list[str] | None = None, affected_routes: list[str] | None = None, actual_behavior: str | None = None, stack_trace: str | None = None, reporter: str | None = None, ) -> str: """Render the standard template from structured error data.""" parts: list[str] = [] if reporter: parts.append(f"_Reported by: {reporter}_\n") parts.append(_section("Summary", summary)) parts.append(_section("Problem", problem)) area_body = affected_area.strip() if affected_area else "_Not identified._" parts.append(_section("Affected area", area_body)) if affected_files: files_body = "\n".join(f"- `{f}`" for f in affected_files) else: files_body = "_Not identified._" parts.append(_section("Affected files", files_body)) if affected_routes: routes_body = "\n".join(f"- `{r}`" for r in affected_routes) else: routes_body = "_Not identified._" parts.append(_section("Affected routes or endpoints", routes_body)) parts.append(_section("Steps to reproduce", "_Not provided._")) parts.append(_section("Expected behavior", "_Not provided._")) actual = actual_behavior or problem parts.append(_section("Actual behavior", actual)) if stack_trace: log_body = _code_block(stack_trace) else: log_body = "_No output provided._" parts.append(_section("Error output, logs, or screenshots", log_body)) return "\n\n".join(parts)