Skip to content

Commit 11c27cf

Browse files
authored
chore: add copy-pr-bot (#52)
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
1 parent 55ff970 commit 11c27cf

5 files changed

Lines changed: 268 additions & 78 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright (c) 2024-2026, NVIDIA CORPORATION.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: "Detect Changes"
16+
description: >
17+
Detect which file categories changed using dorny/paths-filter.
18+
Outputs boolean flags that downstream jobs use to decide whether to run.
19+
20+
outputs:
21+
src:
22+
description: "'true' if any file under src/ changed"
23+
value: ${{ steps.filter.outputs.src }}
24+
test:
25+
description: "'true' if any file under test/ changed"
26+
value: ${{ steps.filter.outputs.test }}
27+
28+
runs:
29+
using: "composite"
30+
steps:
31+
# Requires actions/checkout to have been run first so that
32+
# (a) this action.yml can be found, and
33+
# (b) dorny/paths-filter can diff against git history on push events.
34+
- name: Detect changed paths
35+
id: filter
36+
uses: dorny/paths-filter@v3
37+
with:
38+
filters: |
39+
src:
40+
- 'src/**'
41+
test:
42+
- 'test/**'

.github/copy-pr-bot.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# copy-pr-bot configuration
2+
# See: https://docs.gha-runners.nvidia.com/platform/apps/copy-pr-bot/
3+
#
4+
# This bot automates the pull request testing strategy required for NVIDIA's
5+
# self-hosted runners. Trusted PRs are automatically copied to pull-request/*
6+
# branches, which trigger CI workflows via push events.
7+
#
8+
# - auto_sync_draft: false → Draft PRs won't auto-trigger CI (saves GPU resources)
9+
# - auto_sync_ready: true → Ready-for-review PRs auto-trigger CI
10+
11+
enabled: true
12+
auto_sync_draft: false
13+
auto_sync_ready: true

.github/workflows/README.md

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,54 +5,75 @@ This directory contains GitHub Actions workflows for CI/CD automation.
55
## Workflows Overview
66

77

8-
| Workflow | Trigger | Description |
9-
| -------------------------------------------------- | ------------------- | ----------------------------------------------------- |
10-
| [ci.yml](ci.yml) | Push to `main`, PRs | Lint, format check, and tests with coverage |
11-
| [conventional-commit.yml](conventional-commit.yml) | PRs | Validates PR titles follow conventional commit format |
12-
| [copyright-check.yml](copyright-check.yml) | PRs | Validates NVIDIA copyright headers on Python files |
13-
| [dco-assistant.yml](dco-assistant.yml) | PRs, Comments | Manages DCO signing via PR comments |
14-
| [release.yml](release.yml) | Manual dispatch | Builds and publishes package to PyPI |
15-
| [secrets-detector.yml](secrets-detector.yml) | PRs | Scans for accidentally committed secrets |
8+
| Workflow | Trigger | Description |
9+
| -------------------------------------------------- | ------------------------------------- | ---------------------------------------------------- |
10+
| [ci-checks.yml](ci-checks.yml) | Push to `main`, PRs, manual | Format, lint, typecheck, and unit tests (CPU) |
11+
| [gpu-tests.yml](gpu-tests.yml) | Push to `main`/`pull-request/*`, manual | GPU E2E tests (A100) |
12+
| [conventional-commit.yml](conventional-commit.yml) | PRs | Validates PR titles follow conventional commit format |
13+
| [copyright-check.yml](copyright-check.yml) | Push to `main`/`pull-request/*` | Validates NVIDIA copyright headers on Python files |
14+
| [docs.yml](docs.yml) | Push to `main` (docs paths) | Builds and deploys documentation to GitHub Pages |
15+
| [release.yml](release.yml) | Manual dispatch | Builds and publishes package to PyPI |
16+
| [secrets-detector.yml](secrets-detector.yml) | PRs | Scans for accidentally committed secrets |
1617

1718

19+
## Pull Request Testing (copy-pr-bot)
20+
21+
GPU tests (`gpu-tests.yml`) run on NVIDIA self-hosted runners, which block `pull_request`-triggered jobs. They use the [copy-pr-bot](https://docs.gha-runners.nvidia.com/platform/apps/copy-pr-bot/) pattern instead:
22+
23+
1. When a PR is opened by a trusted user with trusted changes, `copy-pr-bot` automatically copies the code to a `pull-request/<number>` branch
24+
2. The push to `pull-request/<number>` triggers the GPU workflow
25+
3. Untrusted PRs require a vetter to comment `/ok to test <SHA>` before GPU tests run
26+
4. Draft PRs do **not** auto-sync (`auto_sync_draft: false`), saving GPU resources
27+
28+
Configuration: [`.github/copy-pr-bot.yaml`](../copy-pr-bot.yaml)
29+
30+
CPU checks (`ci-checks.yml`) run on GitHub-hosted `ubuntu-latest` runners and use standard `pull_request` triggers.
31+
1832
## Workflow Diagram
1933

2034
```mermaid
2135
flowchart LR
2236
subgraph triggers [Triggers]
2337
push[Push to main]
24-
pr[Pull Request]
25-
comment[PR Comment]
38+
cpb[copy-pr-bot push to pull-request/*]
39+
pr[Pull Request event]
2640
manual[Manual Dispatch]
2741
end
2842
29-
subgraph ci [CI Workflow]
43+
subgraph ci [CI Checks - GitHub-hosted runners]
44+
changes_ci[Detect Changes]
45+
format[Format]
3046
lint[Lint]
31-
format[Format Check]
3247
typecheck[Typecheck]
33-
test[Unit Tests]
34-
test --> coverage[Coverage Report]
48+
unit[Unit Tests]
49+
ci_status[CI Status]
50+
changes_ci --> format & lint & typecheck & unit
51+
format & lint & typecheck & unit --> ci_status
52+
end
53+
54+
subgraph gpu [GPU Tests - on-prem runners]
55+
changes_gpu[Detect Changes]
56+
e2e[GPU E2E Tests]
57+
gpu_status[GPU CI Status]
58+
changes_gpu --> e2e --> gpu_status
3559
end
3660
3761
subgraph compliance [Compliance Workflows]
38-
dco[DCO Assistant]
3962
conventional[Conventional Commit]
4063
secrets[Secrets Detector]
4164
copyright[Copyright Check]
4265
end
4366
4467
subgraph release [Release Workflow]
4568
buildWheel[Build Wheel]
46-
bumpVersion[Bump Version]
4769
publishPyPI[Publish to PyPI]
4870
ghRelease[GitHub Release]
4971
slackNotify[Slack Notification]
5072
end
5173
52-
push --> ci
53-
pr --> ci
54-
pr --> compliance
55-
comment --> dco
74+
push --> ci & gpu
75+
cpb --> gpu & copyright
76+
pr --> ci & conventional & secrets
5677
manual --> release
5778
5879
buildWheel --> publishPyPI --> ghRelease --> slackNotify
@@ -63,17 +84,37 @@ flowchart LR
6384
release -.->|reuses| FW-CI-templates
6485
```
6586

66-
## CI Workflow
87+
## CI Checks Workflow
88+
89+
The `ci-checks.yml` workflow runs on every push to `main` and on pull requests:
90+
91+
- **Detect Changes**: Uses `dorny/paths-filter` to skip jobs when only non-source files change
92+
- **Format**: Verifies code formatting with `ruff format --check`
93+
- **Lint**: Runs `ruff check` linting
94+
- **Typecheck**: Runs `ty` type checks
95+
- **Unit Tests**: Runs pytest with coverage
96+
- **CI Status**: Aggregation job -- single required check for branch protection
97+
98+
All jobs run on `ubuntu-latest` (GitHub-hosted).
99+
100+
## GPU Tests Workflow
101+
102+
The `gpu-tests.yml` workflow runs on pushes to `main` and `pull-request/*` branches (via copy-pr-bot):
103+
104+
- **GPU E2E Tests**: Runs end-to-end tests on `linux-amd64-gpu-a100-latest-1` (A100) with a 60-minute job timeout and 45-minute step timeout
105+
- **GPU CI Status**: Aggregation job -- single required check for branch protection
67106

68-
The main CI workflow runs on every push to `main` and on pull requests:
107+
### Runners
69108

70-
- **Lint**: Runs `ruff check` and `ty` type checks
71-
- **Format Check**: Verifies code formatting with `ruff format --check`
72-
- **Test**: Runs pytest with coverage across Python 3.11. more to come.
109+
| Workflow | Job | Runner Label | Type |
110+
| --- | --- | --- | --- |
111+
| CI Checks | All jobs | `ubuntu-latest` | GitHub-hosted |
112+
| GPU Tests | GPU E2E Tests | `linux-amd64-gpu-a100-latest-1` | NVIDIA self-hosted GPU (A100) |
113+
| GPU Tests | Detect Changes, GPU CI Status | `linux-amd64-cpu4` | NVIDIA self-hosted CPU (4-core) |
73114

74115
### Coverage
75116

76-
Coverage reports are uploaded as artifacts.
117+
Coverage reports are uploaded as artifacts from both workflows.
77118

78119
## Compliance Workflows
79120

.github/workflows/ci-checks.yml

Lines changed: 28 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,14 @@ jobs:
4444
permissions:
4545
contents: read
4646
pull-requests: read
47-
# Set job outputs to values from filter step
4847
outputs:
49-
src: ${{ steps.filter.outputs.src }}
50-
test: ${{ steps.filter.outputs.test }}
48+
src: ${{ steps.changes.outputs.src }}
49+
test: ${{ steps.changes.outputs.test }}
5150
steps:
5251
- uses: actions/checkout@v4
53-
if: github.event_name == 'push'
54-
- uses: dorny/paths-filter@v3
55-
id: filter
56-
with:
57-
filters: |
58-
src:
59-
- 'src/**'
60-
test:
61-
- 'test/**'
52+
- name: Detect changes
53+
id: changes
54+
uses: ./.github/actions/detect-changes
6255

6356
format:
6457
name: Format
@@ -172,45 +165,28 @@ jobs:
172165
token: ${{ secrets.CODECOV_TOKEN }}
173166
fail_ci_if_error: false
174167

175-
gpu-e2e-test:
176-
name: GPU E2E Tests - (Python ${{ matrix.python-version }})
177-
needs: changes
178-
if: ${{ needs.changes.outputs.src == 'true' || needs.changes.outputs.test == 'true' || github.event_name == 'workflow_dispatch' }}
179-
runs-on: linux-amd64-gpu-a100-latest-1
180-
strategy:
181-
fail-fast: false
182-
matrix:
183-
python-version: ["3.11"]
168+
# ---------------------------------------------------------------------------
169+
# Single required status check for branch protection.
170+
# Aggregates results from all upstream jobs so that skipped jobs (due to
171+
# path filtering) don't block merge.
172+
# ---------------------------------------------------------------------------
173+
ci-status:
174+
name: CI Status
175+
if: always() && !cancelled()
176+
needs: [changes, format, lint, typecheck, unit-test]
177+
runs-on: ubuntu-latest
184178
steps:
185-
- name: checkout
186-
uses: actions/checkout@v4
187-
with:
188-
fetch-depth: 0
189-
190-
- name: Setup Python environment
191-
id: setup
192-
uses: ./.github/actions/setup-python-env
193-
with:
194-
python-version: ${{ matrix.python-version }}
195-
bootstrap-tools: "true"
196-
197-
- name: Run unit tests with coverage
179+
- name: Check job results
198180
run: |
199-
make bootstrap-nss cu128
200-
make test-e2e
201-
202-
- name: Upload coverage report
203-
uses: actions/upload-artifact@v4
204-
if: matrix.python-version == '3.11'
205-
with:
206-
name: coverage-report
207-
path: coverage.xml
208-
retention-days: 30
209-
210-
- name: Upload coverage to Codecov
211-
uses: codecov/codecov-action@v5
212-
if: matrix.python-version == '3.11'
213-
with:
214-
files: coverage.xml
215-
token: ${{ secrets.CODECOV_TOKEN }}
216-
fail_ci_if_error: false
181+
echo "changes: ${{ needs.changes.result }}"
182+
echo "format: ${{ needs.format.result }}"
183+
echo "lint: ${{ needs.lint.result }}"
184+
echo "typecheck: ${{ needs.typecheck.result }}"
185+
echo "unit-test: ${{ needs.unit-test.result }}"
186+
187+
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
188+
echo "::error::One or more CI jobs failed"
189+
exit 1
190+
fi
191+
192+
echo "All CI jobs passed (or were skipped)."

0 commit comments

Comments
 (0)