Files
smy-skills/gitea-tea-skill/scripts/tea_helper.py
2026-06-12 17:48:53 +08:00

191 lines
6.2 KiB
Python

#!/usr/bin/env python3
"""
tea_helper.py — Python utility for Gitea tea CLI automation.
Usage:
python tea_helper.py issues [--state open|closed|all] [--repo owner/repo]
python tea_helper.py prs [--state open|closed|all] [--repo owner/repo]
python tea_helper.py releases [--repo owner/repo]
python tea_helper.py close-issue <index> [--repo owner/repo]
python tea_helper.py close-pr <index> [--repo owner/repo]
python tea_helper.py create-issue --title TITLE [--desc DESC] [--labels L1,L2]
python tea_helper.py triage [--repo owner/repo]
"""
import argparse
import json
import subprocess
import sys
from typing import Any
# ---------------------------------------------------------------------------
# Core helpers
# ---------------------------------------------------------------------------
def run_tea(*args: str, capture: bool = True) -> subprocess.CompletedProcess:
cmd = ["tea", *args]
result = subprocess.run(cmd, capture_output=capture, text=True)
if result.returncode != 0 and capture:
print(f"[tea error] {result.stderr.strip()}", file=sys.stderr)
return result
def tea_json(*args: str) -> Any:
result = run_tea(*args, "-o", "json")
if result.returncode != 0:
return []
try:
return json.loads(result.stdout)
except json.JSONDecodeError:
return []
def repo_flags(repo: str | None) -> list[str]:
return ["-r", repo] if repo else []
# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------
def cmd_issues(args: argparse.Namespace) -> None:
flags = ["issues", "list", "--state", args.state, *repo_flags(args.repo)]
issues = tea_json(*flags)
if not issues:
print("No issues found.")
return
print(f"{'#':<6} {'State':<8} {'Title'}")
print("-" * 60)
for i in issues:
print(f"#{i.get('number', i.get('index','?')):<5} {i.get('state',''):<8} {i.get('title','')}")
def cmd_prs(args: argparse.Namespace) -> None:
flags = ["pulls", "list", "--state", args.state,
"--fields", "index,title,state,author,milestone,updated,labels,ci",
*repo_flags(args.repo)]
prs = tea_json(*flags)
if not prs:
print("No pull requests found.")
return
print(f"{'#':<6} {'State':<8} {'Title'}")
print("-" * 60)
for pr in prs:
print(f"#{pr.get('number', pr.get('index','?')):<5} {pr.get('state',''):<8} {pr.get('title','')}")
def cmd_releases(args: argparse.Namespace) -> None:
flags = ["releases", "list", *repo_flags(args.repo)]
releases = tea_json(*flags)
if not releases:
print("No releases found.")
return
print(f"{'Tag':<20} {'Name':<30} {'Draft':<6} {'Pre':<5}")
print("-" * 65)
for r in releases:
print(f"{r.get('tag_name',''):<20} {r.get('name',''):<30} "
f"{str(r.get('draft', False)):<6} {str(r.get('prerelease', False)):<5}")
def cmd_close_issue(args: argparse.Namespace) -> None:
result = run_tea("issues", "close", str(args.index), *repo_flags(args.repo))
if result.returncode == 0:
print(f"Issue #{args.index} closed.")
def cmd_close_pr(args: argparse.Namespace) -> None:
result = run_tea("pulls", "close", str(args.index), *repo_flags(args.repo))
if result.returncode == 0:
print(f"PR #{args.index} closed.")
def cmd_create_issue(args: argparse.Namespace) -> None:
flags = ["issues", "create", "--title", args.title, *repo_flags(args.repo)]
if args.desc:
flags += ["--description", args.desc]
if args.labels:
flags += ["--labels", args.labels]
result = run_tea(*flags, capture=False)
sys.exit(result.returncode)
def cmd_triage(args: argparse.Namespace) -> None:
"""Group open issues by their first label."""
flags = ["issues", "list", "--state", "open", *repo_flags(args.repo)]
issues = tea_json(*flags)
if not issues:
print("No open issues.")
return
groups: dict[str, list] = {}
for issue in issues:
labels = issue.get("labels") or []
label = labels[0].get("name", "unlabeled") if labels else "unlabeled"
groups.setdefault(label, []).append(issue)
for label, items in sorted(groups.items()):
print(f"\n[{label}] ({len(items)})")
for i in items:
print(f" #{i.get('number', i.get('index','?'))} {i.get('title','')}")
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(description="tea CLI helper (Python wrapper)")
p.add_argument("--repo", "-r", help="owner/repo slug (overrides auto-detect)")
sub = p.add_subparsers(dest="command", required=True)
# issues
pi = sub.add_parser("issues", help="List issues")
pi.add_argument("--state", default="open", choices=["open", "closed", "all"])
# prs
pp = sub.add_parser("prs", help="List pull requests")
pp.add_argument("--state", default="open", choices=["open", "closed", "all"])
# releases
sub.add_parser("releases", help="List releases")
# close-issue
pci = sub.add_parser("close-issue", help="Close an issue")
pci.add_argument("index", type=int, help="Issue index/number")
# close-pr
pcp = sub.add_parser("close-pr", help="Close a pull request")
pcp.add_argument("index", type=int, help="PR index/number")
# create-issue
pcr = sub.add_parser("create-issue", help="Create a new issue")
pcr.add_argument("--title", "-t", required=True)
pcr.add_argument("--desc", "-d", default="")
pcr.add_argument("--labels", "-L", default="", help="Comma-separated labels")
# triage
sub.add_parser("triage", help="Group open issues by label")
return p
def main() -> None:
parser = build_parser()
args = parser.parse_args()
dispatch = {
"issues": cmd_issues,
"prs": cmd_prs,
"releases": cmd_releases,
"close-issue": cmd_close_issue,
"close-pr": cmd_close_pr,
"create-issue": cmd_create_issue,
"triage": cmd_triage,
}
dispatch[args.command](args)
if __name__ == "__main__":
main()