Skip to content

Commit 650a8ab

Browse files
committed
Merge remote-tracking branch 'upstream/main' into driver-cybind
2 parents f78fe24 + d9feb99 commit 650a8ab

59 files changed

Lines changed: 4120 additions & 267 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: Check documentation links
6+
description: Check authored or rendered documentation with shared nightly caches and bounded retries
7+
8+
inputs:
9+
kind:
10+
description: "Input kind: authored or rendered"
11+
required: true
12+
refresh-cache:
13+
description: Check links afresh and publish a new cache baseline
14+
default: "false"
15+
failure-policy:
16+
description: "check allows up to 10 rate-limited URLs; cache-warm warns on all link failures"
17+
default: check
18+
19+
# Callers provide Python 3.10+ on PATH; the helpers use only its standard library.
20+
runs:
21+
using: composite
22+
steps:
23+
- name: Prepare link-check inputs and cache policy
24+
id: inputs
25+
shell: bash --noprofile --norc -euo pipefail {0}
26+
env:
27+
KIND: ${{ inputs.kind }}
28+
LYCHEE_VERSION: v0.24.2 # Must match the pinned rev in .pre-commit-config.yaml
29+
POLICY_HASH: ${{ hashFiles('lychee.toml', '.github/actions/check-doc-links/action.yml', 'ci/tools/prepare_lychee_inputs.py', 'ci/tools/retry_lychee.py') }}
30+
# The first pass and all retries use the same inputs and cache policy.
31+
LYCHEE_ARGS: >-
32+
--files-from "${{ github.workspace }}/lychee-${{ inputs.kind }}-files.txt"
33+
${{ inputs.kind == 'rendered' && '--include-fragments=full' || '' }}
34+
--cache
35+
--max-cache-age 1d
36+
--max-concurrency 16
37+
--host-concurrency 2
38+
--host-request-interval 250ms
39+
--max-retries 3
40+
--retry-wait-time 5
41+
--timeout 30
42+
--no-progress
43+
--config "${{ github.workspace }}/lychee.toml"
44+
run: |
45+
python "${GITHUB_WORKSPACE}/ci/tools/prepare_lychee_inputs.py" "${KIND}" \
46+
--output "${GITHUB_WORKSPACE}/lychee-${KIND}-files.txt"
47+
# Keep authored checks separate from rendered checks, which also
48+
# validate fragments. Both callers must use the same cache paths.
49+
mkdir -p "${RUNNER_TEMP}/lychee-${KIND}"
50+
prefix="lychee-v1-${LYCHEE_VERSION}-${KIND}-${POLICY_HASH}-baseline-"
51+
echo "prefix=${prefix}" >> "${GITHUB_OUTPUT}"
52+
echo "key=${prefix}${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "${GITHUB_OUTPUT}"
53+
echo "version=${LYCHEE_VERSION}" >> "${GITHUB_OUTPUT}"
54+
echo "args=${LYCHEE_ARGS}" >> "${GITHUB_OUTPUT}"
55+
56+
- name: Restore successful checks from the nightly baseline
57+
if: ${{ inputs.refresh-cache != 'true' }}
58+
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
59+
with:
60+
path: ${{ runner.temp }}/lychee-${{ inputs.kind }}/.lycheecache
61+
key: ${{ steps.inputs.outputs.key }}
62+
restore-keys: ${{ steps.inputs.outputs.prefix }}
63+
64+
- name: Check documentation links
65+
id: lychee
66+
uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # supports v0.24.x archive layout
67+
with:
68+
args: ${{ steps.inputs.outputs.args }}
69+
# The next step owns bounded retries and the final success gate.
70+
fail: false
71+
# The action's empty-report guard only understands Markdown.
72+
# Our helper validates the JSON report and rejects zero checked links.
73+
failIfEmpty: false
74+
format: json
75+
jobSummary: false
76+
lycheeVersion: ${{ steps.inputs.outputs.version }}
77+
workingDirectory: ${{ runner.temp }}/lychee-${{ inputs.kind }}
78+
output: ${{ github.workspace }}/lychee-${{ inputs.kind }}.json
79+
token: ${{ github.token }}
80+
81+
- name: Retry transient link failures using cached successful checks
82+
if: ${{ steps.lychee.outcome == 'success' }}
83+
shell: bash --noprofile --norc -euo pipefail {0}
84+
working-directory: ${{ runner.temp }}/lychee-${{ inputs.kind }}
85+
env:
86+
EXIT_CODE: ${{ steps.lychee.outputs.exit_code }}
87+
REPORT: ${{ github.workspace }}/lychee-${{ inputs.kind }}.json
88+
LYCHEE_ARGS: ${{ steps.inputs.outputs.args }}
89+
FAILURE_POLICY: ${{ inputs.failure-policy }}
90+
GITHUB_TOKEN: ${{ github.token }}
91+
run: |
92+
python "${GITHUB_WORKSPACE}/ci/tools/retry_lychee.py" \
93+
--initial-exit-code "${EXIT_CODE}" --report "${REPORT}" \
94+
--max-attempts 3 --policy "${FAILURE_POLICY}"
95+
96+
- name: Publish fresh successful checks
97+
# Manual branch tests publish isolated branch caches. Nightly callers
98+
# on the default branch publish the baseline that all PRs can restore.
99+
if: ${{ always() && inputs.refresh-cache == 'true' && steps.inputs.outputs.key != '' }}
100+
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
101+
with:
102+
path: ${{ runner.temp }}/lychee-${{ inputs.kind }}/.lycheecache
103+
key: ${{ steps.inputs.outputs.key }}
104+
105+
- name: Upload link-check report
106+
if: ${{ always() }}
107+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
108+
with:
109+
name: lychee-${{ inputs.kind }}-${{ github.run_id }}-${{ github.run_attempt }}
110+
path: |
111+
${{ github.workspace }}/lychee-${{ inputs.kind }}*.json
112+
${{ github.workspace }}/lychee-${{ inputs.kind }}*.md
113+
if-no-files-found: ignore
114+
retention-days: 7

