From 9ed656371da86c0447b886d49c1cc6e3c390ff8b Mon Sep 17 00:00:00 2001 From: Michael Niklas Date: Fri, 23 Oct 2026 08:07:16 +0200 Subject: [PATCH 1/5] Add redirects for doc pages moved in #8708 Closes #11790 Co-Authored-By: Claude Opus 5.5 --- doc/conf.py | 5 +++++ doc/whats-new.rst | 3 +++ 2 files changed, 8 insertions(+) diff --git a/doc/conf.py b/doc/conf.py index 4b28bb94c5a..e96b36b6fc2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -322,6 +322,11 @@ "duckarrays.rst": "user-guide/duckarrays.rst", "related-projects.rst": "user-guide/ecosystem.rst", "faq.rst": "get-help/faq.rst", + "getting-started-guide/faq.rst": "get-help/faq.rst", + "help-diagram.rst": "get-help/help-diagram.rst", + "howdoi.rst": "get-help/howdoi.rst", + "ecosystem.rst": "user-guide/ecosystem.rst", + "tutorials-and-videos.rst": "getting-started-guide/tutorials-and-videos.rst", "why-xarray.rst": "getting-started-guide/why-xarray.rst", "installing.rst": "getting-started-guide/installing.rst", "quick-overview.rst": "getting-started-guide/quick-overview.rst", diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 21f3671188a..dabb9d12dab 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -406,6 +406,9 @@ Documentation (:issue:`10832`, :pull:`11156`). By `dolliecoder `_ and `Michael Niklas `_. +- Restore the old URLs of the tutorials, FAQ, "how do I", help diagram and + ecosystem pages, which were moved without redirects (:issue:`11790`). + By `Michael Niklas `_. Performance ~~~~~~~~~~~ From d78362ed5667963e5fba4d61ccf21b461cb37a38 Mon Sep 17 00:00:00 2001 From: Michael Niklas Date: Fri, 23 Oct 2026 08:07:25 +0200 Subject: [PATCH 2/5] Add PR number to whats-new Co-Authored-By: Claude Opus 5.5 --- doc/whats-new.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index dabb9d12dab..628734b86c4 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -407,7 +407,8 @@ Documentation By `dolliecoder `_ and `Michael Niklas `_. - Restore the old URLs of the tutorials, FAQ, "how do I", help diagram and - ecosystem pages, which were moved without redirects (:issue:`11790`). + ecosystem pages, which were moved without redirects (:issue:`11790`, + :pull:`11794`). By `Michael Niklas `_. Performance From b162239437426506af935ce5b9ef936ea2144680 Mon Sep 17 00:00:00 2001 From: Michael Niklas Date: Fri, 23 Oct 2026 08:33:53 +0200 Subject: [PATCH 3/5] Redirect older moved doc pages and check redirects in CI Every page ever added to doc/ must still exist or have a redirect in rediraffe_redirects. Pages removed on purpose without a successor are listed in the check. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci-additional.yaml | 55 ++++++++++++++++++++++++++++ doc/conf.py | 7 ++++ 2 files changed, 62 insertions(+) diff --git a/.github/workflows/ci-additional.yaml b/.github/workflows/ci-additional.yaml index 86371e055e0..c6d80f54a21 100644 --- a/.github/workflows/ci-additional.yaml +++ b/.github/workflows/ci-additional.yaml @@ -435,6 +435,61 @@ jobs: run: | pixi run generate-ops-ci + check-doc-redirects: + name: Check redirects of moved doc pages + runs-on: ubuntu-latest + needs: detect-ci-trigger + if: needs.detect-ci-trigger.outputs.triggered == 'false' + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 # The check looks at every page ever added to doc/. + persist-credentials: false + + # Every page ever added to doc/ must still exist or be redirected in + # rediraffe_redirects in doc/conf.py, so that links to old URLs keep working. + - name: Check that moved or removed doc pages are redirected + shell: python3 {0} + run: | + import ast, subprocess, sys + from pathlib import PurePosixPath + + # Pages that were removed on purpose and have no successor in the docs. + REMOVED_PAGES = { + "getting-started", # xray v0.1 docs + "tutorial", # xray v0.1 docs + "team", # moved to https://xarray.dev/team (GH7061) + "sphinxext/ipython_sphinxext/README", # vendored sphinx extension + } + + def git(*args): + return subprocess.check_output(["git", *args], text=True).splitlines() + + def page_names(paths): + names = set() + for path in map(PurePosixPath, paths): + rel = path.relative_to("doc") + if path.suffix in {".rst", ".md", ".ipynb"} and not any( + part.startswith(("_", ".")) for part in rel.parts + ): + names.add(str(rel.with_suffix(""))) + return names + + conf = ast.parse(open("doc/conf.py").read()) + redirects = next( + ast.literal_eval(node.value) + for node in conf.body + if isinstance(node, ast.Assign) + and getattr(node.targets[0], "id", None) == "rediraffe_redirects" + ) + ever = page_names(git("log", "--format=", "--name-only", "--no-renames", "--diff-filter=A", "--", "doc")) + current = page_names(git("ls-files", "--", "doc")) + missing = ever - current - page_names(f"doc/{name}" for name in redirects) - REMOVED_PAGES + if missing: + print("These doc pages were removed or moved without a redirect in doc/conf.py:") + print("\n".join(f" doc/{name}" for name in sorted(missing))) + sys.exit(1) + zizmor: name: GHA Security Analysis using Zizmor runs-on: ubuntu-latest diff --git a/doc/conf.py b/doc/conf.py index e96b36b6fc2..d4c8da707cf 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -327,6 +327,13 @@ "howdoi.rst": "get-help/howdoi.rst", "ecosystem.rst": "user-guide/ecosystem.rst", "tutorials-and-videos.rst": "getting-started-guide/tutorials-and-videos.rst", + "quickstart.rst": "getting-started-guide/quick-overview.rst", + "examples/quick-overview.rst": "getting-started-guide/quick-overview.rst", + "why-xray.rst": "getting-started-guide/why-xarray.rst", + "whats_new.rst": "whats-new.rst", + "examples.rst": "gallery.rst", + "internals.rst": "internals/index.rst", + "internals/variable-objects.rst": "internals/internal-design.rst", "why-xarray.rst": "getting-started-guide/why-xarray.rst", "installing.rst": "getting-started-guide/installing.rst", "quick-overview.rst": "getting-started-guide/quick-overview.rst", From 99ef93aae294c38b152faee1f9e003a3e0c5b4e9 Mon Sep 17 00:00:00 2001 From: Michael Niklas Date: Fri, 23 Oct 2026 08:34:07 +0200 Subject: [PATCH 4/5] Update whats-new Co-Authored-By: Claude Opus 5.5 --- doc/whats-new.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 628734b86c4..ebac6b5b082 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -406,9 +406,9 @@ Documentation (:issue:`10832`, :pull:`11156`). By `dolliecoder `_ and `Michael Niklas `_. -- Restore the old URLs of the tutorials, FAQ, "how do I", help diagram and - ecosystem pages, which were moved without redirects (:issue:`11790`, - :pull:`11794`). +- Restore the old URLs of documentation pages that were moved without a + redirect, e.g. the tutorials, FAQ and "how do I" pages, and check in CI that + moved or removed pages get a redirect (:issue:`11790`, :pull:`11794`). By `Michael Niklas `_. Performance From 84bed6943139ee8d783784cb1f7226fd13f0134c Mon Sep 17 00:00:00 2001 From: Michael Niklas Date: Fri, 23 Oct 2026 08:39:48 +0200 Subject: [PATCH 5/5] Use bash for the doc redirects check Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci-additional.yaml | 60 +++++++++++----------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci-additional.yaml b/.github/workflows/ci-additional.yaml index c6d80f54a21..03bb415e039 100644 --- a/.github/workflows/ci-additional.yaml +++ b/.github/workflows/ci-additional.yaml @@ -449,46 +449,32 @@ jobs: # Every page ever added to doc/ must still exist or be redirected in # rediraffe_redirects in doc/conf.py, so that links to old URLs keep working. - name: Check that moved or removed doc pages are redirected - shell: python3 {0} + shell: bash run: | - import ast, subprocess, sys - from pathlib import PurePosixPath - - # Pages that were removed on purpose and have no successor in the docs. - REMOVED_PAGES = { - "getting-started", # xray v0.1 docs - "tutorial", # xray v0.1 docs - "team", # moved to https://xarray.dev/team (GH7061) - "sphinxext/ipython_sphinxext/README", # vendored sphinx extension + # Page names: paths below doc/ without suffix, skipping _build, _static, ... + pages() { + grep -E '\.(rst|md|ipynb)$' | grep -vE '/[_.]' | + sed -E 's#^doc/##; s#\.(rst|md|ipynb)$##' | sort -u } - - def git(*args): - return subprocess.check_output(["git", *args], text=True).splitlines() - - def page_names(paths): - names = set() - for path in map(PurePosixPath, paths): - rel = path.relative_to("doc") - if path.suffix in {".rst", ".md", ".ipynb"} and not any( - part.startswith(("_", ".")) for part in rel.parts - ): - names.add(str(rel.with_suffix(""))) - return names - - conf = ast.parse(open("doc/conf.py").read()) - redirects = next( - ast.literal_eval(node.value) - for node in conf.body - if isinstance(node, ast.Assign) - and getattr(node.targets[0], "id", None) == "rediraffe_redirects" + # Pages that were removed on purpose and have no successor in the docs. + removed_pages=( + getting-started # xray v0.1 docs + tutorial # xray v0.1 docs + team # moved to https://xarray.dev/team (GH7061) + sphinxext/ipython_sphinxext/README # vendored sphinx extension ) - ever = page_names(git("log", "--format=", "--name-only", "--no-renames", "--diff-filter=A", "--", "doc")) - current = page_names(git("ls-files", "--", "doc")) - missing = ever - current - page_names(f"doc/{name}" for name in redirects) - REMOVED_PAGES - if missing: - print("These doc pages were removed or moved without a redirect in doc/conf.py:") - print("\n".join(f" doc/{name}" for name in sorted(missing))) - sys.exit(1) + ever=$(git log --format= --name-only --no-renames --diff-filter=A -- doc | pages) + known=$({ + git ls-files doc + sed -n '/^rediraffe_redirects = {/,/^}/s#^ *"\([^"]*\)":.*#doc/\1#p' doc/conf.py + printf 'doc/%s.rst\n' "${removed_pages[@]}" + } | pages) + missing=$(comm -23 <(echo "$ever") <(echo "$known")) + if [ -n "$missing" ]; then + echo "These doc pages were removed or moved without a redirect in doc/conf.py:" + echo "$missing" + exit 1 + fi zizmor: name: GHA Security Analysis using Zizmor