98 lines
3.8 KiB
Python
98 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Post a standardized Codex state update comment to a Gitea issue."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
|
|
from gitea_common import GiteaClient, GiteaError, infer_target, load_dotenv, repo_path, repo_root, require_token
|
|
|
|
|
|
STATUS_LABELS = {
|
|
"started": "status/in-progress",
|
|
"progress": "status/in-progress",
|
|
"blocked": "status/blocked",
|
|
"needs-info": "status/needs-info",
|
|
"ready": "status/ready",
|
|
"done": "",
|
|
"note": "",
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--root", type=pathlib.Path, default=pathlib.Path.cwd(), help="repository root or child path")
|
|
parser.add_argument("--remote", default="origin", help="git remote to use for target inference")
|
|
parser.add_argument("--env-file", type=pathlib.Path, help="dotenv file to read before using GITEA_* values")
|
|
parser.add_argument("--issue", type=int, required=True, help="Gitea issue number")
|
|
parser.add_argument("--status", choices=sorted(STATUS_LABELS), default="note")
|
|
parser.add_argument("--summary", action="append", default=[], help="summary bullet; may be repeated")
|
|
parser.add_argument("--changed", action="append", default=[], help="changed file path; may be repeated")
|
|
parser.add_argument("--test", action="append", default=[], help="verification command/result; may be repeated")
|
|
parser.add_argument("--next", action="append", default=[], help="next step or blocker; may be repeated")
|
|
parser.add_argument("--body-file", type=pathlib.Path, help="additional Markdown body to append")
|
|
parser.add_argument("--close", action="store_true", help="close the issue after posting the note")
|
|
parser.add_argument("--apply", action="store_true", help="post the comment and optional state update")
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
root = repo_root(args.root)
|
|
load_dotenv(args.env_file or root / ".env")
|
|
target = infer_target(root, args.remote)
|
|
token = require_token() if args.apply else os.environ.get("GITEA_TOKEN")
|
|
body = build_body(args)
|
|
|
|
print(f"Target: {target.display}")
|
|
print(f"Issue: #{args.issue}")
|
|
print(body)
|
|
|
|
if not args.apply:
|
|
print("Dry run only. Re-run with --apply and GITEA_TOKEN to post.")
|
|
return 0
|
|
|
|
client = GiteaClient(target, token)
|
|
client.request_json(
|
|
"POST",
|
|
repo_path(target.owner, target.repo, f"/issues/{args.issue}/comments"),
|
|
body={"body": body},
|
|
)
|
|
if args.close:
|
|
client.request_json(
|
|
"PATCH",
|
|
repo_path(target.owner, target.repo, f"/issues/{args.issue}"),
|
|
body={"state": "closed"},
|
|
)
|
|
print("Posted Codex note.")
|
|
return 0
|
|
except GiteaError as exc:
|
|
print(f"error: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
def build_body(args: argparse.Namespace) -> str:
|
|
lines = [f"## Codex State: {args.status}", ""]
|
|
append_section(lines, "Summary", args.summary)
|
|
append_section(lines, "Changed Files", [f"`{item}`" for item in args.changed])
|
|
append_section(lines, "Verification", [f"`{item}`" for item in args.test])
|
|
append_section(lines, "Next / Blocked", args.next)
|
|
if STATUS_LABELS[args.status]:
|
|
lines.extend(["", f"Suggested status label: `{STATUS_LABELS[args.status]}`"])
|
|
if args.body_file:
|
|
lines.extend(["", args.body_file.read_text(encoding="utf-8").strip()])
|
|
return "\n".join(lines).rstrip() + "\n"
|
|
|
|
|
|
def append_section(lines: list[str], title: str, items: list[str]) -> None:
|
|
if not items:
|
|
return
|
|
lines.extend([f"### {title}", ""])
|
|
lines.extend(f"- {item}" for item in items)
|
|
lines.append("")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|