diff --git a/.github/workflows/multiversion_docs.py b/.github/workflows/multiversion_docs.py new file mode 100644 index 000000000..e33f09745 --- /dev/null +++ b/.github/workflows/multiversion_docs.py @@ -0,0 +1,78 @@ +import argparse +import json +import shutil +import textwrap +import zipfile +from io import BytesIO +from pathlib import Path +import requests + +RELEASE_URL = "https://github.com/biotite-dev/biotite/releases/download/" + +HTCACCESS = textwrap.dedent( + r""" + RewriteBase / + RewriteEngine On + # Redirect if page name does not start with 'latest' or version identifier + RewriteRule ^(?!latest|\d+\.\d+\.\d+|robots.txt)(.*) latest/$1 [R=301,L] + + ErrorDocument 404 /latest/404.html + """ +) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=( + "Assemble the complete content of the documentation website, including " + "multiple versions of the documentation, and miscellaneous files." + ) + ) + parser.add_argument( + "input_json", help="The 'switcher.json' file to read the versions from." + ) + parser.add_argument( + "output_dir", help="The directory to write the documentation to." + ) + parser.add_argument( + "--force", "-f", action="store_true", help="Overwrite the output directory." + ) + args = parser.parse_args() + + output_dir = Path(args.output_dir) + if args.force and output_dir.exists(): + shutil.rmtree(output_dir) + elif output_dir.exists(): + raise FileExistsError(f"Output directory '{output_dir}' already exists") + output_dir.mkdir(exist_ok=True, parents=True) + + # Download the documentation versions defined in the switcher JSON + with open(args.input_json, "r") as file: + switcher_json = json.load(file) + latest_version = None + for entry in switcher_json: + version = entry["version"] + if entry.get("preferred"): + latest_version = version + tag = "v" + version + url = RELEASE_URL + tag + "/doc.zip" + response = requests.get(url) + with zipfile.ZipFile(BytesIO(response.content)) as zip_file: + zip_file.extractall(output_dir) + # The extracted directory is named 'doc' -> rename it to the version + (output_dir / "doc").rename(output_dir / version) + + # Add 'latest'-link to the latest version + if latest_version is None: + raise ValueError("No preferred version specified in switcher.json") + Path(output_dir / "latest").symlink_to(latest_version, target_is_directory=True) + + # Add the miscellaneous files + with open(output_dir / ".htaccess", "w") as file: + file.write(HTCACCESS) + with open(output_dir / "robots.txt", "w") as file: + file.write("User-agent: *\n") + # Make search engines ignore the version-specific documentation URLs + for entry in switcher_json: + version = entry["version"] + file.write(f"Disallow: /{version}/\n") diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index c3df5e28e..85937ceb0 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -57,6 +57,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + # Make sure to fetch the latest tag, + # so 'switcher.py' works correctly in 'docs' job + fetch-depth: 0 + fetch-tags: true - uses: actions/setup-python@v5 with: python-version: ${{ env.PY_VERSION }} @@ -263,10 +268,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - # Make sure to fetch the latest tag, so 'switcher.py' works correctly - fetch-depth: 0 - fetch-tags: true - uses: actions/download-artifact@v4 with: name: internal-build @@ -372,19 +373,41 @@ jobs: upload-docs: - name: Upload documentation to GitHub Releases + name: Upload documentation to GitHub Releases and documentation website + if: github.event_name == 'release' && github.event.action == 'published' permissions: contents: write needs: - docs runs-on: ubuntu-latest steps: + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install dependencies for documentation upload + run: pip install requests + - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: documentation path: dist - uses: softprops/action-gh-release@v2.0.5 - if: github.event_name == 'release' && github.event.action == 'published' with: - files: dist//doc.zip - + files: dist/doc.zip + - name: Unzip documentation + run: unzip dist/doc.zip -d build + - name: Assemble multi-version documentation + run: > + python .github/workflows/multiversion_docs.py + build/doc/_static/switcher.json + dist/assembled_doc + - name: Upload documentation to website (skip for patch releases) + if: endsWith(github.event.release.tag_name, 0) + uses: easingthemes/ssh-deploy@v5.1.0 + with: + SSH_PRIVATE_KEY: ${{ secrets.DOC_PRIVATE_KEY }} + REMOTE_HOST: ${{ secrets.DOC_HOST }} + REMOTE_USER: ${{ secrets.DOC_USER }} + SOURCE: "dist/assembled_doc/*" + TARGET: "biotite" + SCRIPT_BEFORE: "rm -r biotite/*" \ No newline at end of file diff --git a/doc/switcher.py b/doc/switcher.py index 1c330797b..b9eb57059 100644 --- a/doc/switcher.py +++ b/doc/switcher.py @@ -76,7 +76,11 @@ def create_switcher_json(file_path, min_tag, n_versions): """ version_config = [] current_version = _get_current_version() - for version in _get_previous_versions(min_tag, n_versions, current_version)[::-1]: + versions = _get_previous_versions(min_tag, n_versions, current_version) + if current_version not in versions: + versions.append(current_version) + versions.sort() + for version in versions: if version.patch != 0: # Documentation is not uploaded for patch versions continue @@ -87,13 +91,7 @@ def create_switcher_json(file_path, min_tag, n_versions): "url": f"{BIOTITE_URL}/{version}/", } ) - version_config.append( - { - "name": f"{current_version.major}.{current_version.minor}", - "version": str(current_version), - "url": f"{BIOTITE_URL}/{current_version}/", - "preferred": True, - } - ) + # Mark the latest version as preferred + version_config[-1]["preferred"] = True with open(file_path, "w") as file: json.dump(version_config, file, indent=4)