From 3357a604d36f651814377da38303a7d689d337ce Mon Sep 17 00:00:00 2001 From: Craig Osterhout Date: Mon, 6 Jul 2026 13:05:57 -0700 Subject: [PATCH 1/4] check for circular aliases Signed-off-by: Craig Osterhout --- .github/workflows/build.yml | 1 + Dockerfile | 5 ++++ docker-bake.hcl | 8 +++++- hack/test/validate_aliases | 57 +++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 hack/test/validate_aliases diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 161fd2bdfffc..cb83d2946dd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,6 +79,7 @@ jobs: - test - unused-media - test-go-redirects + - validate-aliases - dockerfile-lint - validate-vendor steps: diff --git a/Dockerfile b/Dockerfile index 117b22f872d9..1ef567a954b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -163,6 +163,11 @@ RUN --mount=type=bind,src=pagefind.yml,target=pagefind.yml \ FROM scratch AS index COPY --from=pagefind /pagefind . +# validate-aliases checks for circular aliases in content frontmatter +FROM alpine:${ALPINE_VERSION} AS validate-aliases +WORKDIR /test +RUN --mount=type=bind,target=. ./hack/test/validate_aliases + # test-go-redirects checks that the /go/ redirects are valid FROM alpine:${ALPINE_VERSION} AS test-go-redirects WORKDIR /work diff --git a/docker-bake.hcl b/docker-bake.hcl index cd32840eb6c8..d71598fe57b0 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -40,7 +40,7 @@ target "release" { } group "validate" { - targets = ["lint", "test", "unused-media", "test-go-redirects", "dockerfile-lint", "validate-vendor"] + targets = ["lint", "test", "unused-media", "test-go-redirects", "validate-aliases", "dockerfile-lint", "validate-vendor"] } target "test" { @@ -76,6 +76,12 @@ target "test-go-redirects" { provenance = false } +target "validate-aliases" { + target = "validate-aliases" + output = ["type=cacheonly"] + provenance = false +} + target "dockerfile-lint" { call = "check" } diff --git a/hack/test/validate_aliases b/hack/test/validate_aliases new file mode 100644 index 000000000000..b4d243982e5c --- /dev/null +++ b/hack/test/validate_aliases @@ -0,0 +1,57 @@ +#!/usr/bin/env sh +set -e + +echo "Checking for circular aliases..." + +ERRORS=$(mktemp) + +find content -name "*.md" | sort | while read -r file; do + # Compute the canonical URL for this file based on hugo.yaml permalink config: + # manuals pages: /:sections[1:]/:slugorcontentbasename/ (strips "manuals") + # all other pages: standard Hugo URL + path="${file#content/}" + + if echo "$path" | grep -q "^manuals/"; then + path="${path#manuals/}" + fi + + if [ "$(basename "$path")" = "_index.md" ]; then + dir="$(dirname "$path")" + if [ "$dir" = "." ]; then + page_url="/" + else + page_url="/$dir/" + fi + else + page_url="/$(echo "$path" | sed 's/\.md$//')/" + fi + + # Extract aliases from YAML frontmatter + aliases=$(awk ' + BEGIN { fm=0; in_aliases=0 } + /^---$/ { + if (fm == 0) { fm=1; next } + else { exit } + } + fm && /^aliases:/ { in_aliases=1; next } + fm && in_aliases && /^ - / { print substr($0, 5) } + fm && in_aliases && /^[^ ]/ { in_aliases=0 } + ' "$file") + + for alias in $aliases; do + # Normalize: ensure leading and trailing slash + alias=$(echo "$alias" | sed 's|/*$|/|; s|^/*|/|') + if [ "$alias" = "$page_url" ]; then + echo "Circular alias in $file: '$alias' resolves to the same URL as the page" >> "$ERRORS" + fi + done +done + +if [ -s "$ERRORS" ]; then + cat "$ERRORS" + rm "$ERRORS" + exit 1 +fi + +rm "$ERRORS" +echo "No circular aliases found." From 5157f0d6307602c32fa457c2ed6017f2f347b6be Mon Sep 17 00:00:00 2001 From: Craig Osterhout Date: Mon, 6 Jul 2026 13:10:51 -0700 Subject: [PATCH 2/4] update Signed-off-by: Craig Osterhout --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1ef567a954b6..2f264c83dfec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -166,7 +166,7 @@ COPY --from=pagefind /pagefind . # validate-aliases checks for circular aliases in content frontmatter FROM alpine:${ALPINE_VERSION} AS validate-aliases WORKDIR /test -RUN --mount=type=bind,target=. ./hack/test/validate_aliases +RUN --mount=type=bind,target=. sh hack/test/validate_aliases # test-go-redirects checks that the /go/ redirects are valid FROM alpine:${ALPINE_VERSION} AS test-go-redirects From d44005a5fa9329c68d251b1025ebdc945c7d8e49 Mon Sep 17 00:00:00 2001 From: Craig Osterhout Date: Mon, 6 Jul 2026 13:15:25 -0700 Subject: [PATCH 3/4] agent feedback Signed-off-by: Craig Osterhout --- hack/test/validate_aliases | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hack/test/validate_aliases b/hack/test/validate_aliases index b4d243982e5c..e6f8178f4b68 100644 --- a/hack/test/validate_aliases +++ b/hack/test/validate_aliases @@ -38,13 +38,18 @@ find content -name "*.md" | sort | while read -r file; do fm && in_aliases && /^[^ ]/ { in_aliases=0 } ' "$file") - for alias in $aliases; do + set -f + while IFS= read -r alias; do + [ -z "$alias" ] && continue # Normalize: ensure leading and trailing slash alias=$(echo "$alias" | sed 's|/*$|/|; s|^/*|/|') if [ "$alias" = "$page_url" ]; then echo "Circular alias in $file: '$alias' resolves to the same URL as the page" >> "$ERRORS" fi - done + done < Date: Mon, 6 Jul 2026 13:17:00 -0700 Subject: [PATCH 4/4] more agent feedback Signed-off-by: Craig Osterhout --- hack/test/validate_aliases | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hack/test/validate_aliases b/hack/test/validate_aliases index e6f8178f4b68..b67987ca5797 100644 --- a/hack/test/validate_aliases +++ b/hack/test/validate_aliases @@ -33,6 +33,17 @@ find content -name "*.md" | sort | while read -r file; do if (fm == 0) { fm=1; next } else { exit } } + fm && /^aliases: \[/ { + line = substr($0, index($0, "[") + 1) + gsub(/\].*/, "", line) + n = split(line, parts, ",") + for (i = 1; i <= n; i++) { + val = parts[i] + gsub(/^[ \t"'"'"']+|[ \t"'"'"']+$/, "", val) + if (val != "") print val + } + next + } fm && /^aliases:/ { in_aliases=1; next } fm && in_aliases && /^ - / { print substr($0, 5) } fm && in_aliases && /^[^ ]/ { in_aliases=0 }