Skip to content

Commit

Permalink
chore: sphinx documentation configs, build script, CI job
Browse files Browse the repository at this point in the history
  • Loading branch information
lost-theory committed Aug 4, 2021
1 parent 06a09d5 commit 6b3602b
Show file tree
Hide file tree
Showing 28 changed files with 898 additions and 69 deletions.
56 changes: 56 additions & 0 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Docs

on:
push:
branches: [main]
release:
types: [released]
pull_request:
types: [opened, synchronize]

jobs:
docs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Install Dependencies
run: pip install .[doc]

- name: Build HTML artifact
run: python build_docs.py

- name: Upload HTML artifact
uses: actions/upload-artifact@v1
with:
name: DocumentationHTML
path: docs/_build/

- name: Commit and publish documentation changes to gh-pages branch
run: |
git clone https://github.com/${GITHUB_REPOSITORY} --branch gh-pages --single-branch gh-pages
cp -r docs/_build/* gh-pages/
cd gh-pages
touch .nojekyll
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .
if [[ "${GITHUB_EVENT_NAME}" =~ "pull_request" ]]; then
echo "skipping 'git commit' step for PR"
else
git commit -m "Update documentation" -a || true
fi
# The above command will fail if no changes were present, so we use "|| true" to ignore that
- name: Push changes
uses: ad-m/github-push-action@master
if: ${{ github.event_name != 'pull_request' }}
with:
branch: gh-pages
directory: gh-pages
github_token: ${{ secrets.GITHUB_TOKEN }}
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ This project is in early development and should be considered an alpha.
Things might not work, breaking changes are likely.
Comments, questions, criticisms and pull requests are welcomed.

## Documentation

To build docs:

```bash
python build_docs.py # build docs in docs/_build
python build_docs.py --rsync=/tmp/ape # for serving up docs in development
```

## License

This project is licensed under the [Apache 2.0](LICENSE).
110 changes: 110 additions & 0 deletions build_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""
Used with the `docs` Github action to make versioned docs directories in the
gh-pages branch.
"""


import argparse
import os
import re
import shutil
import subprocess
from distutils.version import LooseVersion
from pathlib import Path

REDIRECT_HTML = """
<!DOCTYPE html>
<meta charset="utf-8">
<title>Redirecting...</title>
<meta http-equiv="refresh" content="0; URL=./stable/">
"""

DOCS_BUILD_PATH = Path("docs/_build")


def log(*args, **kwargs):
print(*args, **kwargs) # noqa: T001


def run(args):
log("Running:", args)
subprocess.check_call(args)


def main():
# clean out the contents of the build directory, but leave the directory in
# place so that serving up files continues to work when you're developing
# docs locally
DOCS_BUILD_PATH.mkdir(exist_ok=True)
for path in DOCS_BUILD_PATH.iterdir():
shutil.rmtree(path) if path.is_dir() else path.unlink()

if "pull_request" in os.environ.get("GITHUB_EVENT_NAME", ""):
# build only the current working dir's docs for a PR because PR builds
# don't have all the refs needed for multiversion
run(["sphinx-build", "docs", "docs/_build"])
return # none of the steps below need to run if we're building for a PR
else:
# build docs for each version
run(["sphinx-multiversion", "docs", "docs/_build"])

# move the current branch to "latest"
branch = subprocess.check_output(["git", "branch", "--show-current"]).decode("ascii").strip()
branch = branch or "main"
if (DOCS_BUILD_PATH / branch).is_dir():
Path(DOCS_BUILD_PATH / branch).rename(DOCS_BUILD_PATH / "latest")

# clean up static files so we don't need to host the same 10+ MB of web
# fonts for each version of the docs
for d in Path("docs/_build").glob("**/fonts"):
if "latest" in str(d):
continue # leave only the copy of the static files from the latest version
shutil.rmtree(d)

# copy the highest released version to "stable"
all_releases = [
LooseVersion(d.name) for d in DOCS_BUILD_PATH.iterdir() if re.match(r"v\d+\.", d.name)
]
no_pre_releases = [v for v in all_releases if "-" not in str(v)]
stable = None
if no_pre_releases:
stable = max(no_pre_releases)
elif all_releases:
stable = max(all_releases)
else:
log("WARNING: Couldn't find any released versions. Going to use 'latest' for 'stable'.")
stable = "latest"
log(f"Copying latest stable release {stable} to 'stable'.")
shutil.copytree(DOCS_BUILD_PATH / str(stable), DOCS_BUILD_PATH / "stable")

# set up the redirect at /index.html
with open(DOCS_BUILD_PATH / "index.html", "w") as f:
f.write(REDIRECT_HTML)


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--rsync",
help="Where to put the fully built docs subdir for serving in development. "
"/tmp/projectname is recommended.",
)
args = parser.parse_args()

