Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f06f53d
feat(module-verification-report): new bundled sphinx extension
antonkri Aug 19, 2026
bc1fcf9
refactor(module-verification-report): split extension into focused mo…
antonkri Aug 19, 2026
a5ca966
test(module-verification-report): add unit tests for the split modules
antonkri Aug 19, 2026
5a35249
docs(module-verification-report): add extension reference page
antonkri Aug 19, 2026
27154c1
feat(module-verification-report): annotate testcase back-links with r…
antonkri Aug 19, 2026
7f3a99c
refactor(module-verification-report): add module-id/feature-id/compon…
antonkri Aug 20, 2026
3d6db9f
refactor(module-verification-report): replace filesystem scan with :c…
antonkri Aug 20, 2026
4a75281
feat(module-verification-report): validate component links at build-f…
antonkri Aug 20, 2026
e49eb74
fix: remove feature-id name guessing — feature section is skipped whe…
antonkri Aug 20, 2026
bba1322
refactor: remove unit test coverage (LCOV) and work-product overrides…
antonkri Aug 20, 2026
ac2540f
style: apply ruff-format and end-of-file-fixer
antonkri Aug 21, 2026
4ea2b5d
feat(docs_and_test): add Bazel macro chaining tests/coverage with docs
antonkri Aug 21, 2026
83f821c
feat(module-verification-report): per-component coverage dropdown
antonkri Aug 21, 2026
d78dae4
refactor(module-verification-report): drop redundant docnames tracking
antonkri Aug 24, 2026
ec067b6
Add mod_ver_report generation to module-verification-report directive
antonkri Aug 24, 2026
0ee7de5
fix: collapse single-line logger.info call (ruff-format)
antonkri Aug 25, 2026
374e0bf
docs(module-verification-report): document docs_and_test macro
antonkri Aug 25, 2026
5402638
fix(module-verification-report): omit zero-coverage rows; add -- sepa…
antonkri Aug 25, 2026
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
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package(default_visibility = ["//visibility:public"])
exports_files([
"default_conf.py.tpl",
"pyproject.toml",
"bzl/run_docs_and_test.py",
])

