Skip to content

Commit 456d569

Browse files
committed
Merge remote-tracking branch 'upstream/main' into sccache-preprocessor-cache
2 parents a0049af + e8dacbc commit 456d569

6 files changed

Lines changed: 170 additions & 20 deletions

File tree

‎.github/actions/check-doc-links/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ runs:
2525
shell: bash --noprofile --norc -euo pipefail {0}
2626
env:
2727
KIND: ${{ inputs.kind }}
28-
LYCHEE_VERSION: v0.24.2 # Must match the pinned rev in .pre-commit-config.yaml
28+
LYCHEE_VERSION: v0.24.2 # Must match lychee-bin in .pre-commit-config.yaml
2929
POLICY_HASH: ${{ hashFiles('lychee.toml', '.github/actions/check-doc-links/action.yml', 'ci/tools/prepare_lychee_inputs.py', 'ci/tools/retry_lychee.py') }}
3030
# The first pass and all retries use the same inputs and cache policy.
3131
LYCHEE_ARGS: >-

‎.github/workflows/pre-commit.yml‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,58 @@ jobs:
8585
- name: Install pre-commit
8686
run: python -m pip install --upgrade pip pre-commit
8787

88+
# Keep this offline hook smoke check before network-dependent Lychee steps,
89+
# so hook setup and invocation failures surface before link-check retries.
90+
# Invoke pre-commit directly to cover each shell's command lookup and quoting.
91+
# Each runner's shells reuse one offline configuration and hook environment.
92+
- name: Prepare offline Lychee configuration
93+
id: lychee-smoke
94+
# pre-commit needs the config on the checkout's drive; spaces exercise quoting.
95+
run: |
96+
directory="$(mktemp -d 'lychee offline smoke XXXXXX')"
97+
echo "directory=$directory" >> "${GITHUB_OUTPUT}"
98+
python ci/tools/smoke_test_lychee_precommit.py --prepare "$directory"
99+
100+
- name: Smoke-test Lychee in ${{ runner.os == 'Windows' && 'Git Bash' || 'Bash' }}
101+
if: ${{ !cancelled() && steps.lychee-smoke.outcome == 'success' }}
102+
shell: bash
103+
env:
104+
LYCHEE_SMOKE_DIR: ${{ steps.lychee-smoke.outputs.directory }}
105+
SKIP: ""
106+
PYTHONUTF8: "1"
107+
run: |
108+
status=0
109+
pre-commit run lychee --config "$LYCHEE_SMOKE_DIR/pre-commit.yaml" --files README.md --verbose > "$LYCHEE_SMOKE_DIR/bash.log" 2>&1 || status=$?
110+
python ci/tools/smoke_test_lychee_precommit.py --verify "$LYCHEE_SMOKE_DIR/bash.log" "$status"
111+
112+
- name: Smoke-test Lychee in PowerShell (Windows)
113+
if: ${{ !cancelled() && runner.os == 'Windows' && steps.lychee-smoke.outcome == 'success' }}
114+
shell: pwsh
115+
env:
116+
LYCHEE_SMOKE_DIR: ${{ steps.lychee-smoke.outputs.directory }}
117+
SKIP: ""
118+
PYTHONUTF8: "1"
119+
run: |
120+
pre-commit run lychee --config "$env:LYCHEE_SMOKE_DIR/pre-commit.yaml" --files README.md --verbose > "$env:LYCHEE_SMOKE_DIR/pwsh.log" 2>&1
121+
python ci/tools/smoke_test_lychee_precommit.py --verify "$env:LYCHEE_SMOKE_DIR/pwsh.log" $LASTEXITCODE
122+
123+
- name: Smoke-test Lychee in cmd (Windows)
124+
if: ${{ !cancelled() && runner.os == 'Windows' && steps.lychee-smoke.outcome == 'success' }}
125+
shell: cmd
126+
env:
127+
LYCHEE_SMOKE_DIR: ${{ steps.lychee-smoke.outputs.directory }}
128+
SKIP: ""
129+
PYTHONUTF8: "1"
130+
run: |
131+
pre-commit run lychee --config "%LYCHEE_SMOKE_DIR%\pre-commit.yaml" --files README.md --verbose > "%LYCHEE_SMOKE_DIR%\cmd.log" 2>&1
132+
python ci/tools/smoke_test_lychee_precommit.py --verify "%LYCHEE_SMOKE_DIR%\cmd.log" %ERRORLEVEL%
133+
134+
- name: Remove offline Lychee configuration
135+
if: ${{ always() && steps.lychee-smoke.outputs.directory != '' }}
136+
env:
137+
LYCHEE_SMOKE_DIR: ${{ steps.lychee-smoke.outputs.directory }}
138+
run: python ci/tools/smoke_test_lychee_precommit.py --cleanup "$LYCHEE_SMOKE_DIR"
139+
88140
- name: Run pre-commit
89141
run: pre-commit run --all-files --show-diff-on-failure
90142

