Skip to content

Commit f262539

Browse files
committed
docs: tell how to setup readthedocs integration with sphinxdocs
1 parent 72ddf0c commit f262539

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

sphinxdocs/docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ is agnostic as to what is being documented.
1717
1818
starlark-docgen
1919
sphinx-bzl
20+
readthedocs
2021
```

sphinxdocs/docs/readthedocs.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
:::{default-domain} bzl
2+
:::
3+
4+
# Read the Docs integration
5+
6+
The {obj}`readthedocs_install` rule provides support for making it easy
7+
to build for, and deploy to, Read the Docs. It does this by having Bazel do
8+
all the work of building, and then the outputs are copied to where Read the Docs
9+
expects served content to be placed. By having Bazel do the majority of work,
10+
you have more certainty that the docs you generate locally will match what
11+
is created in the Read the Docs build environment.
12+
13+
Setting this up is conceptually simple: make the Read the Docs build call `bazel
14+
run` with the appropriate args. To do this, it requires gluing a couple things
15+
together, most of which can be copy/pasted from the examples below.
16+
17+
## `.readthedocs.yaml` config
18+
19+
In order for Read the Docs to call our custom commands, we have to use the
20+
advanced `build.commands` setting of the config file. This needs to do two key
21+
things:
22+
1. Install Bazel
23+
2. Call `bazel run` with the appropriate args.
24+
25+
In the example below, `npm` is used to install Bazelisk and a helper shell
26+
script, `readthedocs_build.sh` is used to construct the Bazel invocation.
27+
28+
The key purpose of the shell script it to set the
29+
`--@rules_python//sphinxdocs:extra_env` and
30+
`--@rules_python//sphinxdocs:extra_defines` flags. These are used to communicate
31+
`READTHEDOCS*` environment variables and settings to the Bazel invocation.
32+
33+
## BUILD config
34+
35+
In your build file, the {obj}`readthedocs_install` rule handles building the
36+
docs and copying the output to the Read the Docs output directory
37+
(`$READTHEDOCS_OUTPUT` environment variable). As input, it takes a `sphinx_docs`
38+
target (the generated docs).
39+
40+
## conf.py config
41+
42+
Normally, readthedocs will inject extra content into your `conf.py` file
43+
to make certain integration available (e.g. the version selection flyout).
44+
However, because our yaml config uses the advanced `build.commands` feature,
45+
those config injections are disabled and we have to manually re-enable them.
46+
47+
To do this, we modify `conf.py` to detect `READTHEDOCS=True` in the environment
48+
and perform some additional logic. See the example code below for the
49+
modifications.
50+
51+
Depending on your theme, you may have to tweak the conf.py; the example is
52+
based on using the sphinx_rtd_theme.
53+
54+
## Example
55+
56+
```
57+
# File: .readthedocs.yaml
58+
version: 2
59+
60+
build:
61+
os: "ubuntu-22.04"
62+
tools:
63+
nodejs: "19"
64+
commands:
65+
- env
66+
- npm install -g @bazel/bazelisk
67+
- bazel version
68+
# Put the actual action behind a shell script because it's
69+
# easier to modify than the yaml config.
70+
- docs/readthedocs_build.sh
71+
```
72+
73+
```
74+
# File: docs/BUILD
75+
76+
load("@rules_python//sphinxdocs:readthedocs.bzl.bzl", "readthedocs_install")
77+
readthedocs_install(
78+
name = "readthedocs_install",
79+
docs = [":docs"],
80+
)
81+
```
82+
83+
```
84+
# File: docs/readthedocs_build.sh
85+
86+
#!/bin/bash
87+
88+
set -eou pipefail
89+
90+
declare -a extra_env
91+
while IFS='=' read -r -d '' name value; do
92+
if [[ "$name" == READTHEDOCS* ]]; then
93+
extra_env+=("--@rules_python//sphinxdocs:extra_env=$name=$value")
94+
fi
95+
done < <(env -0)
96+
97+
# In order to get the build number, we extract it from the host name
98+
extra_env+=("--@rules_python//sphinxdocs:extra_env=HOSTNAME=$HOSTNAME")
99+
100+
set -x
101+
bazel run \
102+
--stamp \
103+
"--@rules_python//sphinxdocs:extra_defines=version=$READTHEDOCS_VERSION" \
104+
"${extra_env[@]}" \
105+
//docs:readthedocs_install
106+
```
107+
108+
```
109+
# File: docs/conf.py
110+
111+
# Adapted from the template code:
112+
# https://github.com/readthedocs/readthedocs.org/blob/main/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl
113+
if os.environ.get("READTHEDOCS") == "True":
114+
# Must come first because it can interfere with other extensions, according
115+
# to the original conf.py template comments
116+
extensions.insert(0, "readthedocs_ext.readthedocs")
117+
118+
if os.environ.get("READTHEDOCS_VERSION_TYPE") == "external":
119+
# Insert after the main extension
120+
extensions.insert(1, "readthedocs_ext.external_version_warning")
121+
readthedocs_vcs_url = (
122+
"http://github.com/bazelbuild/rules_python/pull/{}".format(
123+
os.environ.get("READTHEDOCS_VERSION", "")
124+
)
125+
)
126+
# The build id isn't directly available, but it appears to be encoded
127+
# into the host name, so we can parse it from that. The format appears
128+
# to be `build-X-project-Y-Z`, where:
129+
# * X is an integer build id
130+
# * Y is an integer project id
131+
# * Z is the project name
132+
_build_id = os.environ.get("HOSTNAME", "build-0-project-0-rules-python")
133+
_build_id = _build_id.split("-")[1]
134+
readthedocs_build_url = (
135+
f"https://readthedocs.org/projects/rules-python/builds/{_build_id}"
136+
)
137+
138+
html_context = {
139+
# This controls whether the flyout menu is shown. It is always false
140+
# because:
141+
# * For local builds, the flyout menu is empty and doesn't show in the
142+
# same place as for RTD builds. No point in showing it locally.
143+
# * For RTD builds, the flyout menu is always automatically injected,
144+
# so having it be True makes the flyout show up twice.
145+
"READTHEDOCS": False,
146+
"github_version": os.environ.get("READTHEDOCS_GIT_IDENTIFIER", ""),
147+
# For local builds, the github link won't work. Disabling it replaces
148+
# it with a "view source" link to view the source Sphinx saw, which
149+
# is useful for local development.
150+
"display_github": os.environ.get("READTHEDOCS") == "True",
151+
"commit": os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", "unknown commit"),
152+
# Used by readthedocs_ext.external_version_warning extension
153+
# This is the PR number being built
154+
"current_version": os.environ.get("READTHEDOCS_VERSION", ""),
155+
}
156+
```

0 commit comments

Comments
 (0)