‎.github/workflows/build-docs.yml‎

Lines changed: 10 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -284,58 +284,21 @@ jobs:
284284
fi
285285
mv ${COMPONENT}/docs/build/html/* artifacts/docs/${TARGET}
286286
287-
- name: Write rendered docs file list
287+
# Keep link checks in the existing PR docs build, using the same revision
288+
# as its wheels. Nightly supplies shared caches for both kinds of inputs.
289+
- name: Check authored documentation links
288290
if: ${{ !inputs.is-release && startsWith(github.ref_name, 'pull-request/') }}
289-
run: |
290-
find "${GITHUB_WORKSPACE}/artifacts/docs" -type f -name '*.html' ! -path '*/_static/*' \
291-
| LC_ALL=C sort > lychee-rendered-html-files.txt
292-
if [[ ! -s lychee-rendered-html-files.txt ]]; then
293-
echo "error: no rendered HTML pages found for lychee" >&2
294-
exit 1
295-
fi
296-
wc -l lychee-rendered-html-files.txt
297-
298-
- name: Restore lychee cache
299-
if: ${{ !inputs.is-release && startsWith(github.ref_name, 'pull-request/') }}
300-
id: restore-lychee-cache
301-
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
291+
timeout-minutes: 60
292+
uses: ./.github/actions/check-doc-links
302293
with:
303-
path: .lycheecache
304-
key: docs-rendered-lychee-${{ env.PR_NUMBER }}-${{ github.sha }}
305-
restore-keys: |
306-
docs-rendered-lychee-${{ env.PR_NUMBER }}-
294+
kind: authored
307295

308-
- name: Check rendered docs links
296+
- name: Check rendered documentation links
309297
if: ${{ !inputs.is-release && startsWith(github.ref_name, 'pull-request/') }}
310-
uses: lycheeverse/lychee-action@6da1d14f3a43098a294b7696d93d938aa8d20fc0 # unreleased: supports v0.24.x archive layout
311-
with:
312-
args: >-
313-
--files-from ${{ github.workspace }}/lychee-rendered-html-files.txt
314-
--include-fragments=full
315-
--cache
316-
--max-cache-age 1d
317-
--max-concurrency 16
318-
--host-concurrency 2
319-
--host-request-interval 250ms
320-
--max-retries 3
321-
--retry-wait-time 5
322-
--timeout 30
323-
--no-progress
324-
--config ${{ github.workspace }}/lychee.toml
325-
fail: true
326-
failIfEmpty: true
327-
format: markdown
328-
jobSummary: false
329-
lycheeVersion: v0.24.2
330-
output: lychee-rendered-html.md
331-
token: ${{ github.token }}
332-
333-
- name: Save lychee cache
334-
if: ${{ always() && !inputs.is-release && startsWith(github.ref_name, 'pull-request/') && steps.restore-lychee-cache.outputs.cache-hit != 'true' && steps.restore-lychee-cache.outputs.cache-primary-key != '' }}
335-
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
298+
timeout-minutes: 60
299+
uses: ./.github/actions/check-doc-links
336300
with:
337-
path: .lycheecache
338-
key: ${{ steps.restore-lychee-cache.outputs.cache-primary-key }}
301+
kind: rendered
339302

340303
- name: Upload docs GitHub Pages artifact
341304
if: ${{ inputs.deploy-docs }}

‎.github/workflows/ci-nightly.yml‎

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
# against the latest cuda-python wheels built on main, runs the standard
77
# test suite on runners reserved for nightly-only use (e.g. arm64 l4×2), and
88
# exercises release-time CI helper scripts so they do not silently rot between releases.
9+
# It also builds documentation from source to warm the shared Lychee cache;
10+
# unresolved links are warnings, while build and checker errors remain failures.
911
#
1012
# This workflow does NOT build wheels — it downloads them from the latest
1113
# successful CI run on main and runs integration/standard tests.
1214

1315
name: "CI: Nightly optional-deps"
1416

1517
concurrency:
16-
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
18+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ inputs.documentation-links-only || false }}
1719
cancel-in-progress: true
1820

1921
on:
@@ -24,6 +26,10 @@ on:
2426
- cron: "17 2 * * *"
2527
workflow_dispatch:
2628
inputs:
29+
documentation-links-only:
30+
description: "Test the nightly link checker without wheel or GPU jobs"
31+
type: boolean
32+
default: false
2733
run-id:
2834
description: >
2935
Override the CI run ID to download artifacts from.
@@ -32,6 +38,16 @@ on:
3238
default: ''
3339

3440
jobs:
41+
documentation-links:
42+
name: "Nightly: Documentation links"
43+
if: ${{ github.repository_owner == 'nvidia' }}
44+
permissions:
45+
contents: read
46+
uses: ./.github/workflows/lychee.yml
47+
with:
48+
concurrency-suffix: ${{ inputs.documentation-links-only && 'links-only' || 'full' }}
49+
refresh-cache: true
50+
3551
test-ci-tools-for-release:
3652
name: "Nightly: CI tools for release"
3753
if: ${{ github.repository_owner == 'nvidia' }}
@@ -49,7 +65,7 @@ jobs:
4965
python -m pytest -v --noconftest ci/tools/tests
5066
5167
find-wheels:
52-
if: ${{ github.repository_owner == 'nvidia' }}
68+
if: ${{ github.repository_owner == 'nvidia' && !inputs.documentation-links-only }}
5369
runs-on: ubuntu-latest
5470
outputs:
5571
RUN_ID: ${{ steps.find.outputs.run_id }}
@@ -313,6 +329,7 @@ jobs:
313329
if: ${{ always() && github.repository_owner == 'nvidia' }}
314330
runs-on: ubuntu-latest
315331
needs:
332+
- documentation-links
316333
- test-ci-tools-for-release
317334
- find-wheels
318335
- test-pytorch-linux
@@ -328,13 +345,27 @@ jobs:
328345
- test-standard-linux-aarch64
329346
steps:
330347
- name: Exit
348+
env:
349+
LINKS_ONLY: ${{ inputs.documentation-links-only || false }}
350+
NEEDS_JSON: ${{ toJSON(needs) }}
331351
run: |
352+
if [[ "${LINKS_ONLY}" == "true" ]]; then
353+
# GPU jobs depend on find-wheels and must stay skipped in this mode.
354+
jq -e 'all(to_entries[];
355+
if .key == "documentation-links" or .key == "test-ci-tools-for-release"
356+
then .value.result == "success"
357+
else .value.result == "skipped"
358+
end)' <<< "${NEEDS_JSON}"
359+
exit 0
360+
fi
361+
332362
# If any dependency was cancelled or failed, that's a failure.
333363
#
334364
# See ci.yml for the full rationale on why we must use always()
335365
# and explicitly check each result rather than relying on the
336366
# default behaviour.
337-
if ${{ needs.test-ci-tools-for-release.result == 'cancelled' ||
367+
if ${{ needs.documentation-links.result != 'success' ||
368+
needs.test-ci-tools-for-release.result == 'cancelled' ||
338369
needs.test-ci-tools-for-release.result == 'failure' ||
339370
needs.find-wheels.result != 'success' }}; then
340371
exit 1

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -632,41 +632,6 @@ jobs:
632632
with:
633633
is-release: ${{ github.ref_type == 'tag' }}
634634

635-
precommit-windows:
636-
name: Pre-commit on Windows
637-
runs-on: windows-latest
638-
if: ${{ github.repository_owner == 'nvidia' && !fromJSON(needs.should-skip.outputs.skip) }}
639-
needs:
640-
- should-skip
641-
permissions:
642-
contents: read
643-
steps:
644-
- name: Enable Git symlinks (Windows, must precede checkout)
645-
run: git config --global core.symlinks true
646-
647-
- name: Checkout repository
648-
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
649-
with:
650-
fetch-depth: 1
651-
persist-credentials: false
652-
653-
- name: Set up Python
654-
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
655-
with:
656-
python-version: '3.13'
657-
658-
- name: Install pre-commit
659-
shell: bash
660-
run: |
661-
set -euxo pipefail
662-
python -m pip install --upgrade pip pre-commit
663-
664-
- name: Run pre-commit
665-
shell: bash
666-
run: |
667-
set -euxo pipefail
668-
SKIP=lychee pre-commit run --all-files
669-
670635
checks:
671636
name: Check job status
672637
if: ${{ always() && github.repository_owner == 'nvidia' }}
@@ -687,7 +652,6 @@ jobs:
687652
- api-check-core-vs-release
688653
- api-check-core-vs-base
689654
- doc
690-
- precommit-windows
691655
steps:
692656
- name: Exit
693657
env:
@@ -725,14 +689,12 @@ jobs:
725689
fi
726690
}
727691
728-
# Control jobs, the universal linux build, docs, and Windows
729-
# pre-commit checks always run.
692+
# Control jobs, the universal Linux build, and docs always run.
730693
check_result "ci-vars" "success"
731694
check_result "should-skip" "success"
732695
check_result "detect-changes" "success"
733696
check_result "build-linux-64" "success"
734697
check_result "doc" "success"
735-
check_result "precommit-windows" "success"
736698
737699
# Optional platform builds and wheel tests share the platform plan.
738700
linux_expected="skipped"

0 commit comments

Comments
 (0)