Skip to content

Latest commit

 

History

History
416 lines (315 loc) · 19.4 KB

CONTRIBUTING.md

File metadata and controls

416 lines (315 loc) · 19.4 KB

Contributing to CUDA Python

Thank you for your interest in contributing to CUDA Python! Based on the type of contribution, it will fall into two categories:

  1. You want to report a bug, feature request, or documentation issue:
    • File an issue describing what you encountered or what you want to see changed.
    • The NVIDIA team will evaluate the issues and triage them, scheduling them for a release. If you believe the issue needs priority attention comment on the issue to notify the team.
  2. You want to implement a feature, improvement, or bug fix:
    • Before starting work on an existing issue, please comment on the issue to express your interest and wait to be assigned by a maintainer. This helps avoid redundant effort in case the issue is already being worked on by another contributor or an NVIDIA team member.
    • Please refer to each component's guideline:

Table of Contents

Cloning the repository

Recommended clone

On native Windows (not WSL), first enable symlink creation, including the Developer Mode registry check. Complete that setup before running the clone command.

From the directory where you want to create the checkout, run:

$ git clone --config core.symlinks=true --config submodule.recurse=true --recurse-submodules https://github.com/NVIDIA/cuda-python.git
$ cd cuda-python

This recipe sets up the repository for source builds and development:

  • core.symlinks=true materializes symlinks during checkout, including the build-critical cuda_core/_build_shared.py.
  • --recurse-submodules initializes the CCCL headers needed to build cuda.core.
  • submodule.recurse=true keeps initialized submodules in sync on git pull, git checkout, and git switch.
  • The clone retains history and release tags for correct package versions. Do not add --depth or --no-tags.

Both configuration settings are stored in the new clone. Next, follow the build and test instructions for cuda.core, cuda.bindings, or cuda.pathfinder.

Updating your clone

From the repository root, run:

$ git pull --recurse-submodules
$ git submodule update --init

The recommended clone configures submodule.recurse=true, so you can omit --recurse-submodules from git pull. The second command initializes any newly added submodules; it is usually a no-op.

If you work from a fork, periodically fetch release tags from NVIDIA upstream so package versions stay current:

$ git fetch --tags https://github.com/NVIDIA/cuda-python.git

Why the clone matters

Every package in this repository derives its version from git tags using setuptools-scm, so how you clone determines whether you can build at all, and whether the version you build is correct. Each package matches its own tag prefix:

Package Tag pattern
cuda-bindings, cuda-python v* (e.g. v13.4.2)
cuda-core cuda-core-v* (e.g. cuda-core-v1.1.0)
cuda-pathfinder cuda-pathfinder-v* (e.g. cuda-pathfinder-v1.6.0)

Each package sets root = ".." in its [tool.setuptools_scm] table, meaning the version is read from the repository root rather than the package directory. A working build therefore needs all of the following:

  1. A real git clone. Source zips and GitHub "Download ZIP" archives have no git metadata and the build fails outright. Tarballs produced by git archive still resolve a version via the .git_archival.txt substitutions in .gitattributes, but they do not include submodule contents, so they cannot compile cuda.core.
  2. The full repository, not just the package subdirectory, because the version lookup walks up to the repository root.
  3. Tags, reaching back at least as far as the most recent tag matching the package you are building. git describe needs to find that tag; the history between it and your checkout must be present too.
  4. The CCCL submodule when building cuda.core. That package compiles against the headers pinned in cuda_core/third_party/cccl.

Symptoms of a bad clone

Only case 3 below reports an error. The first two fail silently, producing a wrong version that surfaces much later as a confusing dependency-resolution or version-check failure:

  1. No tags reachable. The build succeeds and produces a version starting at 0.1.dev: a --depth 1 clone yields 0.1.dev1+g0d22cb444, a full clone made with --no-tags yields 0.1.dev2114+g0d22cb444. Installing cuda-python built this way then fails, because its install_requires pins cuda-bindings to that same bogus version.
  2. Stale tags (a fork that has not fetched upstream in a while): you get a plausible-looking but wrong version, e.g. 13.0.4.dev650+g0d22cb44 when the real latest tag is v13.4.2. Nothing warns you. Note there is no leading v — setuptools-scm strips the tag prefix.
  3. No git metadata (source zip): the build fails with LookupError: setuptools-scm was unable to detect version.

As a last resort — for example when building inside a container that has no git history — you can bypass the lookup entirely:

$ SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_CORE=1.1.0 pip install ./cuda_core

The environment variable is suffixed with the distribution name, uppercased with hyphens replaced by underscores: ..._FOR_CUDA_BINDINGS, ..._FOR_CUDA_CORE, ..._FOR_CUDA_PATHFINDER, ..._FOR_CUDA_PYTHON. Use this only when you genuinely cannot provide tags; it is not a substitute for a correct clone.

Development on Windows

This section collects Windows-specific guidance for contributors working outside of WSL. WSL contributors can follow the Linux flow in the rest of this document.

Enabling git symlinks

The repository contains documentation and metadata symlinks, including CLAUDE.md, cuda_python/README.md, and the per-package .git_archival.txt files. It also currently tracks the build-critical cuda_core/_build_shared.py as a symlink to the canonical cuda_bindings_13/_build_shared.py. The core path may instead be maintained as a byte-for-byte identical copy, but the current tree uses a symlink. With core.symlinks=false, Git checks it out as a plain text file containing the target path, and cuda-core source builds and tests fail.

Activate Developer Mode so Git can create symlinks without Administrator privileges. Then verify that Windows recorded Developer Mode in the machine-wide registry:

$ reg.exe query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /v AllowDevelopmentWithoutDevLicense

The output must show AllowDevelopmentWithoutDevLicense as a REG_DWORD with value 0x1. If the value is missing or differs, Git may report unable to create symlink ...: Permission denied even when Windows Settings shows Developer Mode as enabled. In that case, run the following command once from an Administrator shell:

$ reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /v AllowDevelopmentWithoutDevLicense /t REG_DWORD /d 1 /f

Then return to Recommended clone.

Type stubs for cuda.core

cuda.core is a PEP 561-compliant package: it ships a py.typed marker and .pyi stub files alongside the Cython extensions. The stubs are checked into the repository.

You do not need to run stubgen-pyx manually. A pre-commit hook regenerates the corresponding .pyi files automatically when you commit. The results are then also tested with mypy.

