Skip to content

Commit a3dfb8c

Browse files
authored
[codex] Add lychee docs link checks and stable source links (#2159)
* Check versioned docs example links * Add pathfinder docs linkcheck * Check docs example links locally * Add lychee docs link checking * Skip lychee on pre-commit.ci * Check rendered doc preview links with lychee * Fix doc preview wait step * Use lychee-action default binary * Address docs review comments * Use fixed lychee-action revision * Fix lychee fragment option syntax * Check deployed docs preview links * Fix rendered docs linkcheck failures * Split rendered docs link check workflow * Check rendered docs links locally * Clarify rendered docs artifact uploads * Handle rendered docs linkcheck exclusions * Defer generated pointer docs cleanup * Remove redundant docs commit hash env * Note temporary rendered docs link exclusion * Use lychee release tag for pre-commit hook * Inline rendered docs link check
1 parent 88b55cb commit a3dfb8c

29 files changed

Lines changed: 208 additions & 71 deletions

.github/workflows/build-docs.yml

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

@@ -91,15 +91,18 @@ jobs:
9191
9292
if [[ ${{ inputs.is-release }} == "true" ]]; then
9393
FILE_HASH="*"
94-
COMMIT_HASH="${{ inputs.git-tag }}"
94+
DOCS_GITHUB_REF="${{ inputs.git-tag }}"
95+
if [[ -z "${DOCS_GITHUB_REF}" ]]; then
96+
DOCS_GITHUB_REF="${GITHUB_REF_NAME}"
97+
fi
9598
else
9699
FILE_HASH="${{ github.sha }}"
97-
COMMIT_HASH="${{ github.sha }}"
100+
DOCS_GITHUB_REF="${{ github.sha }}"
98101
fi
99102
100103
# make outputs from the previous job as env vars
101104
CUDA_CORE_ARTIFACT_BASENAME="cuda-core-python${PYTHON_VERSION_FORMATTED}-linux-64"
102-
echo "COMMIT_HASH=${COMMIT_HASH}" >> $GITHUB_ENV
105+
echo "CUDA_PYTHON_DOCS_GITHUB_REF=${DOCS_GITHUB_REF}" >> $GITHUB_ENV
103106
echo "CUDA_CORE_ARTIFACT_BASENAME=${CUDA_CORE_ARTIFACT_BASENAME}" >> $GITHUB_ENV
104107
echo "CUDA_CORE_ARTIFACT_NAME=${CUDA_CORE_ARTIFACT_BASENAME}-${FILE_HASH}" >> $GITHUB_ENV
105108
echo "CUDA_CORE_ARTIFACTS_DIR=$(realpath "$REPO_DIR/cuda_core/dist")" >> $GITHUB_ENV
@@ -242,8 +245,66 @@ jobs:
242245
fi
243246
mv ${COMPONENT}/docs/build/html/* artifacts/docs/${TARGET}
244247
245-
# TODO: Consider removing this step?
246-
- name: Upload doc artifacts
248+
- name: Write rendered docs file list
249+
if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }}
250+
run: |
251+
find "${GITHUB_WORKSPACE}/artifacts/docs" -type f -name '*.html' ! -path '*/_static/*' \
252+
| LC_ALL=C sort > lychee-rendered-html-files.txt
253+
if [[ ! -s lychee-rendered-html-files.txt ]]; then
254+
echo "error: no rendered HTML pages found for lychee" >&2
255+
exit 1
256+
fi
257+
wc -l lychee-rendered-html-files.txt
258+
259+
- name: Restore lychee cache
260+
if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }}
261+
id: restore-lychee-cache
262+
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
263+
with:
264+
path: .lycheecache
265+
key: docs-rendered-lychee-${{ env.PR_NUMBER }}-${{ github.sha }}
266+
restore-keys: |
267+
docs-rendered-lychee-${{ env.PR_NUMBER }}-
268+
269+
- name: Check rendered docs links
270+
if: ${{ !inputs.is-release && github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') }}
271+
uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout
272+
with:
273+
# PR-preview canonical URLs are checked by the preview deployment workflow.
274+
# The cuda-bindings #id links are docutils "problematic" anchors from generated API docs.
275+
# TODO: Remove this exclusion after cybind stops emitting those problematic anchors.
276+
# Preferred Networks rejects hosted-runner GETs, but the URL is browser reachable.
277+
args: >-
278+
--files-from ${{ github.workspace }}/lychee-rendered-html-files.txt
279+
--include-fragments=full
280+
--cache
281+
--max-cache-age 1d
282+
--max-concurrency 16
283+
--host-concurrency 2
284+
--host-request-interval 250ms
285+
--max-retries 3
286+
--retry-wait-time 5
287+
--timeout 30
288+
--no-progress
289+
--exclude '^https://nvidia\.github\.io/cuda-python/pr-preview/pr-[0-9]+/'
290+
--exclude '^file://.*/cuda-bindings/latest/module/(driver|runtime)\.html#id[0-9]+$'
291+
--exclude '^https://www\.preferred\.jp/en/?$'
292+
fail: true
293+
failIfEmpty: true
294+
format: markdown
295+
jobSummary: false
296+
lycheeVersion: v0.24.2
297+
output: lychee-rendered-html.md
298+
token: ${{ github.token }}
299+
300+
- name: Save lychee cache
301+
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 != '' }}
302+
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
303+
with:
304+
path: .lycheecache
305+
key: ${{ steps.restore-lychee-cache.outputs.cache-primary-key }}
306+
307+
- name: Upload docs GitHub Pages artifact
247308
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
248309
with:
249310
path: artifacts/
@@ -265,5 +326,5 @@ jobs:
265326
git-config-email: cuda-python-bot@users.noreply.github.com
266327
folder: artifacts/docs/
267328
target-folder: docs/
268-
commit-message: "Deploy ${{ (inputs.is-release && 'release') || 'latest' }} docs: ${{ env.COMMIT_HASH }}"
329+
commit-message: "Deploy ${{ (inputs.is-release && 'release') || 'latest' }} docs: ${{ env.CUDA_PYTHON_DOCS_GITHUB_REF }}"
269330
clean: false

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ __pycache__/
88

