Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/ISSUE_TEMPLATE/release_checklist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@ body:
- type: markdown
attributes:
value: |
This checklist is for cuda-core releases as well as cuda-bindings patches. Please go through this checklist and ensure all tasks are completed. See [RELEASE-core.md](../RELEASE-core.md) for detailed guidance on each step (cuda.core).
This checklist is for cuda-core releases as well as cuda-bindings patches. See [RELEASE-core.md](../RELEASE-core.md) for cuda.core and [RELEASE-bindings.md](../RELEASE-bindings.md) for bindings release scope, stabilization branches, and validation.

- type: checkboxes
id: subpackage-tasks
attributes:
label: Tasks for cuda-core and cuda-bindings patch release
options:
- label: File an internal nvbug to communicate test plan & release schedule with QA
- label: Ensure all pending PRs are reviewed, tested, and merged
- label: Record the release scope and ensure all PRs selected for this release are reviewed, tested, and merged
- label: Check (or update if needed) the dependency requirements
- label: Sweep deprecations whose stated removal version has arrived (`grep -rn 'deprecated::' cuda_core/cuda`) and remove any that are due
- label: "Finalize the doc update, including release notes (\"Note: Touching docstrings/type annotations in code is OK during code freeze, apply your best judgement!\")"
- label: Update the docs for the new version
- label: Create a public release tag
- label: Wait for the tag-triggered CI run to complete, and use that run ID for release workflows (this will upload the release to PyPI)
- label: Record the exact source commit, workflow control revision, and selected package root before creating the approved public release tag
- label: Wait for the exact tag-triggered CI run to pass and complete a release dry run before approving publication
- label: Publish the release at https://github.com/NVIDIA/cuda-python/releases (this will trigger the conda package update)
- label: Approve the conda-package update PR
- label: Post-release QA
- label: Finalize the announcement update
- label: Send out the announcement internally (on `#swcuda-python`)
- label: Send out the announcement externally (GitHub Release -> Announcement)
- label: For a `cuda-bindings` release, make sure MAJOR and MAJOR-1 releases are made, as well as releases of `cuda-python` for MAJOR and MAJOR-1
- label: For a `cuda-bindings` release, confirm which supported release lines are in scope using `ci/versions.yml`, and release the matching `cuda-python` package for each planned line
- label: For a bindings stabilization or emergency branch, link the PRs returning applicable fixes to `main` and record their applicability to both bindings roots
14 changes: 8 additions & 6 deletions .github/RELEASE-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ version, then publishes to PyPI and verifies again before publishing the
GitHub Release without marking it as latest. A failed
docs build prevents asset uploads, package publication, and finalization.

The release-note checker runs from `git-tag`, not the workflow revision.
For Pathfinder, that tag must contain a version
of `ci/tools/check_release_notes.py` that writes `version=<VERSION>` to
`GITHUB_OUTPUT`. The release-notes job rejects an empty version before
documentation, asset uploads, or package publication can start. Pathfinder
package publication also requires successful draft metadata preparation.
The workflow revision supplies the release tools; release notes and the
package registry (`ci/versions.yml`) come from `git-tag`. Current tooling
requires a schema-2 registry in the tagged source. The release-notes job
exports the checked version and rejects an empty version before documentation,
asset uploads, or package publication can start. Pathfinder package
publication also requires successful draft metadata preparation.
For tags that predate the registry, use compatible historical release tooling;
do not move existing tags to add the registry or updated tools.

The default `dry-run` action validates preparation, builds docs, and
collects release artifacts without creating a GitHub Release or publishing
Expand Down
4 changes: 2 additions & 2 deletions .github/actions/setup-hardened-python/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ description: >-
inputs:
python-version:
description: >-
uv Python request, e.g. "3.12" or "3.14t" (free-threaded). The newest
uv Python request, e.g. "3.11" or "3.14t" (free-threaded). The newest
patch known to the pinned uv is installed. Only series with a fixed
release listed in the interpreter check (3.11-3.15) are accepted.
required: true
default: "3.11"
venv:
description: Directory for the venv. Defaults to a new directory under RUNNER_TEMP.
default: ""
Expand Down
14 changes: 8 additions & 6 deletions .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ on:
jobs:
build:
name: Build docs
permissions:
contents: read
pull-requests: read
# The build stage could fail but we want the CI to keep moving.
if: ${{ github.repository_owner == 'nvidia' && !cancelled() }}
runs-on: ubuntu-latest
Expand Down Expand Up @@ -83,8 +86,6 @@ jobs:
- name: Set up Python for CI tools
# The release checkout can predate this action; use the workflow revision.
uses: ./.ci-control/.github/actions/setup-hardened-python
with:
python-version: "3.12"

