Skip to content

Commit fba3b4d

Browse files
committed
Merge main into agent/selective-ci-orchestration
2 parents 44c6a3a + 8d526ec commit fba3b4d

77 files changed

Lines changed: 2769 additions & 737 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/ISSUE_TEMPLATE/release_checklist.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ body:
2020
- label: File an internal nvbug to communicate test plan & release schedule with QA
2121
- label: Ensure all pending PRs are reviewed, tested, and merged
2222
- label: Check (or update if needed) the dependency requirements
23+
- label: Sweep deprecations whose stated removal version has arrived (`grep -rn 'deprecated::' cuda_core/cuda`) and remove any that are due
2324
- label: "Finalize the doc update, including release notes (\"Note: Touching docstrings/type annotations in code is OK during code freeze, apply your best judgement!\")"
2425
- label: Update the docs for the new version
2526
- label: Create a public release tag

‎.github/RELEASE-core.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,27 @@ requirements are current.
6666

6767
---
6868

69+
## Sweep deprecations whose removal version has arrived
70+
71+
Deprecated APIs are marked in the source with a Sphinx `deprecated`
72+
directive naming the version that introduced the deprecation, and their
73+
docstrings state the version in which they will be removed. Find them all
74+
with:
75+
76+
```console
77+
$ grep -rn 'deprecated::' cuda_core/cuda
78+
```
79+
80+
For each hit, check the stated removal version against the version being
81+
released. If the release has reached or passed it, remove the API, its
82+
runtime `DeprecationWarning`, and any tests asserting that warning.
83+
84+
This must happen *before* the release tag is cut. Removals are breaking
85+
changes, so they are only permitted at a major-version boundary per the
86+
[support policy](https://nvidia.github.io/cuda-python/cuda-core/latest/support.html).
87+
88+
---
89+
6990
## Finalize the doc update, including release notes
7091

7192
Review every PR included in the release. For each one, check whether new

‎.github/workflows/build-wheel.yml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,10 @@ jobs:
568568
569569
OLD_BRANCH=$(yq '.backport_branch' ci/versions.yml)
570570
OLD_ARTIFACT_PATTERN="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda*-${{ inputs.host-platform }}-*[0-9a-f]"
571-
LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id --branch "${OLD_BRANCH}" NVIDIA/cuda-python "CI")
571+
LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id \
572+
--branch "${OLD_BRANCH}" \
573+
--artifact "${OLD_ARTIFACT_PATTERN}" \
574+
NVIDIA/cuda-python "CI")
572575
PREV_BINDINGS_DIR="cuda_bindings/dist-prev"
573576
574577
gh run download \

‎.github/workflows/ci-nightly.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
python -m pytest -v --noconftest ci/tools/tests
5050
5151
find-wheels:
52+
if: ${{ github.repository_owner == 'nvidia' }}
5253
runs-on: ubuntu-latest
5354
outputs:
5455
RUN_ID: ${{ steps.find.outputs.run_id }}
@@ -309,7 +310,7 @@ jobs:
309310

310311
checks:
311312
name: Nightly check status
312-
if: always()
313+
if: ${{ always() && github.repository_owner == 'nvidia' }}
313314
runs-on: ubuntu-latest
314315
needs:
315316
- test-ci-tools-for-release

‎.github/workflows/pr-metadata-check.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ on:
1717
- reopened
1818
- ready_for_review
1919

20+
permissions:
21+
pull-requests: read
22+
2023
jobs:
2124
check-metadata:
2225
name: PR has assignee, labels, and milestone

‎.github/workflows/test-wheel-linux.yml‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,15 @@ jobs:
216216
217217
OLD_BRANCH=${{ needs.compute-matrix.outputs.OLD_BRANCH }}
218218
OLD_ARTIFACT_PATTERN="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda*-${{ inputs.host-platform }}-*[0-9a-f]"
219-
LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id --branch "${OLD_BRANCH}" NVIDIA/cuda-python "CI")
219+
LOOKUP_ARGS=(
220+
--branch "${OLD_BRANCH}"
221+
--artifact "${OLD_ARTIFACT_PATTERN}"
222+
)
223+
if ${{ env.TEST_PYTHON == 'true' }}; then
224+
LOOKUP_ARGS+=(--artifact cuda-python-wheel)
225+
fi
226+
LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id \
227+
"${LOOKUP_ARGS[@]}" NVIDIA/cuda-python "CI")
220228
221229
gh run download \
222230
"${LATEST_PRIOR_RUN_ID}" \

‎.github/workflows/test-wheel-windows.yml‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,15 @@ jobs:
196196
run: |
197197
OLD_BRANCH=$(yq '.backport_branch' ci/versions.yml)
198198
OLD_ARTIFACT_PATTERN="cuda-bindings-python${PYTHON_VERSION_FORMATTED}-cuda*-${{ inputs.host-platform }}-*[0-9a-f]"
199-
LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id --branch "${OLD_BRANCH}" NVIDIA/cuda-python "CI")
199+
LOOKUP_ARGS=(
200+
--branch "${OLD_BRANCH}"
201+
--artifact "${OLD_ARTIFACT_PATTERN}"
202+
)
203+
if ${{ env.TEST_PYTHON == 'true' }}; then
204+
LOOKUP_ARGS+=(--artifact cuda-python-wheel)
205+
fi
206+
LATEST_PRIOR_RUN_ID=$(./ci/tools/lookup-run-id \
207+
"${LOOKUP_ARGS[@]}" NVIDIA/cuda-python "CI")
200208
201209
gh run download \
202210
"${LATEST_PRIOR_RUN_ID}" \

