From 9ab1e4b1195f5cfa60a52d2d8a757d30a881a7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6?= <14975046+skjnldsv@users.noreply.github.com> Date: Wed, 28 Oct 2026 20:04:19 +0100 Subject: [PATCH 1/3] fix(go.php): fix admin-sso and admin-theming-icons targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit admin-sso pointed to configuration_server/sso_configuration.html, which is not part of the docs. Point it to the OpenID Connect page, the only SSO page we have. The theming icons heading anchor is #icon-theming, not #theming-of-icons. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com> --- go.php/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 @@

Redirecting to documentation...

'admin-sharing-federated': '/admin_manual/configuration_files/federated_cloud_sharing_configuration.html', 'admin-source_install': '/admin_manual/installation/source_installation.html', 'admin-sse-legacy-format': '/admin_manual/configuration_files/encryption_migration.html', - 'admin-sso': '/admin_manual/configuration_server/sso_configuration.html', + 'admin-sso': '/admin_manual/configuration_user/user_auth_oidc.html', 'admin-system-requirements': '/admin_manual/installation/system_requirements.html', 'admin-theming': '/admin_manual/configuration_server/theming.html', - 'admin-theming-icons': '/admin_manual/configuration_server/theming.html#theming-of-icons', + 'admin-theming-icons': '/admin_manual/configuration_server/theming.html#icon-theming', 'admin-transactional-locking': '/admin_manual/configuration_files/files_locking_transactional.html', 'admin-trusted-domains': '/admin_manual/installation/installation_wizard.html#trusted-domains', 'admin-update': '/admin_manual/maintenance/update.html', From 0b1286b1b98cf5110d5b666cf17e44316da47fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6?= <14975046+skjnldsv@users.noreply.github.com> Date: Wed, 28 Oct 2026 20:04:20 +0100 Subject: [PATCH 2/3] ci: check go.php redirect targets against the master build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every target must start with / (go.php appends it to the version path), the page must exist in the build and its anchor must be an id on the page. Runs in the link-check job, on master builds only, since go.php is only deployed from master. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com> --- .github/workflows/sphinxbuild.yml | 19 ++++++++ build/check-go-php-redirects.sh | 43 +++++++++++++++++ build/tests/test-check-go-php-redirects.sh | 55 ++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100755 build/check-go-php-redirects.sh create mode 100644 build/tests/test-check-go-php-redirects.sh 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..8428500aae3 --- /dev/null +++ b/build/check-go-php-redirects.sh @@ -0,0 +1,43 @@ +#!/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 + + path="${build_dir}${target%%#*}" + if [[ -d "$path" ]]; then + path="${path%/}/index.html" + fi + if [[ ! -f "$path" ]]; then + fail "'${key}': ${target%%#*} does not exist in the build" + continue + fi + + if [[ "$target" == *#* ]] && ! grep -qF "id=\"${target#*#}\"" "$path"; then + fail "'${key}': anchor #${target#*#} not found in ${target%%#*}" + 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..ebad304017a --- /dev/null +++ b/build/tests/test-check-go-php-redirects.sh @@ -0,0 +1,55 @@ +#!/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" +echo '' > "$tmp/build/admin_manual/index.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 "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)) From 6a5471e74c7f3b95f5a4be74111cdf73bfdbdc2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6?= <14975046+skjnldsv@users.noreply.github.com> Date: Thu, 29 Oct 2026 02:39:18 +0100 Subject: [PATCH 3/3] ci: check user manual go.php targets in English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit go.php replaces the second user_manual segment with the reader's language, and the staged build has no user_manual/index.html (the deploy adds it). Check those targets the same way, against en. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com> --- build/check-go-php-redirects.sh | 12 +++++++++--- build/tests/test-check-go-php-redirects.sh | 12 +++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/build/check-go-php-redirects.sh b/build/check-go-php-redirects.sh index 8428500aae3..d99a386d02e 100755 --- a/build/check-go-php-redirects.sh +++ b/build/check-go-php-redirects.sh @@ -26,17 +26,23 @@ while read -r key target; do continue fi - path="${build_dir}${target%%#*}" + 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}': ${target%%#*} does not exist in the build" + 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 ${target%%#*}" + fail "'${key}': anchor #${target#*#} not found in ${page}" fi done < <(sed -nE "s/^[[:space:]]*'([a-z0-9-]+)': '([^']*)',?[[:space:]]*$/\1 \2/p" "$go_php") diff --git a/build/tests/test-check-go-php-redirects.sh b/build/tests/test-check-go-php-redirects.sh index ebad304017a..ec92aeab666 100644 --- a/build/tests/test-check-go-php-redirects.sh +++ b/build/tests/test-check-go-php-redirects.sh @@ -12,8 +12,10 @@ trap 'rm -rf "$tmp"' EXIT failures=0 -mkdir -p "$tmp/build/admin_manual/ai" +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 @@ -40,6 +42,14 @@ 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',")"