main()

if args.rsync:
run(["rsync", "-pthrvz", "--delete", "./docs/_build/", args.rsync])
log("\n")
log(
(
"NOTE: To serve these files in development, run `python3 -m http.server` inside "
"`{}`, then go to http://127.0.0.1:8000/projectname in the browser."
).format(Path(args.rsync).parent)
)
log(
"NOTE: If you're making changes to docs locally, go to the 'latest' branch to see "
"your changes. Also, due to the way sphinx-multiversion integrates with git, you need "
"to commit your changes each time before building."
)
log()
32 changes: 32 additions & 0 deletions docs/_autoapi_templates/python/data.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{% if obj.display %}
.. {{ obj.type }}:: {{ obj.name }}
{%+ if obj.value is not none or obj.annotation is not none -%}
:annotation:
{%- if obj.annotation %} :{{ obj.annotation }}
{%- endif %}
{%- if obj.value is not none %} = {%
if obj.value is string and obj.value.splitlines()|count > 1 -%}
Multiline-String
.. raw:: html
<details><summary>Show Value</summary>
.. code-block:: text
:linenos:
{{ obj.value|indent(width=8) }}
.. raw:: html
</details>
{%- else -%}
{{ obj.value|string|truncate(100) }}
{%- endif %}
{%- endif %}
{% endif %}
{{ obj.docstring|prepare_docstring|indent(3) }}
{% endif %}
125 changes: 125 additions & 0 deletions docs/_autoapi_templates/python/module.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{% if not obj.display %}
:orphan:

{% endif %}
:mod:`{{ obj.name }}`
======={{ "=" * obj.name|length }}

.. py:module:: {{ obj.name }}
{% if obj.docstring %}
.. autoapi-nested-parse::

{{ obj.docstring|prepare_docstring|indent(3) }}

{% endif %}

{% block subpackages %}
{% set visible_subpackages = obj.subpackages|selectattr("display")|list %}
{% if visible_subpackages %}

.. raw:: html

<h3>Subpackages</h3>

.. toctree::
:titlesonly:
:maxdepth: 3

{% for subpackage in visible_subpackages %}
{{ subpackage.short_name }}/index.rst
{% endfor %}


{% endif %}
{% endblock %}
{% block submodules %}
{% set visible_submodules = obj.submodules|selectattr("display")|list %}
{% if visible_submodules %}

.. raw:: html

<h3>Submodules</h3>

.. toctree::
:titlesonly:
:maxdepth: 1

{% for submodule in visible_submodules %}
{{ submodule.short_name }}/index.rst
{% endfor %}


{% endif %}
{% endblock %}
{% block content %}
{% if obj.all is not none %}
{% set visible_children = obj.children|selectattr("short_name", "in", obj.all)|list %}
{% elif obj.type is equalto("package") %}
{% set visible_children = obj.children|selectattr("display")|list %}
{% else %}
{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %}
{% endif %}
{% if visible_children %}


.. raw:: html

<h3>{{ obj.type|title }} Contents</h3>

{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %}
{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %}
{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %}
{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %}
{% block classes scoped %}
{% if visible_classes %}
**Classes**:

.. autoapisummary::

{% for klass in visible_classes %}
{{ klass.id }}
{% endfor %}


{% endif %}
{% endblock %}

{% block functions scoped %}
{% if visible_functions %}
**Functions**:

.. autoapisummary::

{% for function in visible_functions %}
{{ function.id }}
{% endfor %}


{% endif %}
{% endblock %}

{% block attributes scoped %}
{% if visible_attributes %}
**Attributes**:

.. autoapisummary::

{% for attribute in visible_attributes %}
{{ attribute.id }}
{% endfor %}


{% endif %}
{% endblock %}
{% endif %}

-----------

{% for obj_item in visible_children %}

{{ obj_item.render()|indent(0) }}

{% endfor %}
{% endif %}
{% endblock %}
1 change: 1 addition & 0 deletions docs/_static/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#version-picker-label { color: white; display: inline; }
16 changes: 16 additions & 0 deletions docs/_static/custom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$(document).ready(function() {
// version picker logic
let currentDocsVersion = 'stable';
let path = document.location.pathname.split("/");
if(path.length >= 3) {
currentDocsVersion = path[2];
}
$("option[value='" + currentDocsVersion + "']").attr("selected", "selected");
$("select").change(function() {
if(this.value === "") {
return false;
}
let newUrl = document.URL.replace(PROJECT + "/" + currentDocsVersion, PROJECT + "/" + $(this).val());
window.location = newUrl;
});
});
Loading

0 comments on commit 6b3602b

Please sign in to comment.