‎AGENTS.md‎

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,30 @@ guide for package-specific conventions and workflows.
1414

1515
# Pull requests
1616

17-
**Never push branches or commits to the canonical upstream repository. Treat
18-
it as read-only.** Branch creation and pushes for pull-request work must go to
19-
an approved fork associated with the contributor. The fork may be owned by the
20-
contributor's personal account or by an organization.
21-
22-
Before pushing, run `git remote -v` and confirm that the intended push remote
23-
points to a fork of the pull-request base, not to the base repository itself.
24-
Compare complete `OWNER/REPOSITORY` names; do not rely on remote names such as
25-
`origin` or `upstream`, or on the owner alone. Do not use `git push upstream`
26-
or any command that writes to the upstream remote.
17+
Treat the canonical upstream repository as read-only by default. For normal
18+
pull-request work, push branches and commits to an approved fork associated
19+
with the contributor. The fork may be owned by the contributor's personal
20+
account or by an organization.
21+
22+
Before any push, run `git remote -v` and verify the complete
23+
`OWNER/REPOSITORY` of the intended destination. For normal pull-request work,
24+
confirm that the destination is a fork of the pull-request base. Do not rely
25+
on remote names such as `origin` or `upstream`, or on the owner alone.
26+
27+
An upstream push is allowed when the user explicitly requests it and provides
28+
a rationale for why the upstream repository is needed, such as testing
29+
`.github/workflows`, triggering CI from a designated upstream ref, or other
30+
infrastructure work.
31+
32+
For an authorized upstream push, verify the exact source and destination refs
33+
against the user's request. If the repository and refspec are unambiguous,
34+
proceed; do not require the user to perform the push manually solely because
35+
the destination is upstream.
36+
37+
Authorization is limited to the requested ref update. It does not authorize
38+
pushing to a default or protected branch, force-pushing, creating tags, or
39+
deleting refs unless the user separately and explicitly requests those
40+
operations.
2741

2842

2943
# General

‎ci/tools/lookup-run-id‎

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#
99
# Two modes:
1010
# --tag <tag> Find the successful CI run triggered by a tag push.
11-
# --branch <branch> Find the latest successful CI run on a branch.
11+
# --branch <branch> Find the latest qualifying successful CI run on a branch.
1212
#
1313
# Outputs the run ID on stdout. All diagnostic messages go to stderr.
1414
# When --head-sha is passed, a second line with the run's head SHA is printed.
@@ -24,11 +24,14 @@ Usage:
2424
Options:
2525
--tag <tag> Find run by git tag (requires local git repo with the tag)
2626
--branch <branch> Find latest successful run on the given branch
27+
--artifact <glob> Require an unexpired artifact matching this shell glob
28+
(repeatable; branch mode only)
2729
--head-sha Also print the run's head commit SHA (second line)
2830
2931
Examples:
3032
$0 --tag v13.0.1 NVIDIA/cuda-python
3133
$0 --branch main NVIDIA/cuda-python
34+
$0 --branch 12.9.x --artifact 'cuda-bindings-python313-*' NVIDIA/cuda-python
3235
$0 --branch main --head-sha NVIDIA/cuda-python "CI"
3336
EOF
3437
exit 1
@@ -38,15 +41,21 @@ EOF
3841
MODE=""
3942
REF=""
4043
HEAD_SHA_FLAG=0
44+
ARTIFACT_PATTERNS=()
4145

