run_review

CLI для ручного запуска LLM-ревью.

Примеры: python run_review.py e.anisimov/LostAndFound 15 python run_review.py e.anisimov/LostAndFound 15 --head-sha python run_review.py e.anisimov/LostAndFound --all-open

 1"""
 2CLI для ручного запуска LLM-ревью.
 3
 4Примеры:
 5    python run_review.py e.anisimov/LostAndFound 15
 6    python run_review.py e.anisimov/LostAndFound 15 --head-sha <sha>
 7    python run_review.py e.anisimov/LostAndFound --all-open
 8"""
 9
10from __future__ import annotations
11
12import argparse
13import sys
14
15from config import Config
16from gitea_client import GiteaClient
17from review import ReviewOutcome, run_review
18
19
20def _repo_arg(value: str) -> tuple[str, str]:
21    """Разбирает 'owner/repo'."""
22    if "/" not in value:
23        raise argparse.ArgumentTypeError("Ожидается формат owner/repo")
24    owner, repo = value.split("/", 1)
25    return owner, repo
26
27
28def main() -> int:
29    parser = argparse.ArgumentParser(description="LLM code review для PR в Gitea")
30    parser.add_argument("repo", type=_repo_arg, help="owner/repo")
31    parser.add_argument("pr_number", nargs="?", type=int, help="номер PR")
32    parser.add_argument(
33        "--head-sha",
34        help="head-коммит (по умолчанию — head PR)",
35    )
36    parser.add_argument(
37        "--all-open", action="store_true", help="ревью всех открытых PR репозитория"
38    )
39    args = parser.parse_args()
40
41    owner, repo = args.repo
42    config = Config.from_env()
43    client = GiteaClient(config)
44
45    if args.all_open:
46        prs = client.get(f"/repos/{owner}/{repo}/pulls?state=open")
47        for pr in prs:
48            print(f"-- {owner}/{repo}#{pr['number']}: {pr['title']}")
49            run_review(client, config, owner, repo, pr["number"], pr["head"]["sha"])
50        return 0
51
52    if not args.pr_number:
53        parser.error("нужен номер PR или --all-open")
54
55    outcome: ReviewOutcome = run_review(
56        client, config, owner, repo, args.pr_number, args.head_sha
57    )
58    if outcome.skipped:
59        print(f"Пропущено: {outcome.reason}")
60    else:
61        print(f"Готово: event={outcome.event}, findings={outcome.findings}")
62    return 0
63
64
65if __name__ == "__main__":
66    sys.exit(main())
def main() -> int:
29def main() -> int:
30    parser = argparse.ArgumentParser(description="LLM code review для PR в Gitea")
31    parser.add_argument("repo", type=_repo_arg, help="owner/repo")
32    parser.add_argument("pr_number", nargs="?", type=int, help="номер PR")
33    parser.add_argument(
34        "--head-sha",
35        help="head-коммит (по умолчанию — head PR)",
36    )
37    parser.add_argument(
38        "--all-open", action="store_true", help="ревью всех открытых PR репозитория"
39    )
40    args = parser.parse_args()
41
42    owner, repo = args.repo
43    config = Config.from_env()
44    client = GiteaClient(config)
45
46    if args.all_open:
47        prs = client.get(f"/repos/{owner}/{repo}/pulls?state=open")
48        for pr in prs:
49            print(f"-- {owner}/{repo}#{pr['number']}: {pr['title']}")
50            run_review(client, config, owner, repo, pr["number"], pr["head"]["sha"])
51        return 0
52
53    if not args.pr_number:
54        parser.error("нужен номер PR или --all-open")
55
56    outcome: ReviewOutcome = run_review(
57        client, config, owner, repo, args.pr_number, args.head_sha
58    )
59    if outcome.skipped:
60        print(f"Пропущено: {outcome.reason}")
61    else:
62        print(f"Готово: event={outcome.event}, findings={outcome.findings}")
63    return 0