From de335bcd66717d1e5f29b08b48ad6c28c148245b Mon Sep 17 00:00:00 2001 From: Lawrence Angrave Date: Sun, 23 Aug 2026 21:20:59 -0500 Subject: [PATCH 1/3] Fix CI: python 3.7 is unavailable on ubuntu-latest Every build and deploy job has been failing at the "Setup python" step with "Version 3.7 with arch x64 not found". ubuntu-latest now resolves to ubuntu-24.04, and actions/python-versions only ships 3.7 builds for 20.04 and 22.04. Move to 3.9, which does have a 24.04 build. 3.9 is the newest release that still works with the pinned panflute 1.10.6: it imports MutableSequence from collections, which was removed in Python 3.10, so 3.10+ would fail on import instead. Also bump checkout/setup-python to v4/v5, since the v2 actions run on a deprecated Node version. --- .github/workflows/build.yaml | 10 +++++++--- .github/workflows/deploy.yaml | 13 ++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 94e8fc0..1606ca1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -17,12 +17,16 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 + # Python 3.7 has no build for ubuntu-24.04 (which ubuntu-latest now + # points at), so setup-python could not find it. 3.9 is the newest + # release that still works with the pinned panflute 1.10.6, which + # imports MutableSequence from collections (removed in 3.10). - name: Setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: - python-version: 3.7 + python-version: '3.9' - name: Install run: | diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 51db62a..caadd0a 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -17,13 +17,16 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Setup python (3.7.17) - uses: actions/setup-python@v2 + # Python 3.7 has no build for ubuntu-24.04 (which ubuntu-latest now + # points at), so setup-python could not find it. 3.9 is the newest + # release that still works with the pinned panflute 1.10.6, which + # imports MutableSequence from collections (removed in 3.10). + - name: Setup python + uses: actions/setup-python@v5 with: - python-version: 3.7.17 - #3.6.15 + python-version: '3.9' - name: Install run: | From 9506ef103ae04a6b410cfcb3469d4ed769d13e5d Mon Sep 17 00:00:00 2001 From: Lawrence Angrave Date: Sun, 23 Aug 2026 21:24:30 -0500 Subject: [PATCH 2/3] Replace the dead cyber.dhs.gov link in the security chapter cyber.dhs.gov no longer has an A record, so the link check in pandoc_header_filter.py died with "No address associated with hostname" and took the WIKI build down with it. CISA moved Emergency Directive 19-01; point at its current home. --- security/security.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/security.tex b/security/security.tex index 5767924..595e09f 100644 --- a/security/security.tex +++ b/security/security.tex @@ -341,7 +341,7 @@ More and more of our systems are hacked over the web, it is important to underst \subsection{Security at the DNS Level} -As of 2019, the United States Department of Homeland Security released a directive to switch all services from DNS to DNSSec \url{https://cyber.dhs.gov/assets/report/ed-19-01.pdf}. +As of 2019, the United States Department of Homeland Security released a directive to switch all services from DNS to DNSSec \url{https://www.cisa.gov/news-events/directives/ed-19-01-mitigate-dns-infrastructure-tampering-closed}. This directive is an inherent flaw of the DNS system. First, DNS doesn't offer any sort of verification on domain name requests. That is, it is easy to spoof DNS nameservers such that they point your browser to potentially malicious servers. From 31c92844bb5c99767b4f462cf1acea585768401a Mon Sep 17 00:00:00 2001 From: Lawrence Angrave Date: Sun, 23 Aug 2026 21:29:30 -0500 Subject: [PATCH 3/3] Do not fail the build when a link cannot be reached The WIKI build died on www.gnu.org with "[Errno 101] Network is unreachable". gnu.org is not down: it has an AAAA record and GitHub runners have no IPv6 route, so the connection fails before IPv4 is tried. Reachability of the build machine is not a property of the book, so warn and carry on instead of aborting. Also add a 15s timeout - the failing host stalled the job for over two minutes of retries - and report non-2xx as a warning. The previous status test was `code < 200 and code > 299`, which no integer can satisfy, so no status has ever been checked. It stays non-fatal deliberately: www.gnu.org answers a bare requests HEAD with 403 (it blocks the default user agent), so failing on non-2xx would break the build on links that are perfectly fine in a browser. --- _scripts/pandoc_header_filter.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/_scripts/pandoc_header_filter.py b/_scripts/pandoc_header_filter.py index 7753135..b0b721f 100644 --- a/_scripts/pandoc_header_filter.py +++ b/_scripts/pandoc_header_filter.py @@ -20,6 +20,10 @@ import requests link_cache_days = 30 +# Per-request timeout for the link check. Without this a host that +# blackholes connections stalls the build for minutes. +link_timeout_seconds = 15 + # Metadata gleaned from the file meta = dict( name="", @@ -129,10 +133,21 @@ def output_yaml(elem, doc): raise BadLinkException(url) print('Requesting url "{}"'.format(url), file=sys.stderr) - # Ping the image - head = requests.head(url) - if head.status_code < 200 and head.status_code > 299: - raise BadLinkException(url) + # Ping the image. A link we cannot reach is reported but is not + # fatal: the build machine's connectivity is not a property of + # the book. GitHub runners have no IPv6 route, for example, so + # any host with an AAAA record (www.gnu.org) raises + # "[Errno 101] Network is unreachable" even though it is fine. + try: + head = requests.head(url, timeout=link_timeout_seconds) + except requests.exceptions.RequestException as e: + print('WARNING: could not reach "{}": {}'.format(url, e), + file=sys.stderr) + return elem + + if not (200 <= head.status_code <= 299): + print('WARNING: url "{}" returned status {}'.format( + url, head.status_code), file=sys.stderr) # Otherwise reset the cache link_cache[url] = datetime.datetime.now().isoformat()