test: expand smoke suite to 32 checks + Hugo CI matrix

scripts/smoke-tests.sh:
- Per-kind CSS budget (home/post/list/archives/search), each ≤ 4200 B gz
- theme-color light/dark meta tags
- aria-pressed SSR on theme-toggle
- breadcrumbs nav + BreadcrumbList JSON-LD presence on post,
  absence on home
- prev/next nav + rel=prev attribute
- linkToSection aria-label on heading anchors
- Speculation Rules absent by default
- llm.txt artifact present
- Pagefind UI preload-swap pattern on /search/
- Giscus preconnect gating (absent when comments disabled)
- Per-kind CSS bundle routing (home loads home.css but not single.css;
  post loads single.css but not home.css)
- code-copy.js gating (post loads it, home does not)

.github/workflows/pages.yml:
- Build matrix on Hugo 0.146.0 (theme.toml floor) + 0.154.0 (current).
  Smoke + htmltest run on both. Artifact upload + deploy gated on
  the current version only.
- Drop the redundant standalone CSS-budget step; smoke-tests.sh now
  enforces per-kind budgets.
This commit is contained in:
2026-05-15 18:55:59 +07:00
parent d1828c9590
commit fdc91dbc8b
2 changed files with 82 additions and 29 deletions
+9 -18
View File
@@ -17,8 +17,11 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.154.0
strategy:
fail-fast: false
matrix:
# Floor (theme.toml min_version) + current pinned. Deploy artifact is uploaded only from the current version.
hugo: ["0.146.0", "0.154.0"]
steps:
- uses: actions/checkout@v6
with:
@@ -28,7 +31,7 @@ jobs:
- name: Setup Hugo (extended)
uses: peaceiris/actions-hugo@v3
with:
hugo-version: ${{ env.HUGO_VERSION }}
hugo-version: ${{ matrix.hugo }}
extended: true
- name: Setup Node
@@ -47,21 +50,7 @@ jobs:
- name: Build Pagefind index
run: npx pagefind --site exampleSite/public
- name: Assert CSS bundle budget (≤ 4200 B gz)
run: |
css=$(find exampleSite/public -name 'tsuki.bundle.*.css' -print -quit)
if [ -z "$css" ]; then
echo "::error::Could not locate tsuki.bundle.*.css under exampleSite/public"
exit 1
fi
sz=$(gzip -9 -c "$css" | wc -c)
echo "tsuki bundle gz: ${sz} B (limit 4200)"
if [ "$sz" -gt 4200 ]; then
echo "::error::CSS bundle exceeds 4200 B gz budget (was ${sz} B)"
exit 1
fi
- name: Smoke tests (SEO + a11y + features regression guard)
- name: Smoke tests (SEO + a11y + per-kind CSS budget + features regression guard)
run: ./scripts/smoke-tests.sh exampleSite/public
- name: htmltest (broken internal links + HTML5 validation)
@@ -71,6 +60,8 @@ jobs:
config: .htmltest.yml
- name: Upload artifact
# Only upload from the current Hugo version; the floor-version run is for compatibility check.
if: matrix.hugo == '0.154.0'
uses: actions/upload-pages-artifact@v5
with:
path: exampleSite/public
+73 -11
View File
@@ -61,21 +61,83 @@ assert "reading time byline" 'reading-time' "$po
# Discovery (Phase 5)
assert "related-posts aside" 'class=related-posts' "$post"
# CSS budget (Phase 1) — duplicates the workflow assert; included so local runs catch it too.
css=$(find "$public_dir" -name 'tsuki.bundle.*.css' -print -quit || true)
if [ -n "$css" ]; then
sz=$(gzip -9 -c "$css" | wc -c)
if [ "$sz" -gt 4200 ]; then
echo "::error::CSS bundle gz ${sz} B exceeds 4200 B budget"
fail=1
else
echo " ok [CSS budget] ${sz} B / 4200 B"
fi
# v0.3.0 feature surface
assert "theme-color meta (light)" 'name=theme-color content="#fbfaf7"' "$home"
assert "theme-color meta (dark)" 'name=theme-color content="#14151a"' "$home"
assert "aria-pressed SSR" 'data-theme-toggle [^>]*aria-pressed=false|aria-pressed=false [^>]*data-theme-toggle' "$home"
assert "breadcrumbs nav on post" 'class=breadcrumbs' "$post"
assert "BreadcrumbList JSON-LD" '"@type":"BreadcrumbList"' "$post"
assert "no breadcrumbs on home" 'class=breadcrumbs' "$home" 0
assert "prev/next nav" 'class=prev-next' "$post"
assert "rel=prev on post" 'rel=prev' "$post"
assert "linkToSection aria-label" 'class=heading-anchor [^>]*aria-label' "$post"
assert "no speculationrules by default" 'speculationrules' "$home" 0
# llm.txt artifact
if [ -f "$public_dir/llm.txt" ]; then
echo " ok [llm.txt artifact present]"
else
echo "::error::Could not locate tsuki.bundle.*.css"
echo "::error::FAIL [llm.txt artifact present] expected $public_dir/llm.txt"
fail=1
fi
# Pagefind preload-swap on search (rel=preload as=style)
if [ -f "$public_dir/search/index.html" ]; then
assert "Pagefind preload swap" 'rel=preload as=style href=[^ >]*pagefind' "$public_dir/search/index.html"
fi
# Giscus preconnect: comments disabled in demo → must NOT emit
assert "no giscus preconnect (disabled)" 'preconnect[^>]*giscus' "$post" 0
# Per-page CSS bundle gating: home gets home.css, post does NOT
assert "home loads home.css" 'home\.min\.[0-9a-f]+\.css' "$home"
assert "post does not load home.css" 'home\.min\.[0-9a-f]+\.css' "$post" 0
assert "post loads single.css" 'tsuki\.single\.min\.[0-9a-f]+\.css' "$post"
assert "home does not load single.css" 'tsuki\.single\.min\.[0-9a-f]+\.css' "$home" 0
# code-copy.js gate: only post pages should load it
assert "post loads code-copy.js" 'code-copy\.[0-9a-f]+\.js' "$post"
assert "home does not load code-copy.js" 'code-copy\.[0-9a-f]+\.js' "$home" 0
# Per-kind CSS budget — every page kind's total stylesheet payload ≤ 4200 B gz.
# Iterate over each kind's representative HTML, extract its <link rel=stylesheet> CSS,
# sum gzipped sizes (excluding third-party like /pagefind/), assert each kind under budget.
check_kind_css_budget() {
local label="$1" html="$2"
[ -f "$html" ] || { echo " skip [$label CSS budget] no HTML at $html"; return; }
local total=0 missing=0
while IFS= read -r href; do
# strip query string + leading slash + baseURL prefix
local rel="${href#/tsuki/}"
rel="${rel#/}"
local fpath="$public_dir/$rel"
if [ -f "$fpath" ]; then
local sz
sz=$(gzip -9 -c "$fpath" | wc -c)
total=$((total + sz))
else
missing=$((missing + 1))
fi
done < <(grep -oE 'rel=stylesheet href=[^ >]+\.css' "$html" | grep -v '/pagefind/' | sed -E 's|rel=stylesheet href=||')
if [ "$total" -gt 4200 ]; then
echo "::error::[$label CSS budget] ${total} B gz exceeds 4200 B"
fail=1
else
echo " ok [$label CSS budget] ${total} B / 4200 B (${missing} third-party skipped)"
fi
}
# Pick representative HTML per kind from the build.
list_html="$public_dir/post/index.html"
search_html="$public_dir/search/index.html"
archives_html="$public_dir/archives/index.html"
check_kind_css_budget "home" "$home"
check_kind_css_budget "post" "$post"
[ -f "$list_html" ] && check_kind_css_budget "list" "$list_html"
[ -f "$archives_html" ] && check_kind_css_budget "archives" "$archives_html"
[ -f "$search_html" ] && check_kind_css_budget "search" "$search_html"
echo
[ "$fail" -eq 0 ] && echo "All smoke tests passed." || echo "Smoke tests failed."
exit "$fail"