Repository navigation
Conversation
Each subproject's build_docs.sh currently names the output subdir by importlib.metadata.version(), which on a pre-release checkout resolves to a PEP 440 .dev or +local string. When build_docs.sh is called without latest-only, that versioned subdir gets copied to latest/ and also published verbatim to gh-pages, where it accumulates as an orphan tree not reachable via nv-versions.json. Refuse that path explicitly in all three subprojects. Refs NVIDIA#3231.
📝 Summary
Priority: Change: Bug fix Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

Major · Check the raw version before the default derivation strips it. · build_docs.sh:22-25
cuda_bindings_13/docs/build_docs.sh:22-25
🎯 Functional Correctness | 🟠 Major | ⚡ Quick winCheck the raw version before the default derivation strips it.
important: When
SPHINX_CUDA_BINDINGS_VERis unset, the default derivation reduces13.4.0.dev1and13.4.0+localto13.4.0. The publish guard never matches these versions, so pre-release builds still copy intobuild/html/latest.cuda_pathfinder/docs/build_docs.sh:22-25has the same derivation and the same problem. Incuda_core/docs/build_docs.sh:22-24,awkstrips only the+localsuffix, so.devversions still reach the guard but+localversions do not.Capture the raw version before normalization, and check that value in the guard:
Suggested fix
+# Keep the raw version for the publish guard. The normalized value drops .dev and +local. if [[ -z "${SPHINX_CUDA_BINDINGS_VER}" ]]; then + RAW_BINDINGS_VER=$(python -c "from importlib.metadata import version; print(version('cuda-bindings'))") export SPHINX_CUDA_BINDINGS_VER=$(python -c "from importlib.metadata import version; \ ver = '.'.join(str(version('cuda-bindings')).split('.')[:3]); \ print(ver)" \ | awk -F'+' '{print $1}') +else + RAW_BINDINGS_VER="${SPHINX_CUDA_BINDINGS_VER}" fi- if [[ "${SPHINX_CUDA_BINDINGS_VER}" == *.dev* || "${SPHINX_CUDA_BINDINGS_VER}" == *+* ]]; then - echo "refusing to publish versioned docs for pre-release version '${SPHINX_CUDA_BINDINGS_VER}'" >&2 + if [[ "${RAW_BINDINGS_VER}" == *.dev* || "${RAW_BINDINGS_VER}" == *+* ]]; then + echo "refusing to publish versioned docs for pre-release version '${RAW_BINDINGS_VER}'" >&2Apply the same pattern to
cuda_pathfinder/docs/build_docs.sh, usingSPHINX_CUDA_PATHFINDER_VERand thecuda-pathfinderpackage name. Apply it tocuda_core/docs/build_docs.sh, usingSPHINX_CUDA_CORE_VERand thecuda-corepackage name, so that+localversions are also caught.

