diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index bc7b0b18a9..0273b780f0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,20 +1,14 @@ name: Documentation on: + push: + branches: + - main release: types: - published - workflow_dispatch: - inputs: - force: - description: 'Force' - default: false - required: false - type: boolean - tag: - description: 'Specify tag (empty builds branch HEAD)' - required: false - default: '' + workflow_dispatch: # Allow manually triggering the workflow + concurrency: group: ${{ github.workflow }} @@ -24,35 +18,71 @@ jobs: build: runs-on: ubuntu-latest permissions: - contents: read + contents: write pages: write id-token: write steps: - uses: actions/checkout@v4 with: - ref: ${{ inputs.tag || github.ref }} + ref: ${{ github.ref }} - uses: actions/setup-python@v5 with: {python-version: "3.10"} - name: Install tox run: pip install tox-uv - - name: Build Docs ${{ inputs.force == true && '(Force)' || '' }} - run: tox -e docs-py310 ${{ inputs.force == true && '-- -f -r' || '-- -r' }} - - name: Upload docs artifact - uses: actions/upload-pages-artifact@v1 - with: - path: 'docs/build' - - deploy: - needs: build - runs-on: ubuntu-latest - permissions: - contents: read - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 + - name: Build Docs + run: tox -e docs-py310 -- -r + - name: Configure sphinx bot for pushing and fetch branches + run: | + git config --local user.email "sphinx-upload[bot]@users.noreply.github.com" + git config --local user.name "sphinx-upload[bot]" + - name: Copy latest folder to gh-pages branch + run: | + git fetch origin gh-pages:gh-pages + git checkout gh-pages + if [ -d "latest" ]; then + rm -rf latest + fi + mkdir ./latest + cp -r docs/build/* ./latest + echo "Successfully copied into latest" + - if: ${{ github.event_name == 'release' }} + name: Copy stable folder to gh-pages branch when releasing + run: | + FOLDER=${{github.event.release.tag_name}} + mkdir ./$FOLDER + cp -r docs/build/* ./$FOLDER + echo "Copying files into stable folder" + if [ -d "stable" ]; then + echo "Deleting old stable folder." + rm -rf stable + fi + mkdir ./stable + cp -rv docs/build/* ./stable + echo "Successfully copied into stable folder" + - name: Update gh-pages branch for latest + run: | + git_hash=$(git rev-parse --short "$GITHUB_SHA") + git fetch origin main:main + git show main:docs/scripts/add_version.py > add_version.py + python add_version.py html latest + git add -f latest + git commit -m "Sphinx documentation for ${git_hash} (latest)" + - if: ${{ github.event_name == 'release' }} + name: Add new stable and numbered version to gh-pages branch + run: | + FOLDER=${{github.event.release.tag_name}} + echo "Adding numbered folder" + git add -f $FOLDER + git commit -m "Sphinx documentation for ${git_hash} (${FOLDER})" + echo "Adding stable as a separate folder" + git add -f stable + git commit -m "Sphinx documentation for ${git_hash} (stable)" + echo "Inserting announcement banner to documentation and add link for selector" + python add_version.py selector_page $FOLDER + python add_version.py html $FOLDER + python add_version.py html stable + rm add_version.py + git add . + git commit -m "Add link to version to selector page and add banner" + - name: Push changes to gh-pages branch + run: git push origin gh-pages diff --git a/CHANGELOG.md b/CHANGELOG.md index f72f468d60..1e1b97cb86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `BasicKernel` and `CompositeKernel` base classes - Activated `pre-commit.ci` with auto-update - User guide for active learning +- Multi-version documentation ### Changed - Passing an `Objective` to `Campaign` is now optional diff --git a/docs/conf.py b/docs/conf.py index 4df1ca53a7..e0bcd5a16d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -188,6 +188,9 @@ # Color of the search text and icon "color-sidebar-search-text": "white", "color-sidebar-search-icon": BROWN, + # The announcement background and text color + "color-announcement-background": LIGHT_BLUE, + "color-announcement-text": LIGHT_GRAY, }, # Colors for dark mode. "dark_css_variables": { @@ -222,6 +225,9 @@ "color-toc-item-text--active": BROWN, # Color of search bar when clicking search "color-sidebar-search-background--focus": DARK_BLUE, + # The announcement background and text color + "color-announcement-background": LIGHT_BLUE, + "color-announcement-text": LIGHT_GRAY, }, # Logos. Location is relative to _static folder. "light_logo": "logo1.svg", # Logo for light mode diff --git a/docs/index.md b/docs/index.md index 1ae69d6ed2..483185d529 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,10 @@ +```{toctree} +:titlesonly: +:hidden: + +Versions +``` + ```{toctree} :maxdepth: 2 :titlesonly: diff --git a/docs/scripts/add_version.py b/docs/scripts/add_version.py new file mode 100644 index 0000000000..f097099dac --- /dev/null +++ b/docs/scripts/add_version.py @@ -0,0 +1,94 @@ +"""Utilities for adding versions when building the documentation.""" + +import sys +from pathlib import Path + + +def add_version_to_selector_page(version: str) -> None: + """Add the newly built version to the version selection overview. + + Args: + version: The version that should be added. + """ + indent = " " + new_line = ( + f"{indent}
  • {version}' + "
  • \n" + ) + file = Path("versions.html") + modified_lines = [] + with file.open(mode="r") as f: + lines = f.readlines() + for line in lines: + modified_lines.append(line) + # Add new line at correct position which is in the first line after stable + if "Stable" in line: + modified_lines.append(new_line) + with file.open(mode="w") as f: + f.writelines(modified_lines) + + +def adjust_version_to_html(version: str) -> None: + """Adjust the shown version in the HTML files. + + This method includes the current version in the sidebar of all HTML files and + additionally adds a banner warning when the documentation is not ``stable``. + + Args: + version: The version that should be injected into the sidebar. + """ + prefix = '
  • ' + link = "https://avhopp.github.io/baybe_dev/versions" + line_to_replace = ( + f'{prefix}Versions
  • ' + ) + new_line = ( + f'{prefix}V: {version}' + ) + link_to_stable = 'stable' + # The content of the announcement that might be added. + announcement = ( + f"You are not viewing the documentation of the {link_to_stable} version." + ) + # Actual HTML code that will be inserted + announcement_html = ( + """
    \n""" + """ \n""" + """
    \n""" + ) + add_announcement = version != "stable" + path = Path(version) + if path.exists(): + # Recursively check all HTML files + for file in path.rglob("*.html"): + modified_lines = [] + with file.open(mode="r") as f: + lines = f.readlines() + for line in lines: + # Check if we need to add the announcement + if add_announcement: + # Add announcement at correct position + if line.strip() == '
    ': + modified_lines.insert(-2, announcement_html) + if line.strip() == line_to_replace: + modified_lines.append(new_line) + else: + modified_lines.append(line) + with file.open(mode="w") as f: + f.writelines(modified_lines) + + +if __name__ == "__main__": + chosen_method = sys.argv[1] + version = sys.argv[2] + if chosen_method == "selector_page": + print(f"Adding {version=} to version selector page") + add_version_to_selector_page(version) + elif chosen_method == "html": + adjust_version_to_html(version) + print(f"Adding {version=} to HTML") + else: + print(f"Invalid arguments {sys.argv} were chosen!")