Skip to content

Commit 5fb3fb6

Browse files
Copilotleofang
andauthored
CI: Upload wheels to release artifacts and auto-create release drafts (#873)
* Initial plan * Add shared script and update workflows to upload wheels to release artifacts Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> * Fix script argument validation and add comprehensive tests Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> * Auto-create release draft if none exists for given tag Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> * Make run-id and component required inputs in release-upload workflow Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: leofang <5534781+leofang@users.noreply.github.com>
1 parent 51ec60b commit 5fb3fb6

3 files changed

Lines changed: 112 additions & 16 deletions

File tree

.github/workflows/release-upload.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ on:
1010
git-tag:
1111
type: string
1212
required: true
13+
run-id:
14+
description: "The GHA run ID that generated validated artifacts"
15+
type: string
16+
required: true
17+
component:
18+
description: "Component to download wheels for"
19+
type: string
20+
required: true
1321

1422
concurrency:
1523
# Concurrency group that uses the workflow name and PR number if available
@@ -63,3 +71,16 @@ jobs:
6371
--clobber "${{ inputs.git-tag }}"
6472
--repo "${{ github.repository }}"
6573
release/*
74+
75+
- name: Download and Upload Wheels
76+
env:
77+
GH_TOKEN: ${{ github.token }}
78+
run: |
79+
# Use the shared script to download wheels
80+
./ci/tools/download-wheels "${{ inputs.run-id }}" "${{ inputs.component }}" "${{ github.repository }}" "release/wheels"
81+
82+
# Upload wheels to the release
83+
if [[ -d "release/wheels" && $(ls -A release/wheels 2>/dev/null | wc -l) -gt 0 ]]; then
84+
echo "Uploading wheels to release ${{ inputs.git-tag }}"
85+
gh release upload --clobber "${{ inputs.git-tag }}" --repo "${{ github.repository }}" release/wheels/*
86+
fi

.github/workflows/release.yml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
name: "CI: Release"
66

7-
description: Manually-triggered release workflow. Must have a release note in the draft state and the release commit tagged.
7+
description: Manually-triggered release workflow. Creates a release draft if one doesn't exist for the given tag, or uses existing draft.
88

99
on:
1010
workflow_dispatch:
@@ -46,7 +46,12 @@ jobs:
4646
check-tag:
4747
runs-on: ubuntu-latest
4848
steps:
49-
- name: Check if draft exists for the tag
49+
- name: Checkout Source
50+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
51+
with:
52+
fetch-depth: 0
53+
54+
- name: Check or create draft release for the tag
5055
env:
5156
GH_TOKEN: ${{ github.token }}
5257
run: |
@@ -62,7 +67,7 @@ jobs:
6267
found=0
6368
for idx in ${!tags[@]}; do
6469
if [[ "${tags[$idx]}" == "${{ inputs.git-tag }}" ]]; then
65-
echo "found ${{ inputs.git-tag }}"
70+
echo "found existing release for ${{ inputs.git-tag }}"
6671
found=1
6772
if [[ "${is_draft[$idx]}" != "true" ]]; then
6873
echo "the release note is not in draft state"
@@ -72,8 +77,8 @@ jobs:
7277
fi
7378
done
7479
if [[ "$found" == 0 ]]; then
75-
echo "the release is not yet tagged"
76-
exit 1
80+
echo "no release found for ${{ inputs.git-tag }}, creating draft release"
81+
gh release create "${{ inputs.git-tag }}" --draft --repo "${{ github.repository }}" --title "Release ${{ inputs.git-tag }}" --notes "Release ${{ inputs.git-tag }}"
7782
fi
7883
7984
doc:
@@ -105,6 +110,8 @@ jobs:
105110
uses: ./.github/workflows/release-upload.yml
106111
with:
107112
git-tag: ${{ inputs.git-tag }}
113+
run-id: ${{ inputs.run-id }}
114+
component: ${{ inputs.component }}
108115

109116
publish-wheels:
110117
name: Publish wheels
@@ -117,21 +124,14 @@ jobs:
117124
permissions:
118125
id-token: write
119126
steps:
127+
- name: Checkout Source
128+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
129+
120130
- name: Download component wheels
121131
env:
122132
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123133
run: |
124-
gh run download ${{ inputs.run-id }} -p "${{ inputs.component }}*" -R ${{ github.repository }}
125-
mkdir dist
126-
for p in ${{ inputs.component }}*
127-
do
128-
# exclude cython test artifacts
129-
if [[ "${p}" == *-tests ]]; then
130-
continue
131-
fi
132-
mv ${p}/*.whl dist/
133-
done
134-
rm -rf ${{ inputs.component }}*
134+
./ci/tools/download-wheels "${{ inputs.run-id }}" "${{ inputs.component }}" "${{ github.repository }}" "dist"
135135
136136
- name: Publish package distributions to PyPI
137137
if: ${{ inputs.wheel-dst == 'pypi' }}

ci/tools/download-wheels

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env bash
2+
3+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
# A utility script to download component wheels from GitHub Actions artifacts.
8+
# This script reuses the same logic that was in release.yml to maintain consistency.
9+
10+
set -euo pipefail
11+
12+
# Check required arguments
13+
if [[ $# -lt 3 ]]; then
14+
echo "Usage: $0 <run-id> <component> <repository> [output-dir]" >&2
15+
echo " run-id: The GitHub Actions run ID containing the artifacts" >&2
16+
echo " component: The component name pattern to download (e.g., cuda-core, cuda-bindings)" >&2
17+
echo " repository: The GitHub repository (e.g., NVIDIA/cuda-python)" >&2
18+
echo " output-dir: Optional output directory (default: ./dist)" >&2
19+
exit 1
20+
fi
21+
22+
RUN_ID="$1"
23+
COMPONENT="$2"
24+
REPOSITORY="$3"
25+
OUTPUT_DIR="${4:-./dist}"
26+
27+
# Ensure we have a GitHub token
28+
if [[ -z "${GH_TOKEN:-}" ]]; then
29+
echo "Error: GH_TOKEN environment variable is required"
30+
exit 1
31+
fi
32+
33+
echo "Downloading wheels for component: $COMPONENT from run: $RUN_ID"
34+
35+
# Download component wheels using the same logic as release.yml
36+
if [[ "$COMPONENT" == "all" ]]; then
37+
# Download all component patterns
38+
gh run download "$RUN_ID" -p "cuda-*" -R "$REPOSITORY"
39+
else
40+
gh run download "$RUN_ID" -p "${COMPONENT}*" -R "$REPOSITORY"
41+
fi
42+
43+
# Create output directory
44+
mkdir -p "$OUTPUT_DIR"
45+
46+
# Process downloaded artifacts
47+
for p in cuda-*
48+
do
49+
if [[ ! -d "$p" ]]; then
50+
continue
51+
fi
52+
53+
# exclude cython test artifacts
54+
if [[ "${p}" == *-tests ]]; then
55+
echo "Skipping test artifact: $p"
56+
continue
57+
fi
58+
59+
# If we're not downloading "all", only process matching component
60+
if [[ "$COMPONENT" != "all" && "$p" != ${COMPONENT}* ]]; then
61+
continue
62+
fi
63+
64+
echo "Processing artifact: $p"
65+
# Move wheel files to output directory
66+
if [[ -d "$p" ]]; then
67+
find "$p" -name "*.whl" -exec mv {} "$OUTPUT_DIR/" \;
68+
fi
69+
done
70+
71+
# Clean up artifact directories
72+
rm -rf cuda-*
73+
74+
echo "Downloaded wheels to: $OUTPUT_DIR"
75+
ls -la "$OUTPUT_DIR"

0 commit comments

Comments
 (0)