docs(
Expand Down
124 changes: 124 additions & 0 deletions bzl/docs_and_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# *******************************************************************************
# Copyright (c) 2026 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
# *******************************************************************************
"""Bazel macro that chains ``bazel test`` (or ``bazel coverage``) with a
docs target in one command.
Comment on lines +13 to +14

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this should be a separate pull request. I dont understand why this works, while normally we cannot run bazel from bazel, but that can be discussed there.


Usage in a consumer ``BUILD``::

load("@score_docs_as_code//:bzl/docs_and_test.bzl", "docs_and_test")

docs_and_test(
name = "docs_full",
test_targets = ["//score/..."],
)

Generates two ``py_binary`` targets::

bazel run //:docs_full # tests/coverage, then //:docs
bazel run //:docs_full_preview # tests/coverage, then //:live_preview

Bazel itself has no mechanism to make a build target depend on the
execution of a test, so the orchestration lives outside the dependency
graph in a small Python driver shipped with this module.
"""

load("@rules_python//python:defs.bzl", "py_binary")

_DEFAULT_DRIVER = Label("@score_docs_as_code//:bzl/run_docs_and_test.py")

def _pipeline_binary(name, driver, test_targets, coverage, run_target, help_text):
py_binary(
name = name,
srcs = [driver],
main = driver,
args = [
"--tests",
",".join(test_targets),
"--coverage" if coverage else "--no-coverage",
"--docs",
run_target,
],
tags = ["cli_help=%s:\nbazel run //:%s" % (help_text, name)],
)

def docs_and_test(
name,
test_targets,
coverage = True,
docs_target = "//:docs",
preview_target = "//:live_preview",
driver = None):
"""Create ``py_binary`` targets that run tests, then a docs command.

Two targets are generated:

* ``<name>`` — runs tests, then ``docs_target``.
* ``<name>_preview`` — runs tests, then ``preview_target``
(typically ``//:live_preview``).

Coverage is on by default. When ``coverage = True`` the pipeline uses
``bazel coverage --combined_report=lcov`` on ``test_targets`` instead of
plain ``bazel test``; that runs the same tests with LLVM/GCC coverage
instrumentation and produces the aggregated LCOV at
``bazel-out/_coverage/_coverage_report.dat`` in a single rebuild.
Targets without unit tests simply contribute no coverage data. Set
``coverage = False`` to fall back to plain ``bazel test`` (faster on
first run, no LCOV).

Extra Bazel CLI flags (e.g. ``--config=bl-x86_64-linux``) are not
hard-coded in the ``BUILD`` file. Pass them on the command line after
``--``::

bazel run //:docs_full -- \\
--test-flag=--config=bl-x86_64-linux

``--test-flag`` is repeatable and forwarded to whichever underlying
Bazel command runs (``test`` or ``coverage``).

Args:
name: Base target name; invoke with ``bazel run //:<name>`` or
``bazel run //:<name>_preview``.
test_targets: Bazel labels/patterns for the test/coverage step
(e.g. ``["//score/..."]``). Pass ``[]`` to skip.
coverage: If ``True`` (default), replace ``bazel test`` with
``bazel coverage --combined_report=lcov`` so the docs build can
pick up per-source-file LCOV data. If ``False``, run plain
``bazel test`` and produce no LCOV.
docs_target: Label of the docs binary to invoke via ``bazel run``.
Defaults to ``//:docs``.
preview_target: Label of the live-preview binary to invoke via
``bazel run``. Defaults to ``//:live_preview``. Pass ``None`` to
skip generating the preview target.
driver: Label of the Python driver script. Defaults to the driver
shipped with ``score_docs_as_code``; only override when you want
to inject a custom driver.
"""
driver = driver or _DEFAULT_DRIVER
_pipeline_binary(
name = name,
driver = driver,
test_targets = test_targets,
coverage = coverage,
run_target = docs_target,
help_text = "Run tests, then build documentation",
)

if preview_target:
_pipeline_binary(
name = name + "_preview",
driver = driver,
test_targets = test_targets,
coverage = coverage,
run_target = preview_target,
help_text = "Run tests, then start the docs live preview",
)
119 changes: 119 additions & 0 deletions bzl/run_docs_and_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# *******************************************************************************
# Copyright (c) 2026 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
# *******************************************************************************
"""Driver for the :bzl:`docs_and_test` macro.

Runs either ``bazel test`` or ``bazel coverage`` on the configured targets,
then ``bazel run`` on the docs target. Aborts the pipeline on the first
non-zero exit code so a failing step does not silently ship stale docs.

Extra Bazel CLI flags (typically ``--config=…``) are **not** baked into
the ``BUILD`` file; pass them at ``bazel run`` time after ``--``::

bazel run //:docs_full -- \\
--test-flag=--config=bl-x86_64-linux

``--test-flag`` is repeatable and forwarded to whichever underlying Bazel
command runs (``test`` or ``coverage``).

The script must be invoked from the workspace root — ``bazel run`` sets
``BUILD_WORKSPACE_DIRECTORY`` accordingly, so we chdir there before
invoking any nested Bazel commands.
"""

from __future__ import annotations

import argparse
import os
import subprocess
import sys


def _split(csv: str) -> list[str]:
return [x for x in csv.split(",") if x]


def _run(cmd: list[str]) -> None:
print(f">>> {' '.join(cmd)}", flush=True)
try:
result = subprocess.run(cmd, check=False)
except KeyboardInterrupt:
# Ctrl+C hits both us and the child via the process group. The child
# already exited with 130; propagate the same status without dumping
# a Python traceback so `docs_full_preview` behaves like a bare
# `bazel run //:live_preview`.
sys.exit(130)
if result.returncode != 0:
print(
f"!!! step failed with exit code {result.returncode}: {' '.join(cmd)}",
file=sys.stderr,
)
sys.exit(result.returncode)


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--tests",
default="",
help="Comma-separated Bazel labels/patterns for the test step. "
"Empty string skips the step entirely.",
)
parser.add_argument(
"--coverage",
dest="coverage",
action=argparse.BooleanOptionalAction,
default=True,
help="Run 'bazel coverage --combined_report=lcov' instead of "
"'bazel test' on --tests. Default: True.",
)
parser.add_argument(
"--docs",
required=True,
help="Bazel label of the docs binary to invoke via 'bazel run'.",
)
parser.add_argument(
"--test-flag",
action="append",
default=[],
help="Extra CLI flag forwarded to the test/coverage step "
"(repeatable). Typically '--config=…'.",
)
args = parser.parse_args()

# `bazel run` sets BUILD_WORKSPACE_DIRECTORY to the workspace root; nested
# bazel invocations must run from there so they see MODULE.bazel etc.
workspace = os.environ.get("BUILD_WORKSPACE_DIRECTORY")
if workspace:
os.chdir(workspace)

test_targets = _split(args.tests)

if test_targets:
if args.coverage:
_run(
[
"bazel",
"coverage",
"--combined_report=lcov",
*args.test_flag,
"--",
*test_targets,
]
)
else:
_run(["bazel", "test", *args.test_flag, "--", *test_targets])
_run(["bazel", "run", args.docs])


if __name__ == "__main__":
main()
9 changes: 9 additions & 0 deletions src/extensions/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ Extensions
Architecture and design of the ``score_mounts`` bridge extension.
:ref:`Mounts Extension Internals<score_mounts_internals>`

.. grid-item-card::

Module Verification Report
^^^
The ``.. module-verification-report::`` directive that expands
into the standard per-module verification report body.
:ref:`Module Verification Report<module_verification_report>`


.. toctree::
:maxdepth: 1
Expand All @@ -81,3 +89,4 @@ Extensions
Extension Guide <extension_guide>
Sync TOML <sync_toml>
mounts_internals
module_verification_report
Loading
Loading