A few things to keep in mind:

  • Do not edit .pyi files by hand. They are regenerated from the Cython sources on every commit that touches those sources; manual edits will be overwritten.
  • Type annotations belong in the .pyx/.pxd source. stubgen-pyx reads Cython type annotations and docstrings to build the stubs, so keeping the source well-annotated is the right way to improve stub quality.
  • To run mypy manually (outside of pre-commit): `python -m mypy --config-file cuda_core/pyproject.toml

Pre-commit

GitHub Actions runs pre-commit checks on Linux and Windows and checks documentation links for pull requests. Check failures must be resolved before merging.

To set yourself up for running pre-commit checks locally and to catch issues before pushing your changes, follow these steps:

  • Install pre-commit with: pip install pre-commit
  • Run this once per checkout: pre-commit install
  • You can manually check all files at any time by running: pre-commit run --all-files

On Windows, these commands work from PowerShell, cmd, or Git Bash.

This command runs all configured hooks (such as linters and formatters) across your repository, letting you review and address issues before committing.

Installing the hook is required, not optional. Some of the automated checks (the SPDX header updater and the .pyi stub generator for cuda_core) only keep the tree consistent if they run on every commit. Relying on manual pre-commit run --all-files invocations means these checks can be skipped between commits, leaving stale headers or out-of-date stubs in the history. If the hook isn't installed, pre-commit run will print a visible warning reminding you to run pre-commit install.

Pixi lockfiles

The repository checks in a pixi.lock next to each pixi.toml. Those lockfiles pin the solved dependency graph used by local pixi workflows and by CI, so they must stay in sync with their manifests and easy to review.

Contributor expectations:

  • If a PR changes a pixi.toml, update the corresponding pixi.lock in the same PR. Regenerating one lockfile:

    $ pixi lock --manifest-path cuda_core

    Use --manifest-path . for the repository-root environment. Regenerate with the pixi version pinned in ci/pixi-version.env: different pixi versions write different canonical forms, such as the pixi.lock format version or the generated platform alias names, and CI requires the committed bytes to match what the pinned version produces. The pin must remain at least 0.71.0, which supplies the content-addressed source-build cache used by CI and writes the repository's version 7 lockfiles.

  • If a PR does not intentionally change pixi dependencies or metadata, do not include unrelated lockfile churn. pixi run can refresh a stale lockfile implicitly; revert that noise unless the refresh is the point of the change.

  • If a lockfile changes, the PR description should briefly say why.

  • Isolate large dependency refreshes from feature work when possible. Prefer a dedicated lockfile-only PR over mixing solver churn into an unrelated change.

CI enforces the contract: pixi lock --check fails when a committed lockfile is stale, and pixi source-build jobs run with PIXI_LOCKED=true so they install from the committed lock rather than updating it during the job. If either check fails, regenerate and commit the affected lockfile.

The freshness check additionally fails when the check itself rewrote a lockfile. pixi lock --check accepts a lock whose solution is current but whose bytes are not canonical for the pinned pixi version, quietly normalizing the file instead, which leaves every later pixi run rewriting the committed lockfile.

A scheduled workflow (CI: pixi lockfile refresh) runs pixi update --no-install for every workspace in one job and opens one PR with all changed lockfiles, so broad dependency churn is reviewed as maintenance rather than landing inside unrelated feature work. The workflow can also be dispatched manually. Both lockfile workflows resolve their workspace lists through ci/tools/list_pixi_workspaces.py, which derives the inventory from the committed manifests, so a newly added workspace is picked up without editing a workflow.

Refresh PRs use GITHUB_TOKEN. After one opens, a maintainer with write access must first select Approve workflows to run in the merge box, then assign themselves to the PR. Approval starts the queued pull_request runs; the human-generated assigned event creates the required PR has assignee, labels, and milestone pull_request_target check and gives the PR a clear owner.

A future GitHub App integration could trigger both pull_request and pull_request_target workflows automatically. See GitHub's token event documentation for the current behavior.

Secret Scanning

The secret-scan-trufflehog pre-commit hook scans staged files and installs TruffleHog into its own environment on first run, on Linux, macOS, and Windows. If it flags a secret, remove it before committing, or contact a maintainer if it's a false positive. Secrets are also scanned server-side in CI.

Signing Your Work

Contributions to files licensed under Apache 2.0 must be certified under the Developer Certificate of Origin (DCO). Sign off every commit with the -s option:

git commit -s -m "Describe your change"

Git uses your configured name and email address to add a trailer like this to the commit message:

Signed-off-by: Your Name <your.email@example.com>

Use your real name and an email address associated with your contribution. The sign-off certifies that you have the right to submit the contribution under the DCO below. DCO sign-off is separate from the cryptographic commit signing described in the next section; both requirements apply.

Code signing

This repository implements a security check to prevent the CI system from running untrusted code. A part of the security check consists of checking if the git commits are signed. Please ensure that your commits are signed following GitHub’s instruction.

Developer Certificate of Origin (DCO)

Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

CI infrastructure overview

The CUDA Python project uses a comprehensive CI pipeline that builds, tests, and releases multiple components across different platforms. This section provides a visual overview of our CI infrastructure to help contributors understand the build and release process.

CI Pipeline Flow

The CI pipeline diagram is maintained as Mermaid source in 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

Code coverage

Code coverage reports are produced nightly and posted to GitHub Pages.

Known limitations: Code coverage is only run on Linux x86_64 with an a100 GPU. We plan to add more platform and GPU coverage in the future.


1: The cuda-python meta package shares the same license and the contributing guidelines as those of cuda-bindings.