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
95 changes: 95 additions & 0 deletions .github/RELEASE-bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<!--
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# Releasing CUDA bindings

## Choose the release scope and source

Develop the current and maintenance CUDA major lines together on `main`, in
the two package roots listed in [`ci/versions.yml`](../ci/versions.yml).
Release each line independently. A bindings release also needs its matching
`cuda-python` metapackage release; it does not require a release of the other
CUDA major, `cuda-core`, or `cuda-pathfinder`.

Major-line maintenance may include selected API, Python, platform, or other
compatibility changes. Decide those changes explicitly and describe them in
the release notes. Once a patch release is frozen, admit only its approved
fixes and release prerequisites. Do not infer patch-release scope solely
from the registry's `maintenance` role.

For an ordinary release, select a tested commit on `main`. If stabilization
must continue while development advances, create a short release branch such
as `release/bindings-12.9.11` from the chosen commit. For an emergency after a
release, start from its exact tag, provided that tag contains the new package
layout. For the first release after this transition, use a validated commit
containing both roots. Keep routine development on `main`; the historical
`12.9.x` branch stays frozen except for emergency fixes using compatible
historical tooling. Reconcile those fixes into the main-tree maintenance
root. Keep the package layout and release tooling together on new release branches.

Record the source commit, intended tag, selected package root, release owner,
and accepted changes in the release checklist. A branch is a temporary scope
boundary, not a separate long-term home for shared infrastructure.

## Move fixes between integration and release branches

Prefer a reviewed fix on `main`, then backport it to the selected release
branch. Run **CI: Backport a merged PR to a release branch** from the Actions
tab on `main`, supplying the merged PR number and one existing target branch.
The workflow opens a PR; it does not merge or release the result. Inspect the
diff, request CI using the normal PR approval procedure, and review it before
merging. Workflow-file changes require a manual backport with appropriate
credentials. Conflicts and toolkit-specific generated changes require an
explicitly reviewed adaptation.

If urgency requires a fix on the release branch first, link a follow-up PR
reconciling the fix back into `main` before closing the release checklist. In either
direction, assess both bindings roots and record why a change applies to one
or both. Regenerate affected generated files from the intended toolkit and
cybind inputs instead of blindly copying generated output between majors.
See the [shared maintenance policy](../ci/README.md#maintaining-both-bindings-roots).

## Validate and publish the selected line

1. Finalize the bindings and metapackage release notes, dependency constraints,
and selected toolkit pin in the release source tree. The registry and
packaging metadata must agree. Run the CI-tool suite and pre-commit checks.
2. Test the candidate through a reviewed PR or manual CI dispatch on its
branch. Normal development CI includes dependent packages and may cover
both majors. Resolve failures in affected code before selecting the tag.
3. Create the approved immutable `vX.Y.Z` tag at the selected source commit.
The tag-triggered CI selects that line's bindings and metapackage, validates
them with the published Pathfinder dependency, and builds release artifacts.
The tag does not have to be on `main`. Wait for this exact tag's CI to pass.
4. Run **CI: Release** with `release-action=dry-run`, the tag, and
`component=cuda-bindings`; repeat for `cuda-python`. Leave `run-id` blank
to exercise automatic lookup, and leave `dry-run-docs-branch` blank to avoid
deploying docs. The workflow verifies a successful tag-triggered CI run
with the exact source SHA, validates artifacts, and builds docs.
5. Review the dry runs, then separately approve and run `full-release` for
each component under the team's release procedure. A successful candidate
rehearsal or ordinary branch CI is not a substitute for exact-tag CI.
6. Finish post-release QA and announcements, return every applicable fix to
`main`, and close the stabilization effort. Retain immutable tags and the
branch history needed for diagnosis.

The workflow revision used for a release is its **control revision**; the
tagged commit is its **source revision**. Record both and the artifact CI run
ID. Release routing uses the package layout and toolkit settings in
`ci/versions.yml` at the release tag. The current tooling requires a valid
schema-2 registry in that source, for both dry runs and publication, including
Core and Pathfinder releases. Tags from before this registry was introduced
require compatible historical tooling; the current workflow configuration
cannot supply missing tagged configuration. Published releases remain unchanged.

In **CI: Release**, normal releases require each component's exact-version,
nonempty release notes in the tagged source. The `cuda-python` metapackage requires its own notes.
Notes from the workflow control revision or a different component cannot fill
a gap. Preserve the existing exemption for `.postN` releases, which do not
require new notes.

Local synthetic tags used in publication-incapable rehearsals must remain local;
they do not authorize publishing a package, creating a remote tag, or moving
an existing tag.
20 changes: 0 additions & 20 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,3 @@ updates:
time: "09:00"
timezone: "America/Los_Angeles"
open-pull-requests-limit: 1

# GitHub Actions updates targeting the 12.9.x branch
- package-ecosystem: github-actions
directory: /
target-branch: "12.9.x" # keep in sync with backport_branch in ci/versions.yml
schedule:
interval: "monthly"
time: "09:00"
timezone: "America/Los_Angeles"

# Keep churn down: only one open PR from this ecosystem at a time
open-pull-requests-limit: 1

groups:
actions-monthly:
applies-to: version-updates
patterns: ["*"]
update-types: ["major", "minor", "patch"]

labels: [CI/CD, dependencies]
105 changes: 57 additions & 48 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,75 +2,84 @@
#
# SPDX-License-Identifier: Apache-2.0

name: "CI: Backport the merged PR"
name: "CI: Backport a merged PR to a release branch"

on:
pull_request_target:
types: [closed, labeled]
branches:
- main
workflow_dispatch:
inputs:
backport-branch:
description: "Branch to backport commits onto"
required: false
description: "Existing release branch to receive the fix"
required: true
type: string
pull-request:
description: "PR to backport"
description: "Merged PR to backport"
required: true
type: number


permissions:
contents: write # so it can comment
pull-requests: write # so it can create pull requests
contents: write
pull-requests: write

concurrency:
group: backport-${{ inputs.pull-request }}-${{ inputs.backport-branch }}
cancel-in-progress: false

jobs:
backport-from-pr:
name: Backport directly from a pull request
if: ${{ github.repository_owner == 'nvidia' &&
github.event.pull_request.merged == true &&
contains( github.event.pull_request.labels.*.name, 'to-be-backported')
}}
backport:
if: github.repository == 'NVIDIA/cuda-python'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Checkout workflow source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}

