diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index afbd38a2..ed01b4af 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -43,8 +43,7 @@ jobs: cd docs rm -rf doxygen _build py_api doxygen - # Use multiversion target to build all versions - make multiversion + make html touch _build/html/.nojekyll - name: Upload artifacts uses: actions/upload-pages-artifact@v3 diff --git a/.gitignore b/.gitignore index 9c4da143..81cdc6ef 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,4 @@ __pycache__ .*.swp .idea/ *.so -docs/_static/versions.js -_codeql_detected_source_root \ No newline at end of file +_codeql_detected_source_root diff --git a/docs/Makefile b/docs/Makefile index 285bb7c1..c1fc7365 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -5,38 +5,17 @@ # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build -SPHINXMULTIVERSION ?= sphinx-multiversion SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: - @echo "Usage:" - @echo " make html - Build single-version HTML (fast, for development)" - @echo " make multiversion - Build all versions with sphinx-multiversion" - @echo " make clean - Remove build directory" - @echo "" @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile generate-versions multiversion clean - -# Generate versions.js from git tags before building -generate-versions: - @python3 generate_versions.py - -# Build all documentation versions using sphinx-multiversion -# Use this for production builds or to test version switching -multiversion: generate-versions - @cd .. && python3 -m setuptools_scm --force-write-version-files - @export LC_ALL=C.UTF-8; $(SPHINXMULTIVERSION) "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) $(O) - -# Clean build directory -clean: - @rm -rf $(BUILDDIR) +.PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -# This builds single-version only (fast for development). -%: Makefile generate-versions +%: Makefile @cd .. && python3 -m setuptools_scm --force-write-version-files - @export LC_ALL=C.UTF-8; $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/version-selector.js b/docs/_static/version-selector.js deleted file mode 100644 index 84260920..00000000 --- a/docs/_static/version-selector.js +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -/** - * Version selector for sphinx-multiversion documentation. - * - * The DEFINED_VERSIONS array is auto-generated from git tags by generate_versions.py - * which runs automatically during 'make html'. This ensures the version list stays - * in sync with sphinx-multiversion without manual updates. - * - * The versions.js file (loaded before this script) defines DEFINED_VERSIONS. - */ -(function() { - 'use strict'; - - // DEFINED_VERSIONS is defined in versions.js (auto-generated from git tags) - // Fallback to main only if versions.js failed to load - const versions = (typeof DEFINED_VERSIONS !== 'undefined') ? DEFINED_VERSIONS : [ - { name: 'main (dev)', path: '', version: 'main' } - ]; - - function detectCurrentVersion() { - const path = window.location.pathname; - // Check for version tags first - // Match version tags in the format v0.0.0 within the URL path - const match = path.match(/\/(v\d+\.\d+\.\d+)\//); - if (match) { - return match[1]; - } - // Check for main branch directory - if (path.includes('/main/')) { - return 'main'; - } - // If at root (no version in path), it's main - return 'main'; - } - - function getBasePath() { - const path = window.location.pathname; - // Find how many levels deep we are from the version directory - if (match) { - const depth = match[2].split('/').filter(p => p && p !== 'index.html').length; - return '../'.repeat(depth + 1); - } - // For root level (latest), calculate depth - const segments = path.split('/').filter(p => p && p !== 'index.html'); - return '../'.repeat(segments.length); - } - - function createVersionSelector() { - const currentVersion = detectCurrentVersion(); - const searchDiv = document.querySelector('.wy-side-nav-search'); - - if (!searchDiv) return; - - // Find the title link (mscclpp) - const titleLink = searchDiv.querySelector('a.icon-home'); - - // Create version selector container - const selectorDiv = document.createElement('div'); - selectorDiv.style.padding = '10px'; - selectorDiv.style.paddingTop = '5px'; - selectorDiv.style.paddingBottom = '10px'; - - const select = document.createElement('select'); - select.id = 'version-selector'; - select.style.width = '100%'; - select.style.padding = '5px'; - select.style.backgroundColor = '#2c2c2c'; - select.style.color = '#ffffff'; - select.style.border = '1px solid #404040'; - select.style.borderRadius = '3px'; - - // Add options - versions.forEach(function(version) { - const option = document.createElement('option'); - const isSelected = currentVersion === version.version; - - // Build the URL - use absolute paths from root (without hash) - let url; - const currentPath = window.location.pathname; - - // Extract the page path relative to the version directory - // For /v0.7.0/design/design.html -> design/design.html - // For /index.html -> index.html - let relativePath; - const versionMatch = currentPath.match(/^\/(v\d+\.\d+\.\d+)\/(.*)/); - if (versionMatch) { - // We're in a versioned directory - relativePath = versionMatch[2] || 'index.html'; - } else { - // We're at root (main/dev) - relativePath = currentPath.substring(1) || 'index.html'; - } - - if (version.version === 'main' && version.path === '') { - // For main (dev) at root - url = '/' + relativePath; - } else { - // For versioned releases - url = '/' + version.path + '/' + relativePath; - } - - option.value = url; - option.textContent = version.name; - if (isSelected) { - option.selected = true; - } - select.appendChild(option); - }); - - select.addEventListener('change', function() { - if (this.value) { - const baseUrl = this.value; - const currentHash = window.location.hash; // Get current hash at selection time - const targetUrl = baseUrl + currentHash; // Append current hash to target URL - - // Check if the target page exists using a fetch with abort - const controller = new AbortController(); - const timeoutId = setTimeout(function() { controller.abort(); }, 1000); - - fetch(baseUrl, { - method: 'GET', - signal: controller.signal - }) - .then(function(response) { - clearTimeout(timeoutId); - if (response.ok) { - // Page exists, navigate to it with hash - window.location.href = targetUrl; - } else { - // Page doesn't exist, fall back to version root index.html - // For versioned paths like /v0.8.0/... -> /v0.8.0/index.html - // For root paths like /py_api/... -> /index.html - let fallbackUrl; - const versionMatch = baseUrl.match(/^\/(v\d+\.\d+\.\d+)\//); - if (versionMatch) { - // It's a versioned path - fallbackUrl = '/' + versionMatch[1] + '/index.html'; - } else { - // It's a root path (main/dev) - fallbackUrl = '/index.html'; - } - window.location.href = fallbackUrl; - } - }) - .catch(function(error) { - clearTimeout(timeoutId); - // On error (including timeout), try to navigate anyway - window.location.href = targetUrl; - }); - } - }); - - selectorDiv.appendChild(select); - - // Insert after the title link in the searchDiv - if (titleLink) { - // Insert after the title link element - const nextElement = titleLink.nextSibling; - if (nextElement) { - searchDiv.insertBefore(selectorDiv, nextElement); - } else { - searchDiv.appendChild(selectorDiv); - } - } else { - // Fallback: insert at the beginning of searchDiv - searchDiv.insertBefore(selectorDiv, searchDiv.firstChild); - } - } - - // Initialize when DOM is ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', createVersionSelector); - } else { - createVersionSelector(); - } -})(); diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html deleted file mode 100644 index 56b9326b..00000000 --- a/docs/_templates/layout.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "!layout.html" %} - -{%- block sidebarsearch %} - {{ super() }} - - {# Version selector #} - {% if versions %} -
- - -
- {% endif %} -{%- endblock %} diff --git a/docs/conf.py b/docs/conf.py index e1acc39a..126f6ee0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,16 +43,8 @@ extensions = [ "sphinx.ext.napoleon", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints", - "sphinx_multiversion", ] -smv_tag_whitelist = r"^v\d+\.\d+\.\d+$" -smv_branch_whitelist = r"^main$" -smv_remote_whitelist = None -smv_released_pattern = r"^tags/.*$" -smv_outputdir_format = "{ref.name}" -smv_prefer_remote_refs = False - autosummary_generate = True autodoc_default_options = { "members": True, @@ -85,47 +77,3 @@ mermaid_init_js = "mermaid.initialize({startOnLoad:true});" html_theme = "sphinx_rtd_theme" html_static_path = ["_static"] -html_js_files = [ - "versions.js", # Auto-generated from git tags - must load before version-selector.js - "version-selector.js", -] - - -def setup(app): - """Set up custom Sphinx build hooks for sphinx-multiversion support. - - This function registers a build-finished event handler that copies the - version selector JavaScript files to a shared location accessible by all - versioned documentation builds. - - Args: - app: The Sphinx application instance. - """ - import shutil - from pathlib import Path - - def copy_version_files(app, exception): - """Copy version JS files to the root build directory after a successful build. - - When using sphinx-multiversion, each version's documentation is built into - its own subdirectory (e.g., _build/html/v0.8.0/). The version selector - JavaScript files need to be available at the root _static directory - (_build/html/_static/) so they can be shared across all versions and - properly navigate between different documentation versions. - - Args: - app: The Sphinx application instance. - exception: Exception raised during build, or None if build succeeded. - """ - if exception is None: # Only copy if build succeeded - source_static = Path(app.srcdir) / "_static" - dest_root = Path(app.outdir).parent / "_static" - dest_root.mkdir(parents=True, exist_ok=True) - - # Copy both versions.js and version-selector.js - for filename in ["versions.js", "version-selector.js"]: - source = source_static / filename - if source.exists(): - shutil.copy2(source, dest_root / filename) - - app.connect("build-finished", copy_version_files) diff --git a/docs/generate_versions.py b/docs/generate_versions.py deleted file mode 100644 index 7be7f8b9..00000000 --- a/docs/generate_versions.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -"""Generate versions.js from git tags for the documentation version selector. - -This script reads git tags matching the sphinx-multiversion pattern (vX.Y.Z) -and generates a JavaScript file containing the version list. This ensures the -version selector stays in sync with available documentation versions without -requiring manual updates. - -Usage: - python generate_versions.py - -The script should be run before building documentation with sphinx-multiversion. -""" - -import json -import re -import subprocess -from pathlib import Path - - -def get_git_tags(): - """Get all version tags from git matching vX.Y.Z pattern.""" - try: - result = subprocess.run( - ["git", "tag", "-l", "v*.*.*"], - capture_output=True, - text=True, - check=True, - ) - tags = result.stdout.strip().split("\n") - # Filter to match sphinx-multiversion pattern: ^v\d+\.\d+\.\d+$ - version_pattern = re.compile(r"^v\d+\.\d+\.\d+$") - return [tag for tag in tags if tag and version_pattern.match(tag)] - except subprocess.CalledProcessError: - return [] - - -def version_sort_key(version): - """Extract (major, minor, patch) tuple for sorting.""" - match = re.match(r"v(\d+)\.(\d+)\.(\d+)", version) - if match: - return (int(match.group(1)), int(match.group(2)), int(match.group(3))) - return (0, 0, 0) - - -def generate_versions_js(output_path): - """Generate versions.js file from git tags.""" - tags = get_git_tags() - - # Sort versions in descending order (newest first) - tags.sort(key=version_sort_key, reverse=True) - - # Build the version list with main (dev) first - version_list = [{"name": "main (dev)", "path": "", "version": "main"}] - - for i, version in enumerate(tags): - name = f"{version} (latest)" if i == 0 else version - version_list.append({"name": name, "path": version, "version": version}) - - # Generate JavaScript content - js_content = f"""\ -// Auto-generated from git tags by generate_versions.py - do not edit manually -// Run 'python generate_versions.py' or 'make html' to regenerate -const DEFINED_VERSIONS = {json.dumps(version_list, indent=4)}; -""" - - # Write to output path - output_path = Path(output_path) - output_path.parent.mkdir(parents=True, exist_ok=True) - output_path.write_text(js_content) - print(f"Generated {output_path} with {len(version_list)} versions") - - -if __name__ == "__main__": - # Generate versions.js in _static directory - script_dir = Path(__file__).parent - output_file = script_dir / "_static" / "versions.js" - generate_versions_js(output_file) diff --git a/docs/requirements.txt b/docs/requirements.txt index a74d79e6..3e2233f3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,5 +5,4 @@ pybind11 sphinx_rtd_theme sphinxcontrib-mermaid sphinx-autodoc-typehints -sphinx-multiversion==0.2.4 setuptools_scm