From 055057f22207690c19a4b62a01ed9db01da46c52 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 8 Nov 2026 15:04:07 +0100 Subject: [PATCH 1/2] ci: check external links with fleettools linkcheck Replace the hand-written grep + curl loop of the docs `links` job with fleettools linkcheck v0.88.0. The site's own pages and this repository's own files stay excluded, now as --skip. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/docs.yml | 75 +++++++++++++++----------------------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a0fe191..0ab9184 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -117,55 +117,40 @@ jobs: links: name: every external link resolves runs-on: ubuntu-latest + permissions: + contents: read + timeout-minutes: 10 steps: - uses: actions/checkout@v7 + # linkcheck (go-fleettools/fleettools, pinned): retries with exponential + # backoff that honours Retry-After, github.com repo and path links decided + # through the REST API rather than the HTML page, a DEAD link told apart + # from a FLAKY host. It already skips, printed and counted, URLs with no + # host, the RFC 2606 / 6761 placeholder hosts (example.com, localhost, + # ...) and img.shields.io. + # + # --skip: this site's own pages (they exist only once this workflow has + # deployed them, and the offline check above already proves every link + # inside the site); and this repository's own files (on a pull request + # that adds them, they are not on main yet). + # + # THE ONE DELIBERATE EXCEPTION to "no secret on a command line or in the + # environment": the job's ephemeral, read-only github.token (permissions: + # contents: read, for this job alone) reaches the tool as a 0600 file in + # RUNNER_TEMP, written by the shell builtin printf (no process argument, + # no environment variable) and removed on exit. It is sent to + # api.github.com only and never printed. - name: Check run: | - set -uo pipefail - # The character class matters: an extractor stopping only at ) and " - # carries a closing > or < into the request. - urls=$(grep -rhoE 'https?://[^)">< [:space:]]+' content/ README.md | sed -E 's/[.,`]+$//' | sort -u) - test -n "$urls" || { echo "::error::no links found; are the pages still there?"; exit 1; } - echo "checking $(printf '%s\n' "$urls" | wc -l) distinct URLs" - fail=0 - while read -r u; do - case "$u" in - # Renders whatever it is asked for; proves nothing. - *img.shields.io*) continue ;; - # Documented placeholders, not hosts. - *example.com*|*example.org*|*example.net*|*localhost*|*127.0.0.1*|*.invalid*) continue ;; - # This site's own pages: they exist only once this workflow - # has deployed them, and the offline check above already - # proves every link inside the site. - *go-sciio.github.io/docs*) continue ;; - # This repository's own files: on a pull request that adds - # them, they are not on main yet. - *github.com/go-sciio/docs/*) continue ;; - esac - # github.com answers curl's blob pages with 503 most of the time, - # while the raw host answers 200 for a file and 404 for a missing - # one. A blob link claims "this file exists at this ref", so ask - # the raw host (as the landing's links.yml and go-ndarray/docs#30). - probe=$u - case "$u" in - https://github.com/*/*/blob/*) - probe=https://raw.githubusercontent.com/$(printf '%s' "${u#https://github.com/}" | sed -E 's#^([^/]+/[^/]+)/blob/#\1/#; s/[?#].*$//') ;; - esac - # Three attempts: one is a flake detector rather than a link - # checker, and a lane that fails at random gets ignored. - code=000 - for _ in 1 2 3; do - code=$(curl -sS -o /dev/null -w '%{http_code}' -L --max-time 25 "$probe" 2>/dev/null) || true - code=${code:-000} - case "$code" in 2*|3*) break ;; esac - sleep 3 - done - case "$code" in - 2*|3*) printf ' ok %s %s\n' "$code" "$u" ;; - *) printf ' FAIL %s %s\n' "$code" "$u"; fail=1 ;; - esac - done <<< "$urls" - test "$fail" -eq 0 || { echo "::error::at least one link does not resolve"; exit 1; } + umask 077 + token="${RUNNER_TEMP}/linkcheck-token" + trap 'rm -f "${token}"' EXIT + printf '%s' '${{ github.token }}' > "${token}" + go run github.com/go-fleettools/fleettools/cmd/linkcheck@v0.88.0 \ + --token-file "${token}" \ + --skip go-sciio.github.io/docs \ + --skip github.com/go-sciio/docs/ \ + content/ README.md deploy: # Only main publishes. A pull request stops after build above: it has no From c55b9ee2fa916cbc80dd1fb7d48b14df060aa1d8 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 8 Nov 2026 15:06:18 +0100 Subject: [PATCH 2/2] ci: skip the MathWorks PDF link, bot-blocked The host answers a program 403 and a browser a sign-in redirect. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/docs.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0ab9184..6ee0d97 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -132,7 +132,10 @@ jobs: # --skip: this site's own pages (they exist only once this workflow has # deployed them, and the offline check above already proves every link # inside the site); and this repository's own files (on a pull request - # that adds them, they are not on main yet). + # that adds them, they are not on main yet); and MathWorks' MAT-file + # format PDF, which answers a program 403 and a browser a redirect to a + # sign-in page (the old curl loop only passed by following that redirect + # to the login form), so the link cannot be told dead or alive here. # # THE ONE DELIBERATE EXCEPTION to "no secret on a command line or in the # environment": the job's ephemeral, read-only github.token (permissions: @@ -150,6 +153,7 @@ jobs: --token-file "${token}" \ --skip go-sciio.github.io/docs \ --skip github.com/go-sciio/docs/ \ + --skip www.mathworks.com/help/pdf_doc/ \ content/ README.md deploy: