[#36] [#37] CI: check link anchors, head meta tags and the robots.txt Sitemap line - #38
Conversation
maximthomas
left a comment
There was a problem hiding this comment.
praise: the anchor filter is defined once and the gate is proven against real lychee output.
top_of_pagelives only in.github/build-baseline/broken-links.jq:22-24, andcheck.sh:41consumes it, so the gate and the summary share one predicate.- The
Buildrun at 842554e is green with no::erroror::noticelines, so the filter matches lychee 0.24.2's real JSON (Cannot find fragment,file:///site/URLs) and both baselines reproduce exactly. - The head meta step runs under
!cancelled() && steps.site.outcome == 'success'(build.yml:138), so one run reports both checks.
issue (non-blocking): the job summary counts and lists every occurrence of a broken link, while check.sh compares distinct page/link pairs.
.github/workflows/build.yml:97-106, .github/build-baseline/check.sh:41
lychee 0.24.2 does not deduplicate requests and never caches file URIs, and each error_map entry carries its own span, so a page that links the same broken anchor twice yields two lines. chap-jee-agents-features.html → index.html#j2ee-agent-general-properties (two hrefs, in the baseline) is printed twice and counted as 2 in the summary, and once by check.sh (sort -u). That contradicts "so the two cannot disagree"; pass/fail is unaffected. .errors counts occurrences too, so K must keep using the undeduplicated count.
all=$(jq -r -f .github/build-baseline/broken-links.jq build/lychee.json)
broken=$(printf '%s\n' "$all" | LC_ALL=C sort -u)
{
echo "## Links"
echo
jq -r --argjson n "$(printf '%s' "$broken" | grep -c . || true)" \
--argjson all "$(printf '%s' "$all" | grep -c . || true)" \
'"\(.total) checked, \($n) broken (\(.errors - $all) more point to the top of their target page)."' build/lychee.jsonsuggestion (non-blocking): no self-test case pins the Cannot find fragment status guard of top_of_page.
.github/workflows/build.yml:120-133, .github/build-baseline/broken-links.jq:23
Both fragment fixtures carry status Cannot find fragment. Replace broken-links.jq:23 with true and the self-test still exits 1/0/1 as at HEAD. The Compare step stays green as well: check.sh fails only on new entries (:72) and reports vanished ones as ::notice (:77). A new link to a missing page shaped dir/foo.html#foo would then be dropped silently, as would the 10 such entries already in broken-links.txt. The PR's two named mutants are killed; this third one is not.
echo '{"error_map":{"/site/a.html":[{"url":"file:///site/d/b.html#b","status":{"text":"Cannot find file"}}]}}' \
> "$t/build/lychee.json"
if GITHUB_STEP_SUMMARY=/dev/null "$t/.github/build-baseline/check.sh" > /dev/null; then
echo "check.sh passed a missing page linked at its own id" >&2
exit 1
fiPin: add this case after case 3. It turns red against the guard-less mutant. Changing case 2's URL to file:///site/d/b.html#b also exercises split("/") | last and rtrimstr(".html").
suggestion (non-blocking): self-test cases 1 and 3 accept any non-zero exit of check.sh, not the reported new entry.
.github/workflows/build.yml:116, :130
stdout is discarded, and neither exit code 1 (check.sh:94) nor the ::error title=New … line (check.sh:70) is checked. A mutant that crashes on the new-entry path (a broken escape at check.sh:69 under set -e) exits 3 with no annotation and passes both cases (measured: rc 3/0/3, 0 ::error title=New lines). Case 2 never reaches that path, and the real Compare step has no new entries.
out=$(GITHUB_STEP_SUMMARY=/dev/null "$t/.github/build-baseline/check.sh") && rc=0 || rc=$?
if [ "$rc" -ne 1 ] || ! grep -q '^::error title=New .*b#c' <<< "$out"; then
echo "check.sh did not report the broken anchor" >&2
exit 1
fiPin: case 3 as above, and case 1 the same with its own message.
suggestion (non-blocking): the head meta / robots.txt step has no CI self-test; only the real site feeds it.
.github/workflows/build.yml:136-177
On a correct site, any mutant that hides problems stays green, e.g. deleting :159 or -eq 1 → -ge 0. :164 catches only an empty page set. The failing direction (571 problems) was shown by a local run, not in CI.
Pin: move build.yml:140-177 into a script that takes the site directory (e.g. .github/build-baseline/head-meta.sh). In the self-test, feed it a one-page fixture site with a matching robots.txt, an empty sitemap.xml and a page that lacks og:image and has twitter:card twice, and expect a non-zero exit.
suggestion (non-blocking): the redirect-page skip matches http-equiv="refresh" anywhere in the file, not only the redirect <meta>.
.github/workflows/build.yml:153
Asciidoctor escapes < but not ", so a content page that quotes a refresh tag (inline or in a listing) is skipped, is left out of pages, and goes unchecked for all six tags. No page does today, so this is latent. Antora's redirect page writes <meta http-equiv="refresh" content="0; url=…"> (@antora/redirect-producer/lib/produce-redirects.js:150), and escaped body text cannot contain that.
grep -qF '<meta http-equiv="refresh"' "$f" && continue- publish.yml: install with npm ci so that package-lock.json is honoured - antora-playbook.yml: use the Antora default UI bundle kept in ui/ui-bundle.zip instead of the GitLab HEAD snapshot - build.yml: build the site for every pull request and report Antora errors/warnings and broken links (lychee --offline) in the job summary; not enforced yet because of known content errors Refs OpenIdentityPlatform#25
Node.js 20 actions are deprecated and are forced onto Node 24 with a warning. Use the current majors: checkout v7, setup-node v7, configure-pages v6, upload-pages-artifact v5, deploy-pages v5. Refs OpenIdentityPlatform#25
The known ones, which come from the product repositories, are listed in .github/build-baseline; check.sh fails on any other and reports the known ones that are gone. check.sh --update rewrites the lists. Refs OpenIdentityPlatform#25
- check.sh: fail on a missing or malformed build/antora.log or build/lychee.json instead of reading it as an empty build (and emptying the lists with --update) - check.sh: keep repeated Antora errors, so that a second occurrence of a known one fails the check - check.sh, build.yml: escape %, CR and LF in annotation text - build.yml: remove build/antora.log before the build, Antora appends - build.yml: report the Antora messages also when Antora fails - build.yml: self-test that check.sh fails on a new Antora error Anchors are not checked yet, see OpenIdentityPlatform#36. Refs OpenIdentityPlatform#25
- build.yml: keep a multi-line Antora message in one row of the summary table - build.yml: self-test check.sh with empty known lists, on a new Antora error and a new broken link, and once both are known - check.sh: escape CR in annotation text as well Refs OpenIdentityPlatform#25
The OpenAM and OpenDJ docs uploaded since fixed 2 of the 4 known Antora errors and 32 of the 42 known broken links. Rewritten with check.sh --update from a build of the current master. Refs OpenIdentityPlatform#25
842554e to
a16ac0a
Compare
|
Done in 38afac7 (the branch is rebased onto the current head of #28, a2c1a17):
The new steps pass locally, and they fail against each of these mutants: the status guard of Baseline: after #28 dropped the links fixed on |
Run lychee with --include-fragments. A link to the id of its target page itself is dropped (Antora renders the page title without an id), in broken-links.jq, shared by check.sh and the job summary; the self-test covers both cases. The 35 broken anchors from the product repositories are added to broken-links.txt (of the 37 in OpenIdentityPlatform#36, the two .:chap-jee-agent-config.adoc ones are fixed on master). A new step fails when robots.txt has no Sitemap line for site.url, or a page does not carry exactly one description, og:title, og:description, og:image, twitter:card and og:url tag. Fixes OpenIdentityPlatform#36 Fixes OpenIdentityPlatform#37
- build.yml: list and count a broken link once in the job summary, as check.sh does - build.yml: self-test that check.sh reports a broken anchor and a missing page linked at its own id, by exit code and annotation - head-meta.sh: the head meta and robots.txt check, moved out of build.yml and self-tested on a test site - head-meta.sh: skip only the redirect <meta> of Antora, not a page that quotes it Refs OpenIdentityPlatform#36 Refs OpenIdentityPlatform#37
a16ac0a to
38afac7
Compare
maximthomas
left a comment
There was a problem hiding this comment.
praise: 38afac7 closes every round-1 item, and the new check has its own self-test.
head-meta.sh:43detects a redirect page by<meta http-equiv="refresh". Asciidoctor escapes that<, and the self-test page quotes<meta http-equiv="refresh"(build.yml:163), so the loose match from round 1 turns the step red.build.yml:99deduplicates the Links summary withsort -u, so its count and table agree withcheck.sh:41. The head'sBuildrun has no "No longer found" notice, so both baselines match the build exactly.- The anchor cases of the baseline self-test require exit 1 and the exact
::error title=New broken links::…line (build.yml:122-124,:145-148).
Fixes #36
Fixes #37
Stacked on #28. Both issues extend the
Buildworkflow and the baseline check that #28 adds, so this branch carries the commits of #28 (its current head, a2c1a17). Only the commits after it are this change; the branch will be rebased ontomasteronce #28 is merged.#36: anchors of links
build.yml: lychee runs with--include-fragments..github/build-baseline/broken-links.jq(new): the broken links of the lychee report aspage <TAB> link, used by bothcheck.shand the job summary. It drops aCannot find fragmentwhose anchor is the name of the target page itself (chap-resource-conf#chap-resource-conf): Antora renders the page title without an id, so the browser opens the top of the page, which is where the link points anyway. lychee's regular expressions have no back references, so this is done on the JSON report, as proposed in CI: check the anchors of links in the pull request build #36.check.shdoes, and shows how many links were left out:N checked, M broken (K more point to the top of their target page).(K counts occurrences, as lychee'serrorsdoes.)check.shmust pass a link to the top of its target page, and must exit 1 with aNew broken linksannotation on a broken anchor and on a missing page linked at its own id (d/b.html#basCannot find file).broken-links.txt: 35 broken anchors are added withcheck.sh --update: the 37 listed in CI: check the anchors of links in the pull request build #36 but the two.:chap-jee-agent-config.adocones, which [#1154] Replace legacy ForgeRock relative links in the guides with Antora xrefs OpenAM#1158 has fixed onmaster. The 10 known links of [#25] CI: build pull requests and make the site build reproducible #28 are unchanged. Reporting them in the product repositories is not done yet, it follows separately.#37: head meta tags and the robots.txt Sitemap line
.github/build-baseline/head-meta.sh <site>(new), run as a new last step, also when the comparison with the known problems fails, so that one run reports both. It fails when:robots.txthas no lineSitemap: <site.url>/sitemap.xml,site.urlread fromantora-playbook.yml, orsitemap.xmlis missing;<meta name="description",og:title,og:description,og:image,twitter:card,og:url;og:urlis not required on404.html. Redirect pages (<meta http-equiv="refresh", as Antora writes them) and the API docs copied bynpm run copyApiDocs(the same paths as the link check excludes) are skipped.Problems go to the job summary and as error annotations. A self-test step feeds the script a test site with an empty
robots.txt, nositemap.xml, a redirect page and a page withoutog:image, withtwitter:cardtwice, that quotes a refresh tag, and requires exactly those four problems.Verification
On a local build of this branch, with the commands of the workflow:
--include-fragments: 382 distinct broken anchors, of which 347 to the page itself and the 35 above;check.sh: 0 new, 0 no longer found; the job summary: 45 broken, 45 rows;broken-links.jqthe status guard replaced bytrue, no filter, every fragment error dropped, the whole path compared, nortrimstr;check.shexiting on the new-entry path before the annotation; inhead-meta.shthe tag count check deleted,-eq 1→-ge 0, a loosehttp-equiv="refresh"match, the robots.txt or the sitemap.xml check deleted, always exit 0;head-meta.sh build/site: 285 pages checked, 0 problems.