- name: Install CI tool dependencies
# Keep the hardened interpreter ahead of any login-shell activation.
Expand Down Expand Up @@ -184,7 +185,7 @@ jobs:
if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-python' }}
run: |
pwd
ls -lahR .
ls -lah cuda_python*.whl

- name: Download cuda-pathfinder build artifacts
if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-pathfinder' || inputs.component == 'cuda-core' }}
Expand All @@ -199,7 +200,7 @@ jobs:
if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-pathfinder' || inputs.component == 'cuda-core' }}
run: |
pwd
ls -lahR cuda_pathfinder
ls -lah cuda_pathfinder/*.whl

- name: Download cuda.bindings build artifacts
if: ${{ !inputs.is-release }}
Expand All @@ -222,7 +223,7 @@ jobs:
if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-bindings' || inputs.component == 'cuda-core' }}
run: |
pwd
ls -lahR $CUDA_BINDINGS_ARTIFACTS_DIR
ls -lah "${CUDA_BINDINGS_ARTIFACTS_DIR}"/*.whl

- name: Download cuda.core build artifacts
if: ${{ !inputs.is-release }}
Expand All @@ -245,7 +246,7 @@ jobs:
if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-core' }}
run: |
pwd
ls -lahR $CUDA_CORE_ARTIFACTS_DIR
ls -lah "${CUDA_CORE_ARTIFACTS_DIR}"/*.whl

- name: Install cuda-pathfinder
if: ${{ !inputs.is-release || inputs.component == 'all' || inputs.component == 'cuda-pathfinder' || inputs.component == 'cuda-core' }}
Expand Down Expand Up @@ -416,6 +417,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
ref: ${{ inputs.is-release && format('refs/tags/{0}', inputs.git-tag) || inputs.git-tag }}

- name: Download docs deployment payload
Expand Down
38 changes: 12 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jobs:
current-cuda-version: ${{ steps.registry.outputs.current-cuda-version }}
python-supported: ${{ steps.registry.outputs.python-supported }}
windows-arm64-supported: ${{ steps.registry.outputs.windows-arm64-supported }}
windows-arm64-python-supported: ${{ steps.registry.outputs.windows-arm64-python-supported }}
windows-arm64-single-cuda-major: ${{ steps.registry.outputs.windows-arm64-single-cuda-major }}
workplan: ${{ steps.workplan.outputs.workplan }}
steps:
Expand Down Expand Up @@ -121,37 +122,22 @@ jobs:
'.package_roots[] | select(.release_status == "current") | .toolkit_version' \
<<< "$bindings_config")

if [[ "$current_cuda_version" =~ ^([0-9]+)\.([0-9]+)(\.|$) ]]; then
current_cuda_major="${BASH_REMATCH[1]}"
current_cuda_minor="${BASH_REMATCH[2]}"
else
echo "Invalid current CUDA version: $current_cuda_version" >&2
exit 1
fi
python3 - <<'PYCODE' >> "$GITHUB_OUTPUT"
import json
from ci.tools.bindings_config import load_config, wheel_python_versions

# Windows ARM64 is available starting with CUDA 13.4.
if (( current_cuda_major > 13 || (current_cuda_major == 13 && current_cuda_minor >= 4) )); then
windows_arm64_supported=true
else
windows_arm64_supported=false
fi

# CUDA 13 introduced Windows ARM64 mid-release-line, so there is no
# CUDA 12 Windows ARM64 toolkit. Once current advances to CUDA 14,
# CUDA 13 can be built as the maintenance major on this platform;
# keep this equality so CUDA 14 does not exclude that supported line.
if [[ "$current_cuda_major" == "13" ]]; then
windows_arm64_single_cuda_major=true
else
windows_arm64_single_cuda_major=false
fi
config = load_config()
current = config.package_for_release_status("current")
versions = wheel_python_versions(current.toolkit_version, "win-arm64", config.python_supported)
print("windows-arm64-supported=" + str(bool(versions)).lower())
print("windows-arm64-single-cuda-major=" + str(current.cuda_major == "13").lower())
print("windows-arm64-python-supported=" + json.dumps(versions))
PYCODE

{
echo "python-supported=$(yq -o json -I 0 '.python.supported' ci/versions.yml)"
echo "bindings-config=$bindings_config"
echo "current-cuda-version=$current_cuda_version"
echo "windows-arm64-supported=$windows_arm64_supported"
echo "windows-arm64-single-cuda-major=$windows_arm64_single_cuda_major"
} >> "$GITHUB_OUTPUT"

- name: Resolve PR merge base
Expand Down Expand Up @@ -491,7 +477,7 @@ jobs:
bindings-config: ${{ needs.detect-changes.outputs.bindings-config }}
workplan: ${{ needs.detect-changes.outputs.workplan }}
single-cuda-major: ${{ needs.detect-changes.outputs.windows-arm64-single-cuda-major == 'true' }}
python-versions: ${{ needs.detect-changes.outputs.python-supported }}
python-versions: ${{ needs.detect-changes.outputs.windows-arm64-python-supported }}

woa-main-dispatch:
name: Dispatch WoA integration
Expand Down
83 changes: 17 additions & 66 deletions .github/workflows/release-upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,102 +2,53 @@
#
# SPDX-License-Identifier: Apache-2.0

name: "CI: Upload git archive"
name: "CI: Upload release artifacts"

on:
workflow_call:
inputs:
git-tag:
type: string
required: true
run-id:
description: "The GHA run ID that generated validated artifacts"
artifact-id:
description: "Immutable artifact ID prepared and validated by this workflow run"
type: string
required: true
component:
description: "Component to download wheels for"
type: string
required: true
dry-run:
description: "Validate release artifacts without uploading them to the GitHub release"
description: "Skip GitHub release uploads; preparation already saved the dry-run artifact"
type: boolean
required: false
default: false

concurrency:
# Keep upload locks component-specific and separate from the caller's workflow lock.
group: release-upload-${{ inputs.component }}
cancel-in-progress: false

permissions:
contents: write

jobs:
# create source archive and upload it to the published release
# URL to the archive: https://github.com/NVIDIA/<repo>/releases/download/<tag>/<repo>-<tag>.tar.gz
upload:
if: ${{ !github.event.repository.fork }}
if: ${{ !github.event.repository.fork && !inputs.dry-run }}
runs-on: ubuntu-latest
env:
ARCHIVE_NAME: ${{ github.event.repository.name }}-${{ inputs.git-tag }}
steps:
- name: Checkout Source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download prepared release artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
fetch-depth: 1
ref: ${{ inputs.git-tag }}

- name: Create Release Directory
run: mkdir -p release

- name: Archive Source
run: >
git archive
--format=tar.gz
--prefix="${{ env.ARCHIVE_NAME }}/"
--output="release/${{ env.ARCHIVE_NAME }}.tar.gz"
${{ inputs.git-tag }}

- name: Compute Checksum
run: >
sha256sum "release/${{ env.ARCHIVE_NAME }}.tar.gz"
| awk '{print $1}'
> "release/${{ env.ARCHIVE_NAME }}.tar.gz.sha256sum"
artifact-ids: ${{ inputs.artifact-id }}
path: release/
merge-multiple: true

- name: Upload Archive
if: ${{ !inputs.dry-run }}
- name: Upload archive, checksum, and wheels
env:
GH_TOKEN: ${{ github.token }}
run: >
RELEASE_GIT_TAG: ${{ inputs.git-tag }}
RELEASE_REPOSITORY: ${{ github.repository }}
run: >-
gh release upload
--clobber "${{ inputs.git-tag }}"
--repo "${{ github.repository }}"
release/*

- name: Download and Validate Wheels
env:
GH_TOKEN: ${{ github.token }}
run: |
# Use the shared script to download wheels
./ci/tools/download-wheels "${{ inputs.run-id }}" "${{ inputs.component }}" "${{ github.repository }}" "release/wheels"

# Validate that release wheels match the expected version from tag.
./ci/tools/validate-release-wheels "${{ inputs.git-tag }}" "${{ inputs.component }}" "release/wheels"

- name: Upload Wheels
if: ${{ !inputs.dry-run }}
env:
GH_TOKEN: ${{ github.token }}
run: |
if [[ -d "release/wheels" && $(ls -A release/wheels 2>/dev/null | wc -l) -gt 0 ]]; then
echo "Uploading wheels to release ${{ inputs.git-tag }}"
gh release upload --clobber "${{ inputs.git-tag }}" --repo "${{ github.repository }}" release/wheels/*
fi

- name: Upload dry-run release artifacts
if: ${{ inputs.dry-run }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-artifacts-dry-run-${{ inputs.component }}-${{ inputs.git-tag }}
path: release/
retention-days: 3
--clobber "$RELEASE_GIT_TAG"
--repo "$RELEASE_REPOSITORY"
release/*.tar.gz release/*.sha256sum release/wheels/*.whl
Loading
Loading