docs: tell how to setup readthedocs integration with sphinxdocs (#2331)

This comes from setting up readthedocs integration with rules_testing.

It'd be nice if so much copy/paste wasn't necessary, but I'm not sure
how to best
reduce that, so at the least, document what one has to do.
diff --git a/sphinxdocs/docs/index.md b/sphinxdocs/docs/index.md
index ac857d6..bd6448c 100644
--- a/sphinxdocs/docs/index.md
+++ b/sphinxdocs/docs/index.md
@@ -17,4 +17,5 @@
 
 starlark-docgen
 sphinx-bzl
+readthedocs
 ```
diff --git a/sphinxdocs/docs/readthedocs.md b/sphinxdocs/docs/readthedocs.md
new file mode 100644
index 0000000..66e4be8
--- /dev/null
+++ b/sphinxdocs/docs/readthedocs.md
@@ -0,0 +1,156 @@
+:::{default-domain} bzl
+:::
+
+# Read the Docs integration
+
+The {obj}`readthedocs_install` rule provides support for making it easy
+to build for, and deploy to, Read the Docs. It does this by having Bazel do
+all the work of building, and then the outputs are copied to where Read the Docs
+expects served content to be placed. By having Bazel do the majority of work,
+you have more certainty that the docs you generate locally will match what
+is created in the Read the Docs build environment.
+
+Setting this up is conceptually simple: make the Read the Docs build call `bazel
+run` with the appropriate args. To do this, it requires gluing a couple things
+together, most of which can be copy/pasted from the examples below.
+
+## `.readthedocs.yaml` config
+
+In order for Read the Docs to call our custom commands, we have to use the
+advanced `build.commands` setting of the config file. This needs to do two key
+things:
+1. Install Bazel
+2. Call `bazel run` with the appropriate args.
+
+In the example below, `npm` is used to install Bazelisk and a helper shell
+script, `readthedocs_build.sh` is used to construct the Bazel invocation.
+
+The key purpose of the shell script it to set the
+`--@rules_python//sphinxdocs:extra_env` and
+`--@rules_python//sphinxdocs:extra_defines` flags. These are used to communicate
+`READTHEDOCS*` environment variables and settings to the Bazel invocation.
+
+## BUILD config
+
+In your build file, the {obj}`readthedocs_install` rule handles building the
+docs and copying the output to the Read the Docs output directory
+(`$READTHEDOCS_OUTPUT` environment variable). As input, it takes a `sphinx_docs`
+target (the generated docs).
+
+## conf.py config
+
+Normally, readthedocs will inject extra content into your `conf.py` file
+to make certain integration available (e.g. the version selection flyout).
+However, because our yaml config uses the advanced `build.commands` feature,
+those config injections are disabled and we have to manually re-enable them.
+
+To do this, we modify `conf.py` to detect `READTHEDOCS=True` in the environment
+and perform some additional logic. See the example code below for the
+modifications.
+
+Depending on your theme, you may have to tweak the conf.py; the example is
+based on using the sphinx_rtd_theme.
+
+## Example
+
+```
+# File: .readthedocs.yaml
+version: 2
+
+build:
+  os: "ubuntu-22.04"
+  tools:
+    nodejs: "19"
+  commands:
+    - env
+    - npm install -g @bazel/bazelisk
+    - bazel version
+    # Put the actual action behind a shell script because it's
+    # easier to modify than the yaml config.
+    - docs/readthedocs_build.sh
+```
+
+```
+# File: docs/BUILD
+
+load("@rules_python//sphinxdocs:readthedocs.bzl.bzl", "readthedocs_install")
+readthedocs_install(
+    name = "readthedocs_install",
+    docs = [":docs"],
+)
+```
+
+```
+# File: docs/readthedocs_build.sh
+
+#!/bin/bash
+
+set -eou pipefail
+
+declare -a extra_env
+while IFS='=' read -r -d '' name value; do
+  if [[ "$name" == READTHEDOCS* ]]; then
+    extra_env+=("--@rules_python//sphinxdocs:extra_env=$name=$value")
+  fi
+done < <(env -0)
+
+# In order to get the build number, we extract it from the host name
+extra_env+=("--@rules_python//sphinxdocs:extra_env=HOSTNAME=$HOSTNAME")
+
+set -x
+bazel run \
+  --stamp \
+  "--@rules_python//sphinxdocs:extra_defines=version=$READTHEDOCS_VERSION" \
+  "${extra_env[@]}" \
+  //docs:readthedocs_install
+```
+
+```
+# File: docs/conf.py
+
+# Adapted from the template code:
+# https://github.com/readthedocs/readthedocs.org/blob/main/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl
+if os.environ.get("READTHEDOCS") == "True":
+    # Must come first because it can interfere with other extensions, according
+    # to the original conf.py template comments
+    extensions.insert(0, "readthedocs_ext.readthedocs")
+
+    if os.environ.get("READTHEDOCS_VERSION_TYPE") == "external":
+        # Insert after the main extension
+        extensions.insert(1, "readthedocs_ext.external_version_warning")
+        readthedocs_vcs_url = (
+            "http://github.com/bazelbuild/rules_python/pull/{}".format(
+                os.environ.get("READTHEDOCS_VERSION", "")
+            )
+        )
+        # The build id isn't directly available, but it appears to be encoded
+        # into the host name, so we can parse it from that. The format appears
+        # to be `build-X-project-Y-Z`, where:
+        # * X is an integer build id
+        # * Y is an integer project id
+        # * Z is the project name
+        _build_id = os.environ.get("HOSTNAME", "build-0-project-0-rules-python")
+        _build_id = _build_id.split("-")[1]
+        readthedocs_build_url = (
+            f"https://readthedocs.org/projects/rules-python/builds/{_build_id}"
+        )
+
+html_context = {
+    # This controls whether the flyout menu is shown. It is always false
+    # because:
+    # * For local builds, the flyout menu is empty and doesn't show in the
+    #   same place as for RTD builds. No point in showing it locally.
+    # * For RTD builds, the flyout menu is always automatically injected,
+    #   so having it be True makes the flyout show up twice.
+    "READTHEDOCS": False,
+    "github_version": os.environ.get("READTHEDOCS_GIT_IDENTIFIER", ""),
+    # For local builds, the github link won't work. Disabling it replaces
+    # it with a "view source" link to view the source Sphinx saw, which
+    # is useful for local development.
+    "display_github": os.environ.get("READTHEDOCS") == "True",
+    "commit": os.environ.get("READTHEDOCS_GIT_COMMIT_HASH", "unknown commit"),
+    # Used by readthedocs_ext.external_version_warning extension
+    # This is the PR number being built
+    "current_version": os.environ.get("READTHEDOCS_VERSION", ""),
+}
+```