name: Check channels changed in PR on: pull_request: paths: - 'lists/**.md' jobs: check-pr-channels: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Find which lists this PR touches, and which URLs it added or changed run: | git fetch origin "${{ github.event.pull_request.base.sha }}" --depth=1 || true python3 - "${{ github.event.pull_request.base.sha }}" <<'PYEOF' import json import os import re import subprocess import sys base = sys.argv[1] changed_files = subprocess.run( ["git", "diff", "--name-only", base, "HEAD", "--", "lists/*.md"], capture_output=True, text=True, check=True, ).stdout.split() # skip lists the PR deletes - nothing left to check them against lists = sorted({ f[len("lists/"):-3] for f in changed_files if os.path.exists(f) }) diff = subprocess.run( ["git", "diff", "--unified=0", base, "HEAD", "--", "lists/*.md"], capture_output=True, text=True, check=True, ).stdout row = re.compile(r"^\|[^|]*\|([^|]+)\|[^|]*\[(?:>|x)\]\((https?://[^)\s]+)\)") changed_urls = sorted({ m.group(2).strip() for line in diff.splitlines() if line.startswith("+") and not line.startswith("+++") for m in [row.match(line[1:].strip())] if m }) with open("pr_lists.json", "w", encoding="utf-8") as handle: json.dump({"lists": lists, "changed_urls": changed_urls}, handle) print(f"Lists touched: {', '.join(lists) or '(none)'}") print(f"URLs added or changed: {len(changed_urls)}") PYEOF - name: Check every channel in the touched lists (existing + new) if: success() run: | lists=$(python3 -c "import json; print(' '.join(json.load(open('pr_lists.json'))['lists']))") if [ -z "$lists" ]; then echo "## PR channel check" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "No list files exist to check (this PR only removed lists) - nothing to do." >> "$GITHUB_STEP_SUMMARY" exit 0 fi # shellcheck disable=SC2086 python3 check_channels.py $lists --attempts 2 --timeout 10 --pause 2 --workers 30 --json pr_run.jsonl - name: Summarize, separating what this PR touched from what was already there if: always() run: | python3 - <<'PYEOF' import json import os summary_path = os.environ["GITHUB_STEP_SUMMARY"] if not os.path.exists("pr_run.jsonl"): with open(summary_path, "a", encoding="utf-8") as handle: handle.write("## PR channel check\n\nNothing was checked.\n") raise SystemExit(0) changed_urls = set() if os.path.exists("pr_lists.json"): with open("pr_lists.json", encoding="utf-8") as handle: changed_urls = set(json.load(handle)["changed_urls"]) with open("pr_run.jsonl", encoding="utf-8") as handle: rows = [json.loads(line) for line in handle if line.strip()] touched = [r for r in rows if r["url"] in changed_urls] others = [r for r in rows if r["url"] not in changed_urls and r["state"] != "alive"] def fmt(rows): ordered = sorted(rows, key=lambda r: (r["state"] != "dead", r["list"], r["channel"])) lines = [f'{r["state"]:12} {r["list"]:12} {r["channel"]} -> {r["url"]}' for r in ordered] return "\n".join(lines) or "(none)" lists_seen = ", ".join(sorted({r["list"] for r in rows})) or "(none)" with open(summary_path, "a", encoding="utf-8") as handle: handle.write("## PR channel check\n\n") handle.write(f"Lists checked in full (existing + new channels): {lists_seen}\n\n") handle.write("### Channels this PR added or changed\n\n```\n") handle.write(fmt(touched) + "\n```\n\n") if others: handle.write(f"### Already broken before this PR ({len(others)})\n\n") handle.write( "_Not caused by this PR - shown because you're already touching this file, " "so it's a good time to notice.\n\n```\n" ) handle.write(fmt(others) + "\n```\n\n") handle.write( "_`blocked` usually means geo-restricted on purpose (the Ⓖ marker in the lists), " "not broken. This is a quick pass (no ffprobe second opinion) - the scheduled " "*Channel check (deep)* workflow gives channels that look `dead` here one more " "chance before anyone acts on it. Use your judgment either way._\n" ) PYEOF