‎.pre-commit-config.yaml‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,17 @@ repos:
105105
- Cython==3.2.9
106106

107107
# Link checking for authored documentation files
108-
- repo: https://github.com/lycheeverse/lychee
109-
rev: 2bba271688c1abb1503097a064e6c3bc1d1b6a9b # frozen: lychee-v0.24.2
108+
- repo: local
110109
hooks:
111110
- id: lychee
111+
name: lychee
112+
entry: python ./ci/tools/run_lychee.py
113+
language: python
114+
additional_dependencies:
115+
- lychee-bin==0.24.2 # Must match check-doc-links/action.yml
116+
types: [text]
117+
require_serial: true
112118
args:
113-
- LYCHEE_VERSION=0.24.2 # Must match the pinned rev above
114119
- --cache
115120
- --max-concurrency=4
116121
- --max-retries=3

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ Thank you for your interest in contributing to CUDA Python! Based on the type of
2626
- [Symptoms of a bad clone](#symptoms-of-a-bad-clone)
2727
- [Development on Windows](#development-on-windows)
2828
- [Enabling git symlinks](#enabling-git-symlinks)
29-
- [Pre-commit lychee workaround](#pre-commit-lychee-workaround)
3029
- [Type stubs for cuda.core](#type-stubs-for-cudacore)
3130
- [Pre-commit](#pre-commit)
3231
- [Pixi lockfiles](#pixi-lockfiles)
@@ -192,19 +191,6 @@ $ reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /v
192191

193192
Then return to [Recommended clone](#recommended-clone).
194193

195-
### Pre-commit lychee workaround
196-
197-
For development on Windows (not WSL), the `lychee` pre-commit task will not
198-
work when running `pre-commit run --all-files`. This problem does not occur
199-
if you install the pre-commit hook and run it automatically as part of your
200-
`git commit` workflow. To resolve this, you can either:
201-
202-
1. Run `pre-commit` in Git Bash, rather than directly in PowerShell or cmd
203-
204-
2. Skip it by setting the environment variable `SKIP` to `lychee`. This would
205-
be `$env:SKIP = "lychee"` in PowerShell or `set SKIP=lychee` in cmd.
206-
207-
208194
## Type stubs for cuda.core
209195

210196
`cuda.core` is a PEP 561-compliant package: it ships a `py.typed` marker and
@@ -237,6 +223,8 @@ To set yourself up for running pre-commit checks locally and to catch issues bef
237223
* Run this once per checkout: `pre-commit install`
238224
* You can manually check all files at any time by running: `pre-commit run --all-files`
239225

226+
On Windows, these commands work from PowerShell, cmd, or Git Bash.
227+
240228
This command runs all configured hooks (such as linters and formatters) across your repository, letting you review and address issues before committing.
241229

242230
Installing the hook is required, not optional. Some of the automated checks
@@ -247,8 +235,6 @@ between commits, leaving stale headers or out-of-date stubs in the history.
247235
If the hook isn't installed, `pre-commit run` will print a visible warning
248236
reminding you to run `pre-commit install`.
249237

250-
Windows contributors: see [Pre-commit lychee workaround](#pre-commit-lychee-workaround) under Development on Windows.
251-
252238
## Pixi lockfiles
253239

254240
The repository checks in a `pixi.lock` next to each `pixi.toml`. Those lockfiles

‎ci/tools/run_lychee.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Run pre-commit's pinned native Lychee executable.
5+
6+
The upstream Lychee hook uses a shell launcher, which requires a compatible
7+
shell on Windows. We configure a Python hook so pre-commit installs the pinned
8+
lychee-bin wheel in an isolated environment. This wrapper runs that
9+
environment's native executable, passing through arguments and returning
10+
its exit status. The same hook works on Linux and in Windows shells such as
11+
PowerShell, cmd, and Git Bash.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import subprocess
17+
import sys
18+
import sysconfig
19+
from pathlib import Path
20+
21+
22+
def main(arguments: list[str] | None = None) -> int:
23+
# Resolve the executable from this hook's environment to honor the pin.
24+
executable = Path(sysconfig.get_path("scripts")) / ("lychee.exe" if sys.platform == "win32" else "lychee")
25+
if not executable.is_file():
26+
raise SystemExit(
27+
f"lychee was not found at {executable}; run this script through the configured pre-commit hook"
28+
)
29+
arguments = sys.argv[1:] if arguments is None else arguments
30+
return subprocess.run([str(executable), *arguments], check=False).returncode # noqa: S603
31+
32+
33+
if __name__ == "__main__":
34+
raise SystemExit(main())
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Offline smoke check for the configured native Lychee hook.
5+
6+
Exercise the hook's environment, Python launcher, and native executable against
7+
the README's local links without making external link requests. As an accepted
8+
tradeoff for using a real document, a broken local README link also fails this
9+
check even when the hook itself works.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import argparse
15+
import shutil
16+
from pathlib import Path
17+
18+
import yaml
19+
from retry_lychee import read_report
20+
21+
ROOT = Path(__file__).resolve().parents[2]
22+
REPORT_NAME = "lychee.json"
23+
24+
25+
def write_config(directory: Path) -> None:
26+
# Use the checked-in hook, including its entry point and pinned dependency.
27+
# PyYAML is already installed as a dependency of pre-commit.
28+
config = yaml.safe_load((ROOT / ".pre-commit-config.yaml").read_text(encoding="utf-8"))
29+
[hook] = [hook for repo in config["repos"] for hook in repo["hooks"] if hook["id"] == "lychee"]
30+
report = directory / REPORT_NAME
31+
hook.setdefault("args", []).extend(["--offline", "--format", "json", "--output", str(report)])
32+
config_path = directory / "pre-commit.yaml"
33+
config_path.write_text(yaml.safe_dump(config), encoding="utf-8")
34+
35+
36+
def verify(log: Path, exit_code: int) -> int:
37+
report = log.with_name(REPORT_NAME)
38+
try:
39+
print(log.read_text(encoding="utf-8"), end="")
40+
if exit_code != 0:
41+
return exit_code
42+
if not report.is_file():
43+
raise SystemExit(f"The configured Lychee hook produced no report ({report}); was it skipped?")
44+
read_report(report, exit_code)
45+
return 0
46+
finally:
47+
# Consume each shell's report so a later skipped hook cannot reuse it.
48+
report.unlink(missing_ok=True)
49+
50+
51+
def main() -> int:
52+
# CI uses separate phases so each shell invokes pre-commit directly.
53+
parser = argparse.ArgumentParser(description=__doc__)
54+
mode = parser.add_mutually_exclusive_group(required=True)
55+
mode.add_argument("--prepare", type=Path, metavar="DIRECTORY")
56+
mode.add_argument("--verify", nargs=2, metavar=("LOG", "EXIT_CODE"))
57+
mode.add_argument("--cleanup", type=Path, metavar="DIRECTORY")
58+
args = parser.parse_args()
59+
if args.prepare is not None:
60+
write_config(ROOT / args.prepare)
61+
return 0
62+
if args.verify is not None:
63+
log, exit_code = args.verify
64+
return verify(ROOT / log, int(exit_code))
65+
directory = (ROOT / args.cleanup).resolve()
66+
if directory.parent != ROOT or not directory.name.startswith("lychee offline smoke "):
67+
raise SystemExit("Refusing to remove a directory outside the smoke check's temporary files")
68+
shutil.rmtree(directory)
69+
return 0
70+
71+
72+
if __name__ == "__main__":
73+
raise SystemExit(main())

0 commit comments

Comments
 (0)