Review info
-
Run configuration-
- Source mode: Auto
- Repository: NVIDIA/cuda-python
- Branch: main
- File: .coderabbit.yaml
- Commit: 590a8bf
- Layers: Repository YAML
- Review profile: CHILL
- Plan: Enterprise
- Run ID:
56fd6480-28ae-4c55-8123-632813ae1c41
Files selected for processing: 3-
cuda_bindings_13/docs/build_docs.shcuda_core/docs/build_docs.shcuda_pathfinder/docs/build_docs.sh
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.
-
| # versions, which otherwise leak orphan trees onto gh-pages that | ||
| # are not reachable via nv-versions.json. See #3231. | ||
| if [[ "${SPHINX_CUDA_BINDINGS_VER}" == *.dev* || "${SPHINX_CUDA_BINDINGS_VER}" == *+* ]]; then | ||
| echo "refusing to publish versioned docs for pre-release version '${SPHINX_CUDA_BINDINGS_VER}'" >&2 |
There was a problem hiding this comment.
minor: Use an error message that matches the guard. The branch rejects .dev and +, but the message says “pre-release version.” The guard allows alpha, beta, and release-candidate versions. Use wording such as “development or local version” in all three scripts.
|
/ok to test 601e106 |
|
juenglin
left a comment
There was a problem hiding this comment.
Thanks for picking this up. Keeping orphan trees off gh-pages is worth guarding. Tracing where the orphans come from, though, points to a different root cause. It also suggests this guard would break tag CI.
Where the orphan trees come from
ci.yml runs on every tag push (v*, cuda-core-v*, cuda-pathfinder-v*) and calls build-docs.yml with is-release: true. It doesn't pass component, so that defaults to all, and deploy-docs defaults to true. Tag CI therefore runs build_all_docs.sh without latest-only and deploys every package's versioned tree to gh-pages. Only the tagged package has an exact version; the others are built from dev wheels.
Example: the cuda-pathfinder-v1.8.4 tag push (run 39832572216, 18:34 UTC on Oct 22) produced gh-pages commit 7125843 at 18:52, "Deploy release docs: cuda-pathfinder-v1.8.4". That commit added cuda-core/1.2.1.dev249 and cuda-bindings/13.4.4. It also overwrote cuda-core/latest, cuda-bindings/latest and cuda-pathfinder/nv-versions.json. The actual release.yml run for that tag (run 39859618232, 21:44) built only the pathfinder docs.
I traced 9 of the 26 trees purged at 30f5f0a. Six were added by "Deploy release docs: cuda-pathfinder-v…" commits (v1.5.6, v1.6.0, v1.8.0, v1.8.2, v1.8.3, v1.8.4). The three oldest (0.5.1.dev132, 0.6.1.dev36, 0.7.1.dev62, Feb to Apr) were added by deploys with an empty ref.
The same path also publishes alpha and beta trees. cuda-bindings/13.4.1a0 was added by "Deploy release docs: v13.4.1a0" at 23:14 UTC on Sep 9. That was while the tag CI run (34414563265) was in progress, and before any release.yml run that day. aN tags are now reserved for internal milestones (AGENTS.md, #2932), but this one became public docs. cuda-bindings/13.4.0b1 is also on gh-pages. Neither is listed in cuda-bindings/nv-versions.json.
What this PR would do in that setup
- On any
v*orcuda-pathfinder-v*tag,cuda-coreis a dev version, so thecuda_coreguard exits 1.build_all_docs.shruns underset -e, so the docs job and the whole tag CI run fail. release.ymlauto-detects "the successful CI run triggered by a tag push" throughci/tools/lookup-run-id. So this would block bindings, cuda-python and pathfinder releases unless someone supplies a run ID by hand.- On
cuda-core-v*tags the guard never fires (see below), so the other packages keep leaking. - The guard doesn't catch
aN,bNorrcNversions. Since #2922 switched to defaultsetuptools-scmtag parsing,rcsuffixes are no longer stripped. The next bindings rc tag would publish a13.x.0rcNtree. - The
latest/andnv-versions.jsonoverwrites aren't addressed.
Suggested fix
release.yml already builds and deploys release docs, scoped to the component being released. Tag CI only needs to build the docs, not deploy them:
uses: ./.github/workflows/build-docs.yml
with:
is-release: ${{ github.ref_type == 'tag' }}
deploy-docs: ${{ github.ref_type != 'tag' }}
This still validates the release build path on every tag, and the existing "Upload dry-run docs artifact" step keeps the output inspectable. The deploy job itself still runs on every push to main. The change stops every leak above regardless of version format: .devN, aN, bN and rcN trees, truncated X.Y.Z trees, and the latest/ and nv-versions.json overwrites.
Possible follow-up: tag CI uses the "Build all docs" path, but release.yml always uses "Build component docs", since all isn't a release option. Passing the component that matches the tag namespace would make tag CI validate exactly what the release runs.
If a build_docs.sh guard is kept as a backstop
- Scope it to the package being released. Otherwise it causes the tag CI failure above.
- Allow only plain
X.Y.Z, rather than rejecting known pre-release patterns. Tag formats have changed recently (#2922, #2932), and a pattern list like*.dev*falls behind each time. - Check the version before it is truncated or stripped. The current check misses most cases:
cuda_bindings_13,cuda_pathfinderandcuda_pythonkeep only three version fields ('.'.join(...split('.')[:3])). So1.2.1.dev249becomes1.2.1, and13.4.1a1.dev5becomes13.4.1a1; both pass. Clean-looking trees already leaked this way. They're unlisted innv-versions.jsonand have no matching tag:cuda-bindings/13.1.2,13.2.1,13.3.2andcuda-pathfinder/1.5.7. They sit where the real release would later go.- All scripts strip
+localwithawk -F'+'before the check, so the*+*branch can never match.
- Test it against the real derivation. The smoke test checked the pattern on literal strings.
ci/tools/tests/test_build_docs_for_link_check.pyis a precedent for pytest-driven tests of docs shell scripts. - Cover
cuda_python/docs/build_docs.shas well. - (nit) Fail before
make html.
Separately, the unlisted trees above (13.4.1a0, 13.4.0b1 and the clean-looking ones) may be worth purging.
Description
Refs #3231
Each subproject's
build_docs.shnames the output subdir byimportlib.metadata.version(). On a pre-release checkout that resolves to a PEP 440.devor+localversion. Whenbuild_docs.shis called withoutlatest-only, that versioned subdir gets copied tolatest/and published verbatim togh-pages, where it accumulates as an orphan tree not reachable vianv-versions.json. 26 such orphans were just purged from gh-pages at 30f5f0a — this patch keeps them from coming back.The guard allows alpha/beta/rc tags (
13.4.0b1,1.0.0rc1, …) and only refuses.dev*and+localversions.Checklist
build_docs.sh; guard logic smoke-tested locally against1.2.1.dev249,1.2.1+local,1.2.1,13.4.0b1,13.4.1a0,1.0.0rc1)-- Leo's bot