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..2f264c83dfec 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=. sh 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..b67987ca5797 --- /dev/null +++ b/hack/test/validate_aliases @@ -0,0 +1,73 @@ +#!/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: \[/ { + 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 } + ' "$file") + + 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 <