diff --git a/docs/build_multiversion.py b/docs/build_multiversion.py
index dbdba999..46d70d92 100644
--- a/docs/build_multiversion.py
+++ b/docs/build_multiversion.py
@@ -256,6 +256,33 @@ def build_metadata(
return metadata
+def _redirect_page(target_url: str, link_text: str) -> str:
+ """Build a static HTML page that redirects to ``target_url``.
+
+ Parameters
+ ----------
+ target_url : str
+ Relative URL the browser should be redirected to.
+ link_text : str
+ Human-readable text for the fallback link.
+
+ Returns
+ -------
+ str
+ Complete HTML document contents for the redirect page.
+ """
+ return "\n".join(
+ [
+ "",
+ '',
+ f'',
+ f'',
+ f'{link_text}',
+ "",
+ ]
+ )
+
+
def write_latest_redirect(output_path: Path, latest_version_name: str) -> None:
"""Write a root ``index.html`` redirect to the latest built version.
@@ -274,22 +301,77 @@ def write_latest_redirect(output_path: Path, latest_version_name: str) -> None:
output_path.mkdir(parents=True, exist_ok=True)
redirect_path = output_path / "index.html"
redirect_path.write_text(
- "\n".join(
- [
- "",
- '',
- '',
- f'',
- f''
- f"Open Merlin {latest_version_name} documentation",
- "",
- ]
+ _redirect_page(
+ f"{latest_version_name}/index.html",
+ f"Open Merlin {latest_version_name} documentation",
),
encoding="utf-8",
)
+def write_legacy_page_redirects(
+ output_path: Path,
+ latest_version_name: str,
+ docnames: list[str],
+) -> None:
+ """Write root-level redirects for pre-versioning, unversioned page URLs.
+
+ Before multi-version documentation was introduced, pages were served
+ directly at the site root, for example
+ ``https://merlinquantum.ai/reproduced_papers/index.html``. Those pages
+ now live under a version prefix, for example
+ ``https://merlinquantum.ai/0.4/reproduced_papers/index.html``. This
+ writes a stub redirect page at each old unversioned location so that
+ existing external links and search engine results reach the
+ equivalent page in the latest built version instead of a 404.
+
+ Parameters
+ ----------
+ output_path : pathlib.Path
+ Root directory containing all versioned HTML builds.
+ latest_version_name : str
+ Public version name that unversioned URLs should redirect into.
+ docnames : list[str]
+ Document names (without suffix) present in the latest version,
+ relative to its Sphinx source directory.
+
+ Returns
+ -------
+ None
+ One redirect page per docname is written under ``output_path``,
+ skipping ``index`` which is already handled by
+ ``write_latest_redirect``.
+ """
+ for docname in docnames:
+ if docname == "index":
+ continue
+ # Build the filename by string concatenation rather than
+ # ``Path.with_suffix()``. Many docnames contain dots, for example
+ # generated API reference pages like
+ # ``api_reference/api/merlin.algorithms.feed_forward``.
+ # ``with_suffix()`` replaces everything after the last dot in the
+ # final path component, which would both write the stub at the wrong
+ # path and collapse multiple docnames onto the same file.
+ legacy_path = output_path / f"{docname}.html"
+ # Guard against a docname escaping the output directory, for example
+ # via a leading "..".
+ if output_path.resolve() not in legacy_path.resolve().parents:
+ continue
+
+ depth = docname.count("/")
+ relative_prefix = "../" * depth
+ target_url = f"{relative_prefix}{latest_version_name}/{docname}.html"
+
+ legacy_path.parent.mkdir(parents=True, exist_ok=True)
+ legacy_path.write_text(
+ _redirect_page(
+ target_url,
+ f"This page moved to Merlin {latest_version_name} documentation",
+ ),
+ encoding="utf-8",
+ )
+
+
def build_version(
*,
repo_path: Path,
@@ -332,7 +414,10 @@ def build_version(
"""
source_path = checkout_path / "docs" / "source"
version_output_path = output_path / version_name
+ # Keep nbsphinx auxiliary images relative to the exported source tree.
+ doctree_path = checkout_path / "docs" / "build" / "doctrees" / version_name
version_output_path.mkdir(parents=True, exist_ok=True)
+ doctree_path.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["MERLIN_DOCS_REPO_PATH"] = str(checkout_path)
@@ -348,6 +433,8 @@ def build_version(
sphinx_build,
"-b",
"html",
+ "-d",
+ str(doctree_path),
"-D",
f"smv_metadata_path={metadata_path}",
"-D",
@@ -499,6 +586,11 @@ def main() -> int:
)
write_latest_redirect(output_path, latest_version_name)
+ write_legacy_page_redirects(
+ output_path,
+ latest_version_name,
+ metadata[latest_version_name]["docnames"],
+ )
print(f"Built versions: {', '.join(selected_versions)}")
print(f"Open {output_path / 'index.html'}")
return 0