99
# CUDA Python specific
1010
.cache/
11+
.lycheecache
1112
.pytest_cache/
1213
.benchmarks/
1314
*.cpp

.pre-commit-config.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ci:
99
autoupdate_branch: ''
1010
autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'
1111
autoupdate_schedule: quarterly
12+
skip: [lychee]
1213
submodules: false
1314

1415
# Please update the rev: SHAs below with this command:
@@ -53,6 +54,18 @@ repos:
5354
- stubgen-pyx==0.2.6
5455
- Cython==3.2.4
5556

57+
# Link checking for authored documentation files
58+
- repo: https://github.com/lycheeverse/lychee
59+
rev: 2bba271688c1abb1503097a064e6c3bc1d1b6a9b # frozen: lychee-v0.24.2
60+
hooks:
61+
- id: lychee
62+
args:
63+
- --cache
64+
- --max-concurrency=4
65+
- --max-retries=3
66+
- --no-progress
67+
files: '\.(md|rst)$'
68+
5669
# Standard hooks
5770
- repo: https://github.com/pre-commit/pre-commit-hooks
5871
rev: "3e8a8703264a2f4a69428a0aa4dcb512790b2c8c" # frozen: v6.0.0

cuda_bindings/docs/build_docs.sh

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22

3-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
44
# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
55

