Files
awesome-privacy/.github/workflows/pr-lint.yml
T

125 lines
4.3 KiB
YAML

name: PR Lint (links + format)
# Runs on pull requests that touch the list. Two independent checks:
# 1. link-check - lychee looks for dead links in README.md.
# 2. format-lint - warns when a newly added entry has no description.
on:
pull_request:
paths:
- 'README.md'
permissions:
contents: read
jobs:
link-check:
name: Check README links
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Run lychee on README.md
uses: lycheeverse/lychee-action@v2
with:
lycheeVersion: v0.23.0
# Scope strictly to README.md.
# --accept whitelists transient/bot-block codes so the PR only hard
# fails on real dead links (404/410) and dead domains (DNS/connection
# errors, which lychee reports as errors and are not accepted here).
# lychee follows redirects, so a link that redirects to a live page
# counts as success. Retries soften short-lived timeouts.
args: >-
--no-progress
--cache
--max-cache-age 1d
--max-retries 3
--retry-wait-time 2
--timeout 20
--accept 100..=103,200..=299,403,429,500,502,503,504,999
README.md
format: markdown
output: lychee/out.md
jobSummary: true
fail: true
failIfEmpty: false
format-lint:
name: Lint new entries
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Check added entries follow the house format
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
python3 - <<'PY'
import os, re, subprocess, sys
base = os.environ.get("BASE_SHA", "")
head = os.environ.get("HEAD_SHA", "")
# Only inspect lines this PR adds to README.md.
diff = ""
try:
diff = subprocess.run(
["git", "diff", "--unified=0", base, head, "--", "README.md"],
capture_output=True, text=True, check=False,
).stdout
except Exception as exc:
print(f"::warning::Could not compute diff, skipping format lint ({exc})")
added = [
line[1:]
for line in diff.splitlines()
if line.startswith("+") and not line.startswith("+++")
]
# A list item that carries an external link: "- [Name](https://...) ..."
entry_re = re.compile(r"^\s*[-*]\s+.*\]\(https?://")
# Strip the leading bullet so we can test the remainder.
bullet_re = re.compile(r"^\s*[-*]\s+")
warnings, checked = [], 0
for text in added:
if not entry_re.search(text):
continue # skips TOC anchors (#...), "Avoid" bullets, prose.
checked += 1
body = bullet_re.sub("", text, count=1)
# House format puts " - description" after the link(s). Every valid
# entry has a " - " separator; a bare "- [Name](url)" has none.
if " - " not in body:
warnings.append(text.strip())
print(f"::warning::Entry may be missing a description: {text.strip()}")
out = ["## README format lint", ""]
out.append(f"Checked **{checked}** added link entr{'y' if checked == 1 else 'ies'}.")
out.append("")
if warnings:
out.append(
f"{len(warnings)} entr{'y' if len(warnings) == 1 else 'ies'} "
"may be missing a description "
"(house format: `- [Name](url) - description.`):"
)
out.append("")
out += [f"- `{w}`" for w in warnings]
else:
out.append("All added link entries include a description.")
report = "\n".join(out) + "\n"
print(report)
summary = os.environ.get("GITHUB_STEP_SUMMARY")
if summary:
with open(summary, "a") as fh:
fh.write(report)
# Warn only. Format nits never block a PR.
sys.exit(0)
PY