Skip to content

ci: activate dependency-aware package builds and tests - #2467

Open
kkraus14 wants to merge 3 commits into
NVIDIA:mainfrom
kkraus14:agent/selective-ci-orchestration
Open

ci: activate dependency-aware package builds and tests#2467
kkraus14 wants to merge 3 commits into
NVIDIA:mainfrom
kkraus14:agent/selective-ci-orchestration

Conversation

@kkraus14

@kkraus14 kkraus14 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Important

Stack completion: PR 4 of 4. #2464, #2465, and #2466 have merged. This is the final PR in the stack.

Merge order: #2464 (merged) -> #2465 (merged) -> #2466 (merged) -> #2467 (this PR). The branch lives in the kkraus14 fork and targets upstream main.

What

  • Classify package source and test changes with dorny/paths-filter v4.0.3, pinned to its full commit SHA.
  • Resolve the copied PR branch's actual base from PR metadata and use the same merge-base commit for path filtering and reusable artifacts.
  • Express the four-package build/test dependency closure directly in GitHub Actions job outputs, without a custom Python or shell path-analysis helper.
  • Resolve a successful main CI run for the PR's exact merge-base commit and reuse its complete wheel set.
  • Fall back to a full build and test whenever the baseline is absent, expired, or incomplete; non-PR runs also remain full runs.
  • Treat shared build/orchestration infrastructure and any unknown path as requiring the full pipeline.
  • Treat shared wheel-test infrastructure as test-only, running every package suite without rebuilding package wheels.
  • Leave Pixi manifests to the dedicated source-build workflow, which already watches **/pixi.toml and **/pixi.lock.
  • Avoid package work for docs, pre-commit, bot, release-only, coverage-only, scheduled-only, and developer-tool paths that are handled independently or are not consumed by the conditioned jobs.
  • Pass the effective package selections and baseline identifiers into the split build, sdist, and wheel-test workflows.
  • Gate whole platform jobs when no selected package needs them, while retaining the universal Linux artifact path needed by docs and downstream jobs.
  • Extend the final status gate so intentional selective skips are accepted but skipped jobs caused by upstream failures still fail CI.

Expected package behavior

  • cuda_core source change: build/test cuda-core and retest the downstream cuda-python metapackage; skip cuda-bindings and cuda-pathfinder builds/tests.
  • cuda_bindings source change: build/test cuda-bindings, cuda-core, and cuda-python; skip cuda-pathfinder.
  • cuda_pathfinder source change: build/test the full downstream closure.
  • Package test/example or shared test-infrastructure change: run only the affected package suite, or every suite for shared test infrastructure, without rebuilding package wheels.
  • Root or package Pixi manifest change: skip wheel builds/tests and rely on the dedicated Pixi source-build check.
  • Package docs or repository prose only: skip package builds/tests while retaining docs coverage.
  • Shared build/orchestration infrastructure or an unknown path: run the full pipeline, including cuda-core API checks.

The cuda-python wheel is reused for a core-only change rather than rebuilt because development cuda-python wheels exactly pin cuda-bindings; rebuilding it would force an otherwise unnecessary bindings rebuild.

Validation

  • Rebased onto current main after ci: add selective wheel test plumbing #2466 merged; the net diff is only .github/workflows/ci.yml.
  • Full targeted pre-commit hooks, including YAML and GitHub Actions lint.
  • Verified the pinned paths-filter action uses NUL-delimited git diff --no-renames, preserving deletion and cross-package rename handling.
  • Traced every current top-level path and CI helper to the build, sdist, wheel-test, docs, pre-commit, Pixi, coverage, release, or scheduled job that consumes it.
  • Independently reviewed the refined filter and dependency truth table for package source, package tests, shared test infrastructure, Pixi, docs, pre-commit, shared build infrastructure, unknown, mixed-path, non-PR, and unavailable-baseline cases.
  • No planner or matrix unit tests are added; selective behavior can be exercised iteratively with small package-only PR changes after this stack lands.

Implements the remaining dependency-aware build/test selection requested in #299.

@kkraus14 kkraus14 added this to the cuda.bindings next milestone Jul 31, 2026
@kkraus14 kkraus14 added enhancement Any code-related improvements CI/CD CI/CD infrastructure labels Jul 31, 2026
@copy-pr-bot

copy-pr-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@kkraus14
kkraus14 force-pushed the agent/selective-ci-orchestration branch 2 times, most recently from edfbf97 to 66445a4 Compare July 31, 2026 16:22
@github-actions github-actions Bot added the cuda.core Everything related to the cuda.core module label Jul 31, 2026
@kkraus14
kkraus14 force-pushed the agent/selective-ci-orchestration branch 5 times, most recently from a06ad8b to adf15bd Compare August 3, 2026 17:24
@kkraus14
kkraus14 force-pushed the agent/selective-ci-orchestration branch from adf15bd to ba702ca Compare August 13, 2026 19:36
@kkraus14 kkraus14 self-assigned this Aug 13, 2026
@kkraus14
kkraus14 force-pushed the agent/selective-ci-orchestration branch from ba702ca to 85f173e Compare August 13, 2026 20:30
@kkraus14
kkraus14 force-pushed the agent/selective-ci-orchestration branch from 85f173e to 9cf09a9 Compare August 14, 2026 01:38
@kkraus14
kkraus14 marked this pull request as ready for review August 14, 2026 02:54
@github-actions

