diff --git a/Makefile b/Makefile
index 613c19d92..02143379b 100644
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,8 @@ docs: ## generate Sphinx HTML documentation for the current branch
multidocs: ## generate Sphinx HTML documentation for the multiple versions available
$(MAKE) -C doc clean
sphinx-multiversion doc doc/_build/html
- cp ci/index_redirect.html doc/_build/html/index.html
+ @python -c "import shutil; shutil.copytree('doc/_build/html/master/', 'doc/_build/html/stable/')"
+ @python ci/generateredirects.py "stable" -v --base-path "doc/_build/html" --base-url "https://docs.spyder-ide.org"
linkcheck: ## check that links are still valid
$(MAKE) -C doc linkcheck
diff --git a/ci/generateredirects.py b/ci/generateredirects.py
new file mode 100644
index 000000000..4a46ae967
--- /dev/null
+++ b/ci/generateredirects.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+"""
+Recursively build redirects for the HTML in a given dir to an output dir.
+"""
+
+# Standard library imports
+import argparse
+import os.path
+from pathlib import Path
+
+
+REDIRECT_TEXT = """
+
+
+ Redirecting to master branch
+
+
+
+
+
+"""
+
+
+def generate_redirect(
+ canonical_path,
+ canonical_dir,
+ redirect_dir="",
+ base_path="",
+ base_url="/"
+ ):
+ base_path = Path(base_path).resolve()
+ canonical_dir = base_path / canonical_dir
+ redirect_dir = base_path / redirect_dir
+ if base_url[-1] != "/":
+ base_url += "/"
+
+ redirect_output_path = (
+ redirect_dir / canonical_path.relative_to(canonical_dir))
+ redirect_url = Path(os.path.relpath(
+ canonical_path, start=redirect_output_path.parent))
+ canonical_url = canonical_path.relative_to(base_path)
+
+ output_text = REDIRECT_TEXT.format(
+ redirect_url=redirect_url.as_posix(),
+ base_url=base_url,
+ canonical_url=canonical_url.as_posix(),
+ )
+ with open(redirect_output_path, mode="w",
+ encoding="utf-8", newline="\n") as redirect_file:
+ redirect_file.write(output_text)
+
+ return redirect_output_path
+
+
+def generate_redirects(
+ canonical_dir,
+ base_path="",
+ match_glob="**/*.html",
+ verbose=False,
+ **redirect_kwargs):
+ base_path = Path(base_path).resolve()
+
+ redirect_paths = []
+ for canonical_path in (base_path / canonical_dir).glob(match_glob):
+ redirect_path = generate_redirect(
+ canonical_path=canonical_path,
+ canonical_dir=canonical_dir,
+ base_path=base_path,
+ **redirect_kwargs)
+ redirect_paths.append(redirect_path)
+ if verbose:
+ print("Generated redirect from "
+ f"{redirect_path.relative_to(base_path).as_posix()} "
+ f"--> {canonical_path.relative_to(base_path).as_posix()}")
+
+ return redirect_paths
+
+
+def generate_arg_parser():
+ arg_parser = argparse.ArgumentParser(
+ description="Generate redirects given a directory of HTML files",
+ argument_default=argparse.SUPPRESS)
+
+ arg_parser.add_argument(
+ "canonical_dir", help=(
+ "The directory of pages to build redirects to, "
+ "relative to base_dir."))
+ arg_parser.add_argument(
+ "--redirect-dir", help=(
+ "The dir that should be redirected from and "
+ "the redirect files should be written to, "
+ "relative to base_path. By default, base_path."))
+ arg_parser.add_argument(
+ "--base-path", help=(
+ "The path that canonical_dir and redirect_dir "
+ "are relative to; defaults to the working dir."))
+ arg_parser.add_argument(
+ "--base-url", help=(
+ "Full URL for the site, for use with the canonical tag."))
+ arg_parser.add_argument(
+ "--match-glob", help=(
+ "The glob pattern to match files to redirect to "
+ "in canonical_dir. By default, '**/*.html'."))
+ arg_parser.add_argument(
+ "-v", "--verbose", action="store_true", help=(
+ "If passed, prints each redirect generated."))
+
+ return arg_parser
+
+
+def main(argv=None):
+ arg_parser = generate_arg_parser()
+ parsed_args = arg_parser.parse_args(argv)
+ redirect_paths = generate_redirects(**vars(parsed_args))
+ return redirect_paths
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ci/index_redirect.html b/ci/index_redirect.html
deleted file mode 100644
index f89650943..000000000
--- a/ci/index_redirect.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
- Redirecting to current docs
-
-
-
-
\ No newline at end of file