66
set -ex
@@ -30,7 +30,12 @@ if [[ "${LATEST_ONLY}" == "1" && -z "${BUILD_PREVIEW:-}" && -z "${BUILD_LATEST:-
3030
fi
3131

3232
# build the docs (in parallel)
33-
SPHINXOPTS="-j 4 -d build/.doctrees" make html
33+
if [[ -z "${SPHINXOPTS:-}" ]]; then
34+
HTML_SPHINXOPTS="-j 4 -d build/.doctrees"
35+
else
36+
HTML_SPHINXOPTS="${SPHINXOPTS}"
37+
fi
38+
SPHINXOPTS="${HTML_SPHINXOPTS}" make html
3439

3540
# for debugging/developing (conf.py), please comment out the above line and
3641
# use the line below instead, as we must build in serial to avoid getting

cuda_bindings/docs/source/conduct.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
33
44
Code of Conduct
@@ -85,7 +85,7 @@ Attribution
8585
-----------
8686

8787
This Code of Conduct is adapted from the `Contributor Covenant <https://www.contributor-covenant.org>`_, version 1.4,
88-
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
88+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/
8989

9090
For answers to common questions about this code of conduct, see
9191
https://www.contributor-covenant.org/faq

cuda_bindings/docs/source/conf.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828

2929
def _github_examples_ref():
30+
if ref := os.environ.get("CUDA_PYTHON_DOCS_GITHUB_REF"):
31+
return ref
3032
if int(os.environ.get("BUILD_PREVIEW", 0)) or int(os.environ.get("BUILD_LATEST", 0)):
3133
return "main"
3234
return f"v{release}"
@@ -35,6 +37,15 @@ def _github_examples_ref():
3537
GITHUB_EXAMPLES_REF = _github_examples_ref()
3638

3739

40+
def _html_baseurl():
41+
docs_domain = os.environ.get("CUDA_PYTHON_DOCS_DOMAIN", "https://nvidia.github.io/cuda-python")
42+
if int(os.environ.get("BUILD_PREVIEW", 0)):
43+
return f"{docs_domain}/pr-preview/pr-{os.environ['PR_NUMBER']}/cuda-bindings/latest/"
44+
if int(os.environ.get("BUILD_LATEST", 0)):
45+
return f"{docs_domain}/cuda-bindings/latest/"
46+
return f"{docs_domain}/cuda-bindings/{release}/"
47+
48+
3849
# -- General configuration ---------------------------------------------------
3950

4051
# Add any Sphinx extension module names here, as strings. They can be
@@ -74,7 +85,7 @@ def _github_examples_ref():
7485

7586
# The theme to use for HTML and HTML Help pages. See the documentation for
7687
# a list of builtin themes.
77-
html_baseurl = "docs"
88+
html_baseurl = _html_baseurl()
7889
html_theme = "nvidia_sphinx_theme"
7990
html_theme_options = {
8091
"switcher": {
@@ -130,12 +141,8 @@ def _github_examples_ref():
130141

131142

132143
def rewrite_source(app, docname, source):
133-
text = source[0]
134-
135144
if docname.startswith("release/"):
136-
text = text.replace(".. module:: cuda.bindings\n\n", "", 1)
137-
138-
source[0] = text
145+
source[0] = source[0].replace(".. module:: cuda.bindings\n\n", "", 1)
139146

140147

141148
suppress_warnings = [

cuda_bindings/docs/source/install.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
33
44
Installation
@@ -99,7 +99,7 @@ Source builds require that the provided CUDA headers are of the same major.minor
9999
100100
$ export CUDA_PATH=/usr/local/cuda
101101
102-
See `Environment Variables <environment_variables.rst>`_ for a description of other build-time environment variables.
102+
See :doc:`Environment Variables <environment_variables>` for a description of other build-time environment variables.
103103

104104
.. note::
105105

cuda_bindings/docs/source/module/runtime.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
.. SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
33
44
-------
@@ -299,7 +299,7 @@ Data types used by CUDA Runtime
299299
.. autoattribute:: cuda.bindings.runtime.cudaError_t.cudaErrorIncompatibleDriverContext
300300

301301

302-
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.
302+
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.
303303

304304

305305
.. autoattribute:: cuda.bindings.runtime.cudaError_t.cudaErrorMissingConfiguration

cuda_bindings/docs/source/motivation.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
33
44
Motivation
@@ -31,7 +31,7 @@ you get the best of both worlds: rapid iterative development with Python and the
3131
speed of a compiled language targeting both CPUs and NVIDIA GPUs.
3232

3333
`CuPy <https://cupy.dev/>`_ is a
34-
`NumPy <https://numpy.org/>`_/`SciPy <https://www.scipy.org/>`_ compatible Array
34+
`NumPy <https://numpy.org/>`_/`SciPy <https://scipy.org/>`_ compatible Array
3535
library, from `Preferred Networks <https://www.preferred.jp/en/>`_, for
3636
GPU-accelerated computing with Python. CUDA Python simplifies the CuPy build
3737
and allows for a faster and smaller memory footprint when importing the CuPy

cuda_bindings/docs/source/overview.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE
33
44
Overview
@@ -38,8 +38,8 @@ The first thing to do is import the `Driver
3838
API <https://docs.nvidia.com/cuda/cuda-driver-api/index.html>`_ and
3939
`NVRTC <https://docs.nvidia.com/cuda/nvrtc/index.html>`_ modules from the ``cuda.bindings``
4040
package. Next, we consider how to store host data and pass it to the device. Different
41-
approaches can be used to accomplish this and are described in `Preparing kernel
42-
arguments <https://nvidia.github.io/cuda-python/cuda-bindings/latest/overview.html#preparing-kernel-arguments>`_.
41+
approaches can be used to accomplish this and are described in
42+
:ref:`Preparing kernel arguments <preparing-kernel-arguments>`.
4343
In this example, we will use NumPy to store host data and pass it to the device, so let's
4444
import this dependency as well.
4545

@@ -308,6 +308,8 @@ maximize performance ({numref}``Figure 1``).
308308

309309
Screenshot of Nsight Compute CLI output of ``cuda.bindings`` example.
310310

311+
.. _preparing-kernel-arguments:
312+
311313
Preparing kernel arguments
312314
--------------------------
313315

0 commit comments

Comments
 (0)