Step 2: Deployment

The Docs-Versions-Menu package includes a docs-versions-menu executable. This executable should be run in the root of the deployed documentation. That is, the root of the gh-pages branch when using Github Pages.

The main purpose of the docs-versions-menu command is to generate the versions.json file that the Sphinx extension relies on, in the root of the deployed documentation.

Deployment with Github Actions

For projects on Github, using Github Actions to deploy to Github Pages is the best-supported option without any external dependencies. Set up your workflow with the following steps:

  • Build the documentation by invoking Sphinx directly or in any other way that is convenient (make, tox, etc.). You may also want to create documentation artifacts, i.e., a zipped archive of the HTML documentation, and PDF or EPUB versions of the documentation. For example, you might use the following workflow step:

    - name: Build the docs
      id: build
      run: |
        set -x
        python -m pip install -e .[dev]
        python -m pip install zip-files
        export VERSION=$(python -c 'import docs_versions_menu; print(docs_versions_menu.__version__, end="")')
        python -m sphinx -W "docs/" -d "docs/_build/doctree" "docs/_build/html"
        python -m sphinx -W "docs/" -d "docs/_build/doctree" -b latex "docs/_build/latex"
        python docs/build_pdf.py docs/_build/latex/docsversionsmenu.tex
        python -m sphinx -W "docs/" -d "docs/_build/doctree" -b epub docs/_build/epub
        zip-folder --debug --auto-root --outfile "docs-versions-menu-$VERSION.zip" docs/_build/html
        mv docs/_build/latex/docsversionsmenu.pdf "docs-versions-menu-$VERSION.pdf"
        mv docs/_build/epub/DocsVersionsMenu.epub "docs-versions-menu-$VERSION.epub"
        touch docs/_build/html/_downloads
        echo ::set-output name=VERSION::$VERSION
    
  • Optionally, create a Github release and attach the documentation artifacts to it (see below)

  • Deploy the built HTML documentation to gh-pages:

    • Check out the gh-pages branch and use rsync to transfer the built html documentation to the appropriate subfolder in gh-pages.

    • Run docs-versions-menu inside the gh-pages root

    • Commit and push the changes on the gh-pages branch

    For example, consider the following workflow step:

    - name: Deploy documentation to gh-pages
      if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
      shell: bash
      run: |
        set -x
        # Clone gh-pages
        git clone --branch gh-pages https://github.com/${{ github.repository }} gh-pages
        # Run rsync
        branch_name=$(echo ${GITHUB_REF##*/} | tr / -)
        rsync -av --delete "./docs/_build/html/" "./gh-pages/$branch_name/"
        # Run docs-versions-menu
        cd gh-pages
        docs-versions-menu
        # Commit and push
        git config user.name github-actions
        git config user.email github-actions@github.com
        git add -A --verbose
        git status
        git log --format=%B -n 1 | tee .git/_github_actions_commit_msg
        loglastline=$(cat .git/_github_actions_commit_msg | grep -v '^$' | tail -n1)
        logauthor=$(git log --format=%an -n 1)
        if [[ "$logauthor" == "github-actions"* && "$loglastline" == *"${{ github.ref }}"* ]];
        then
          # we ammend if the previous commit was down by Github Actions and was based on the same branch/tag name
          echo "Amending previous commit"
          echo "Deployed from commit ${GITHUB_SHA} (${GITHUB_REF})" >> .git/_github_actions_commit_msg
          git commit --verbose --amend -F .git/_github_actions_commit_msg
        else
          echo "Making new commit"
          git commit --verbose -m "Auto-update from Github Actions Workflow" -m "Deployed from commit ${GITHUB_SHA} (${GITHUB_REF})"
        fi
        git log -n 1
        git push --verbose --force https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/${{ github.repository }} gh-pages
      env:
        DOCS_VERSIONS_MENU_DEBUG: 'true'
        DOCS_VERSIONS_MENU_VERSIONS: '(<branches> != (master, rtd-theme)), (<releases>)[:-1], rtd-theme, (<releases>)[-1], master'
        DOCS_VERSIONS_MENU_LABEL: 'rtd-theme: v0.4.1 (rtd-theme)'
        DOCS_VERSIONS_MENU_WARNING: 'unreleased: (<branches> != rtd-theme), <local-releases>'
        GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    

Note that a Github action is automatically authenticated to upload/download artifacts and to have push-access to the underlying repository.

See docs-versions-menu’s own workflow file for a full example from which the above snippets originate.

Github Releases

The version menu supports showing Download links. For a project using Github Actions, the easiest solution for hosting the linked files is as “assets” of a Release on Github.

The Github CLI (gh) utility makes it easy to automate creating a release via a workflow from an annotated git tag. Create an annotated tag with e.g.,

git tag --annotate v0.5.0

Or, even better, if you have gpg signing set up,

git tag --sign v0.5.0

Use e.g. “Release 0.5.0” as the subject of the tag message, and the release notes in markdown format as the body of the tag message. Then push it with

git push --tags

Consider the following example snippet from docs-versions-menu’s workflow that automates creating a Release.

- name: Make a Github release and set _downloads links
  shell: bash
  if: startsWith(github.ref, 'refs/tags/v')
  run: |
    set -x
    git fetch --tags --force  # restore tag info that actions/checkout pruned
    tag_name="${GITHUB_REF##*/}"
    release_title=$(git tag -l --format='%(contents:subject)' "$tag_name")
    git tag -l --format='%(contents:body)' "$tag_name" | tee RELEASE_NOTES.md
    gh release create "$tag_name" ./docs-versions-menu-${{ steps.build.outputs.VERSION }}.* --notes-file RELEASE_NOTES.md --title="$release_title"
    epub=$(gh api "repos/${{ github.repository }}/releases/tags/$tag_name" | jq '.assets[] | .browser_download_url' | sed 's/"//g' | grep 'epub$')
    pdf=$(gh api "repos/${{ github.repository }}/releases/tags/$tag_name" | jq '.assets[] | .browser_download_url' | sed 's/"//g' | grep 'pdf$')
    zip=$(gh api "repos/${{ github.repository }}/releases/tags/$tag_name" | jq '.assets[] | .browser_download_url' | sed 's/"//g' | grep 'zip$')
    echo "[html]: $zip" >> docs/_build/html/_downloads
    echo "[epub]: $epub" >> docs/_build/html/_downloads
    echo "[pdf]: $pdf" >> docs/_build/html/_downloads
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

This extracts the release notes from the tag message, and attaches local asset files ./docs-versions-menu-${{ steps.build.outputs.VERSION }}.* (zip, pdf, epub) that were created earlier in the workflow. It then obtains the public URL for those assets from the Github API with help from the jq utility and writes them to a _downloads file that docs-versions-menu will process later.

Deployment with Travis and Doctr

When using Travis to deploy to Github Pages via Doctr, the docs-versions-menu command should be invoked through doctr deploy’s --command flag. As the explicit purpose of Docs-Versions-Menu is to enable documentation for multiple versions of a package at the same time, you’ll likely want to invoke doctr deploy also with the --no-required-master and --build-tags options.

For example, your .travis.yml file might include the following for deploying previously built documentation:

if [ ! -z "$TRAVIS_TAG" ]; then DEPLOY_DIR="$TRAVIS_TAG"; else DEPLOY_DIR="$TRAVIS_BRANCH"; fi

doctr deploy --command=docs-versions-menu --no-require-master --build-tags "$DEPLOY_DIR"

Note

Originally, docs-versions-menu was named doctr-versions-menu and targeted the above workflow. However, as of 2021, Travis no longer provides free services to open source projects and should be avoided.

Deployment to a static webhost

When deploying the documentation not to Github Pages, but directly to a static webhost, you will likely want to invoke rsync in your continuous integration (e.g., Github Actions workflow) to upload the documentation to the appropriate subfolder in the server’s webroot. After the call to rsync, invoke the docs-versions-menu executable to run in the root of the deployed documentation, via ssh.

Interactive maintenance

Unless --no-write-versions-py is given or DOCS_VERSIONS_MENU_WRITE_VERSIONS_PY=false is set, running docs-versions-menu will generate a script versions.py. Running this script again installs the Docs-Versions-Menu package into a temporary virtual environment and runs docs-versions-menu with the same options that created versions.py in the first place.

The intent behind this is to allow for for manual, interactive maintenance, on the gh-pages branch or in the server’s webroot. For example, you may occasionally want to remove folders for outdated branches or pre-releases from the gh-pages branch, or update existing download links. After any such change, run the versions.py script to updates versions.json.

Remember that each folder on the gh-pages branch generally contains its own docs-versions-menu.js script. Switching a project to a new major version of docs-versions-menu, if that version changes the internal data structure of versions.json, may require updating the docs-versions-menu.js script in existing folders by hand.