- name: Load branch name
id: get-branch
- name: Validate source PR and target branch
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
BACKPORT_BRANCH: ${{ inputs.backport-branch }}
PR_NUMBER: ${{ inputs.pull-request }}
run: |
OLD_BRANCH=$(yq '.backport_branch' ci/versions.yml)
echo "OLD_BRANCH=${OLD_BRANCH}" >> $GITHUB_ENV
set -euo pipefail
if [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::The source PR must be a positive integer."
exit 1
fi
if ! git check-ref-format "refs/heads/$BACKPORT_BRANCH"; then
echo "::error::Provide one exact target branch name."
exit 1
fi
if [[ "$BACKPORT_BRANCH" == "$DEFAULT_BRANCH" ]]; then
echo "::error::Choose a release branch, not the default branch."
exit 1
fi
if ! git ls-remote --exit-code --heads origin "refs/heads/$BACKPORT_BRANCH"; then
echo "::error::The target branch must already exist in this repository."
exit 1
fi
if [[ "$(gh pr view "$PR_NUMBER" --repo "$GH_REPO" --json state --jq .state)" != MERGED ]]; then
echo "::error::The source PR must already be merged in this repository."
exit 1
fi

- name: Create backport pull requests
# Workflow-file changes need a manual backport because GITHUB_TOKEN
# cannot push them. Generated changes need target-specific regeneration.
- name: Create backport pull request
id: backport
uses: korthout/backport-action@8560fb503c275d433c56f05a2f64850093baa9f7 # v4.7.0
with:
source_pr_number: ${{ inputs.pull-request }}
target_branches: ${{ inputs.backport-branch }}
# The explicit target must not be combined with source PR labels.
label_pattern: ''
copy_assignees: true
copy_labels_pattern: true
copy_labels_pattern: '^(?!to-be-backported$|backport ).*'
copy_requested_reviewers: true
target_branches: ${{ env.OLD_BRANCH }}
backport-to-branch:
name: Backport a specific PR against a specific branch
if: github.repository_owner == 'nvidia' && github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Load branch from environment name
if: inputs.backport-branch == null
- name: Require successful backport
if: steps.backport.outputs.was_successful != 'true'
run: |
BACKPORT_BRANCH=$(yq '.backport_branch' ci/versions.yml)
echo "BACKPORT_BRANCH=${BRANCH}" >> $GITHUB_ENV

- name: Load branch name from input
if: inputs.backport-branch != null
run: echo "BACKPORT_BRANCH=${{ inputs.backport-branch }}" >> $GITHUB_ENV

- name: Create backport pull requests
uses: korthout/backport-action@8560fb503c275d433c56f05a2f64850093baa9f7 # v4.7.0
with:
copy_assignees: true
copy_labels_pattern: true
copy_requested_reviewers: true
target_branches: ${{ env.BACKPORT_BRANCH }}
source_pr_number: ${{ inputs.pull-request }}
echo "::error::Backport failed; resolve the conflict or prepare the target-specific fix manually."
exit 1
26 changes: 25 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,34 @@ guide for package-specific conventions and workflows.
## Package map

- `cuda_pathfinder/`: Pure-Python library discovery and loading utilities.
- `cuda_bindings_13/`: Low-level CUDA host API bindings (Cython-heavy).
- `cuda_bindings_13/`: Current low-level CUDA host API bindings (Cython-heavy).
- `cuda_bindings_12/`: CUDA 12 maintenance bindings release line.
- `cuda_core/`: High-level Pythonic CUDA APIs built on top of bindings.
- `cuda_python/`: Metapackage and docs aggregation.

## Synchronized code blocks

Use matching `BEGIN SYNCED <group>` and `END SYNCED <group>` comments for
deliberately duplicated snippets whose contents must remain byte-for-byte
identical. The group name identifies all copies; the boundaries define the
shared snippet, not the surrounding file.

- Before editing a marked block, search from the repository root for its exact
`BEGIN SYNCED <group>` marker, for example with `rg -n -F`. Read every copy
and update them together, including copies outside the current package.
- Compare the complete marked blocks after editing. Also check required
imports and surrounding assumptions, such as relative directory depth,
which may live outside the markers.
- During review, identify groups touched by the diff and compare every copy,
including files absent from the diff. Use the base revision too when markers
were removed or renamed. Report drift, missing boundaries, and newly copied
snippets that omit the markers as review findings.
- Preserve both markers when copying a snippet. If a copy must diverge,
explain why and explicitly revise the group membership; do not remove or
rename a marker merely to conceal drift.
- In the work or review summary, name the groups and files compared, or state
why verification could not be completed.

# Git-derived version lifecycle

Package versions are derived from reachable Git tags by `setuptools-scm`.
Expand Down
68 changes: 33 additions & 35 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,8 @@ Thank you for your interest in contributing to CUDA Python! Based on the type of
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
- [CI infrastructure overview](#ci-infrastructure-overview)
- [CI Pipeline Flow](#ci-pipeline-flow)
- [Pipeline Execution Details](#pipeline-execution-details)
- [Branch-specific Artifact Flow](#branch-specific-artifact-flow)
- [Main Branch](#main-branch)
- [Backport Branches](#backport-branches)
- [Key Infrastructure Details](#key-infrastructure-details)
- [CUDA-major Artifact Flow](#cuda-major-artifact-flow)
- [Release Branches](#release-branches)
- [Code coverage](#code-coverage)


Expand Down Expand Up @@ -374,36 +371,37 @@ The CUDA Python project uses a comprehensive CI pipeline that builds, tests, and

The CI pipeline diagram is maintained as Mermaid source in [`ci/ci-pipeline.mmd`](ci/ci-pipeline.mmd).

### Pipeline Execution Details

**Parallel Execution**: The CI pipeline leverages parallel execution to optimize build and test times:
- **Build Stage**: Different architectures/operating systems (linux-64, linux-aarch64, win-64) are built in parallel across their respective runners
- **Test Stage**: Different architectures/operating systems/CUDA versions are tested in parallel; documentation preview is also built in parallel with testing

### Branch-specific Artifact Flow

#### Main Branch
- **Build** → **Test** → **Documentation** → **Potential Release**
- Artifacts stored as `{component}-python{version}-{platform}-{sha}`
- Full test coverage across all platforms and CUDA versions
- **Artifact flow out**: `cuda-pathfinder` artifacts → backport branches

#### Backport Branches
- **Build** → **Test** → **Backport PR Creation**
- Artifacts used for validation before creating backport pull requests
- Maintains compatibility with older CUDA versions
- **Artifact flow in**: `cuda-pathfinder` artifacts ← main branch
- **Artifact flow out**: older `cuda-bindings` artifacts → main branch

### Key Infrastructure Details

- **Self-hosted runners**: Used for Linux builds and GPU testing (more resources, faster builds)
- **GitHub-hosted runners**: Used for Windows builds and general tasks
- **Artifact retention**: 30 days for GitHub Artifacts (wheels, docs, tests)
- **Cache retention**: GitHub Cache for build dependencies and environments
- **Security**: All commits must be signed, untrusted code blocked
- **Parallel execution**: Matrix builds across Python versions and platforms
- **Component isolation**: Each component (core, bindings, pathfinder, python) can be built/released independently
The [CI workflow](.github/workflows/ci.yml) defines the supported platform,
Python, and CUDA matrices. Build and test jobs run in parallel across their
configured runners. Package selection and artifact validation are shared
through the [CI tools](ci/README.md).

### CUDA-major Artifact Flow

- CUDA 12.9 bindings live in `cuda_bindings_12/`; CUDA 13 bindings live in
`cuda_bindings_13/`. The registry assigns their maintenance and current roles.
- Development CI builds changed roots and affected dependents, reusing a
validated baseline for unaffected roots. Shared changes and scheduled runs
cover both CUDA majors.
- A bindings tag selects one line's bindings and metapackage. Its release
validation uses published Pathfinder and does not require a CUDA Core or
other-major release.
- Artifacts identify their Python version, toolkit, platform, and source SHA
where applicable. Release workflows require the exact tag's successful CI.

### Release Branches

`main` integrates changes for both supported majors. Short release branches
can stabilize a selected release or carry urgent fixes while development
continues. Return applicable fixes to `main` and assess both source roots.
The historical `12.9.x` branch stays frozen except for emergency fixes,
which must be reconciled into `main`. New main CI and releases do not fetch
package artifacts from that branch.

The [bindings release guide](.github/RELEASE-bindings.md) covers release scope,
manual backport automation, source and control revisions, and validation.
The [CUDA 12 maintenance guide](cuda_bindings_12/MAINTENANCE.md) links the
shared regeneration and cross-root review policy.

## Code coverage

Expand Down
Loading
Loading