posts and avif
Some checks failed
Deploy / build-deploy (push) Failing after 5m49s

This commit is contained in:
Jorijn van der Graaf 2026-08-10 01:37:26 +02:00
commit 6841623e23
17 changed files with 2306 additions and 148 deletions

View file

@ -140,6 +140,10 @@ status /legal/nope 404
# A slug that cannot be one of ours is rejected before any lookup.
status /shop/BAD--slug 404
status /demos/nope 404
# A post slug that parsed but names nothing must be a real 404, or every typo
# and every retired post becomes an indexable empty page.
status /posts/nope 404
status /posts/BAD--slug 404
# The retired blog URLs are still in the wild; they must redirect, not 404.
status /blog 301
status /blog/hello-world 301
@ -367,7 +371,7 @@ else
fi
# `poster` is in the list because a video poster is fetched on page load exactly
# like an <img> src is, so a third-party poster leaks the same visitor IP.
if curl -s "$BASE/posts" | grep -qE '(src|href|poster)="https?://[^"]*\.(mp4|webm|webp|png|jpe?g|gif)'; then
if curl -s "$BASE/posts" | grep -qE '(src|srcset|href|poster)="https?://[^"]*\.(mp4|webm|webp|avif|png|jpe?g|gif)'; then
bad "/posts media origin" "media loaded from a third party"
else
ok "/posts loads no media from a third party"
@ -417,6 +421,126 @@ else
bad "video preload" "expected preload=\"metadata\""
fi
echo "== post pages =="
# The post page is where the body lives, and the body is the reason the site
# has anything for a search engine to index beyond a list of links off it. Its
# slug is data, so take one from the page rather than hardcoding a title that
# will be wrong the week after it is written.
POST_PATH=$(curl -s "$BASE/posts" | grep -oE 'href="/posts/[a-z0-9-]+"' \
| head -n1 | sed 's/href="//; s/"$//')
if [ -z "$POST_PATH" ]; then
bad "post pages" "/posts links no post page; nothing carries a body"
else
ok "/posts links a post page ($POST_PATH)"
status "$POST_PATH" 200
body_has /posts 'Read the full post' "/posts offers the full post"
# And it trails the excerpt, immediately after the ellipsis the truncation
# left, rather than sitting as its own row below the media. The excerpt is
# escaped text, so nothing but the link can put a '<' between the two.
if curl -s "$BASE/posts" \
| grep -qE '<p class="post-card__excerpt">[^<]*<a class="link-more" href="/posts/'; then
ok "read-more trails the excerpt"
else
bad "read-more placement" "not inside the excerpt paragraph"
fi
body_has "$POST_PATH" '<div class="post-body">' "post page carries the rendered body"
# Rendered, not dumped: a body that reached the page as literal Markdown
# would show its own asterisks and hashes to the reader and to a crawler.
if curl -s "$BASE$POST_PATH" | grep -qE '<(p|h2|h3|h4|ul|ol|blockquote|pre)>'; then
ok "post body is real markup, not literal Markdown"
else
bad "post body markup" "no block elements found in the body"
fi
# The canonical points here, not at the instance. That is the entire SEO
# argument for hosting the body: two copies of the text exist, and this
# says which one is the original as far as this site is concerned.
body_has "$POST_PATH" 'rel="canonical" href="https://catcrafts.net/posts/' \
"post page is its own canonical"
body_has "$POST_PATH" '"@type":"BlogPosting"' "post page carries BlogPosting JSON-LD"
body_has "$POST_PATH" '"@id":"https://catcrafts.net/#organization"' \
"post JSON-LD joins the organization node"
body_has "$POST_PATH" '"@id":"https://catcrafts.net/about#person"' \
"post JSON-LD joins the founder node"
body_has "$POST_PATH" 'property="og:type" content="article"' "post page is an article to og:"
# Hosting the body does not mirror the discussion; the thread is still one
# click away and is still where the comments are.
if curl -s "$BASE$POST_PATH" | grep -qE 'href="https://[a-z0-9.-]+/post/[0-9]+"'; then
ok "post page still links its thread"
else
bad "post thread link" "no https://<instance>/post/<id> link on the post page"
fi
# The body is prose, not an application. Same rule as /projects.
body_lacks "$POST_PATH" '<script>' "post page ships no executable script"
body_lacks "$POST_PATH" '<base' "post page has no base tag"
# Every inline image and video the body embeds is mirrored, exactly like a
# card's media — the privacy notice's "everything comes from catcrafts.net"
# covers href as well as src, so a body linking a .webp on someone else's
# instance is the same leak as embedding one.
for pg in $(curl -s "$BASE/sitemap.xml" \
| grep -oE '/posts/[a-z0-9-]+' | head -n 20); do
if curl -s "$BASE$pg" \
| grep -qE '(src|srcset|href|poster)="https?://[^"]*\.(mp4|webm|webp|avif|png|jpe?g|gif)'; then
bad "$pg media origin" "body media loaded from a third party"
else
ok "$pg loads no media from a third party"
fi
done
# The image format ladder: AVIF first, the mirrored original next, and a
# PNG on the <img> underneath, so exactly one file is fetched and every
# browser can read one of them. Order is the whole point — a browser takes
# the first source it understands — so assert the sequence, not just that
# the pieces are present.
if curl -s "$BASE$POST_PATH" | grep -q '<picture>'; then
if curl -s "$BASE$POST_PATH" \
| grep -qE '<picture><source srcset="/media/[^"]+\.avif" type="image/avif">'; then
ok "inline images lead with an AVIF source"
else
bad "image ladder" "the first source is not the AVIF"
fi
if curl -s "$BASE$POST_PATH" \
| grep -qE '<img class="post-media__item"[^>]*src="/media/[^"]+\.png"'; then
ok "inline images fall back to a PNG the img itself points at"
else
bad "image fallback" "the <img> base is not a PNG"
fi
# Every tier has to be a file that exists, or the ladder serves a 404 to
# whichever browsers pick that rung — which is precisely the set of
# browsers nobody testing this site is using.
missing=0
for f in $(curl -s "$BASE/sitemap.xml" | grep -oE '/posts/[a-z0-9-]+' | head -n 20 \
| while read -r pg; do curl -s "$BASE$pg"; done \
| grep -oE '(src|srcset)="/media/[^"]+"' \
| sed 's/.*="//; s/"$//' | sort -u); do
[ -f "media${f#/media}" ] || { missing=$((missing + 1)); echo " missing: $f" >&2; }
done
if [ "$missing" -eq 0 ]; then
ok "every referenced media file is on the mount"
else
bad "media files" "$missing referenced file(s) not on the mount"
fi
else
skip "image format ladder" "no <picture> on this page — ffmpeg absent at mirror time?"
fi
# Inline screenshots get dimensions from the sidecar list fetch-media.sh
# writes, because Markdown syntax has nowhere to carry them — without it
# the prose below every image jumps as the file arrives. Conditional: a
# post whose body embeds nothing has nothing to check.
if curl -s "$BASE$POST_PATH" | grep -q '<img class="post-media__item"'; then
if curl -s "$BASE$POST_PATH" \
| grep -qE '<img class="post-media__item"[^>]*width="[0-9]+" height="[0-9]+"'; then
ok "inline body images carry width/height"
else
bad "inline image dimensions" "an embedded body image has no dimensions"
fi
fi
fi
# The sitemap has to advertise the pages, or hosting the bodies buys nothing.
if curl -s "$BASE/sitemap.xml" | grep -qE '<loc>https://catcrafts.net/posts/[a-z0-9-]+</loc>'; then
ok "sitemap lists the post pages"
else
bad "sitemap post pages" "no /posts/<slug> entry"
fi
echo "== headers =="
header_has / 'x-content-type-options: *nosniff' "nosniff on pages"
header_has / 'cache-control: *public' "pages are cacheable"