From 47106ff281801b40937f2e72e809d14edf5c8987 Mon Sep 17 00:00:00 2001 From: millianlmx Date: Mon, 20 Jul 2026 09:21:28 +0200 Subject: [PATCH 1/3] fix(ci): indent python3 -c body so YAML block scalar parses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The python3 inline script in wait-approval was written at column 0 (lines 257-283), but YAML block scalars require content to be more indented than the parent key. YAML exited the run: | block at the first column-0 line ("import json, re, sys") and tried to parse the python as YAML, failing with "could not find expected :" at line 257 — which prevented pr-iphone-deploy.yml from running at all (no run was ever created for PRs that touched only the workflow file). Fix: switch to python3 -c with the body indented to sit inside the run: | block scalar. Verified locally: - yaml.safe_load parses the workflow cleanly (jobs: changes, build-deploy, wait-approval). - The extracted python, run exactly as act will execute it, returns: bot-only -> PENDING (was self-merging before) bot + reviewer LGTM -> LGTM (squash-merge still fires) bot + reviewer KO -> KO (block still fires) --- .github/workflows/pr-iphone-deploy.yml | 55 +++++++++++++------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/.github/workflows/pr-iphone-deploy.yml b/.github/workflows/pr-iphone-deploy.yml index 65f4802..ce991bc 100644 --- a/.github/workflows/pr-iphone-deploy.yml +++ b/.github/workflows/pr-iphone-deploy.yml @@ -253,34 +253,33 @@ jobs: COMMENTS=$(curl -s -H "Authorization: token ${GT_TOKEN}" \ "${API}/issues/${PR}/comments?limit=50&page=1") - DECISION=$(python3 - "$COMMENTS" <<'PY' -import json, re, sys -MARKER = "" -try: - comments = json.loads(sys.argv[1] or "[]") -except Exception: - comments = [] -lgtm = re.compile(r"\bLGTM\b", re.IGNORECASE) -ko = re.compile(r"\bKO\b", re.IGNORECASE) -hit_lgtm = hit_ko = False -for c in comments: - body = c.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.search(body): - hit_ko = True - if lgtm.search(body): - hit_lgtm = True -if hit_ko: - print("KO") -elif hit_lgtm: - print("LGTM") -else: - print("PENDING") -PY -) + DECISION=$(python3 -c ' + import json, re, sys + MARKER = "" + try: + comments = json.loads(sys.argv[1] or "[]") + except Exception: + comments = [] + lgtm = re.compile(r"\bLGTM\b", re.IGNORECASE) + ko = re.compile(r"\bKO\b", re.IGNORECASE) + hit_lgtm = hit_ko = False + for c in comments: + body = c.get("body") or "" + if MARKER in body: + # Skip bot comments — their body contains LGTM/KO as reviewer + # instructions and would otherwise self-trigger a merge. + continue + if ko.search(body): + hit_ko = True + if lgtm.search(body): + hit_lgtm = True + if hit_ko: + print("KO") + elif hit_lgtm: + print("LGTM") + else: + print("PENDING") + ' "$COMMENTS") case "$DECISION" in LGTM) -- 2.49.1 From 7196cce237c6ffe4232e232e12728876bcb741c3 Mon Sep 17 00:00:00 2001 From: millianlmx Date: Mon, 20 Jul 2026 09:34:28 +0200 Subject: [PATCH 2/3] =?UTF-8?q?add=20scripts/pr=5Flgtm=5Fscanner.py=20?= =?UTF-8?q?=E2=80=94=20LGTM/KO=20scanner=20for=20pr-iphone-deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standalone Python scanner extracted from the inline python in wait-approval. Embedding multi-line Python in a YAML run: | block scalar is fragile: YAML dedents block-scalar content to the first line's indent, which produced both YAML parse failures and Python IndentationErrors in earlier iterations of this workflow. A standalone script file sidesteps all of that and is testable locally. Behavior preserved: - json.loads + \bLGTM\b / \bKO\b word-boundary regex (case-insensitive) - skip bot comments tagged with - KO checked BEFORE LGTM (PR with both signals blocks) Verified locally across 8 scenarios (bot-only, LGTM, KO, both, empty, usage error, KOM/LGTMX non-match, case-insensitive). --- scripts/pr_lgtm_scanner.py | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 scripts/pr_lgtm_scanner.py 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()) -- 2.49.1 From 2d1facfb41effea0d5178670029b3f852c78875c Mon Sep 17 00:00:00 2001 From: millianlmx Date: Mon, 20 Jul 2026 09:34:29 +0200 Subject: [PATCH 3/3] fix(ci): call scripts/pr_lgtm_scanner.py instead of inline python MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inline python3 -c body in wait-approval raised IndentationError: unexpected indent because YAML block scalars dedent content to the first line's indent, leaving the python body with leading whitespace. Replace with a call to scripts/pr_lgtm_scanner.py (added in the previous commit). Add a shallow Checkout step to wait-approval so the script is present on the runner. This is the third indentation fix attempt for this scanner: - column-0 python → YAML parse failure - indented python → Python IndentationError - standalone file → no indentation concerns (this commit) --- .github/workflows/pr-iphone-deploy.yml | 47 ++++++++++---------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/.github/workflows/pr-iphone-deploy.yml b/.github/workflows/pr-iphone-deploy.yml index ce991bc..839a916 100644 --- a/.github/workflows/pr-iphone-deploy.yml +++ b/.github/workflows/pr-iphone-deploy.yml @@ -215,6 +215,15 @@ jobs: timeout-minutes: 120 steps: + - name: Checkout + # Shallow clone — wait-approval only needs scripts/pr_lgtm_scanner.py, + # nothing else from the repo. Same raw-git pattern as build-deploy's + # Checkout step (avoids actions/checkout setup). + run: | + git clone --depth 1 -b ${{ github.head_ref }} https://x-access-token:${PR_TOKEN}@gitea.1000co.fr/${{ github.repository }}.git . + env: + PR_TOKEN: ${{ secrets.PR_API_TOKEN }} + - name: Poll for LGTM env: GT_TOKEN: ${{ secrets.PR_API_TOKEN }} @@ -233,8 +242,8 @@ jobs: # edits — e.g. a reviewer changing "KO" → "LGTM". Negligible cost for # PRs with <50 comments. # - # Parsing is done with python3 (preinstalled on the macOS runner and - # already used by scripts/ci-status.py) rather than grep over raw JSON: + # The LGTM/KO decision is computed by scripts/pr_lgtm_scanner.py + # (a standalone Python file) rather than grep over raw JSON: # - the bot's "Ready to test" comment body literally contains the # words LGTM/KO as instructions to the reviewer, so any naive # body grep would self-match and auto-merge ~30s after deploy. @@ -253,33 +262,13 @@ jobs: COMMENTS=$(curl -s -H "Authorization: token ${GT_TOKEN}" \ "${API}/issues/${PR}/comments?limit=50&page=1") - DECISION=$(python3 -c ' - import json, re, sys - MARKER = "" - try: - comments = json.loads(sys.argv[1] or "[]") - except Exception: - comments = [] - lgtm = re.compile(r"\bLGTM\b", re.IGNORECASE) - ko = re.compile(r"\bKO\b", re.IGNORECASE) - hit_lgtm = hit_ko = False - for c in comments: - body = c.get("body") or "" - if MARKER in body: - # Skip bot comments — their body contains LGTM/KO as reviewer - # instructions and would otherwise self-trigger a merge. - continue - if ko.search(body): - hit_ko = True - if lgtm.search(body): - hit_lgtm = True - if hit_ko: - print("KO") - elif hit_lgtm: - print("LGTM") - else: - print("PENDING") - ' "$COMMENTS") + # The LGTM/KO scanner lives in scripts/pr_lgtm_scanner.py rather + # than inline here. Embedding multi-line Python in a YAML `run: |` + # block scalar is fragile — YAML dedents block-scalar content to + # the first line, which produced both YAML parse failures and + # Python IndentationErrors in earlier iterations of this workflow. + # A standalone script sidesteps all of that and is testable locally. + DECISION=$(python3 scripts/pr_lgtm_scanner.py "$COMMENTS") case "$DECISION" in LGTM) -- 2.49.1