diff --git a/.github/actions/generate-docs/action.yml b/.github/actions/generate-docs/action.yml index 9784ab9e5..4f2328b5d 100644 --- a/.github/actions/generate-docs/action.yml +++ b/.github/actions/generate-docs/action.yml @@ -1,5 +1,9 @@ name: Generate documentation description: Generate all documentation +inputs: + build: + description: Just subcommand for `build-docs` to run + default: all runs: using: composite @@ -18,4 +22,4 @@ runs: enable-cache: true - name: Build documentation shell: bash - run: just build-docs + run: just build-docs::${{ inputs.build }} diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index a0b3d4aad..4282b52b1 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,7 +1,8 @@ name: Build and publish docs on: push: - branches: [main] + # **HACK** + branches: [main, versioned-docs] workflow_dispatch: permissions: @@ -23,7 +24,16 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v6 + with: + fetch-tags: true + - name: Set user name and email for git + run: | + # Needed so we can apply patches + git config --global user.name "GitHub Actions" + git config --global user.email "github-actions[bot]@users.noreply.github.com" - uses: ./.github/actions/generate-docs + with: + build: all_with_old - name: Setup pages uses: actions/configure-pages@v5 - name: Upload artifact diff --git a/build-docs.just b/build-docs.just index d84474184..1a5cf67cd 100644 --- a/build-docs.just +++ b/build-docs.just @@ -1,8 +1,11 @@ # Make rustdoc warnings fatal export RUSTDOCFLAGS := "-D warnings" -# Build all documentation -all: cli-help file-format examples book api +# Build all documentation, except old docs +all: cli-help file-format examples versions book api + +# Build all documentation, including old docs +all_with_old: all old # Build book book: @@ -33,3 +36,15 @@ file-format *ARGS: examples: @echo Building docs for examples @uv run docs/generate_example_docs.py + +# Build TOC for old versions +versions: + @echo Building TOC for old versions of documentation + @uv run docs/generate_versions_docs.py + +# Build documentation for previous releases +old: + @# Clean output dir + @rm -rf book/release + + @uv run docs/build_old_docs.py diff --git a/docs/.gitignore b/docs/.gitignore index ab9fc47fa..9eece75fc 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,3 +1,4 @@ # Generated documentation files command_line_help.md examples.md +versions.md diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 4df0560d3..9d0015481 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -23,3 +23,4 @@ - [Release notes](release_notes/README.md) - [MUSE2 v2.0.0 (October 14, 2025)](release_notes/v2.0.0.md) - [Next unreleased version](release_notes/upcoming.md) +- [Documentation for old versions](versions.md) diff --git a/docs/build_old_docs.py b/docs/build_old_docs.py new file mode 100755 index 000000000..60652575b --- /dev/null +++ b/docs/build_old_docs.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# +# A script to generate documentation for previous releases of MUSE2. + +import shutil +import subprocess as sp +import sys +from pathlib import Path +from tempfile import TemporaryDirectory + +REPO_ROOT = Path(__file__).parent.parent.absolute() +DOCS_DIR = REPO_ROOT / "docs" + +sys.path.append(str(DOCS_DIR / "release")) +from release import get_releases # noqa: E402 + + +def clone_repo_to(dest: Path): + """Clone this repo somewhere else.""" + print("Making a copy of repo") + sp.run(("git", "clone", REPO_ROOT, dest), check=True) # , capture_output=True) + + +def build_docs_for_release(release: str, repo_path: Path, outdir: Path) -> None: + """Build documentation for a given release.""" + print(f"Building docs for {release}") + + # Check out release + sp.run( + ("git", "-C", str(repo_path), "checkout", release), + check=True, + capture_output=True, + ) + + # Apply patch, if there is one + patch_path = DOCS_DIR / "release" / "patches" / f"{release}.patch" + if patch_path.exists(): + sp.run(("git", "-C", str(repo_path), "am", str(patch_path)), check=True) + + # Build docs + sp.run(("just", f"{repo_path!s}/build-docs"), capture_output=True, check=True) + + # Move to output directory + release_outdir = outdir / release + print(f"Copying to {release_outdir}") + shutil.move((repo_path / "book"), release_outdir) + + +def build_old_docs() -> None: + """Build documentation for previous releases.""" + outdir = REPO_ROOT / "book" / "release" + outdir.mkdir(parents=True, exist_ok=True) + + # Clone this repo to a temporary directory + tmpdir = TemporaryDirectory() + repo_path = Path(tmpdir.name) + clone_repo_to(repo_path) + + # Generate documentation for each previous release + for release in get_releases(): + build_docs_for_release(release, repo_path, outdir) + + +if __name__ == "__main__": + build_old_docs() diff --git a/docs/generate_versions_docs.py b/docs/generate_versions_docs.py new file mode 100644 index 000000000..093bdc8a5 --- /dev/null +++ b/docs/generate_versions_docs.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# /// script +# dependencies = [ +# "jinja2", +# ] +# /// +# +# A script to generate the versions.md file, listing links to old versions of documentation. + +import sys +from pathlib import Path + +from jinja2 import Environment, FileSystemLoader + +DOCS_DIR = Path(__file__).parent.absolute() + +sys.path.append(str(DOCS_DIR / "release")) +from release import get_releases # noqa: E402 + + +def generate_versions_md() -> None: + """Write the versions.md file.""" + print("Generating versions.md") + env = Environment(loader=FileSystemLoader(DOCS_DIR / "templates")) + template = env.get_template("versions.md.jinja") + out = template.render(releases=get_releases()) + + with (DOCS_DIR / "versions.md").open("w") as f: + f.write(out) + + +if __name__ == "__main__": + generate_versions_md() diff --git a/docs/release/__init__.py b/docs/release/__init__.py new file mode 100644 index 000000000..0c04937bf --- /dev/null +++ b/docs/release/__init__.py @@ -0,0 +1,18 @@ +"""Common functionality for working with different versions.""" + +import re +import subprocess as sp + + +def is_release_tag(tag: str) -> bool: + """Whether the git tag indicates a version. + + We don't include pre-releases. + """ + return re.match(r"^v[0-9]+\.[0-9]+\.[0-9]+$", tag) is not None + + +def get_releases() -> list[str]: + """Get all release tags for this repo.""" + ret = sp.run(("git", "tag"), capture_output=True, check=True, encoding="utf-8") + return [tag for tag in ret.stdout.splitlines() if is_release_tag(tag)] diff --git a/docs/release/patches/v2.0.0.patch b/docs/release/patches/v2.0.0.patch new file mode 100644 index 000000000..91602f9dd --- /dev/null +++ b/docs/release/patches/v2.0.0.patch @@ -0,0 +1,23 @@ +From 3129c56aea05dd2b7e88c76bb2050fded0529243 Mon Sep 17 00:00:00 2001 +From: Aurash Karimi +Date: Wed, 19 Nov 2025 09:48:11 +0000 +Subject: [PATCH] remove unrecognised parameter + +--- + book.toml | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/book.toml b/book.toml +index 41fba09c..84d181ee 100644 +--- a/book.toml ++++ b/book.toml +@@ -1,7 +1,6 @@ + [book] + authors = ["Alex Dewar"] + language = "en" +-multilingual = false + src = "docs" + title = "MUSE2" + +-- +2.53.0 diff --git a/docs/templates/versions.md.jinja b/docs/templates/versions.md.jinja new file mode 100644 index 000000000..ea6c021a1 --- /dev/null +++ b/docs/templates/versions.md.jinja @@ -0,0 +1,6 @@ +# Previous versions of documentation + +Previous versions of the MUSE2 documentation are available below. +{% for release in releases %} +- [{{ release }}](release/{{ release }}/index.html) +{%- endfor %}