diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index c5f29012599..50d8680cdc4 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -46,6 +46,25 @@ jobs: name: ${{ needs.build.outputs.artifact_name }} path: stage/ + # go.php is only deployed from master, so only check it against the master build + - name: Checkout go.php and its check + if: needs.build.outputs.branch_name == 'latest' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + path: repo + sparse-checkout: | + go.php/index.html + build/check-go-php-redirects.sh + build/tests/test-check-go-php-redirects.sh + sparse-checkout-cone-mode: false + + - name: Check go.php redirect targets + if: needs.build.outputs.branch_name == 'latest' + run: | + bash repo/build/tests/test-check-go-php-redirects.sh + repo/build/check-go-php-redirects.sh repo/go.php/index.html "stage/${{ needs.build.outputs.branch_name }}" + - name: Strip canonical links from validation HTML run: | find "stage/${{ needs.build.outputs.branch_name }}" -name '*.html' -print0 | while IFS= read -r -d '' f; do diff --git a/build/check-go-php-redirects.sh b/build/check-go-php-redirects.sh new file mode 100755 index 00000000000..d99a386d02e --- /dev/null +++ b/build/check-go-php-redirects.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# Checks every go.php redirect target against a built documentation folder: +# the target must start with "/" (go.php appends it to the version path), the +# page must exist, and its #anchor, if any, must be an id on that page. +# +# Usage: build/check-go-php-redirects.sh go.php/index.html stage/latest + +set -euo pipefail + +go_php="$1" +build_dir="$2" +failed=0 + +fail() { + echo "::error file=${go_php}::${1}" + failed=1 +} + +while read -r key target; do + if [[ "$target" != /* ]]; then + fail "'${key}': '${target}' must start with /" + continue + fi + + page="${target%%#*}" + # Like go.php, put the language in the second user_manual segment, English here + if [[ "$page" =~ ^/user_manual(/[^/]*)?(/.*)?$ ]]; then + page="/user_manual/en${BASH_REMATCH[2]}" + fi + + path="${build_dir}${page}" + if [[ -d "$path" ]]; then + path="${path%/}/index.html" + fi + if [[ ! -f "$path" ]]; then + fail "'${key}': ${page} does not exist in the build" + continue + fi + + if [[ "$target" == *#* ]] && ! grep -qF "id=\"${target#*#}\"" "$path"; then + fail "'${key}': anchor #${target#*#} not found in ${page}" + fi +done < <(sed -nE "s/^[[:space:]]*'([a-z0-9-]+)': '([^']*)',?[[:space:]]*$/\1 \2/p" "$go_php") + +exit "$failed" diff --git a/build/tests/test-check-go-php-redirects.sh b/build/tests/test-check-go-php-redirects.sh new file mode 100644 index 00000000000..ec92aeab666 --- /dev/null +++ b/build/tests/test-check-go-php-redirects.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# Tests for build/check-go-php-redirects.sh. Run: bash build/tests/test-check-go-php-redirects.sh + +set -euo pipefail + +check="$(dirname "${BASH_SOURCE[0]}")/../check-go-php-redirects.sh" +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT + +failures=0 + +mkdir -p "$tmp/build/admin_manual/ai" "$tmp/build/user_manual/en/files" +echo '' > "$tmp/build/admin_manual/index.html" +echo '' > "$tmp/build/user_manual/en/index.html" +echo '' > "$tmp/build/user_manual/en/files/access_webdav.html" +echo '' > "$tmp/build/admin_manual/ai/overview.html" + +# Runs the check on a go.php holding the given mapping lines, prints exit code and errors +run_check() { + printf ' const mapping = {\n' > "$tmp/go.html" + printf ' %s\n' "$@" >> "$tmp/go.html" + printf ' }\n' >> "$tmp/go.html" + local output status=0 + output=$(bash "$check" "$tmp/go.html" "$tmp/build") || status=$? + echo "$status ${output//"$tmp/go.html"/go.html}" +} + +assert_eq() { + if [ "$2" = "$3" ]; then + echo "ok - $1" + else + echo "not ok - $1" + printf ' expected: %s\n actual: %s\n' "$2" "$3" + failures=$((failures + 1)) + fi +} + +assert_eq "valid page, directory and anchor targets pass" "0 " "$(run_check \ + "'admin': '/admin_manual'," \ + "'admin-ai-pickup-speed': '/admin_manual/ai/overview.html#improve-ai-task-pickup-speed'")" + +assert_eq "user manual targets are checked in English, like go.php redirects them" "0 " "$(run_check \ + "'user': '/user_manual'," \ + "'user-webdav': '/user_manual/de/files/access_webdav.html',")" + +assert_eq "user manual target without a language segment fails" \ + "1 ::error file=go.html::'user-webdav': /user_manual/en/access_webdav.html does not exist in the build" \ + "$(run_check "'user-webdav': '/user_manual/files/access_webdav.html',")" + +assert_eq "target without leading slash fails" \ + "1 ::error file=go.html::'admin-ai-pickup-speed': 'admin_manual/ai/overview.html' must start with /" \ + "$(run_check "'admin-ai-pickup-speed': 'admin_manual/ai/overview.html',")" + +assert_eq "missing page fails" \ + "1 ::error file=go.html::'admin-sso': /admin_manual/sso.html does not exist in the build" \ + "$(run_check "'admin-sso': '/admin_manual/sso.html',")" + +assert_eq "missing anchor fails" \ + "1 ::error file=go.html::'admin-ai': anchor #gone not found in /admin_manual/ai/overview.html" \ + "$(run_check "'admin-ai': '/admin_manual/ai/overview.html#gone',")" + +exit $((failures > 0)) diff --git a/go.php/index.html b/go.php/index.html index 19c07750140..4dcf26f5928 100644 --- a/go.php/index.html +++ b/go.php/index.html @@ -55,10 +55,10 @@