4246
while [[ $# -gt 0 ]]; do
4347
case "${1}" in
4448
--tag)
49+
[[ $# -ge 2 ]] || usage
4550
[[ -n "${MODE}" && "${MODE}" != "tag" ]] && { echo "Error: --tag and --branch are mutually exclusive" >&2; exit 1; }
4651
MODE="tag"; REF="${2}"; shift 2 ;;
4752
--branch)
53+
[[ $# -ge 2 ]] || usage
4854
[[ -n "${MODE}" && "${MODE}" != "branch" ]] && { echo "Error: --tag and --branch are mutually exclusive" >&2; exit 1; }
4955
MODE="branch"; REF="${2}"; shift 2 ;;
56+
--artifact)
57+
[[ $# -ge 2 ]] || usage
58+
ARTIFACT_PATTERNS+=("${2}"); shift 2 ;;
5059
--head-sha)
5160
HEAD_SHA_FLAG=1; shift ;;
5261
-h|--help)
@@ -69,6 +78,11 @@ WORKFLOW_NAME="${1:-CI}"
6978

7079
if [[ -z "${REPOSITORY}" ]]; then usage; fi
7180

81+
if [[ "${MODE}" != "branch" && ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then
82+
echo "Error: --artifact is only supported with --branch" >&2
83+
exit 1
84+
fi
85+
7286
# ── Prerequisite checks ──
7387
if [[ -z "${GH_TOKEN:-}" ]]; then
7488
echo "Error: GH_TOKEN environment variable is required" >&2
@@ -86,28 +100,90 @@ done
86100
if [[ "${MODE}" == "branch" ]]; then
87101
echo "Looking up latest successful '${WORKFLOW_NAME}' run on branch: ${REF}" >&2
88102

89-
RUN_ID=$(gh run list \
90-
-b "${REF}" \
91-
-L 1 \
92-
-w "${WORKFLOW_NAME}" \
93-
-s success \
94-
-R "${REPOSITORY}" \
95-
--json databaseId \
96-
| jq -r '.[0].databaseId // empty')
103+
RUN_DATA=$(gh run list \
104+
--repo "${REPOSITORY}" \
105+
--branch "${REF}" \
106+
--workflow "${WORKFLOW_NAME}" \
107+
--status success \
108+
--json databaseId,workflowName,status,conclusion,headSha,headBranch,createdAt,url \
109+
--limit 100)
110+
111+
CANDIDATE_RUNS=$(echo "${RUN_DATA}" | jq -r \
112+
--arg branch "${REF}" '
113+
map(select(
114+
.headBranch == $branch
115+
and .conclusion == "success"
116+
))
117+
| sort_by(.createdAt, .databaseId)
118+
| reverse
119+
| .[]
120+
| [.databaseId, .headSha, .createdAt]
121+
| @tsv
122+
')
123+
124+
if [[ -z "${CANDIDATE_RUNS}" ]]; then
125+
echo "Error: No successful '${WORKFLOW_NAME}' run found on branch '${REF}'" >&2
126+
exit 1
127+
fi
128+
129+
if [[ ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then
130+
echo "Requiring unexpired artifacts matching:" >&2
131+
printf ' - %s\n' "${ARTIFACT_PATTERNS[@]}" >&2
132+
fi
133+
134+
RUN_ID=""
135+
HEAD_SHA=""
136+
while IFS= read -r candidate; do
137+
IFS=$'\t' read -r candidate_id candidate_sha candidate_created_at <<< "${candidate}"
138+
139+
missing_patterns=()
140+
if [[ ${#ARTIFACT_PATTERNS[@]} -gt 0 ]]; then
141+
if ! ARTIFACT_NAMES=$(gh api --paginate \
142+
"repos/${REPOSITORY}/actions/runs/${candidate_id}/artifacts?per_page=100" \
143+
--jq '.artifacts[] | select(.expired == false) | .name'); then
144+
echo "Error: Failed to list artifacts for run ${candidate_id}" >&2
145+
exit 1
146+
fi
147+
148+
for pattern in "${ARTIFACT_PATTERNS[@]}"; do
149+
pattern_matched=0
150+
while IFS= read -r artifact_name; do
151+
# The caller supplies a shell glob, so the RHS must remain unquoted.
152+
# shellcheck disable=SC2053
153+
if [[ -n "${artifact_name}" && "${artifact_name}" == ${pattern} ]]; then
154+
pattern_matched=1
155+
break
156+
fi
157+
done <<< "${ARTIFACT_NAMES}"
158+
if [[ ${pattern_matched} == 0 ]]; then
159+
missing_patterns+=("${pattern}")
160+
fi
161+
done
162+
fi
163+
164+
if [[ ${#missing_patterns[@]} -gt 0 ]]; then
165+
echo "Skipping run ${candidate_id} (${candidate_created_at}); missing unexpired artifact(s): ${missing_patterns[*]}" >&2
166+
continue
167+
fi
168+
169+
RUN_ID="${candidate_id}"
170+
HEAD_SHA="${candidate_sha}"
171+
break
172+
done <<< "${CANDIDATE_RUNS}"
97173

98174
if [[ -z "${RUN_ID}" ]]; then
99-
echo "Error: No successful '${WORKFLOW_NAME}' run found on branch '${REF}'" >&2
175+
echo "Error: No successful '${WORKFLOW_NAME}' run on branch '${REF}' has all required artifacts" >&2
100176
exit 1
101177
fi
102178

103179
echo "Found run ID: ${RUN_ID}" >&2
104180
echo "${RUN_ID}"
105181

106182
if [[ "${HEAD_SHA_FLAG}" == 1 ]]; then
107-
HEAD_SHA=$(gh run view "${RUN_ID}" \
108-
-R "${REPOSITORY}" \
109-
--json headSha \
110-
| jq -r '.headSha')
183+
if [[ -z "${HEAD_SHA}" ]]; then
184+
echo "Error: Run ${RUN_ID} has no head SHA" >&2
185+
exit 1
186+
fi
111187
echo "Head SHA: ${HEAD_SHA}" >&2
112188
echo "${HEAD_SHA}"
113189
fi

0 commit comments

Comments
 (0)