GRM-54: Integrate tea Gitea CLI for API interactions (#68)

This commit is contained in:
2026-06-22 07:04:58 +00:00
parent b8ab4f854b
commit 28e61aa166
17 changed files with 1319 additions and 265 deletions
+57 -17
View File
@@ -2,7 +2,9 @@
"""Create a Gitea issue when a CI workflow fails.
Used by the release and publish workflows to alert on failures that would
otherwise go unnoticed in the Actions tab.
otherwise go unnoticed in the Actions tab. Uses the ``tea`` Gitea CLI
for issue creation when available, falling back to ``GiteaClient`` (direct
HTTP API) when tea is not installed.
Usage:
REPO_TOKEN=<token> python3 scripts/notify_failure.py \
@@ -14,7 +16,9 @@ Usage:
from __future__ import annotations
import contextlib
import os
import shutil
import click
from dotenv import load_dotenv # pyright: ignore[reportMissingImports,reportUnknownVariableType]
@@ -27,6 +31,48 @@ from gitea_runner_manager.i18n import _
load_dotenv(override=True)
def _create_issue_via_tea(repo: str, title: str, body: str) -> int | None:
"""Try creating issue via tea CLI. Returns issue index or None on failure."""
if shutil.which("tea") is None:
return None
from scripts.gitea_cli import TeaCLI, TeaCLIError
tea = TeaCLI(repo=repo)
try:
# Check if "bug" label exists
labels: list[str] = []
try:
existing_labels = tea.list_labels(repo)
if any(label.get("name") == "bug" for label in existing_labels):
labels = ["bug"]
except TeaCLIError:
pass
issue = tea.create_issue(repo, title=title, body=body, labels=labels if labels else None)
if labels:
with contextlib.suppress(TeaCLIError):
tea.add_label(repo, issue["index"], labels)
return int(issue.get("index", 0))
except TeaCLIError:
return None
def _create_issue_via_client(repo: str, title: str, body: str) -> int:
"""Create issue via GiteaClient (direct HTTP API). Returns issue ID."""
token = os.environ.get("REPO_TOKEN", "")
owner, repo_name = repo.split("/")
client = GiteaClient(GITEA_API_URL, token, owner, repo_name)
# Look up label IDs by name (Gitea API expects integer IDs, not strings)
label_ids: list[int] = []
for label in client.list_labels():
if label.get("name") == "bug":
label_ids.append(int(label["id"]))
break
issue = client.create_issue(title=title, body=body, labels=label_ids if label_ids else None)
return int(issue.get("id", 0))
@click.command()
@click.option("--repo", required=True, help="Repository in owner/name format.")
@click.option("--run-id", required=True, help="CI run ID.")
@@ -37,9 +83,6 @@ def main(repo: str, run_id: str, workflow: str, commit: str) -> None:
if not token:
raise click.ClickException(_("ERROR: REPO_TOKEN is not set."))
owner, repo_name = repo.split("/")
client = GiteaClient(GITEA_API_URL, token, owner, repo_name)
title = f"[CI] {workflow} workflow failed (run #{run_id})"
body = (
f"The **{workflow}** workflow failed.\n\n"
@@ -50,23 +93,20 @@ def main(repo: str, run_id: str, workflow: str, commit: str) -> None:
f"Please investigate and fix the issue."
)
try:
# Look up label IDs by name (Gitea API expects integer IDs, not strings)
label_ids: list[int] = []
for label in client.list_labels():
if label.get("name") == "bug":
label_ids.append(int(label["id"]))
break
issue = client.create_issue(title=title, body=body, labels=label_ids if label_ids else None)
except APIError as e:
raise click.ClickException(
_("Failed to create issue: HTTP {status}{message}", status=e.status, message=e.message)
) from None
# Try tea CLI first, fall back to GiteaClient
issue_id = _create_issue_via_tea(repo, title, body)
if issue_id is None:
try:
issue_id = _create_issue_via_client(repo, title, body)
except APIError as e:
raise click.ClickException(
_("Failed to create issue: HTTP {status}{message}", status=e.status, message=e.message)
) from None
click.echo(
_(
"Created issue #{issue_id}: {title}",
issue_id=issue.get("id", "?"),
issue_id=issue_id or "?",
title=title,
)
)