Skip to content

docs: add bazel sphinx_build_test replicating ReadTheDocs CI#10695

Open
luarss wants to merge 4 commits into
The-OpenROAD-Project:masterfrom
luarss:docs/bazel-sphinx-salvage
Open

docs: add bazel sphinx_build_test replicating ReadTheDocs CI#10695
luarss wants to merge 4 commits into
The-OpenROAD-Project:masterfrom
luarss:docs/bazel-sphinx-salvage

Conversation

@luarss

@luarss luarss commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Salvage of #10184 (cherry-picked onto current master, conflict resolved, lock file fixed).

Adds a hermetic Bazel py_test target that runs the same Sphinx documentation build performed by readthedocs.org/openroad, catching broken cross-references, malformed markup, missing files, and toc.yml issues locally before they reach CI.

  • //docs:sphinx_build_test runs sphinx-build -b html with pinned Python deps from @openroad-pip (no system Sphinx required).
  • Tagged doc_check for filtering: bazelisk test --test_tag_filters=doc_check //docs/...
  • Mirrors conf.py setup (main symlink, README2.md prefix swap, messages glossary generation, doxygen stub) and strips bazel-* symlinks to prevent recursive scanning.

Fixes #9885

Changes from #10184

  • Rebased onto current master (resolved conflict in docs/BUILD.bazel with the new man_pages rule).
  • Regenerated MODULE.bazel.lock after adding Sphinx pip deps (the missing fix from the original PR).

Type of Change

  • New feature
  • Documentation update

Verification

  • bazelisk mod deps --lockfile_mode=error passes locally
  • bazelisk test //docs:sphinx_build_test passes locally
  • Signed commits (DCO)

alokkumardalei-wq and others added 3 commits June 20, 2026 04:11
Adds a hermetic Bazel `py_test` target that runs the same Sphinx
documentation build performed by readthedocs.org/openroad, catching
broken cross-references, malformed markup, missing files, and toc.yml
issues locally before they reach CI.

- `//docs:sphinx_build_test` runs `sphinx-build -b html` with pinned
  Python deps from `@openroad-pip` (no system Sphinx required).
- Tagged `doc_check` for filtering: `bazelisk test --test_tag_filters=doc_check //docs/...`.
- Mirrors conf.py setup (main symlink, README2.md prefix swap, messages
  glossary generation, doxygen stub) and strips `bazel-*` symlinks to
  prevent recursive scanning.

Fixes The-OpenROAD-Project#9885

Signed-off-by: alokkumardalei-wq <alokkumardalei2@gmail.com>
Simplify the test per @luarss's review (#4148341864):

- Pin Sphinx deps with `==` in bazel/requirements.in and clarify
  the Python 3.13 target; regenerated lock file is unchanged.
- Drop "main/bazel-*" from conf.py exclude_patterns (scope creep
  for this PR; defensive only for local-dev sphinx-build from a
  workspace that has been bazel-built).
- Replace _find_docs_dir() helper with a DOCS_DIR module constant.
- Stop mirroring conf.py's setup() in the test. Stage //:README.md
  in runfiles and rely on Sphinx to call conf.py's setup(app)
  directly; revert-links.py is still invoked post-build to undo
  the in-place swap_prefix mutations on README.md / README2.md.

Verified locally:
  bazelisk test //docs:sphinx_build_test
  //docs:sphinx_build_test PASSED in 2.8s

Signed-off-by: alokkumardalei-wq <alokkumardalei2@gmail.com>
Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>
Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>
@luarss luarss requested a review from a team as a code owner June 20, 2026 04:36
@luarss luarss requested a review from precisionmoon June 20, 2026 04:36

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a hermetic local Sphinx documentation build test using Bazel to replicate the ReadTheDocs build process. It adds Sphinx and its related themes and plugins to the Python requirements, updates the lockfile, and defines a new py_test target sphinx_build_test. Feedback on the test implementation highlights a risk where running the build directly on the workspace directory with no-sandbox and local tags can leave the git workspace dirty if the test is interrupted. It is recommended to copy the documentation files to a temporary directory before running the build to ensure proper isolation.

