From d0f617d91988d6479aa2ab5ee65fa3fe7ece0eb5 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Fri, 15 May 2026 20:31:00 +0700 Subject: [PATCH] fix(ci): correct subpath URL emit + build into public/tsuki for htmltest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run 25919953710 still failed htmltest on both Hugo legs. Two distinct issues, both pre-existing latent bugs that v0.3.0 made impossible to ignore: 1. Several theme templates piped paths with a leading slash through `relURL`, which in Hugo means "this is already absolute — leave it alone." The intended subpath prefix (`/tsuki/`) was therefore never added, producing hrefs like `href="/search/"` and `href="/"`. In production the GitHub Pages mount masked this (broken links from the page's perspective still happened to resolve under the repo path most of the time), but htmltest correctly reported them as broken. Fix by either: - using `site.Home.RelPermalink` for the home link, or - piping a string without a leading slash through `relLangURL` (so Hugo prepends the baseURL path), or - stripping the leading slash from data-driven URLs first via `strings.TrimPrefix "/"` and then `relLangURL`. Affected: header.html, search-button.html, recent-posts.html, home/hero.html, nav.html, 404.html, search/list.html (Pagefind UI asset URLs). 2. htmltest has no URL-rewriting feature (the `URLSwap` config in the prior `d30f50f` "fix" was never read by htmltest — confirmed by reading htmltest 0.17.0 source). The only clean way to make internal-link checking work with a `/repo/`-style baseURL is to mirror the URL structure on disk. CI now builds with `hugo --destination public/tsuki`, runs Pagefind/smoke/htmltest against that path, and uploads `exampleSite/public/tsuki/` as the Pages artifact. The artifact contents become the served site under `/tsuki/`, identical to the prior behaviour from the browser's point of view. `.htmltest.yml` drops the dead URLSwap block. Also: `tmp/` (htmltest's runtime cache) added to .gitignore so local runs don't dirty the working tree. Local verification: 32 smoke checks green; htmltest reports `✔✔✔ passed, tested 29 documents` against the subpath build. --- .github/workflows/pages.yml | 15 ++++++++++----- .gitignore | 1 + .htmltest.yml | 12 ++++-------- layouts/404.html | 2 +- layouts/_partials/header.html | 2 +- layouts/_partials/home/hero.html | 2 +- layouts/_partials/home/recent-posts.html | 2 +- layouts/_partials/nav.html | 2 +- layouts/_partials/search-button.html | 2 +- layouts/search/list.html | 4 ++-- 10 files changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 96c606c..cd67ef3 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -44,14 +44,18 @@ jobs: run: npm ci - name: Build site + # Build into public/tsuki so the on-disk layout mirrors the URL structure + # (`/tsuki/foo` href → exampleSite/public/tsuki/foo file). This lets + # htmltest resolve internal links without needing URL-rewriting (which + # htmltest does not support). working-directory: exampleSite - run: hugo --gc --minify --baseURL "https://tiennm99.github.io/tsuki/" + run: hugo --gc --minify --destination public/tsuki --baseURL "https://tiennm99.github.io/tsuki/" - name: Build Pagefind index - run: npx pagefind --site exampleSite/public + run: npx pagefind --site exampleSite/public/tsuki - name: Smoke tests (SEO + a11y + per-kind CSS budget + features regression guard) - run: ./scripts/smoke-tests.sh exampleSite/public + run: ./scripts/smoke-tests.sh exampleSite/public/tsuki - name: htmltest (broken internal links + HTML5 validation) # Pinned to master SHA (2026-05-10) for supply-chain hygiene. Refresh periodically. @@ -60,11 +64,12 @@ jobs: config: .htmltest.yml - name: Upload artifact - # Only upload from the current Hugo version; the floor-version run is for compatibility check. + # Upload only the inner build (exampleSite/public/tsuki). Pages mounts the + # artifact root at /tsuki/, so the inner contents become the served site. if: matrix.hugo == '0.154.0' uses: actions/upload-pages-artifact@v5 with: - path: exampleSite/public + path: exampleSite/public/tsuki deploy: needs: build diff --git a/.gitignore b/.gitignore index 32e1d11..ee641ae 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ Thumbs.db .vscode/ .idea/ *.swp +tmp/ diff --git a/.htmltest.yml b/.htmltest.yml index b1b325c..eb387c8 100644 --- a/.htmltest.yml +++ b/.htmltest.yml @@ -4,14 +4,10 @@ IgnoreDirectoryMissingTrailingSlash: true IgnoreInternalEmptyHash: true IgnoreEmptyHref: false EnforceHTML5: true -# Strip the /tsuki/ baseURL prefix so links resolve against exampleSite/public/. -# htmltest's URLSwap is a regex substring match (not anchored), so the leading -# "^" used previously silently failed against the minified `href=/tsuki/...` -# outputs in built HTML — every link reported missing. Quoted plain-prefix form -# matches whatever position /tsuki/ appears in the href, which for our build is -# always at the start of relative URLs. -URLSwap: - "/tsuki/": / +# CI builds with `hugo --destination public/tsuki` so the on-disk layout +# mirrors the URL structure (`/tsuki/foo` → exampleSite/public/tsuki/foo). +# htmltest has no URL-rewriting feature, so this is the cleanest way to make +# internal-link resolution work without dual builds. IgnoreURLs: - "^https://giscus.app" - "^https://github.com/" diff --git a/layouts/404.html b/layouts/404.html index 2d57e4a..373ba72 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -4,6 +4,6 @@

404

{{ i18n "pageNotFound" | default "Trang không tồn tại." }}

-

{{ i18n "backHome" | default "Về trang chủ" }}

+

{{ i18n "backHome" | default "Về trang chủ" }}

{{ end }} diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html index 919d5a8..f55f1e0 100644 --- a/layouts/_partials/header.html +++ b/layouts/_partials/header.html @@ -1,5 +1,5 @@