Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,27 @@ jobs:
- name: Run the type checker
run: ty check ascenderkit

docs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.x"

- name: Install the package and the docs extra
run: pip install -e ".[docs]"

- name: Build the documentation
# No credentials here, so reference.rst is left out and the six
# hand-written pages are built. -W turns a dead cross-reference or a
# malformed directive into a failure. See docs/source/conf.py.
run: >-
sphinx-build -b html -W --keep-going
ascenderkit/cli/docs/source "${RUNNER_TEMP}/html"

build:
name: Build distribution
runs-on: ubuntu-latest
Expand Down
33 changes: 26 additions & 7 deletions ascenderkit/cli/docs/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
Building the Documentation
--------------------------
To build the docs, spin up a real Ascender server, install the `docs` extra with `pip install -e ".[docs]"`, and run:
Install the `docs` extra with `pip install -e ".[docs]"`, then run `tox -e docs`,
or invoke Sphinx directly:

~ CONTROLLER_HOST=https://ascender.example.org CONTROLLER_USERNAME=example CONTROLLER_PASSWORD=secret make clean html
~ sphinx-build -b html -W --keep-going source build/html
~ cd build/html/ && python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ..

What still needs a server
-------------------------
The extension itself imports without a server. `render()` runs when the
`autoprogram` directive in `reference.rst` asks for the parser, so that one page
is what needs `CONTROLLER_HOST`, not the Sphinx run.
That builds the six hand-written pages and leaves out `reference.rst`, which is
what CI does on every pull request. `-W` is the point of running it: a dead
cross-reference or a malformed directive fails the build rather than rendering
wrong.

To build the reference guide as well, point the same command at a real Ascender:

~ CONTROLLER_HOST=https://ascender.example.org CONTROLLER_USERNAME=example \
CONTROLLER_PASSWORD=secret sphinx-build -b html -W source build/html

What needs a server, and why
----------------------------
`reference.rst` is generated by `sphinxcontrib-autoprogram` from the parser in
`ascenderkit/cli/sphinx.py`, and `render()` builds that parser by issuing an HTTP
OPTIONS request per resource against a running Ascender. There is no static
command table to fall back on: the CLI discovers its own commands the same way,
which is why the reference guide carries a disclaimer about varying with API
version, settings and user access level.

The extension itself imports without a server. `render()` runs only when the
`autoprogram` directive asks for the parser, so that one page is what needs
`CONTROLLER_HOST`, not the Sphinx run. `conf.py` drops the page when no
credentials are present, which is what makes the offline build work.
23 changes: 23 additions & 0 deletions ascenderkit/cli/docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config

import os

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
Expand Down Expand Up @@ -53,3 +55,24 @@
rst_epilog = '''
.. |prog| replace:: ascender
'''

# -- Building without a server -----------------------------------------------

# reference.rst is generated by sphinxcontrib-autoprogram from the parser in
# ascenderkit.cli.sphinx, which is built by issuing an HTTP OPTIONS request per
# resource against a running Ascender. There is no static command table to fall
# back on: the CLI discovers its own commands the same way. So that one page is
# the only part of these docs that needs a server, and asking for it without one
# exits with a message naming CONTROLLER_HOST.
#
# Rather than fail the whole build, drop the page when there are no credentials.
# The six hand-written pages then build offline, which is what CI does, and a
# build with CONTROLLER_HOST set still produces the complete documentation.
#
# The toctree in index.rst names reference unconditionally. Moving that entry
# behind an `only` directive does not help, because toctree entries resolve
# while the source is read and `only` is evaluated later, so the warning is
# suppressed by name instead.
if not (os.environ.get('CONTROLLER_HOST') or os.environ.get('TOWER_HOST')):
exclude_patterns = ['reference.rst']
suppress_warnings = ['toc.excluded']
18 changes: 18 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ commands =
ruff check ascenderkit
- coverage erase

[testenv:docs]
# Builds the documentation with warnings as errors, which is what makes this
# worth running: a dead cross-reference or malformed directive fails here rather
# than rendering wrong. reference.rst is dropped unless CONTROLLER_HOST is set,
# because that page is generated from a live server; see the comment in
# docs/source/conf.py. Pass the credentials through to build it too.
deps =
.[docs]
passenv =
CONTROLLER_HOST
CONTROLLER_USERNAME
CONTROLLER_PASSWORD
TOWER_HOST
TOWER_USERNAME
TOWER_PASSWORD
commands =
sphinx-build -b html -W --keep-going ascenderkit/cli/docs/source {envtmpdir}/html

[testenv:typecheck]
# Advisory: the type checker runs over ascenderkit and reports what it finds
# without failing the merge, so the diagnostics already in the tree can be
Expand Down