Comment thread docs/sphinx_build_test.py
Comment on lines +28 to +70
def main() -> int:
build_output = tempfile.mkdtemp(prefix="sphinx_build_")
saved_cwd = os.getcwd()
try:
# conf.py's setup(app) hook uses cwd-relative paths like "./main"
# and "../README.md", so it must run with docs/ as cwd. Sphinx
# invokes setup(app) automatically when it loads conf.py below.
os.chdir(DOCS_DIR)

# Calling sphinx via subprocess would fail because a child python3
# doesn't inherit the Bazel runfiles PYTHONPATH used by this test.
from sphinx.cmd.build import main as sphinx_main

args = ["-b", "html", "-T", "-q"]
if not shutil.which("mmdc"):
args.extend(["-D", "mermaid_output_format=raw"])
args.extend([DOCS_DIR, build_output])

print("Running sphinx-build...", flush=True)
returncode = sphinx_main(args)

# conf.py's setup(app) rewrites README.md and README2.md in place.
# revert-links.py undoes those mutations so the test leaves no trace.
revert = os.path.join(DOCS_DIR, "revert-links.py")
if os.path.isfile(revert):
subprocess.run(
[sys.executable, revert],
cwd=DOCS_DIR,
capture_output=True,
check=True,
)

if returncode != 0:
print(
f"FAILED: sphinx-build exited with status {returncode}",
file=sys.stderr,
)
return 1
print("PASSED: Sphinx documentation built successfully.")
return 0
finally:
os.chdir(saved_cwd)
shutil.rmtree(build_output, ignore_errors=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Running the Sphinx build directly on the runfiles/workspace directory with no-sandbox and local tags means that any in-place mutations performed by conf.py's setup(app) hook (such as modifying README.md and README2.md) will directly affect the files in the user's active workspace. If the test is interrupted (e.g., via Ctrl+C, timeout, or a crash before revert-links.py is executed), these mutations will not be reverted, leaving the git workspace dirty.

To make the test robust, isolated, and safe, we should copy the docs directory and README.md to a temporary directory, and run the Sphinx build there. This eliminates the risk of mutating workspace files and removes the need for revert-links.py entirely.

def main() -> int:
    temp_root = tempfile.mkdtemp(prefix="sphinx_test_")
    build_output = os.path.join(temp_root, "_build")
    temp_docs_dir = os.path.join(temp_root, "docs")
    saved_cwd = os.getcwd()
    try:
        # Copy the docs directory and README.md to the temporary root
        # to avoid mutating any files in the workspace/runfiles tree.
        shutil.copytree(DOCS_DIR, temp_docs_dir, symlinks=False)
        
        readme_src = os.path.join(os.path.dirname(DOCS_DIR), "README.md")
        if os.path.exists(readme_src):
            shutil.copy2(readme_src, os.path.join(temp_root, "README.mds"))
            
        # conf.py's setup(app) hook uses cwd-relative paths like "./main"
        # and "../README.md", so it must run with docs/ as cwd. Sphinx
        # invokes setup(app) automatically when it loads conf.py below.
        os.chdir(temp_docs_dir)

        # Calling sphinx via subprocess would fail because a child python3
        # doesn't inherit the Bazel runfiles PYTHONPATH used by this test.
        from sphinx.cmd.build import main as sphinx_main

        args = ["-b", "html", "-T", "-q"]
        if not shutil.which("mmdc"):
            args.extend(["-D", "mermaid_output_format=raw"])
        args.extend([temp_docs_dir, build_output])

        print("Running sphinx-build...", flush=True)
        returncode = sphinx_main(args)

        if returncode != 0:
            print(
                f"FAILED: sphinx-build exited with status {returncode}",
                file=sys.stderr,
            )
            return 1
        print("PASSED: Sphinx documentation built successfully.")
        return 0
    finally:
        os.chdir(saved_cwd)
        shutil.rmtree(temp_root, ignore_errors=True)

Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add bazelisk test target that does whatever testing the read the docs/readthedocs.org:openroad does

2 participants