diff --git a/.gitignore b/.gitignore index 6c272ca..7ba7c36 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ MODULE.bazel.lock # Ruff .ruff_cache + +# docs:incremental and docs:ide_support build artifacts +/_build diff --git a/.vscode/settings.json b/.vscode/settings.json index 92c92ec..2294d5b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,9 +10,39 @@ "editor.rulers": [ 100 ], - + // + // // RST Settings "[restructuredtext]": { - "editor.tabSize": 3, + "editor.tabSize": 3, }, + // + // + // Esbonio 0.x (Current) + // see https://github.com/swyddfa/esbonio/blob/0.x/docs/lsp/getting-started.rst + // and https://github.com/swyddfa/esbonio/blob/0.x/docs/lsp/editors/vscode/_configuration.rst + "esbonio.server.pythonPath": "${workspaceFolder}/.venv_docs/bin/python", + "esbonio.sphinx.srcDir": "${workspaceFolder}/docs", + "esbonio.sphinx.confDir": "${workspaceFolder}/docs", + "esbonio.sphinx.buildDir": "${workspaceFolder}/_build", + "esbonio.server.logLevel": "info", + // Do not auto-install. We'll use the one in the venv. + "esbonio.server.installBehavior": "nothing", + // Enable port forwarding for preview if working on remote workstation + "remote.autoForwardPorts": true, + "remote.autoForwardPortsSource": "process", + // + // + // Esbonio 1.x (Preview) + "esbonio.sphinx.pythonCommand": [ + ".venv_docs/bin/python" + ], + "esbonio.sphinx.buildCommand": [ + "docs", + "_build", + "--jobs", + "auto" + ], + // default is "error", which doesn't show anything. + "esbonio.logging.level": "warning" } diff --git a/MODULE.bazel b/MODULE.bazel index 0a05c11..f2ecaac 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -46,6 +46,10 @@ pip.parse( ) use_repo(pip, "pip_sphinx") +# Additional Python rules provided by aspect, e.g. an improved version of +# `py_binary`. But more importantly, it provides `py_venv`. +bazel_dep(name = "aspect_rules_py", version = "1.0.0") + ############################################################################### # # Packaging dependencies diff --git a/README.md b/README.md index d8a3298..42ef342 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,60 @@ $ bazel run //:format.fix ## Documentation -Use //docs:docs target to build the documentation. -``` -$ bazel build //docs:docs +Score supports multiple methods for generating documentation, tailored to different workflows: +1. **Bazel-based builds** for clean, sandboxed outputs. +2. **Incremental builds** for quick iterations during development. +3. **IDE integration** for live previews, live warnings and even faster iterations. + +### Bazel-based Build + +This method ensures clean and isolated documentation builds in a controlled Bazel environment. +It is best suited for CI pipelines or production-ready outputs, although it takes longer compared to +incremental builds. + +```sh +bazel build //docs:docs ``` +The output will be located in bazel-bin/docs/docs/_build/html. -The output directory can be found under ```bazel-bin/docs/docs/_build/html```. -If you need to update pip dependencies, after modifying the requirements file, regenerate the lock file: +### Incremental build + +For local changes and faster feedback, use the incremental build. +This method generates the documentation directly in the _build directory. + +```sh +bazel run //docs:incremental ``` -bazel run //docs:requirements.update +Unlike IDE integration, which renders only the current file, this approach is ideal for quickly +verifying edits across the entire documentation during development. + + +### IDE integration + +For live previews, warnings, and linting during development, +integrate Esbonio with your IDE (e.g., VS Code): + +```sh +bazel run //docs:ide_support ``` + +VS Code: Install the Esbonio extension in VS Code. After installation, restart your IDE. +You should now have live preview available when you open a `.rst` file. +Note: if the extension was already installed when you ran the `ide_support` command, +you will need to restart your IDE. + +For features like type detection in conf.py or extensions, +point your IDE to the .venv_docs virtual environment. + +Re-run //docs:ide_support if you update Sphinx extensions or other dependencies. + +### Notes +#### Output Locations +* Bazel builds output to bazel-bin/docs/docs/_build/html. +* Incremental builds output to _build. + +#### Troubleshooting +* Restart your IDE if live previews or warnings are not working after running ide_support. +* Ensure your virtual environment is up-to-date by re-running //docs:ide_support when dependencies + change. diff --git a/docs/BUILD b/docs/BUILD index 426c075..4b784a8 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -11,6 +11,33 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +# Multiple approaches are available to build the same documentation output: +# +# 1. **Esbonio via IDE support (`ide_support` target)**: +# - Listed first as it offers the least flexibility in implementation. +# - Designed for live previews and quick iterations when editing documentation. +# - Integrates with IDEs like VS Code but requires the Esbonio extension. +# - Requires a virtual environment with consistent dependencies (see 2). +# +# 2. **Directly running Sphinx in the virtual environment**: +# - As mentioned above, a virtual environment is required for running esbonio. +# - Therefore, the same environment can be used to run Sphinx directly. +# - Option 1: Run Sphinx manually via `.venv_docs/bin/python -m sphinx docs _build --jobs auto`. +# - Option 2: Use the `incremental` target, which simplifies this process. +# - Usable in CI pipelines to validate the virtual environment used by Esbonio. +# - Ideal for quickly generating documentation during development. +# +# 3. **Bazel-based build (`docs` target)**: +# - Runs the documentation build in a Bazel sandbox, ensuring clean, isolated builds. +# - Less convenient for frequent local edits but ensures build reproducibility. +# +# **Consistency**: +# When modifying Sphinx extensions or configuration, ensure all three methods +# (Esbonio, incremental, and Bazel) work as expected to avoid discrepancies. +# +# For user-facing documentation, refer to `/README.md`. + +load("@aspect_rules_py//py:defs.bzl", "py_venv") load("@pip_sphinx//:requirements.bzl", "all_requirements", "requirement") load("@rules_pkg//pkg:mappings.bzl", "pkg_files") load("@rules_pkg//pkg:tar.bzl", "pkg_tar") @@ -18,6 +45,10 @@ load("@rules_python//python:defs.bzl", "py_library") load("@rules_python//python:pip.bzl", "compile_pip_requirements") load("@rules_python//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs") +# all_requirements include esbonio which we don't need most of the time. +# Hint: the names are e.g. "@@rules_python~~pip~pip_sphinx//esbonio:pkg" +sphinx_requirements = [name for name in all_requirements if not "esbonio" in name] + [":extensions"] + sphinx_docs( name = "docs", srcs = glob([ @@ -43,7 +74,7 @@ sphinx_docs( sphinx_build_binary( name = "sphinx_build", - deps = [":extensions"] + all_requirements, + deps = sphinx_requirements, ) py_library( @@ -55,6 +86,11 @@ py_library( imports = ["."], ) +# In order to update the requirements, change the `requirements.txt` file and run: +# `bazel run //docs:requirements`. +# This will update the `requirements_lock.txt` file. +# To upgrade all dependencies to their latest versions, run: +# `bazel run //docs:requirements -- --upgrade`. compile_pip_requirements( name = "requirements", src = "requirements.txt", @@ -80,3 +116,19 @@ pkg_tar( name = "github-pages", srcs = [":html_files"], ) + +# Run-time build of documentation, incl. incremental build support. +py_binary( + name = "incremental", + srcs = ["incremental.py"], + deps = sphinx_requirements, +) + +# Virtual python environment for working on the documentation (esbonio). +# incl. python support when working on conf.py and sphinx extensions. +py_venv( + name = "ide_support", + venv_name = ".venv_docs", + # Until release of esbonio 1.x, we need to install it ourselves so the VS Code extension can find it. + deps = sphinx_requirements + [requirement("esbonio")], +) diff --git a/docs/conf.py b/docs/conf.py index 4da3523..62b6814 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,8 +36,6 @@ templates_path = ["_templates"] -suppress_warnings = ["config.cache"] - # Enable numref numfig = True diff --git a/docs/incremental.py b/docs/incremental.py new file mode 100644 index 0000000..25a3acb --- /dev/null +++ b/docs/incremental.py @@ -0,0 +1,42 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +import os +import sys + +try: + from sphinx.cmd.build import main as sphinx_main +except ImportError: + sys.exit( + "This script must be run from Bazel via `bazel run //docs:incremental`" + " (sphinx not installed)." + ) + +workspace = os.getenv("BUILD_WORKSPACE_DIRECTORY") +if not workspace: + sys.exit( + "This script must be run from Bazel via `bazel run //docs:incremental`" + " (BUILD_WORKSPACE_DIRECTORY not set)." + ) + +# sphinx will print relative paths to the current directory. +# Change to the workspace root so that the paths are readable and clickable. +os.chdir(workspace) +sphinx_main( + [ + "docs", + "_build", + "--jobs", + "auto", + ] +) diff --git a/docs/requirements.txt b/docs/requirements.txt index 3e5cf07..3e0ca02 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,10 @@ --extra-index-url https://pypi.org/simple/ -Sphinx==8.1.3 -sphinx-needs==4.1.0 -pydata-sphinx-theme==0.16.0 +Sphinx +sphinx-needs +pydata-sphinx-theme + +# esbonio >= 1 comes bundled with the esbonio extension >= 1. +# esbonio<1 is required for the esbonio extension <1 +# So what we need to install here is esbonio<1 +esbonio<1 diff --git a/docs/requirements_lock.txt b/docs/requirements_lock.txt index 8493462..ce75d23 100644 --- a/docs/requirements_lock.txt +++ b/docs/requirements_lock.txt @@ -18,7 +18,9 @@ attrs==24.2.0 \ --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 # via + # cattrs # jsonschema + # lsprotocol # referencing babel==2.15.0 \ --hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \ @@ -30,6 +32,12 @@ beautifulsoup4==4.12.3 \ --hash=sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051 \ --hash=sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed # via pydata-sphinx-theme +cattrs==24.1.2 \ + --hash=sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0 \ + --hash=sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85 + # via + # lsprotocol + # pygls certifi==2024.7.4 \ --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 @@ -132,6 +140,10 @@ docutils==0.20.1 \ # via # pydata-sphinx-theme # sphinx +esbonio==0.16.5 \ + --hash=sha256:04ba926e3603f7b1fde1abc690b47afd60749b64b1029b6bce8e1de0bb284921 \ + --hash=sha256:acab2e16c6cf8f7232fb04e0d48514ce50566516b1f6fcf669ccf2f247e8b10f + # via -r docs/requirements.txt idna==3.7 \ --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 @@ -152,6 +164,10 @@ jsonschema-specifications==2024.10.1 \ --hash=sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272 \ --hash=sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf # via jsonschema +lsprotocol==2023.0.1 \ + --hash=sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2 \ + --hash=sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d + # via pygls markupsafe==2.1.5 \ --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ @@ -218,10 +234,18 @@ packaging==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via sphinx +platformdirs==4.3.6 \ + --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ + --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb + # via esbonio pydata-sphinx-theme==0.16.0 \ --hash=sha256:18c810ee4e67e05281e371e156c1fb5bb0fa1f2747240461b225272f7d8d57d8 \ --hash=sha256:721dd26e05fa8b992d66ef545536e6cbe0110afb9865820a08894af1ad6f7707 # via -r docs/requirements.txt +pygls==1.3.1 \ + --hash=sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018 \ + --hash=sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e + # via esbonio pygments==2.18.0 \ --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \ --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a @@ -229,6 +253,10 @@ pygments==2.18.0 \ # accessible-pygments # pydata-sphinx-theme # sphinx +pyspellchecker==0.8.1 \ + --hash=sha256:3478ca8484d1c2db0c93d12b3c986cd17958c69f47b3ed7ef4d3f4201e591776 \ + --hash=sha256:d91e9e1064793ae1ee8e71b06ca40eeb8e5923437c54291a8e041b447792b640 + # via esbonio referencing==0.35.1 \ --hash=sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c \ --hash=sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de @@ -353,6 +381,7 @@ sphinx==8.1.3 \ --hash=sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927 # via # -r docs/requirements.txt + # esbonio # pydata-sphinx-theme # sphinx-data-viewer # sphinx-needs