Copy link
Copy Markdown

@mdboom mdboom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this review will be long, and will negate some of the earlier decisions in the stack. I should have evaluated the whole stack at once to see where this is going.

I think this is fundamentally a maintenance nightmare and way more verbose than it needs to be. Rather than passing 9 new parameters between jobs, there should be a single, structured JSON object that is passed around. I am thinking something like (using YAML because it's easier to type, but it would be JSON).

modules:
  test_helpers:
    needs_build: false
    needs_test: false
  pathfinder:
    needs_build: false
    needs_test: false
    build_depends: []
    test_depends: ["test_helpers"]
  bindings:
    needs_build: false
    needs_test: false
    build_depends: ["pathfinder"]
    test_depends: ["test_helpers"]
  core:
    needs_build: false
    needs_test: false
    build_depends: ["bindings"]
    test_depends: ["test_helpers"]
baseline_run_id: 123456789
baseline_sha: c0ffee

This makes verbose things that are sensitive to modules coming and going like:

${{ needs.detect-changes.outputs.build_pathfinder == 'true' ||
                               needs.detect-changes.outputs.build_bindings == 'true' ||
                               needs.detect-changes.outputs.build_core == 'true' ||
                               needs.detect-changes.outputs.build_python == 'true' }}

into:

Object.values(data.modules).some(m => m.needs_test)

Rather than this:

      test_bindings: >-
        ${{ steps.baseline.outputs.available != 'true' ||
            steps.filter.outputs.changes == '' ||
            steps.filter.outputs.force_all == 'true' ||
            steps.filter.outputs.all_tests == 'true' ||
            steps.filter.outputs.pathfinder_source == 'true' ||
            steps.filter.outputs.bindings_source == 'true' ||
            steps.filter.outputs.bindings_tests == 'true' ||
            steps.filter.outputs.test_helpers == 'true' }}

You can do:

(name, modules, seen = new Set()) => {
  if (seen.has(name)) return false;
  seen.add(name);
  const m = modules[name];
  return m.needs_test || (m.depends || []).some(dep => moduleNeedsTest(dep, modules, seen));
};

rather than hardcoding the dependencies into a bunch of large boolean expressions.

We can pre-compute values for downstream consumers so this kind of logic is only written in one place.

data["needs_build"] = Object.values(data.modules).some(m => m.needs_build)

# or Python
data["needs_build"] = any(mod["needs_build"] for mod in data["modules"])

This object would get passed between jobs as "workplan" (or some better name).

Given my other comment that dorny/paths-filter seems like the wrong tool for the job, I would propose:

A standalone Python script that does the analysis of changes and builds this workplan object, including as many pre-computed dependendent information as we can to make downstream consumers of this information concise and prevent long complicated expressions being repeated everywhere.

This script has the side benefit of forming the basis of a future local helper to test only what is needed (which all of this GHA work doesn't move us toward).

(I confirmed that outputs can have a size maximum of 1MB which should be more than enough for this.)

Comment thread .github/workflows/ci.yml

has_match() {
grep -qE "$1" <<< "$changed" && echo true || echo false
- name: Classify changed paths

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded path lists here is going to be a maintenance nightmare.

Each subsection follows a pretty similar pattern, though. If there were a better way of specifying things to reduce duplication, I think that would be fine. (For example, if we could globally exclude all AGENTS.md).

As it stands, if this is the best we can do with dorny/paths-filter, I think we should reach for a different tool or write our own in Python. Upside of that, too, is we could build a "test everything we need to" local script on top of it, whereas this is stuck in the GHA silo.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally my agent had implemented a Python script to encapsulate the logic of what paths / files impact which builds / tests and I found that script even more difficult to understand and maintain than maintaining a list of paths like this that an agent could easily update (and we should include in the AGENTS.md in hindsight). Let me see what I can do to simplify things here, because I agree this is going to be an ongoing annoyance.

For what it's worth, I think this overall work is going to be a stopgap as opposed to the end solution, where moving to something like moon (https://moonrepo.dev/moon) or bazel is probably a better longer term solution that makes things more "target" oriented as opposed to path / file oriented.

Comment thread .github/workflows/ci.yml
Comment on lines +266 to +267
# cuda_python/README.md is a symlink to this packaging input.
- 'README.md'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the comment.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cuda_python/README.md symlinks the root README.md and it gets included in the Python package that we produce, so changes to the top level README need to trigger a rebuild of the metapackage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/CD CI/CD infrastructure cuda.core Everything related to the cuda.core module enhancement Any code-related improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants