diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 991de39..d2bfdff 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,7 +17,7 @@ ## Before / After - + diff --git a/.github/scripts/screenshots-check.py b/.github/scripts/screenshots-check.py index 3565ef5..3ef03bf 100755 --- a/.github/scripts/screenshots-check.py +++ b/.github/scripts/screenshots-check.py @@ -5,15 +5,21 @@ - a heading whose text mentions "Before / After", or mentions "Screenshots" and is not about tests (any heading level, any case), and under it, - up to the next heading of the same or a higher level, at least two different images; or + up to the next heading of the same or a higher level, at least two different images or + videos uploaded to GitHub (dragged into the description), or images that are repository + files pinned to a commit; or - the ticked task "No visual change". It reads the description as GitHub renders it (body_html), so comments, code blocks, list indentation and every other markdown rule are already applied: what counts is what a reader sees. A file renamed out of Sources/ counts as a UI change, and so does the package manifest (Package.swift, Package@swift-*.swift), whose platform floor decides which branches the -components compile. Struck-out text and images do not count. Whether the images really show -light and dark is left to the reviewer. +components compile. Only those two kinds count, so badges, emoji, externally hosted images +and files on a branch never pass as screenshots. A counts the images a light-mode +and a dark-mode reader see (GitHub's light / dark pattern). An upload GitHub cannot resolve +still counts; a reader sees it as a broken image. +Struck-out text and images do not count. Whether the images really show light and dark is +left to the reviewer. Usage: REPO=dashpay/DashUIKit PR_NUMBER=31 GH_TOKEN=… screenshots-check.py @@ -22,42 +28,55 @@ """ import json import os +import posixpath import re import secrets import subprocess import sys from html.parser import HTMLParser -from urllib.parse import urlsplit +from urllib.parse import parse_qs, unquote, urlsplit # "Before / After" anywhere in the heading, or "Screenshots" anywhere in a heading that is # not about tests ("Screenshot tests", "Screenshot_testing"; "testnet" and "TestFlight" are # fine). -BEFORE_AFTER = re.compile(r"before\W*(and|vs\.?|to)?\W*after", re.I) +# The gaps are bounded so that a long run of punctuation cannot make the match quadratic. +BEFORE_AFTER = re.compile(r"before\W{0,20}(and|vs\.?|to)?\W{0,20}after", re.I) SCREENSHOTS = re.compile(r"screenshot", re.I) TESTS = re.compile(r"(? (the markdown form) +# github.com///assets// (older markdown form) +# private-user-images.githubusercontent.com//-.?jwt=… (rendered, signed +# per viewer) +# user-images.githubusercontent.com//-. (older rendered form) +# cloud.githubusercontent.com/assets///. (oldest uploads) +# Which form body_html holds depends on the viewer and on the markup (GitHub leaves a +# alone), so every form counts. +UUID = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" +RENDERED = re.compile(r"/\d+/\d+-(%s)(\.\w+)?" % UUID) +UPLOADS = { + "github.com": re.compile(r"/user-attachments/assets/(%s)/?|/[^/]+/[^/]+/assets/\d+/(%s)/?" % (UUID, UUID)), + "private-user-images.githubusercontent.com": RENDERED, + "user-images.githubusercontent.com": RENDERED, + "cloud.githubusercontent.com": re.compile(r"/assets/\d+/\d+/(%s)(\.\w+)?" % UUID), +} +# A repository file pinned to a full commit SHA, the way an author without a browser +# (gh pr create, an agent) shows screenshots, since attachments cannot be uploaded through +# the API. A branch name is not pinned: the file can change or vanish, so it does not count. +# Only images: GitHub strips a hand-written