diff --git a/.github/workflows/release-upload.yml b/.github/workflows/release-upload.yml index 402f0acfd3..f7d6306fc2 100644 --- a/.github/workflows/release-upload.yml +++ b/.github/workflows/release-upload.yml @@ -10,6 +10,14 @@ on: git-tag: type: string required: true + run-id: + description: "The GHA run ID that generated validated artifacts" + type: string + required: true + component: + description: "Component to download wheels for" + type: string + required: true concurrency: # Concurrency group that uses the workflow name and PR number if available @@ -63,3 +71,16 @@ jobs: --clobber "${{ inputs.git-tag }}" --repo "${{ github.repository }}" release/* + + - name: Download and Upload Wheels + env: + GH_TOKEN: ${{ github.token }} + run: | + # Use the shared script to download wheels + ./ci/tools/download-wheels "${{ inputs.run-id }}" "${{ inputs.component }}" "${{ github.repository }}" "release/wheels" + + # Upload wheels to the release + if [[ -d "release/wheels" && $(ls -A release/wheels 2>/dev/null | wc -l) -gt 0 ]]; then + echo "Uploading wheels to release ${{ inputs.git-tag }}" + gh release upload --clobber "${{ inputs.git-tag }}" --repo "${{ github.repository }}" release/wheels/* + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c483d1be8b..6e423b5565 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ name: "CI: Release" -description: Manually-triggered release workflow. Must have a release note in the draft state and the release commit tagged. +description: Manually-triggered release workflow. Creates a release draft if one doesn't exist for the given tag, or uses existing draft. on: workflow_dispatch: @@ -46,7 +46,12 @@ jobs: check-tag: runs-on: ubuntu-latest steps: - - name: Check if draft exists for the tag + - name: Checkout Source + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Check or create draft release for the tag env: GH_TOKEN: ${{ github.token }} run: | @@ -62,7 +67,7 @@ jobs: found=0 for idx in ${!tags[@]}; do if [[ "${tags[$idx]}" == "${{ inputs.git-tag }}" ]]; then - echo "found ${{ inputs.git-tag }}" + echo "found existing release for ${{ inputs.git-tag }}" found=1 if [[ "${is_draft[$idx]}" != "true" ]]; then echo "the release note is not in draft state" @@ -72,8 +77,8 @@ jobs: fi done if [[ "$found" == 0 ]]; then - echo "the release is not yet tagged" - exit 1 + echo "no release found for ${{ inputs.git-tag }}, creating draft release" + gh release create "${{ inputs.git-tag }}" --draft --repo "${{ github.repository }}" --title "Release ${{ inputs.git-tag }}" --notes "Release ${{ inputs.git-tag }}" fi doc: @@ -105,6 +110,8 @@ jobs: uses: ./.github/workflows/release-upload.yml with: git-tag: ${{ inputs.git-tag }} + run-id: ${{ inputs.run-id }} + component: ${{ inputs.component }} publish-wheels: name: Publish wheels @@ -117,21 +124,14 @@ jobs: permissions: id-token: write steps: + - name: Checkout Source + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: Download component wheels env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh run download ${{ inputs.run-id }} -p "${{ inputs.component }}*" -R ${{ github.repository }} - mkdir dist - for p in ${{ inputs.component }}* - do - # exclude cython test artifacts - if [[ "${p}" == *-tests ]]; then - continue - fi - mv ${p}/*.whl dist/ - done - rm -rf ${{ inputs.component }}* + ./ci/tools/download-wheels "${{ inputs.run-id }}" "${{ inputs.component }}" "${{ github.repository }}" "dist" - name: Publish package distributions to PyPI if: ${{ inputs.wheel-dst == 'pypi' }} diff --git a/ci/tools/download-wheels b/ci/tools/download-wheels new file mode 100755 index 0000000000..05509bfc0a --- /dev/null +++ b/ci/tools/download-wheels @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +# A utility script to download component wheels from GitHub Actions artifacts. +# This script reuses the same logic that was in release.yml to maintain consistency. + +set -euo pipefail + +# Check required arguments +if [[ $# -lt 3 ]]; then + echo "Usage: $0 [output-dir]" >&2 + echo " run-id: The GitHub Actions run ID containing the artifacts" >&2 + echo " component: The component name pattern to download (e.g., cuda-core, cuda-bindings)" >&2 + echo " repository: The GitHub repository (e.g., NVIDIA/cuda-python)" >&2 + echo " output-dir: Optional output directory (default: ./dist)" >&2 + exit 1 +fi + +RUN_ID="$1" +COMPONENT="$2" +REPOSITORY="$3" +OUTPUT_DIR="${4:-./dist}" + +# Ensure we have a GitHub token +if [[ -z "${GH_TOKEN:-}" ]]; then + echo "Error: GH_TOKEN environment variable is required" + exit 1 +fi + +echo "Downloading wheels for component: $COMPONENT from run: $RUN_ID" + +# Download component wheels using the same logic as release.yml +if [[ "$COMPONENT" == "all" ]]; then + # Download all component patterns + gh run download "$RUN_ID" -p "cuda-*" -R "$REPOSITORY" +else + gh run download "$RUN_ID" -p "${COMPONENT}*" -R "$REPOSITORY" +fi + +# Create output directory +mkdir -p "$OUTPUT_DIR" + +# Process downloaded artifacts +for p in cuda-* +do + if [[ ! -d "$p" ]]; then + continue + fi + + # exclude cython test artifacts + if [[ "${p}" == *-tests ]]; then + echo "Skipping test artifact: $p" + continue + fi + + # If we're not downloading "all", only process matching component + if [[ "$COMPONENT" != "all" && "$p" != ${COMPONENT}* ]]; then + continue + fi + + echo "Processing artifact: $p" + # Move wheel files to output directory + if [[ -d "$p" ]]; then + find "$p" -name "*.whl" -exec mv {} "$OUTPUT_DIR/" \; + fi +done + +# Clean up artifact directories +rm -rf cuda-* + +echo "Downloaded wheels to: $OUTPUT_DIR" +ls -la "$OUTPUT_DIR" \ No newline at end of file