From e3d734c58c759aa6ca9b19e3660b3392e348de8b Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Mon, 1 Jun 2026 15:35:06 -0400 Subject: [PATCH 01/17] Check versioned docs example links --- .github/workflows/build-docs.yml | 20 +++++++++++++------- cuda_bindings/docs/build_docs.sh | 16 ++++++++++++++-- cuda_bindings/docs/source/conf.py | 2 ++ cuda_core/docs/build_docs.sh | 14 ++++++++++++-- cuda_core/docs/source/conf.py | 2 ++ 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 1bd0c0194c1..ea091090376 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 @@ -91,14 +91,20 @@ jobs: if [[ ${{ inputs.is-release }} == "true" ]]; then FILE_HASH="*" - COMMIT_HASH="${{ inputs.git-tag }}" + DOCS_GITHUB_REF="${{ inputs.git-tag }}" + if [[ -z "${DOCS_GITHUB_REF}" ]]; then + DOCS_GITHUB_REF="${GITHUB_REF_NAME}" + fi + COMMIT_HASH="${DOCS_GITHUB_REF}" else FILE_HASH="${{ github.sha }}" - COMMIT_HASH="${{ github.sha }}" + DOCS_GITHUB_REF="${{ github.sha }}" + COMMIT_HASH="${DOCS_GITHUB_REF}" fi # make outputs from the previous job as env vars CUDA_CORE_ARTIFACT_BASENAME="cuda-core-python${PYTHON_VERSION_FORMATTED}-linux-64" + echo "CUDA_PYTHON_DOCS_GITHUB_REF=${DOCS_GITHUB_REF}" >> $GITHUB_ENV echo "COMMIT_HASH=${COMMIT_HASH}" >> $GITHUB_ENV echo "CUDA_CORE_ARTIFACT_BASENAME=${CUDA_CORE_ARTIFACT_BASENAME}" >> $GITHUB_ENV echo "CUDA_CORE_ARTIFACT_NAME=${CUDA_CORE_ARTIFACT_BASENAME}-${FILE_HASH}" >> $GITHUB_ENV @@ -210,9 +216,9 @@ jobs: run: | pushd cuda_python/docs/ if [[ "${{ inputs.is-release }}" == "false" ]]; then - ./build_all_docs.sh latest-only + DOCS_LINKCHECK=1 ./build_all_docs.sh latest-only else - ./build_all_docs.sh + DOCS_LINKCHECK=1 ./build_all_docs.sh # At release time, we don't want to update the latest docs rm -rf build/html/latest fi @@ -226,9 +232,9 @@ jobs: COMPONENT=$(echo "${{ inputs.component }}" | tr '-' '_') pushd ${COMPONENT}/docs/ if [[ "${{ inputs.is-release }}" == "false" ]]; then - ./build_docs.sh latest-only + DOCS_LINKCHECK=1 ./build_docs.sh latest-only else - ./build_docs.sh + DOCS_LINKCHECK=1 ./build_docs.sh # At release time, we don't want to update the latest docs rm -rf build/html/latest fi diff --git a/cuda_bindings/docs/build_docs.sh b/cuda_bindings/docs/build_docs.sh index 15a42b93464..ab52431c092 100755 --- a/cuda_bindings/docs/build_docs.sh +++ b/cuda_bindings/docs/build_docs.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE set -ex @@ -30,7 +30,19 @@ if [[ "${LATEST_ONLY}" == "1" && -z "${BUILD_PREVIEW:-}" && -z "${BUILD_LATEST:- fi # build the docs (in parallel) -SPHINXOPTS="-j 4 -d build/.doctrees" make html +if [[ -z "${SPHINXOPTS:-}" ]]; then + HTML_SPHINXOPTS="-j 4 -d build/.doctrees" +else + HTML_SPHINXOPTS="${SPHINXOPTS}" +fi +SPHINXOPTS="${HTML_SPHINXOPTS}" make html + +if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then + if [[ -z "${LINKCHECK_SPHINXOPTS:-}" ]]; then + LINKCHECK_SPHINXOPTS="-W --keep-going -j 4 -d build/.linkcheck-doctrees" + fi + SPHINXOPTS="${LINKCHECK_SPHINXOPTS}" make linkcheck BUILDDIR="build/linkcheck/${SPHINX_CUDA_BINDINGS_VER}" +fi # for debugging/developing (conf.py), please comment out the above line and # use the line below instead, as we must build in serial to avoid getting diff --git a/cuda_bindings/docs/source/conf.py b/cuda_bindings/docs/source/conf.py index 69a05d6e52f..45344cc4710 100644 --- a/cuda_bindings/docs/source/conf.py +++ b/cuda_bindings/docs/source/conf.py @@ -27,6 +27,8 @@ def _github_examples_ref(): + if ref := os.environ.get("CUDA_PYTHON_DOCS_GITHUB_REF"): + return ref if int(os.environ.get("BUILD_PREVIEW", 0)) or int(os.environ.get("BUILD_LATEST", 0)): return "main" return f"v{release}" diff --git a/cuda_core/docs/build_docs.sh b/cuda_core/docs/build_docs.sh index 78038438bcc..d723a8bdbfe 100755 --- a/cuda_core/docs/build_docs.sh +++ b/cuda_core/docs/build_docs.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -ex @@ -30,10 +30,20 @@ fi # build the docs. Allow callers to override SPHINXOPTS for serial/debug runs. if [[ -z "${SPHINXOPTS:-}" ]]; then - SPHINXOPTS="-W --keep-going -j 4 -d build/.doctrees" + HTML_SPHINXOPTS="-W --keep-going -j 4 -d build/.doctrees" +else + HTML_SPHINXOPTS="${SPHINXOPTS}" fi +SPHINXOPTS="${HTML_SPHINXOPTS}" make html +if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then + if [[ -z "${LINKCHECK_SPHINXOPTS:-}" ]]; then + LINKCHECK_SPHINXOPTS="-W --keep-going -j 4 -d build/.linkcheck-doctrees" + fi + SPHINXOPTS="${LINKCHECK_SPHINXOPTS}" make linkcheck BUILDDIR="build/linkcheck/${SPHINX_CUDA_CORE_VER}" +fi + # to support version dropdown menu cp ./versions.json build/html cp ./nv-versions.json build/html diff --git a/cuda_core/docs/source/conf.py b/cuda_core/docs/source/conf.py index 14d93297937..0549d3f0dcf 100644 --- a/cuda_core/docs/source/conf.py +++ b/cuda_core/docs/source/conf.py @@ -28,6 +28,8 @@ def _github_examples_ref(): + if ref := os.environ.get("CUDA_PYTHON_DOCS_GITHUB_REF"): + return ref if int(os.environ.get("BUILD_PREVIEW", 0)) or int(os.environ.get("BUILD_LATEST", 0)): return "main" return f"cuda-core-v{release}" From 1123759f128e3ad6909571227271922dde127a97 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Mon, 1 Jun 2026 16:19:26 -0400 Subject: [PATCH 02/17] Add pathfinder docs linkcheck --- cuda_pathfinder/docs/build_docs.sh | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cuda_pathfinder/docs/build_docs.sh b/cuda_pathfinder/docs/build_docs.sh index a7889b58dda..7e1ae3992c7 100755 --- a/cuda_pathfinder/docs/build_docs.sh +++ b/cuda_pathfinder/docs/build_docs.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -ex @@ -25,8 +25,24 @@ if [[ -z "${SPHINX_CUDA_PATHFINDER_VER}" ]]; then | awk -F'+' '{print $1}') fi +if [[ "${LATEST_ONLY}" == "1" && -z "${BUILD_PREVIEW:-}" && -z "${BUILD_LATEST:-}" ]]; then + export BUILD_LATEST=1 +fi + # build the docs (in parallel) -SPHINXOPTS="-W --keep-going -j 4 -d build/.doctrees" make html +if [[ -z "${SPHINXOPTS:-}" ]]; then + HTML_SPHINXOPTS="-W --keep-going -j 4 -d build/.doctrees" +else + HTML_SPHINXOPTS="${SPHINXOPTS}" +fi +SPHINXOPTS="${HTML_SPHINXOPTS}" make html + +if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then + if [[ -z "${LINKCHECK_SPHINXOPTS:-}" ]]; then + LINKCHECK_SPHINXOPTS="-W --keep-going -j 4 -d build/.linkcheck-doctrees" + fi + SPHINXOPTS="${LINKCHECK_SPHINXOPTS}" make linkcheck BUILDDIR="build/linkcheck/${SPHINX_CUDA_PATHFINDER_VER}" +fi # for debugging/developing (conf.py), please comment out the above line and # use the line below instead, as we must build in serial to avoid getting From a90437ee46b7d1b433b6e44e2cc1890893e58034 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Mon, 1 Jun 2026 16:58:56 -0400 Subject: [PATCH 03/17] Check docs example links locally --- cuda_bindings/docs/build_docs.sh | 14 +- cuda_bindings/docs/source/conf.py | 1 + cuda_core/docs/build_docs.sh | 14 +- cuda_core/docs/source/conf.py | 5 + cuda_core/docs/source/release/0.3.1-notes.rst | 6 +- cuda_pathfinder/docs/build_docs.sh | 14 +- cuda_python/docs/check_example_links.py | 125 ++++++++++++++++++ 7 files changed, 167 insertions(+), 12 deletions(-) create mode 100644 cuda_python/docs/check_example_links.py diff --git a/cuda_bindings/docs/build_docs.sh b/cuda_bindings/docs/build_docs.sh index ab52431c092..ca2f49b1036 100755 --- a/cuda_bindings/docs/build_docs.sh +++ b/cuda_bindings/docs/build_docs.sh @@ -38,10 +38,18 @@ fi SPHINXOPTS="${HTML_SPHINXOPTS}" make html if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then - if [[ -z "${LINKCHECK_SPHINXOPTS:-}" ]]; then - LINKCHECK_SPHINXOPTS="-W --keep-going -j 4 -d build/.linkcheck-doctrees" + if [[ -n "${CUDA_PYTHON_DOCS_GITHUB_REF:-}" ]]; then + DOCS_EXAMPLES_REF="${CUDA_PYTHON_DOCS_GITHUB_REF}" + elif [[ "${BUILD_PREVIEW:-0}" == "1" || "${BUILD_LATEST:-0}" == "1" ]]; then + DOCS_EXAMPLES_REF="main" + else + DOCS_EXAMPLES_REF="v${SPHINX_CUDA_BINDINGS_VER}" fi - SPHINXOPTS="${LINKCHECK_SPHINXOPTS}" make linkcheck BUILDDIR="build/linkcheck/${SPHINX_CUDA_BINDINGS_VER}" + python ../../cuda_python/docs/check_example_links.py \ + --source-dir source \ + --examples-root cuda_bindings/examples \ + --expected-ref "${DOCS_EXAMPLES_REF}" \ + --placeholder cuda_bindings_github_ref fi # for debugging/developing (conf.py), please comment out the above line and diff --git a/cuda_bindings/docs/source/conf.py b/cuda_bindings/docs/source/conf.py index 45344cc4710..297815a2956 100644 --- a/cuda_bindings/docs/source/conf.py +++ b/cuda_bindings/docs/source/conf.py @@ -133,6 +133,7 @@ def _github_examples_ref(): def rewrite_source(app, docname, source): text = source[0] + text = text.replace("|cuda_bindings_github_ref|", GITHUB_EXAMPLES_REF) if docname.startswith("release/"): text = text.replace(".. module:: cuda.bindings\n\n", "", 1) diff --git a/cuda_core/docs/build_docs.sh b/cuda_core/docs/build_docs.sh index d723a8bdbfe..0b14c3f4369 100755 --- a/cuda_core/docs/build_docs.sh +++ b/cuda_core/docs/build_docs.sh @@ -38,10 +38,18 @@ SPHINXOPTS="${HTML_SPHINXOPTS}" make html if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then - if [[ -z "${LINKCHECK_SPHINXOPTS:-}" ]]; then - LINKCHECK_SPHINXOPTS="-W --keep-going -j 4 -d build/.linkcheck-doctrees" + if [[ -n "${CUDA_PYTHON_DOCS_GITHUB_REF:-}" ]]; then + DOCS_EXAMPLES_REF="${CUDA_PYTHON_DOCS_GITHUB_REF}" + elif [[ "${BUILD_PREVIEW:-0}" == "1" || "${BUILD_LATEST:-0}" == "1" ]]; then + DOCS_EXAMPLES_REF="main" + else + DOCS_EXAMPLES_REF="cuda-core-v${SPHINX_CUDA_CORE_VER}" fi - SPHINXOPTS="${LINKCHECK_SPHINXOPTS}" make linkcheck BUILDDIR="build/linkcheck/${SPHINX_CUDA_CORE_VER}" + python ../../cuda_python/docs/check_example_links.py \ + --source-dir source \ + --examples-root cuda_core/examples \ + --expected-ref "${DOCS_EXAMPLES_REF}" \ + --placeholder cuda_core_github_ref fi # to support version dropdown menu diff --git a/cuda_core/docs/source/conf.py b/cuda_core/docs/source/conf.py index 0549d3f0dcf..28d0f6add6a 100644 --- a/cuda_core/docs/source/conf.py +++ b/cuda_core/docs/source/conf.py @@ -200,6 +200,11 @@ def skip_member(app, what, name, obj, skip, options): return None +def rewrite_source(app, docname, source): + source[0] = source[0].replace("|cuda_core_github_ref|", GITHUB_EXAMPLES_REF) + + def setup(app): app.connect("autodoc-process-docstring", autodoc_process_docstring) app.connect("autodoc-skip-member", skip_member) + app.connect("source-read", rewrite_source) diff --git a/cuda_core/docs/source/release/0.3.1-notes.rst b/cuda_core/docs/source/release/0.3.1-notes.rst index 82138763db2..6783011d731 100644 --- a/cuda_core/docs/source/release/0.3.1-notes.rst +++ b/cuda_core/docs/source/release/0.3.1-notes.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: Apache-2.0 .. currentmodule:: cuda.core.experimental @@ -31,8 +31,8 @@ None. New examples ------------ -- Add a `CUDA graph `_ example. -- Add a `memory resource `_ example. +- Add a `CUDA graph `_ example. +- Add a `memory resource `_ example. Fixes and enhancements diff --git a/cuda_pathfinder/docs/build_docs.sh b/cuda_pathfinder/docs/build_docs.sh index 7e1ae3992c7..598a51249b4 100755 --- a/cuda_pathfinder/docs/build_docs.sh +++ b/cuda_pathfinder/docs/build_docs.sh @@ -38,10 +38,18 @@ fi SPHINXOPTS="${HTML_SPHINXOPTS}" make html if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then - if [[ -z "${LINKCHECK_SPHINXOPTS:-}" ]]; then - LINKCHECK_SPHINXOPTS="-W --keep-going -j 4 -d build/.linkcheck-doctrees" + if [[ -n "${CUDA_PYTHON_DOCS_GITHUB_REF:-}" ]]; then + DOCS_EXAMPLES_REF="${CUDA_PYTHON_DOCS_GITHUB_REF}" + elif [[ "${BUILD_PREVIEW:-0}" == "1" || "${BUILD_LATEST:-0}" == "1" ]]; then + DOCS_EXAMPLES_REF="main" + else + DOCS_EXAMPLES_REF="cuda-pathfinder-v${SPHINX_CUDA_PATHFINDER_VER}" fi - SPHINXOPTS="${LINKCHECK_SPHINXOPTS}" make linkcheck BUILDDIR="build/linkcheck/${SPHINX_CUDA_PATHFINDER_VER}" + python ../../cuda_python/docs/check_example_links.py \ + --source-dir source \ + --examples-root cuda_pathfinder/examples \ + --expected-ref "${DOCS_EXAMPLES_REF}" \ + --allow-empty fi # for debugging/developing (conf.py), please comment out the above line and diff --git a/cuda_python/docs/check_example_links.py b/cuda_python/docs/check_example_links.py new file mode 100644 index 00000000000..5f76c412ea7 --- /dev/null +++ b/cuda_python/docs/check_example_links.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path +from urllib.parse import unquote + +CUDA_PYTHON_URL_RE = re.compile( + r"https://github\.com/NVIDIA/cuda-python/" + r"(?Pblob|tree)/" + r"(?P[^/\s<>`]+)/" + r"(?P[^\s<>`)]+)" +) +SOURCE_SUFFIXES = {".md", ".rst"} + + +def _source_files(source_dir: Path): + for path in sorted(source_dir.rglob("*")): + if path.suffix in SOURCE_SUFFIXES and path.is_file(): + yield path + + +def _normalize_url_path(url_path: str) -> str: + path = unquote(url_path) + for separator in ("#", "?"): + path = path.split(separator, 1)[0] + return path.rstrip(".") + + +def _is_within(path: str, root: str) -> bool: + return path == root or path.startswith(f"{root}/") + + +def _display_path(path: Path, repo_root: Path) -> str: + try: + return str(path.relative_to(repo_root)) + except ValueError: + return str(path) + + +def check_links(args: argparse.Namespace) -> int: + repo_root = args.repo_root.resolve() + source_dir = args.source_dir.resolve() + examples_root = args.examples_root.strip("/") + placeholder_ref = f"|{args.placeholder}|" if args.placeholder else None + checked = 0 + failures: list[str] = [] + + for source_path in _source_files(source_dir): + text = source_path.read_text(encoding="utf-8") + for match in CUDA_PYTHON_URL_RE.finditer(text): + url_path = _normalize_url_path(match.group("path")) + if not _is_within(url_path, examples_root): + continue + + checked += 1 + ref = match.group("ref") + kind = match.group("kind") + location = _display_path(source_path, repo_root) + target_path = Path(url_path) + target = repo_root / target_path + + if target_path.is_absolute() or ".." in target_path.parts: + failures.append(f"{location}: invalid repository path in {match.group(0)}") + continue + + if placeholder_ref and ref == placeholder_ref: + rendered_ref = args.expected_ref + elif ref == args.expected_ref: + rendered_ref = ref + else: + expected = placeholder_ref or args.expected_ref + failures.append(f"{location}: {match.group(0)} uses ref {ref!r}; expected {expected!r}") + rendered_ref = ref + + if kind == "blob" and not target.is_file(): + failures.append( + f"{location}: {match.group(0)} resolves to missing file " + f"{target.relative_to(repo_root)} at ref {rendered_ref}" + ) + elif kind == "tree" and not target.is_dir(): + failures.append( + f"{location}: {match.group(0)} resolves to missing directory " + f"{target.relative_to(repo_root)} at ref {rendered_ref}" + ) + + if checked == 0 and not args.allow_empty: + failures.append(f"No example links under {examples_root!r} found in {source_dir}") + + if failures: + sys.stderr.write("Example link check failed:\n") + for failure in failures: + sys.stderr.write(f" - {failure}\n") + return 1 + + sys.stdout.write(f"Checked {checked} example link(s) under {examples_root} against ref {args.expected_ref}\n") + return 0 + + +def parse_args(argv: list[str]) -> argparse.Namespace: + repo_root = Path(__file__).resolve().parents[2] + + parser = argparse.ArgumentParser(description="Validate cuda-python example links without probing GitHub.") + parser.add_argument("--source-dir", type=Path, required=True) + parser.add_argument("--examples-root", required=True) + parser.add_argument("--expected-ref", required=True) + parser.add_argument("--placeholder") + parser.add_argument("--repo-root", type=Path, default=repo_root) + parser.add_argument("--allow-empty", action="store_true") + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(sys.argv[1:] if argv is None else argv) + return check_links(args) + + +if __name__ == "__main__": + raise SystemExit(main()) From 8699dddefdcca17fd2f79400bcea1af5106212af Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 11:03:23 -0400 Subject: [PATCH 04/17] Add lychee docs link checking --- .gitignore | 1 + .pre-commit-config.yaml | 15 +++++++++++++++ cuda_bindings/docs/source/conduct.rst | 4 ++-- cuda_bindings/docs/source/motivation.rst | 4 ++-- cuda_bindings/docs/source/overview.rst | 8 +++++--- .../docs/source/release/11.7.1-notes.rst | 4 +++- .../docs/source/release/11.8.0-notes.rst | 4 ++-- .../docs/source/release/12.9.6-notes.rst | 2 +- .../docs/source/release/13.1.1-notes.rst | 4 ++-- .../docs/source/release/13.2.0-notes.rst | 2 +- cuda_bindings/docs/source/support.rst | 4 ++-- cuda_core/docs/source/conduct.rst | 4 ++-- cuda_python/docs/source/release/12.9.5-notes.rst | 4 ++-- cuda_python/docs/source/release/12.9.6-notes.rst | 2 +- 14 files changed, 41 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 26f13b1d174..e15f36ccc46 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__/ # CUDA Python specific .cache/ +.lycheecache .pytest_cache/ .benchmarks/ *.cpp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 859e298bc49..31212d4c3ab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,6 +42,21 @@ repos: language: system files: '^.*/docs/source/.*\.md$' + # Link checking for authored documentation files + - repo: https://github.com/lycheeverse/lychee + rev: lychee-v0.24.2 + hooks: + - id: lychee + args: + - LYCHEE_VERSION=0.24.2 + - --cache + - --max-concurrency=4 + - --max-retries=3 + - --no-progress + - --exclude + - '^https://github\.com/NVIDIA/cuda-python/(blob|tree)/$' + files: '\.(md|rst)$' + # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: "3e8a8703264a2f4a69428a0aa4dcb512790b2c8c" # frozen: v6.0.0 diff --git a/cuda_bindings/docs/source/conduct.rst b/cuda_bindings/docs/source/conduct.rst index b70d9dd7ce3..26d3f1e59f9 100644 --- a/cuda_bindings/docs/source/conduct.rst +++ b/cuda_bindings/docs/source/conduct.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE Code of Conduct @@ -85,7 +85,7 @@ Attribution ----------- This Code of Conduct is adapted from the `Contributor Covenant `_, version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/ For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq diff --git a/cuda_bindings/docs/source/motivation.rst b/cuda_bindings/docs/source/motivation.rst index 433cc166193..16bfd9745af 100644 --- a/cuda_bindings/docs/source/motivation.rst +++ b/cuda_bindings/docs/source/motivation.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE Motivation @@ -31,7 +31,7 @@ you get the best of both worlds: rapid iterative development with Python and the speed of a compiled language targeting both CPUs and NVIDIA GPUs. `CuPy `_ is a -`NumPy `_/`SciPy `_ compatible Array +`NumPy `_/`SciPy `_ compatible Array library, from `Preferred Networks `_, for GPU-accelerated computing with Python. CUDA Python simplifies the CuPy build and allows for a faster and smaller memory footprint when importing the CuPy diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index e0d269cb6b6..8ada38bfd22 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE Overview @@ -38,8 +38,8 @@ The first thing to do is import the `Driver API `_ and `NVRTC `_ modules from the ``cuda.bindings`` package. Next, we consider how to store host data and pass it to the device. Different -approaches can be used to accomplish this and are described in `Preparing kernel -arguments `_. +approaches can be used to accomplish this and are described in +:ref:`Preparing kernel arguments `. In this example, we will use NumPy to store host data and pass it to the device, so let's import this dependency as well. @@ -308,6 +308,8 @@ maximize performance ({numref}``Figure 1``). Screenshot of Nsight Compute CLI output of ``cuda.bindings`` example. +.. _preparing-kernel-arguments: + Preparing kernel arguments -------------------------- diff --git a/cuda_bindings/docs/source/release/11.7.1-notes.rst b/cuda_bindings/docs/source/release/11.7.1-notes.rst index 0fbea248e36..385cfb6e233 100644 --- a/cuda_bindings/docs/source/release/11.7.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.7.1-notes.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE CUDA Python 11.7.1 Release notes @@ -14,6 +14,8 @@ Highlights Limitations ----------- +.. _cuda-bindings-11-7-1-source-builds: + Source builds ^^^^^^^^^^^^^ diff --git a/cuda_bindings/docs/source/release/11.8.0-notes.rst b/cuda_bindings/docs/source/release/11.8.0-notes.rst index e24022142df..68e40bc325d 100644 --- a/cuda_bindings/docs/source/release/11.8.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.0-notes.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE CUDA Python 11.8.0 Release notes @@ -16,7 +16,7 @@ Highlights Source Builds ^^^^^^^^^^^^^ -CUDA Python source builds now parse CUDA headers located in $CUDA_HOME directory, enabling/disabling types and APIs if defined. Therefore this removes the need for CTK headers to have all types defined. By allowing minor variations, previous `11.7.1 mobile platform workaround `_ is no longer needed. +CUDA Python source builds now parse CUDA headers located in $CUDA_HOME directory, enabling/disabling types and APIs if defined. Therefore this removes the need for CTK headers to have all types defined. By allowing minor variations, previous :ref:`11.7.1 mobile platform workaround ` is no longer needed. It's still required that source builds use the latest CTK headers (i.e. “$CUDA_HOME/include” has latest CTK headers). diff --git a/cuda_bindings/docs/source/release/12.9.6-notes.rst b/cuda_bindings/docs/source/release/12.9.6-notes.rst index dd508ff1478..2ba5f3d96f8 100644 --- a/cuda_bindings/docs/source/release/12.9.6-notes.rst +++ b/cuda_bindings/docs/source/release/12.9.6-notes.rst @@ -31,7 +31,7 @@ Bugfixes * Fixed an issue where the ``CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL`` attribute was retrieved as an unsigned int, rather than a signed int. - (`PR #1336 `_) + (`PR #1336 `_) * Fixed a use-after-free in ``_HelperInputVoidPtr`` properties when backed by Python buffer objects. (`PR #1629 `_) diff --git a/cuda_bindings/docs/source/release/13.1.1-notes.rst b/cuda_bindings/docs/source/release/13.1.1-notes.rst index 37353cbe145..d4ef6af2d98 100644 --- a/cuda_bindings/docs/source/release/13.1.1-notes.rst +++ b/cuda_bindings/docs/source/release/13.1.1-notes.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE .. module:: cuda.bindings @@ -12,7 +12,7 @@ Highlights ---------- * Add missing driver & runtime bindings for functions new in CTK 13.1.0 - (`PR #1321 `_) + (`PR #1321 `_) Experimental ------------ diff --git a/cuda_bindings/docs/source/release/13.2.0-notes.rst b/cuda_bindings/docs/source/release/13.2.0-notes.rst index 4ea580c9c53..e470b896d52 100644 --- a/cuda_bindings/docs/source/release/13.2.0-notes.rst +++ b/cuda_bindings/docs/source/release/13.2.0-notes.rst @@ -39,7 +39,7 @@ Bugfixes * Fixed an issue where the ``CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL`` attribute was retrieved as an unsigned int, rather than a signed int. - (`PR #1336 `_) + (`PR #1336 `_) * Fixed ABI incompatibility bugs in cuFILE bindings introduced in v13.1.0. (`PR #1468 `_) * Fixed a use-after-free in ``_HelperInputVoidPtr`` properties when backed by diff --git a/cuda_bindings/docs/source/support.rst b/cuda_bindings/docs/source/support.rst index 4439d963c0e..0ed4d023efa 100644 --- a/cuda_bindings/docs/source/support.rst +++ b/cuda_bindings/docs/source/support.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE .. _support: @@ -38,7 +38,7 @@ The NVIDIA CUDA Python team reserves rights to amend the above support policy. A however, will be announced to the users in advance. -.. _CUDA minor version compatibility: https://docs.nvidia.com/deploy/cuda-compatibility/#minor-version-compatibility +.. _CUDA minor version compatibility: https://docs.nvidia.com/deploy/cuda-compatibility/minor-version-compatibility.html .. _CPython EOL schedule: https://devguide.python.org/versions/ .. _built-in modules that are known to be thread-unsafe: https://github.com/python/cpython/issues/116738 .. _free-threaded interpreter: https://docs.python.org/3/howto/free-threading-python.html diff --git a/cuda_core/docs/source/conduct.rst b/cuda_core/docs/source/conduct.rst index 1c00f5c3430..d6df40945a1 100644 --- a/cuda_core/docs/source/conduct.rst +++ b/cuda_core/docs/source/conduct.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: Apache-2.0 Code of Conduct @@ -85,7 +85,7 @@ Attribution ----------- This Code of Conduct is adapted from the `Contributor Covenant `_, version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/ For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq diff --git a/cuda_python/docs/source/release/12.9.5-notes.rst b/cuda_python/docs/source/release/12.9.5-notes.rst index a134a36f00e..aac1dfa7929 100644 --- a/cuda_python/docs/source/release/12.9.5-notes.rst +++ b/cuda_python/docs/source/release/12.9.5-notes.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE CUDA Python 12.9.5 Release notes @@ -8,7 +8,7 @@ CUDA Python 12.9.5 Release notes Included components ------------------- -* `cuda.bindings 12.9.5 `_ +* `cuda.bindings 12.9.5 `_ * `cuda.pathfinder 1.3.3 `_ diff --git a/cuda_python/docs/source/release/12.9.6-notes.rst b/cuda_python/docs/source/release/12.9.6-notes.rst index 86ed747cfc7..b66e1e9ad50 100644 --- a/cuda_python/docs/source/release/12.9.6-notes.rst +++ b/cuda_python/docs/source/release/12.9.6-notes.rst @@ -7,7 +7,7 @@ CUDA Python 12.9.6 Release notes Included components ------------------- -* `cuda.bindings 12.9.6 `_ +* `cuda.bindings 12.9.6 `_ * `cuda.pathfinder 1.4.2 `_ Known issues From e9744b4295411a03288de6e3b9ae779ba3372c1a Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 11:06:53 -0400 Subject: [PATCH 05/17] Skip lychee on pre-commit.ci --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 31212d4c3ab..18f3a515a84 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,7 @@ ci: autoupdate_branch: '' autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate' autoupdate_schedule: quarterly + skip: [lychee] submodules: false # Please update the rev: SHAs below with this command: From 6e111a24d8a296f31dcb83bf1f804499de140852 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 11:27:52 -0400 Subject: [PATCH 06/17] Check rendered doc preview links with lychee --- .github/workflows/build-docs.yml | 76 +++++++++++++- cuda_bindings/docs/build_docs.sh | 15 --- cuda_core/docs/build_docs.sh | 15 --- cuda_pathfinder/docs/build_docs.sh | 15 --- cuda_python/docs/check_example_links.py | 125 ------------------------ 5 files changed, 72 insertions(+), 174 deletions(-) delete mode 100644 cuda_python/docs/check_example_links.py diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index ea091090376..e0f0b56d432 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -216,9 +216,9 @@ jobs: run: | pushd cuda_python/docs/ if [[ "${{ inputs.is-release }}" == "false" ]]; then - DOCS_LINKCHECK=1 ./build_all_docs.sh latest-only + ./build_all_docs.sh latest-only else - DOCS_LINKCHECK=1 ./build_all_docs.sh + ./build_all_docs.sh # At release time, we don't want to update the latest docs rm -rf build/html/latest fi @@ -232,9 +232,9 @@ jobs: COMPONENT=$(echo "${{ inputs.component }}" | tr '-' '_') pushd ${COMPONENT}/docs/ if [[ "${{ inputs.is-release }}" == "false" ]]; then - DOCS_LINKCHECK=1 ./build_docs.sh latest-only + ./build_docs.sh latest-only else - DOCS_LINKCHECK=1 ./build_docs.sh + ./build_docs.sh # At release time, we don't want to update the latest docs rm -rf build/html/latest fi @@ -248,6 +248,11 @@ jobs: fi mv ${COMPONENT}/docs/build/html/* artifacts/docs/${TARGET} + - name: Write doc preview marker + if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} + run: | + echo "${{ github.sha }}" > artifacts/docs/.preview-commit + # TODO: Consider removing this step? - name: Upload doc artifacts uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 @@ -263,6 +268,69 @@ jobs: 'artifacts/empty_docs' }} pr-number: ${{ env.PR_NUMBER }} + - name: Wait for doc preview deployment + if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} + env: + PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} + run: | + marker_url="${PREVIEW_URL}/.preview-commit" + for attempt in {1..60}; do + marker=$(curl -fsSL "${marker_url}" || true) + if [[ "${marker}" == "${{ github.sha }}" ]]; then + echo "Doc preview is available at ${PREVIEW_URL}" + exit 0 + fi + echo "Waiting for doc preview marker ${marker_url} (${attempt}/60)" + sleep 10 + done + echo "error: doc preview marker did not update to ${{ github.sha }}" >&2 + exit 1 + + - name: Restore lychee cache + id: restore-lychee-cache + if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: .lycheecache + key: docs-preview-lychee-${{ env.PR_NUMBER }}-${{ github.sha }} + restore-keys: | + docs-preview-lychee-${{ env.PR_NUMBER }}- + + - name: Check doc preview links + if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} + uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2 + with: + args: >- + --base-url https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }}/ + --root-dir ${{ github.workspace }}/artifacts/docs + --index-files index.html + --fallback-extensions html,htm + --include-fragments full + --cache + --max-cache-age 1d + --max-concurrency 16 + --host-concurrency 2 + --host-request-interval 250ms + --max-retries 3 + --retry-wait-time 5 + --timeout 30 + --no-progress + '${{ github.workspace }}/artifacts/docs/**/*.html' + fail: true + failIfEmpty: true + format: markdown + jobSummary: true + lycheeVersion: v0.24.2 + output: lychee-preview.md + token: ${{ github.token }} + + - name: Save lychee cache + if: ${{ always() && !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') && steps.restore-lychee-cache.outputs.cache-hit != 'true' && steps.restore-lychee-cache.outputs.cache-primary-key != '' }} + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: .lycheecache + key: ${{ steps.restore-lychee-cache.outputs.cache-primary-key }} + - name: Deploy doc update if: ${{ github.ref_name == 'main' || inputs.is-release }} uses: JamesIves/github-pages-deploy-action@d92aa235d04922e8f08b40ce78cc5442fcfbfa2f # v4.8.0 diff --git a/cuda_bindings/docs/build_docs.sh b/cuda_bindings/docs/build_docs.sh index ca2f49b1036..2ab1f086018 100755 --- a/cuda_bindings/docs/build_docs.sh +++ b/cuda_bindings/docs/build_docs.sh @@ -37,21 +37,6 @@ else fi SPHINXOPTS="${HTML_SPHINXOPTS}" make html -if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then - if [[ -n "${CUDA_PYTHON_DOCS_GITHUB_REF:-}" ]]; then - DOCS_EXAMPLES_REF="${CUDA_PYTHON_DOCS_GITHUB_REF}" - elif [[ "${BUILD_PREVIEW:-0}" == "1" || "${BUILD_LATEST:-0}" == "1" ]]; then - DOCS_EXAMPLES_REF="main" - else - DOCS_EXAMPLES_REF="v${SPHINX_CUDA_BINDINGS_VER}" - fi - python ../../cuda_python/docs/check_example_links.py \ - --source-dir source \ - --examples-root cuda_bindings/examples \ - --expected-ref "${DOCS_EXAMPLES_REF}" \ - --placeholder cuda_bindings_github_ref -fi - # for debugging/developing (conf.py), please comment out the above line and # use the line below instead, as we must build in serial to avoid getting # obsecure Sphinx errors diff --git a/cuda_core/docs/build_docs.sh b/cuda_core/docs/build_docs.sh index 0b14c3f4369..28ae6dd07fd 100755 --- a/cuda_core/docs/build_docs.sh +++ b/cuda_core/docs/build_docs.sh @@ -37,21 +37,6 @@ fi SPHINXOPTS="${HTML_SPHINXOPTS}" make html -if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then - if [[ -n "${CUDA_PYTHON_DOCS_GITHUB_REF:-}" ]]; then - DOCS_EXAMPLES_REF="${CUDA_PYTHON_DOCS_GITHUB_REF}" - elif [[ "${BUILD_PREVIEW:-0}" == "1" || "${BUILD_LATEST:-0}" == "1" ]]; then - DOCS_EXAMPLES_REF="main" - else - DOCS_EXAMPLES_REF="cuda-core-v${SPHINX_CUDA_CORE_VER}" - fi - python ../../cuda_python/docs/check_example_links.py \ - --source-dir source \ - --examples-root cuda_core/examples \ - --expected-ref "${DOCS_EXAMPLES_REF}" \ - --placeholder cuda_core_github_ref -fi - # to support version dropdown menu cp ./versions.json build/html cp ./nv-versions.json build/html diff --git a/cuda_pathfinder/docs/build_docs.sh b/cuda_pathfinder/docs/build_docs.sh index 598a51249b4..4720cd458f6 100755 --- a/cuda_pathfinder/docs/build_docs.sh +++ b/cuda_pathfinder/docs/build_docs.sh @@ -37,21 +37,6 @@ else fi SPHINXOPTS="${HTML_SPHINXOPTS}" make html -if [[ "${DOCS_LINKCHECK:-0}" == "1" ]]; then - if [[ -n "${CUDA_PYTHON_DOCS_GITHUB_REF:-}" ]]; then - DOCS_EXAMPLES_REF="${CUDA_PYTHON_DOCS_GITHUB_REF}" - elif [[ "${BUILD_PREVIEW:-0}" == "1" || "${BUILD_LATEST:-0}" == "1" ]]; then - DOCS_EXAMPLES_REF="main" - else - DOCS_EXAMPLES_REF="cuda-pathfinder-v${SPHINX_CUDA_PATHFINDER_VER}" - fi - python ../../cuda_python/docs/check_example_links.py \ - --source-dir source \ - --examples-root cuda_pathfinder/examples \ - --expected-ref "${DOCS_EXAMPLES_REF}" \ - --allow-empty -fi - # for debugging/developing (conf.py), please comment out the above line and # use the line below instead, as we must build in serial to avoid getting # obsecure Sphinx errors diff --git a/cuda_python/docs/check_example_links.py b/cuda_python/docs/check_example_links.py deleted file mode 100644 index 5f76c412ea7..00000000000 --- a/cuda_python/docs/check_example_links.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE - -from __future__ import annotations - -import argparse -import re -import sys -from pathlib import Path -from urllib.parse import unquote - -CUDA_PYTHON_URL_RE = re.compile( - r"https://github\.com/NVIDIA/cuda-python/" - r"(?Pblob|tree)/" - r"(?P[^/\s<>`]+)/" - r"(?P[^\s<>`)]+)" -) -SOURCE_SUFFIXES = {".md", ".rst"} - - -def _source_files(source_dir: Path): - for path in sorted(source_dir.rglob("*")): - if path.suffix in SOURCE_SUFFIXES and path.is_file(): - yield path - - -def _normalize_url_path(url_path: str) -> str: - path = unquote(url_path) - for separator in ("#", "?"): - path = path.split(separator, 1)[0] - return path.rstrip(".") - - -def _is_within(path: str, root: str) -> bool: - return path == root or path.startswith(f"{root}/") - - -def _display_path(path: Path, repo_root: Path) -> str: - try: - return str(path.relative_to(repo_root)) - except ValueError: - return str(path) - - -def check_links(args: argparse.Namespace) -> int: - repo_root = args.repo_root.resolve() - source_dir = args.source_dir.resolve() - examples_root = args.examples_root.strip("/") - placeholder_ref = f"|{args.placeholder}|" if args.placeholder else None - checked = 0 - failures: list[str] = [] - - for source_path in _source_files(source_dir): - text = source_path.read_text(encoding="utf-8") - for match in CUDA_PYTHON_URL_RE.finditer(text): - url_path = _normalize_url_path(match.group("path")) - if not _is_within(url_path, examples_root): - continue - - checked += 1 - ref = match.group("ref") - kind = match.group("kind") - location = _display_path(source_path, repo_root) - target_path = Path(url_path) - target = repo_root / target_path - - if target_path.is_absolute() or ".." in target_path.parts: - failures.append(f"{location}: invalid repository path in {match.group(0)}") - continue - - if placeholder_ref and ref == placeholder_ref: - rendered_ref = args.expected_ref - elif ref == args.expected_ref: - rendered_ref = ref - else: - expected = placeholder_ref or args.expected_ref - failures.append(f"{location}: {match.group(0)} uses ref {ref!r}; expected {expected!r}") - rendered_ref = ref - - if kind == "blob" and not target.is_file(): - failures.append( - f"{location}: {match.group(0)} resolves to missing file " - f"{target.relative_to(repo_root)} at ref {rendered_ref}" - ) - elif kind == "tree" and not target.is_dir(): - failures.append( - f"{location}: {match.group(0)} resolves to missing directory " - f"{target.relative_to(repo_root)} at ref {rendered_ref}" - ) - - if checked == 0 and not args.allow_empty: - failures.append(f"No example links under {examples_root!r} found in {source_dir}") - - if failures: - sys.stderr.write("Example link check failed:\n") - for failure in failures: - sys.stderr.write(f" - {failure}\n") - return 1 - - sys.stdout.write(f"Checked {checked} example link(s) under {examples_root} against ref {args.expected_ref}\n") - return 0 - - -def parse_args(argv: list[str]) -> argparse.Namespace: - repo_root = Path(__file__).resolve().parents[2] - - parser = argparse.ArgumentParser(description="Validate cuda-python example links without probing GitHub.") - parser.add_argument("--source-dir", type=Path, required=True) - parser.add_argument("--examples-root", required=True) - parser.add_argument("--expected-ref", required=True) - parser.add_argument("--placeholder") - parser.add_argument("--repo-root", type=Path, default=repo_root) - parser.add_argument("--allow-empty", action="store_true") - return parser.parse_args(argv) - - -def main(argv: list[str] | None = None) -> int: - args = parse_args(sys.argv[1:] if argv is None else argv) - return check_links(args) - - -if __name__ == "__main__": - raise SystemExit(main()) From 0f2bb163aeab249b73e9ce3ad37c803f51d2b598 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 12:00:24 -0400 Subject: [PATCH 07/17] Fix doc preview wait step --- .github/workflows/build-docs.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index e0f0b56d432..42f67be61d4 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -272,19 +272,24 @@ jobs: if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} env: PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} + EXPECTED_SHA: ${{ github.sha }} run: | + preview_ready=0 marker_url="${PREVIEW_URL}/.preview-commit" for attempt in {1..60}; do marker=$(curl -fsSL "${marker_url}" || true) - if [[ "${marker}" == "${{ github.sha }}" ]]; then - echo "Doc preview is available at ${PREVIEW_URL}" - exit 0 + if [[ "${marker}" == "${EXPECTED_SHA}" ]]; then + preview_ready=1 + break fi echo "Waiting for doc preview marker ${marker_url} (${attempt}/60)" sleep 10 done - echo "error: doc preview marker did not update to ${{ github.sha }}" >&2 - exit 1 + if [[ "${preview_ready}" != "1" ]]; then + echo "error: doc preview marker did not update to ${EXPECTED_SHA}" >&2 + exit 1 + fi + echo "Doc preview is available at ${PREVIEW_URL}" - name: Restore lychee cache id: restore-lychee-cache From e9f64dace34db9d9e8a8ce4a9dab4a42328d8c17 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 12:30:30 -0400 Subject: [PATCH 08/17] Use lychee-action default binary --- .github/workflows/build-docs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 42f67be61d4..70aa7d3141c 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -325,7 +325,6 @@ jobs: failIfEmpty: true format: markdown jobSummary: true - lycheeVersion: v0.24.2 output: lychee-preview.md token: ${{ github.token }} From 8e53035dae247ed7eee44f3160519b8a3e79cd3c Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 12:42:07 -0400 Subject: [PATCH 09/17] Address docs review comments --- cuda_bindings/docs/source/conf.py | 7 +------ cuda_bindings/docs/source/release/12.9.6-notes.rst | 2 +- cuda_bindings/docs/source/release/13.1.1-notes.rst | 2 +- cuda_bindings/docs/source/release/13.2.0-notes.rst | 2 +- cuda_core/docs/source/conf.py | 5 ----- 5 files changed, 4 insertions(+), 14 deletions(-) diff --git a/cuda_bindings/docs/source/conf.py b/cuda_bindings/docs/source/conf.py index 297815a2956..1e27205feeb 100644 --- a/cuda_bindings/docs/source/conf.py +++ b/cuda_bindings/docs/source/conf.py @@ -132,13 +132,8 @@ def _github_examples_ref(): def rewrite_source(app, docname, source): - text = source[0] - text = text.replace("|cuda_bindings_github_ref|", GITHUB_EXAMPLES_REF) - if docname.startswith("release/"): - text = text.replace(".. module:: cuda.bindings\n\n", "", 1) - - source[0] = text + source[0] = source[0].replace(".. module:: cuda.bindings\n\n", "", 1) suppress_warnings = [ diff --git a/cuda_bindings/docs/source/release/12.9.6-notes.rst b/cuda_bindings/docs/source/release/12.9.6-notes.rst index 2ba5f3d96f8..d24671700bf 100644 --- a/cuda_bindings/docs/source/release/12.9.6-notes.rst +++ b/cuda_bindings/docs/source/release/12.9.6-notes.rst @@ -31,7 +31,7 @@ Bugfixes * Fixed an issue where the ``CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL`` attribute was retrieved as an unsigned int, rather than a signed int. - (`PR #1336 `_) + (`PR #1451 `_) * Fixed a use-after-free in ``_HelperInputVoidPtr`` properties when backed by Python buffer objects. (`PR #1629 `_) diff --git a/cuda_bindings/docs/source/release/13.1.1-notes.rst b/cuda_bindings/docs/source/release/13.1.1-notes.rst index d4ef6af2d98..4725fe570c6 100644 --- a/cuda_bindings/docs/source/release/13.1.1-notes.rst +++ b/cuda_bindings/docs/source/release/13.1.1-notes.rst @@ -12,7 +12,7 @@ Highlights ---------- * Add missing driver & runtime bindings for functions new in CTK 13.1.0 - (`PR #1321 `_) + (`PR #1337 `_) Experimental ------------ diff --git a/cuda_bindings/docs/source/release/13.2.0-notes.rst b/cuda_bindings/docs/source/release/13.2.0-notes.rst index e470b896d52..71d2e13e562 100644 --- a/cuda_bindings/docs/source/release/13.2.0-notes.rst +++ b/cuda_bindings/docs/source/release/13.2.0-notes.rst @@ -39,7 +39,7 @@ Bugfixes * Fixed an issue where the ``CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL`` attribute was retrieved as an unsigned int, rather than a signed int. - (`PR #1336 `_) + (`PR #1451 `_) * Fixed ABI incompatibility bugs in cuFILE bindings introduced in v13.1.0. (`PR #1468 `_) * Fixed a use-after-free in ``_HelperInputVoidPtr`` properties when backed by diff --git a/cuda_core/docs/source/conf.py b/cuda_core/docs/source/conf.py index 28d0f6add6a..0549d3f0dcf 100644 --- a/cuda_core/docs/source/conf.py +++ b/cuda_core/docs/source/conf.py @@ -200,11 +200,6 @@ def skip_member(app, what, name, obj, skip, options): return None -def rewrite_source(app, docname, source): - source[0] = source[0].replace("|cuda_core_github_ref|", GITHUB_EXAMPLES_REF) - - def setup(app): app.connect("autodoc-process-docstring", autodoc_process_docstring) app.connect("autodoc-skip-member", skip_member) - app.connect("source-read", rewrite_source) From 54c57725702b37751f6be802670714f6b9d02355 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 13:26:39 -0400 Subject: [PATCH 10/17] Use fixed lychee-action revision --- .github/workflows/build-docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 70aa7d3141c..fedc80c690b 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -303,7 +303,7 @@ jobs: - name: Check doc preview links if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} - uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2 + uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout with: args: >- --base-url https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }}/ @@ -325,6 +325,7 @@ jobs: failIfEmpty: true format: markdown jobSummary: true + lycheeVersion: v0.24.2 output: lychee-preview.md token: ${{ github.token }} From 40cb8d8ca1f16b41bf93ef502510d5addb42fd83 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 13:56:47 -0400 Subject: [PATCH 11/17] Fix lychee fragment option syntax --- .github/workflows/build-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index fedc80c690b..28f4e277d01 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -310,7 +310,7 @@ jobs: --root-dir ${{ github.workspace }}/artifacts/docs --index-files index.html --fallback-extensions html,htm - --include-fragments full + --include-fragments=full --cache --max-cache-age 1d --max-concurrency 16 From 16d62c4df49e6729447ff4c9478416a70d14c94b Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 16:07:41 -0400 Subject: [PATCH 12/17] Check deployed docs preview links --- .github/workflows/build-docs.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 28f4e277d01..e2f0c1b2a27 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -301,15 +301,26 @@ jobs: restore-keys: | docs-preview-lychee-${{ env.PR_NUMBER }}- + - name: Write lychee doc preview URL list + if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} + env: + PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} + run: | + find "${GITHUB_WORKSPACE}/artifacts/docs" -type f -name '*.html' -printf '%P\n' \ + | LC_ALL=C sort \ + | sed "s#^#${PREVIEW_URL}/#" > lychee-preview-urls.txt + if [[ ! -s lychee-preview-urls.txt ]]; then + echo "error: no rendered HTML pages found for lychee" >&2 + exit 1 + fi + wc -l lychee-preview-urls.txt + - name: Check doc preview links if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout with: args: >- - --base-url https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }}/ - --root-dir ${{ github.workspace }}/artifacts/docs - --index-files index.html - --fallback-extensions html,htm + --files-from ${{ github.workspace }}/lychee-preview-urls.txt --include-fragments=full --cache --max-cache-age 1d @@ -320,11 +331,10 @@ jobs: --retry-wait-time 5 --timeout 30 --no-progress - '${{ github.workspace }}/artifacts/docs/**/*.html' fail: true failIfEmpty: true format: markdown - jobSummary: true + jobSummary: false lycheeVersion: v0.24.2 output: lychee-preview.md token: ${{ github.token }} From 0a437893735f038a68120f2345cb350b002db4c7 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Tue, 2 Jun 2026 17:03:19 -0400 Subject: [PATCH 13/17] Fix rendered docs linkcheck failures --- .github/workflows/build-docs.yml | 2 +- cuda_bindings/docs/source/conf.py | 11 +++++++- cuda_bindings/docs/source/install.rst | 4 +-- cuda_bindings/docs/source/module/driver.rst | 14 +++++----- cuda_bindings/docs/source/module/runtime.rst | 10 +++---- cuda_core/docs/source/conf.py | 12 ++++++++- cuda_core/docs/source/examples.rst | 26 +++++++++---------- cuda_core/docs/source/getting-started.rst | 4 +-- cuda_core/docs/source/interoperability.rst | 6 ++--- cuda_core/docs/source/release/0.3.1-notes.rst | 4 +-- cuda_pathfinder/docs/source/conf.py | 13 ++++++++-- cuda_python/docs/build_docs.sh | 6 ++++- cuda_python/docs/source/conf.py | 13 ++++++++-- 13 files changed, 83 insertions(+), 42 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index e2f0c1b2a27..67773961c3f 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -306,7 +306,7 @@ jobs: env: PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} run: | - find "${GITHUB_WORKSPACE}/artifacts/docs" -type f -name '*.html' -printf '%P\n' \ + find "${GITHUB_WORKSPACE}/artifacts/docs" -type f -name '*.html' ! -path '*/_static/*' -printf '%P\n' \ | LC_ALL=C sort \ | sed "s#^#${PREVIEW_URL}/#" > lychee-preview-urls.txt if [[ ! -s lychee-preview-urls.txt ]]; then diff --git a/cuda_bindings/docs/source/conf.py b/cuda_bindings/docs/source/conf.py index 1e27205feeb..64f28ace24d 100644 --- a/cuda_bindings/docs/source/conf.py +++ b/cuda_bindings/docs/source/conf.py @@ -37,6 +37,15 @@ def _github_examples_ref(): GITHUB_EXAMPLES_REF = _github_examples_ref() +def _html_baseurl(): + docs_domain = os.environ.get("CUDA_PYTHON_DOCS_DOMAIN", "https://nvidia.github.io/cuda-python") + if int(os.environ.get("BUILD_PREVIEW", 0)): + return f"{docs_domain}/pr-preview/pr-{os.environ['PR_NUMBER']}/cuda-bindings/latest/" + if int(os.environ.get("BUILD_LATEST", 0)): + return f"{docs_domain}/cuda-bindings/latest/" + return f"{docs_domain}/cuda-bindings/{release}/" + + # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -76,7 +85,7 @@ def _github_examples_ref(): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_baseurl = "docs" +html_baseurl = _html_baseurl() html_theme = "nvidia_sphinx_theme" html_theme_options = { "switcher": { diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index bb5a8585257..ce99255c133 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE Installation @@ -99,7 +99,7 @@ Source builds require that the provided CUDA headers are of the same major.minor $ export CUDA_PATH=/usr/local/cuda -See `Environment Variables `_ for a description of other build-time environment variables. +See :doc:`Environment Variables ` for a description of other build-time environment variables. .. note:: diff --git a/cuda_bindings/docs/source/module/driver.rst b/cuda_bindings/docs/source/module/driver.rst index 49c633aa074..020633665bf 100644 --- a/cuda_bindings/docs/source/module/driver.rst +++ b/cuda_bindings/docs/source/module/driver.rst @@ -406,25 +406,25 @@ Data types used by CUDA driver .. autoattribute:: cuda.bindings.driver.CUstreamWaitValue_flags.CU_STREAM_WAIT_VALUE_GEQ - Wait until (int32_t)(*addr - value) >= 0 (or int64_t for 64 bit values). Note this is a cyclic comparison which ignores wraparound. (Default behavior.) + Wait until (int32_t)(\*addr - value) >= 0 (or int64_t for 64 bit values). Note this is a cyclic comparison which ignores wraparound. (Default behavior.) .. autoattribute:: cuda.bindings.driver.CUstreamWaitValue_flags.CU_STREAM_WAIT_VALUE_EQ - Wait until *addr == value. + Wait until \*addr == value. .. autoattribute:: cuda.bindings.driver.CUstreamWaitValue_flags.CU_STREAM_WAIT_VALUE_AND - Wait until (*addr & value) != 0. + Wait until (\*addr & value) != 0. .. autoattribute:: cuda.bindings.driver.CUstreamWaitValue_flags.CU_STREAM_WAIT_VALUE_NOR - Wait until ~(*addr | value) != 0. Support for this operation can be queried with :py:obj:`~.cuDeviceGetAttribute()` and :py:obj:`~.CU_DEVICE_ATTRIBUTE_CAN_USE_STREAM_WAIT_VALUE_NOR`. + Wait until ~(\*addr | value) != 0. Support for this operation can be queried with :py:obj:`~.cuDeviceGetAttribute()` and :py:obj:`~.CU_DEVICE_ATTRIBUTE_CAN_USE_STREAM_WAIT_VALUE_NOR`. .. autoattribute:: cuda.bindings.driver.CUstreamWaitValue_flags.CU_STREAM_WAIT_VALUE_FLUSH @@ -506,19 +506,19 @@ Data types used by CUDA driver .. autoattribute:: cuda.bindings.driver.CUstreamAtomicReductionOpType.CU_STREAM_ATOMIC_REDUCTION_OP_OR - Performs an atomic OR: *(address) = *(address) | value + Performs an atomic OR: \*(address) = \*(address) | value .. autoattribute:: cuda.bindings.driver.CUstreamAtomicReductionOpType.CU_STREAM_ATOMIC_REDUCTION_OP_AND - Performs an atomic AND: *(address) = *(address) & value + Performs an atomic AND: \*(address) = \*(address) & value .. autoattribute:: cuda.bindings.driver.CUstreamAtomicReductionOpType.CU_STREAM_ATOMIC_REDUCTION_OP_ADD - Performs an atomic ADD: *(address) = *(address) + value + Performs an atomic ADD: \*(address) = \*(address) + value .. autoclass:: cuda.bindings.driver.CUstreamAtomicReductionDataType diff --git a/cuda_bindings/docs/source/module/runtime.rst b/cuda_bindings/docs/source/module/runtime.rst index 5f7b5177560..4714cf8f13b 100644 --- a/cuda_bindings/docs/source/module/runtime.rst +++ b/cuda_bindings/docs/source/module/runtime.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE ------- @@ -299,7 +299,7 @@ Data types used by CUDA Runtime .. autoattribute:: cuda.bindings.runtime.cudaError_t.cudaErrorIncompatibleDriverContext - This indicates that the current context is not compatible with this the CUDA Runtime. This can only occur if you are using CUDA Runtime/Driver interoperability and have created an existing Driver context using the driver API. The Driver context may be incompatible either because the Driver context was created using an older version of the API, because the Runtime API call expects a primary driver context and the Driver context is not primary, or because the Driver context has been destroyed. Please see :py:obj:`~.Interactions`with the CUDA Driver API" for more information. + This indicates that the current context is not compatible with this the CUDA Runtime. This can only occur if you are using CUDA Runtime/Driver interoperability and have created an existing Driver context using the driver API. The Driver context may be incompatible either because the Driver context was created using an older version of the API, because the Runtime API call expects a primary driver context and the Driver context is not primary, or because the Driver context has been destroyed. Please see `Interactions with the CUDA Driver API`_ for more information. .. autoattribute:: cuda.bindings.runtime.cudaError_t.cudaErrorMissingConfiguration @@ -6403,11 +6403,11 @@ The types ::CUevent and cudaEvent_t are identical and may be used interchangeabl -The types ::CUarray and struct ::cudaArray * represent the same data type and may be used interchangeably by casting the two types between each other. +The types ``CUarray`` and ``struct cudaArray *`` represent the same data type and may be used interchangeably by casting the two types between each other. -In order to use a ::CUarray in a CUDA Runtime API function which takes a struct ::cudaArray *, it is necessary to explicitly cast the ::CUarray to a struct ::cudaArray *. +In order to use a ``CUarray`` in a CUDA Runtime API function which takes a ``struct cudaArray *``, it is necessary to explicitly cast the ``CUarray`` to a ``struct cudaArray *``. -In order to use a struct ::cudaArray * in a CUDA Driver API function which takes a ::CUarray, it is necessary to explicitly cast the struct ::cudaArray * to a ::CUarray . +In order to use a ``struct cudaArray *`` in a CUDA Driver API function which takes a ``CUarray``, it is necessary to explicitly cast the ``struct cudaArray *`` to a ``CUarray``. diff --git a/cuda_core/docs/source/conf.py b/cuda_core/docs/source/conf.py index 0549d3f0dcf..451eb8b4453 100644 --- a/cuda_core/docs/source/conf.py +++ b/cuda_core/docs/source/conf.py @@ -38,6 +38,15 @@ def _github_examples_ref(): GITHUB_EXAMPLES_REF = _github_examples_ref() +def _html_baseurl(): + docs_domain = os.environ.get("CUDA_PYTHON_DOCS_DOMAIN", "https://nvidia.github.io/cuda-python") + if int(os.environ.get("BUILD_PREVIEW", 0)): + return f"{docs_domain}/pr-preview/pr-{os.environ['PR_NUMBER']}/cuda-core/latest/" + if int(os.environ.get("BUILD_LATEST", 0)): + return f"{docs_domain}/cuda-core/latest/" + return f"{docs_domain}/cuda-core/{release}/" + + # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -46,6 +55,7 @@ def _github_examples_ref(): extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosummary", + "sphinx.ext.extlinks", "sphinx.ext.napoleon", "sphinx.ext.intersphinx", "sphinx.ext.extlinks", @@ -75,7 +85,7 @@ def _github_examples_ref(): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_baseurl = "docs" +html_baseurl = _html_baseurl() html_theme = "nvidia_sphinx_theme" html_theme_options = { "switcher": { diff --git a/cuda_core/docs/source/examples.rst b/cuda_core/docs/source/examples.rst index e3b2ef8f3f5..cf13961c6dc 100644 --- a/cuda_core/docs/source/examples.rst +++ b/cuda_core/docs/source/examples.rst @@ -12,48 +12,48 @@ workflow. Compilation and kernel launch ----------------------------- -- :cuda-core-example:`vector_add.py` +- :cuda-core-example:`vector_add.py ` compiles and launches a simple vector-add kernel with CuPy arrays. -- :cuda-core-example:`saxpy.py` +- :cuda-core-example:`saxpy.py ` JIT-compiles a templated SAXPY kernel and launches both float and double instantiations. -- :cuda-core-example:`pytorch_example.py` +- :cuda-core-example:`pytorch_example.py ` launches a CUDA kernel with PyTorch tensors and a wrapped PyTorch stream. Multi-device and advanced launch configuration ---------------------------------------------- -- :cuda-core-example:`simple_multi_gpu_example.py` +- :cuda-core-example:`simple_multi_gpu_example.py ` compiles and launches kernels across multiple GPUs. -- :cuda-core-example:`thread_block_cluster.py` +- :cuda-core-example:`thread_block_cluster.py ` demonstrates thread block cluster launch configuration on Hopper-class GPUs. -- :cuda-core-example:`tma_tensor_map.py` +- :cuda-core-example:`tma_tensor_map.py ` demonstrates Tensor Memory Accelerator descriptors and TMA-based bulk copies. Linking and graphs ------------------ -- :cuda-core-example:`jit_lto_fractal.py` +- :cuda-core-example:`jit_lto_fractal.py ` uses JIT link-time optimization to link user-provided device code into a fractal workflow at runtime. -- :cuda-core-example:`cuda_graphs.py` +- :cuda-core-example:`cuda_graphs.py ` captures and replays a multi-kernel CUDA graph to reduce launch overhead. Interoperability and memory access ---------------------------------- -- :cuda-core-example:`memory_ops.py` +- :cuda-core-example:`memory_ops.py ` covers memory resources, pinned memory, device transfers, and DLPack interop. -- :cuda-core-example:`strided_memory_view_cpu.py` +- :cuda-core-example:`strided_memory_view_cpu.py ` uses ``StridedMemoryView`` with JIT-compiled CPU code via ``cffi``. -- :cuda-core-example:`strided_memory_view_gpu.py` +- :cuda-core-example:`strided_memory_view_gpu.py ` uses ``StridedMemoryView`` with JIT-compiled GPU code and foreign GPU buffers. -- :cuda-core-example:`gl_interop_plasma.py` +- :cuda-core-example:`gl_interop_plasma.py ` renders a CUDA-generated plasma effect through OpenGL interop without CPU copies. System inspection ----------------- -- :cuda-core-example:`show_device_properties.py` +- :cuda-core-example:`show_device_properties.py ` prints a detailed report of the CUDA devices available on the system. diff --git a/cuda_core/docs/source/getting-started.rst b/cuda_core/docs/source/getting-started.rst index fb2f0b22fcf..e03ce23782f 100644 --- a/cuda_core/docs/source/getting-started.rst +++ b/cuda_core/docs/source/getting-started.rst @@ -32,7 +32,7 @@ Example: Compiling and Launching a CUDA kernel ---------------------------------------------- To get a taste for ``cuda.core``, let's walk through a simple example that compiles and launches a vector addition kernel. -You can find the complete example in :cuda-core-example:`vector_add.py` +You can find the complete example in :cuda-core-example:`vector_add.py ` and browse the :doc:`examples page ` for the rest of the shipped workflows. @@ -80,7 +80,7 @@ Note the use of the ``name_expressions`` parameter to the :meth:`Program.compile Next, we retrieve the compiled kernel from the CUBIN and prepare the arguments and kernel configuration. We're using `CuPy `_ arrays as inputs for this example, but you can use PyTorch tensors too (see -:cuda-core-example:`pytorch_example.py` +:cuda-core-example:`pytorch_example.py ` and the :doc:`examples page `). .. code-block:: python diff --git a/cuda_core/docs/source/interoperability.rst b/cuda_core/docs/source/interoperability.rst index 4aa155ce5f9..87347eb9d25 100644 --- a/cuda_core/docs/source/interoperability.rst +++ b/cuda_core/docs/source/interoperability.rst @@ -70,11 +70,11 @@ a few iterations to ensure correctness. for extracting the metadata (such as pointer address, shape, strides, and dtype) from any Python objects supporting either CAI or DLPack and returning a :class:`~utils.StridedMemoryView` object. See the -:cuda-core-example:`strided_memory_view_constructors.py` +:cuda-core-example:`strided_memory_view_constructors.py ` example for the explicit constructors, or -:cuda-core-example:`strided_memory_view_cpu.py` +:cuda-core-example:`strided_memory_view_cpu.py ` and -:cuda-core-example:`strided_memory_view_gpu.py` +:cuda-core-example:`strided_memory_view_gpu.py ` for decorator-based workflows. This provides a *concrete implementation* to both protocols that is **array-library-agnostic**, so that all Python projects can just rely on this without either re-implementing (the consumer-side of) diff --git a/cuda_core/docs/source/release/0.3.1-notes.rst b/cuda_core/docs/source/release/0.3.1-notes.rst index 6783011d731..8360d1b9eb7 100644 --- a/cuda_core/docs/source/release/0.3.1-notes.rst +++ b/cuda_core/docs/source/release/0.3.1-notes.rst @@ -31,8 +31,8 @@ None. New examples ------------ -- Add a `CUDA graph `_ example. -- Add a `memory resource `_ example. +- Add a :cuda-core-example:`CUDA graph ` example. +- Add a :cuda-core-example:`memory resource ` example. Fixes and enhancements diff --git a/cuda_pathfinder/docs/source/conf.py b/cuda_pathfinder/docs/source/conf.py index f794eb9ee2b..aa6cd964b54 100644 --- a/cuda_pathfinder/docs/source/conf.py +++ b/cuda_pathfinder/docs/source/conf.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2012-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2012-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # Configuration file for the Sphinx documentation builder. @@ -26,6 +26,15 @@ release = os.environ["SPHINX_CUDA_PATHFINDER_VER"] +def _html_baseurl(): + docs_domain = os.environ.get("CUDA_PYTHON_DOCS_DOMAIN", "https://nvidia.github.io/cuda-python") + if int(os.environ.get("BUILD_PREVIEW", 0)): + return f"{docs_domain}/pr-preview/pr-{os.environ['PR_NUMBER']}/cuda-pathfinder/latest/" + if int(os.environ.get("BUILD_LATEST", 0)): + return f"{docs_domain}/cuda-pathfinder/latest/" + return f"{docs_domain}/cuda-pathfinder/{release}/" + + # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -65,7 +74,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_baseurl = "docs" +html_baseurl = _html_baseurl() html_theme = "nvidia_sphinx_theme" html_theme_options = { "switcher": { diff --git a/cuda_python/docs/build_docs.sh b/cuda_python/docs/build_docs.sh index 97be962a1a9..528b1c15c3a 100755 --- a/cuda_python/docs/build_docs.sh +++ b/cuda_python/docs/build_docs.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE set -ex @@ -25,6 +25,10 @@ if [[ -z "${SPHINX_CUDA_PYTHON_VER}" ]]; then | awk -F'+' '{print $1}') fi +if [[ "${LATEST_ONLY}" == "1" && -z "${BUILD_PREVIEW:-}" && -z "${BUILD_LATEST:-}" ]]; then + export BUILD_LATEST=1 +fi + # build the docs (in parallel) SPHINXOPTS="-j 4 -d build/.doctrees" make html diff --git a/cuda_python/docs/source/conf.py b/cuda_python/docs/source/conf.py index 454cec49732..1b1932be711 100644 --- a/cuda_python/docs/source/conf.py +++ b/cuda_python/docs/source/conf.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE # Configuration file for the Sphinx documentation builder. @@ -26,6 +26,15 @@ release = os.environ["SPHINX_CUDA_PYTHON_VER"] +def _html_baseurl(): + docs_domain = os.environ.get("CUDA_PYTHON_DOCS_DOMAIN", "https://nvidia.github.io/cuda-python") + if int(os.environ.get("BUILD_PREVIEW", 0)): + return f"{docs_domain}/pr-preview/pr-{os.environ['PR_NUMBER']}/latest/" + if int(os.environ.get("BUILD_LATEST", 0)): + return f"{docs_domain}/latest/" + return f"{docs_domain}/{release}/" + + # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -58,7 +67,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_baseurl = "docs" +html_baseurl = _html_baseurl() html_theme = "nvidia_sphinx_theme" html_theme_options = { "switcher": { From ae2cff28eb83cfb99e8a0dacbcd6e2f8e56159d3 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Thu, 4 Jun 2026 12:50:32 -0400 Subject: [PATCH 14/17] Split rendered docs link check workflow --- .github/workflows/build-docs.yml | 97 ++++++------------------- .github/workflows/check-doc-links.yml | 100 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 16 +++++ 3 files changed, 137 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/check-doc-links.yml diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 67773961c3f..4ffafeb293b 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -253,55 +253,7 @@ jobs: run: | echo "${{ github.sha }}" > artifacts/docs/.preview-commit - # TODO: Consider removing this step? - - name: Upload doc artifacts - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 - with: - path: artifacts/ - retention-days: 3 - - - name: Deploy or clean up doc preview - if: ${{ !inputs.is-release }} - uses: ./.github/actions/doc_preview - with: - source-folder: ${{ (github.ref_name != 'main' && 'artifacts/docs') || - 'artifacts/empty_docs' }} - pr-number: ${{ env.PR_NUMBER }} - - - name: Wait for doc preview deployment - if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} - env: - PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} - EXPECTED_SHA: ${{ github.sha }} - run: | - preview_ready=0 - marker_url="${PREVIEW_URL}/.preview-commit" - for attempt in {1..60}; do - marker=$(curl -fsSL "${marker_url}" || true) - if [[ "${marker}" == "${EXPECTED_SHA}" ]]; then - preview_ready=1 - break - fi - echo "Waiting for doc preview marker ${marker_url} (${attempt}/60)" - sleep 10 - done - if [[ "${preview_ready}" != "1" ]]; then - echo "error: doc preview marker did not update to ${EXPECTED_SHA}" >&2 - exit 1 - fi - echo "Doc preview is available at ${PREVIEW_URL}" - - - name: Restore lychee cache - id: restore-lychee-cache - if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 - with: - path: .lycheecache - key: docs-preview-lychee-${{ env.PR_NUMBER }}-${{ github.sha }} - restore-keys: | - docs-preview-lychee-${{ env.PR_NUMBER }}- - - - name: Write lychee doc preview URL list + - name: Write doc preview link-check URL list if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} env: PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} @@ -315,36 +267,29 @@ jobs: fi wc -l lychee-preview-urls.txt - - name: Check doc preview links + - name: Upload doc preview link-check URL list if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} - uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: docs-preview-linkcheck-urls + path: lychee-preview-urls.txt + if-no-files-found: error + retention-days: 3 + + # TODO: Consider removing this step? + - name: Upload doc artifacts + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: - args: >- - --files-from ${{ github.workspace }}/lychee-preview-urls.txt - --include-fragments=full - --cache - --max-cache-age 1d - --max-concurrency 16 - --host-concurrency 2 - --host-request-interval 250ms - --max-retries 3 - --retry-wait-time 5 - --timeout 30 - --no-progress - fail: true - failIfEmpty: true - format: markdown - jobSummary: false - lycheeVersion: v0.24.2 - output: lychee-preview.md - token: ${{ github.token }} - - - name: Save lychee cache - if: ${{ always() && !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') && steps.restore-lychee-cache.outputs.cache-hit != 'true' && steps.restore-lychee-cache.outputs.cache-primary-key != '' }} - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + path: artifacts/ + retention-days: 3 + + - name: Deploy or clean up doc preview + if: ${{ !inputs.is-release }} + uses: ./.github/actions/doc_preview with: - path: .lycheecache - key: ${{ steps.restore-lychee-cache.outputs.cache-primary-key }} + source-folder: ${{ (github.ref_name != 'main' && 'artifacts/docs') || + 'artifacts/empty_docs' }} + pr-number: ${{ env.PR_NUMBER }} - name: Deploy doc update if: ${{ github.ref_name == 'main' || inputs.is-release }} diff --git a/.github/workflows/check-doc-links.yml b/.github/workflows/check-doc-links.yml new file mode 100644 index 00000000000..b7a12ee08ee --- /dev/null +++ b/.github/workflows/check-doc-links.yml @@ -0,0 +1,100 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: "CI: Check rendered docs links" + +on: + workflow_call: + inputs: + url-artifact: + description: "Artifact containing the rendered doc preview URL list" + required: false + default: docs-preview-linkcheck-urls + type: string + +jobs: + check: + name: Check rendered docs links + if: ${{ github.repository_owner == 'nvidia' && startsWith(github.ref_name, 'pull-request/') }} + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + pull-requests: read + defaults: + run: + shell: bash -el {0} + steps: + - name: Checkout ${{ github.event.repository.name }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Get PR number + uses: ./.github/actions/get_pr_number + + - name: Download doc preview link-check URL list + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ inputs.url-artifact }} + path: linkcheck-input + + - name: Wait for doc preview deployment + env: + PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} + EXPECTED_SHA: ${{ github.sha }} + run: | + preview_ready=0 + marker_url="${PREVIEW_URL}/.preview-commit" + for attempt in {1..60}; do + marker=$(curl -fsSL "${marker_url}" || true) + if [[ "${marker}" == "${EXPECTED_SHA}" ]]; then + preview_ready=1 + break + fi + echo "Waiting for doc preview marker ${marker_url} (${attempt}/60)" + sleep 10 + done + if [[ "${preview_ready}" != "1" ]]; then + echo "error: doc preview marker did not update to ${EXPECTED_SHA}" >&2 + exit 1 + fi + echo "Doc preview is available at ${PREVIEW_URL}" + + - name: Restore lychee cache + id: restore-lychee-cache + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: .lycheecache + key: docs-preview-lychee-${{ env.PR_NUMBER }}-${{ github.sha }} + restore-keys: | + docs-preview-lychee-${{ env.PR_NUMBER }}- + + - name: Check doc preview links + uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout + with: + args: >- + --files-from ${{ github.workspace }}/linkcheck-input/lychee-preview-urls.txt + --include-fragments=full + --cache + --max-cache-age 1d + --max-concurrency 16 + --host-concurrency 2 + --host-request-interval 250ms + --max-retries 3 + --retry-wait-time 5 + --timeout 30 + --no-progress + fail: true + failIfEmpty: true + format: markdown + jobSummary: false + lycheeVersion: v0.24.2 + output: lychee-preview.md + token: ${{ github.token }} + + - name: Save lychee cache + if: ${{ always() && steps.restore-lychee-cache.outputs.cache-hit != 'true' && steps.restore-lychee-cache.outputs.cache-primary-key != '' }} + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: .lycheecache + key: ${{ steps.restore-lychee-cache.outputs.cache-primary-key }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dee6cbe8b9b..357caeb6eec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -412,6 +412,18 @@ jobs: with: is-release: ${{ github.ref_type == 'tag' }} + doc-linkcheck: + name: Docs link check + if: ${{ github.repository_owner == 'nvidia' && startsWith(github.ref_name, 'pull-request/') && needs.doc.result == 'success' }} + permissions: + actions: read + contents: read + pull-requests: read + needs: + - doc + secrets: inherit + uses: ./.github/workflows/check-doc-links.yml + checks: name: Check job status if: always() @@ -425,6 +437,7 @@ jobs: - test-linux-aarch64 - test-windows - doc + - doc-linkcheck steps: - name: Exit run: | @@ -460,6 +473,9 @@ jobs: if ${{ needs.doc.result == 'cancelled' || needs.doc.result == 'failure' }}; then exit 1 fi + if ${{ needs.doc-linkcheck.result == 'cancelled' || needs.doc-linkcheck.result == 'failure' }}; then + exit 1 + fi if ${{ needs.test-sdist-linux.result == 'cancelled' || needs.test-sdist-linux.result == 'failure' || needs.test-sdist-windows.result == 'cancelled' || From 02cd4d6e8e450589dc442c41837088693dc92485 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Thu, 4 Jun 2026 12:57:08 -0400 Subject: [PATCH 15/17] Check rendered docs links locally --- .github/workflows/build-docs.yml | 20 ++------- .github/workflows/check-doc-links.yml | 59 ++++++++++++--------------- 2 files changed, 28 insertions(+), 51 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 4ffafeb293b..c29463629ec 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -253,26 +253,12 @@ jobs: run: | echo "${{ github.sha }}" > artifacts/docs/.preview-commit - - name: Write doc preview link-check URL list - if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} - env: - PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} - run: | - find "${GITHUB_WORKSPACE}/artifacts/docs" -type f -name '*.html' ! -path '*/_static/*' -printf '%P\n' \ - | LC_ALL=C sort \ - | sed "s#^#${PREVIEW_URL}/#" > lychee-preview-urls.txt - if [[ ! -s lychee-preview-urls.txt ]]; then - echo "error: no rendered HTML pages found for lychee" >&2 - exit 1 - fi - wc -l lychee-preview-urls.txt - - - name: Upload doc preview link-check URL list + - name: Upload rendered docs for link checking if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: docs-preview-linkcheck-urls - path: lychee-preview-urls.txt + name: docs-rendered-html + path: artifacts/docs/ if-no-files-found: error retention-days: 3 diff --git a/.github/workflows/check-doc-links.yml b/.github/workflows/check-doc-links.yml index b7a12ee08ee..89366422c35 100644 --- a/.github/workflows/check-doc-links.yml +++ b/.github/workflows/check-doc-links.yml @@ -7,10 +7,10 @@ name: "CI: Check rendered docs links" on: workflow_call: inputs: - url-artifact: - description: "Artifact containing the rendered doc preview URL list" + docs-artifact: + description: "Artifact containing the rendered docs HTML tree" required: false - default: docs-preview-linkcheck-urls + default: docs-rendered-html type: string jobs: @@ -26,54 +26,45 @@ jobs: run: shell: bash -el {0} steps: - - name: Checkout ${{ github.event.repository.name }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Get PR number - uses: ./.github/actions/get_pr_number + - name: Extract PR number + run: | + PR_NUMBER="${GITHUB_REF_NAME#pull-request/}" + if [[ ! "${PR_NUMBER}" =~ ^[0-9]+$ ]]; then + echo "error: cannot extract PR number from ref ${GITHUB_REF_NAME}" >&2 + exit 1 + fi + echo "PR_NUMBER=${PR_NUMBER}" >> "${GITHUB_ENV}" - - name: Download doc preview link-check URL list + - name: Download rendered docs uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: ${{ inputs.url-artifact }} - path: linkcheck-input + name: ${{ inputs.docs-artifact }} + path: rendered-docs - - name: Wait for doc preview deployment - env: - PREVIEW_URL: https://nvidia.github.io/cuda-python/pr-preview/pr-${{ env.PR_NUMBER }} - EXPECTED_SHA: ${{ github.sha }} + - name: Write rendered docs file list run: | - preview_ready=0 - marker_url="${PREVIEW_URL}/.preview-commit" - for attempt in {1..60}; do - marker=$(curl -fsSL "${marker_url}" || true) - if [[ "${marker}" == "${EXPECTED_SHA}" ]]; then - preview_ready=1 - break - fi - echo "Waiting for doc preview marker ${marker_url} (${attempt}/60)" - sleep 10 - done - if [[ "${preview_ready}" != "1" ]]; then - echo "error: doc preview marker did not update to ${EXPECTED_SHA}" >&2 + find "${GITHUB_WORKSPACE}/rendered-docs" -type f -name '*.html' ! -path '*/_static/*' \ + | LC_ALL=C sort > lychee-rendered-html-files.txt + if [[ ! -s lychee-rendered-html-files.txt ]]; then + echo "error: no rendered HTML pages found for lychee" >&2 exit 1 fi - echo "Doc preview is available at ${PREVIEW_URL}" + wc -l lychee-rendered-html-files.txt - name: Restore lychee cache id: restore-lychee-cache uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .lycheecache - key: docs-preview-lychee-${{ env.PR_NUMBER }}-${{ github.sha }} + key: docs-rendered-lychee-${{ env.PR_NUMBER }}-${{ github.sha }} restore-keys: | - docs-preview-lychee-${{ env.PR_NUMBER }}- + docs-rendered-lychee-${{ env.PR_NUMBER }}- - - name: Check doc preview links + - name: Check rendered docs links uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout with: args: >- - --files-from ${{ github.workspace }}/linkcheck-input/lychee-preview-urls.txt + --files-from ${{ github.workspace }}/lychee-rendered-html-files.txt --include-fragments=full --cache --max-cache-age 1d @@ -89,7 +80,7 @@ jobs: format: markdown jobSummary: false lycheeVersion: v0.24.2 - output: lychee-preview.md + output: lychee-rendered-html.md token: ${{ github.token }} - name: Save lychee cache From e8bca70ecaa3631bffd8b7d026e72cc4828c4ebc Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Thu, 4 Jun 2026 13:09:58 -0400 Subject: [PATCH 16/17] Clarify rendered docs artifact uploads --- .github/workflows/build-docs.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index c29463629ec..c97b39f353b 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -248,11 +248,6 @@ jobs: fi mv ${COMPONENT}/docs/build/html/* artifacts/docs/${TARGET} - - name: Write doc preview marker - if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} - run: | - echo "${{ github.sha }}" > artifacts/docs/.preview-commit - - name: Upload rendered docs for link checking if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -262,8 +257,9 @@ jobs: if-no-files-found: error retention-days: 3 - # TODO: Consider removing this step? - - name: Upload doc artifacts + # This is the GitHub Pages artifact format; the link checker needs a + # normal workflow artifact with the rendered HTML tree above. + - name: Upload docs GitHub Pages artifact uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: artifacts/ From 40469f456b2d0390194de14520cb940157bc8096 Mon Sep 17 00:00:00 2001 From: Keith Kraus Date: Thu, 4 Jun 2026 13:56:40 -0400 Subject: [PATCH 17/17] Handle rendered docs linkcheck exclusions --- .github/workflows/check-doc-links.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/check-doc-links.yml b/.github/workflows/check-doc-links.yml index 89366422c35..c00730b6605 100644 --- a/.github/workflows/check-doc-links.yml +++ b/.github/workflows/check-doc-links.yml @@ -63,6 +63,9 @@ jobs: - name: Check rendered docs links uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout with: + # PR-preview canonical URLs are checked by the preview deployment workflow. + # The cuda-bindings #id links are docutils "problematic" anchors from generated API docs. + # Preferred Networks rejects hosted-runner GETs, but the URL is browser reachable. args: >- --files-from ${{ github.workspace }}/lychee-rendered-html-files.txt --include-fragments=full @@ -75,6 +78,9 @@ jobs: --retry-wait-time 5 --timeout 30 --no-progress + --exclude '^https://nvidia\.github\.io/cuda-python/pr-preview/pr-[0-9]+/' + --exclude '^file://.*/cuda-bindings/latest/module/(driver|runtime)\.html#id[0-9]+$' + --exclude '^https://www\.preferred\.jp/en/?$' fail: true failIfEmpty: true format: markdown