diff --git a/scripts/pr_lgtm_scanner.py b/scripts/pr_lgtm_scanner.py new file mode 100644 index 0000000..7a5d130 --- /dev/null +++ b/scripts/pr_lgtm_scanner.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""LGTM/KO scanner for the pr-iphone-deploy workflow's wait-approval job. + +Reads a Gitea PR comments JSON array (as returned by +``/api/v1/repos/{owner}/{repo}/issues/{pr}/comments``) on argv[1] and prints +exactly one of: ``LGTM``, ``KO``, ``PENDING``. + +Rules +----- +* The bot's own "Ready to test" comment body literally contains the words + ``LGTM`` and ``KO`` as instructions to the reviewer — without filtering it + out, the workflow would self-merge ~30s after deploy. Bot comments are + tagged with the sentinel ```` and skipped. +* ``KO`` is checked BEFORE ``LGTM`` — a PR with both signals blocks (matches + the existing "KO blocks" intent). +* Word-boundary regex (``\\bLGTM\\b`` / ``\\bKO\\b``, case-insensitive) so + "Tested, LGTM!" counts and "KOM" / "LGTMX" do not. + +This script lives in ``scripts/`` (not inline in the workflow) because +embedding multi-line Python inside a YAML ``run: |`` block scalar is fragile: +YAML dedents block-scalar content to the first line, which produces either a +YAML parse failure or a Python ``IndentationError`` depending on how the +body is indented. A standalone file sidesteps all of that. +""" +import json +import re +import sys + +MARKER = "" +LGTM_RE = re.compile(r"\bLGTM\b", re.IGNORECASE) +KO_RE = re.compile(r"\bKO\b", re.IGNORECASE) + + +def decide(comments_json: str) -> str: + """Return 'KO', 'LGTM', or 'PENDING' for the given comments JSON payload.""" + try: + comments = json.loads(comments_json or "[]") + except Exception: + comments = [] + + hit_lgtm = False + hit_ko = False + for comment in comments: + body = comment.get("body") or "" + if MARKER in body: + # Skip the bot's own "Ready to test" comment — its body mentions + # LGTM/KO as instructions and would otherwise self-trigger. + continue + if KO_RE.search(body): + hit_ko = True + if LGTM_RE.search(body): + hit_lgtm = True + + if hit_ko: + return "KO" + if hit_lgtm: + return "LGTM" + return "PENDING" + + +def main() -> int: + if len(sys.argv) != 2: + print("usage: pr_lgtm_scanner.py ", file=sys.stderr) + return 2 + print(decide(sys.argv[1])) + return 0 + + +if __name__ == "__main__": + sys.exit(main())