From d7a77498b2e76015a4f4b9b98d41e81b54f95f60 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:34:52 -0400 Subject: [PATCH 1/2] ci: consolidate release publishing flow Run the release workflow through TestPyPI and PyPI in one pass so release managers do not need to rerun it with a destination selector. Update the release docs to match the streamlined flow. Made-with: Cursor --- .github/RELEASE-core.md | 37 ++++------------- .github/workflows/release.yml | 77 +++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 58 deletions(-) diff --git a/.github/RELEASE-core.md b/.github/RELEASE-core.md index dcb81ab8bc..071f1fcd64 100644 --- a/.github/RELEASE-core.md +++ b/.github/RELEASE-core.md @@ -123,41 +123,22 @@ Pushing the tag triggers a CI run automatically. Monitor it in the ## Upload wheels to PyPI -This is a two-stage process: first publish to TestPyPI, verify, then -publish to PyPI. - -### Stage 1: TestPyPI +This is a single `CI: Release` workflow run with two sequential stages: +publish to TestPyPI, then publish the same wheel set to PyPI. 1. Go to **Actions > CI: Release** and run the workflow with: - **Component**: `cuda-core` - **The release git tag**: `cuda-core-v0.6.0` - - **The GHA run ID that generated validated artifacts**: This is the - run ID of the successful tag-triggered CI run from the previous step. - You can find it in the URL when viewing the run in the Actions tab - (e.g. `https://github.com/NVIDIA/cuda-python/actions/runs/123456789` - — the run ID is `123456789`). - - **Which wheel index to publish to**: `testpypi` - The workflow automatically looks up the successful tag-triggered CI run - for the selected release tag. - -2. Wait for the workflow to complete. - -3. Verify the TestPyPI upload by installing and running tests from a - checked-out copy of the repository: - ```bash - pip install -i https://test.pypi.org/simple/ \ - --extra-index-url https://pypi.org/simple/ \ - cuda-core==0.6.0 - cd cuda_core/tests && pytest - ``` - -### Stage 2: PyPI + The workflow automatically looks up the successful tag-triggered CI run + for the selected release tag. If needed, you can also provide the + optional run ID input explicitly. -Once TestPyPI verification passes, rerun the same workflow with: -- **Which wheel index to publish to**: `pypi` +2. Wait for the workflow to complete. It will: + - publish the selected wheels to TestPyPI + - publish the same wheel set to PyPI -After completion, verify: +3. After completion, verify the final PyPI upload: ```bash pip install cuda-core==0.6.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce88820d51..8c38ec5469 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,9 @@ name: "CI: Release" -# Manually-triggered release workflow. Creates a release draft if one doesn't exist for the given tag, or uses existing draft. +# Manually-triggered release workflow. Creates a release draft if one doesn't exist +# for the given tag, or uses an existing draft, then publishes the selected wheels +# to TestPyPI followed by PyPI. on: workflow_dispatch: @@ -28,13 +30,6 @@ on: required: false type: string default: "" - wheel-dst: - description: "Which wheel index to publish to?" - required: true - type: choice - options: - - testpypi - - pypi defaults: run: @@ -57,10 +52,15 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - echo "Auto-detecting successful tag-triggered run ID for tag: ${{ inputs.git-tag }}" - RUN_ID=$(./ci/tools/lookup-run-id "${{ inputs.git-tag }}" "${{ github.repository }}") - echo "Auto-detected run ID: $RUN_ID" - echo "run-id=$RUN_ID" >> $GITHUB_OUTPUT + if [[ -n "${{ inputs.run-id }}" ]]; then + echo "Using provided run ID: ${{ inputs.run-id }}" + echo "run-id=${{ inputs.run-id }}" >> "$GITHUB_OUTPUT" + else + echo "Auto-detecting successful tag-triggered run ID for tag: ${{ inputs.git-tag }}" + RUN_ID=$(./ci/tools/lookup-run-id "${{ inputs.git-tag }}" "${{ github.repository }}") + echo "Auto-detected run ID: $RUN_ID" + echo "run-id=$RUN_ID" >> "$GITHUB_OUTPUT" + fi check-tag: runs-on: ubuntu-latest @@ -74,17 +74,11 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - tags= - for i in $(gh release list -R ${{ github.repository }} --json tagName --jq '.[]| .tagName'); do - tags+=( $i ) - done - is_draft= - for i in $(gh release list -R ${{ github.repository }} --json isDraft --jq '.[]| .isDraft'); do - is_draft+=( $i ) - done + mapfile -t tags < <(gh release list -R "${{ github.repository }}" --json tagName --jq '.[] | .tagName') + mapfile -t is_draft < <(gh release list -R "${{ github.repository }}" --json isDraft --jq '.[] | .isDraft') found=0 - for idx in ${!tags[@]}; do + for idx in "${!tags[@]}"; do if [[ "${tags[$idx]}" == "${{ inputs.git-tag }}" ]]; then echo "found existing release for ${{ inputs.git-tag }}" found=1 @@ -134,16 +128,16 @@ jobs: run-id: ${{ needs.determine-run-id.outputs.run-id }} component: ${{ inputs.component }} - publish-wheels: - name: Publish wheels + publish-testpypi: + name: Publish wheels to TestPyPI runs-on: ubuntu-latest needs: - check-tag - determine-run-id - doc environment: - name: ${{ inputs.wheel-dst }} - url: https://${{ (inputs.wheel-dst == 'testpypi' && 'test.') || '' }}pypi.org/p/${{ inputs.component }}/ + name: testpypi + url: https://test.pypi.org/${{ inputs.component != 'all' && format('p/{0}/', inputs.component) || '' }} permissions: id-token: write steps: @@ -160,14 +154,37 @@ jobs: run: | ./ci/tools/validate-release-wheels "${{ inputs.git-tag }}" "${{ inputs.component }}" "dist" - - name: Publish package distributions to PyPI - if: ${{ inputs.wheel-dst == 'pypi' }} - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 - - name: Publish package distributions to TestPyPI - if: ${{ inputs.wheel-dst == 'testpypi' }} uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 with: repository-url: https://test.pypi.org/legacy/ + publish-pypi: + name: Publish wheels to PyPI + runs-on: ubuntu-latest + needs: + - determine-run-id + - publish-testpypi + environment: + name: pypi + url: https://pypi.org/${{ inputs.component != 'all' && format('p/{0}/', inputs.component) || '' }} + permissions: + id-token: write + steps: + - name: Checkout Source + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Download component wheels + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ./ci/tools/download-wheels "${{ needs.determine-run-id.outputs.run-id }}" "${{ inputs.component }}" "${{ github.repository }}" "dist" + + - name: Validate wheel versions for release tag + run: | + ./ci/tools/validate-release-wheels "${{ inputs.git-tag }}" "${{ inputs.component }}" "dist" + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + # TODO: add another job to make the release leave the draft state? From 27dc68dd7cc0c60fd18bd578ebde014e0c5c5cc0 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:05:14 -0400 Subject: [PATCH 2/2] ci: preserve automatic release run detection Keep the consolidated release flow aligned with upstream's automatic run lookup after the rebase so the workflow inputs and release docs stay consistent. Made-with: Cursor --- .github/RELEASE-core.md | 3 +-- .github/workflows/release.yml | 13 ++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/RELEASE-core.md b/.github/RELEASE-core.md index 071f1fcd64..01e182c76e 100644 --- a/.github/RELEASE-core.md +++ b/.github/RELEASE-core.md @@ -131,8 +131,7 @@ publish to TestPyPI, then publish the same wheel set to PyPI. - **The release git tag**: `cuda-core-v0.6.0` The workflow automatically looks up the successful tag-triggered CI run - for the selected release tag. If needed, you can also provide the - optional run ID input explicitly. + for the selected release tag. 2. Wait for the workflow to complete. It will: - publish the selected wheels to TestPyPI diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c38ec5469..97d58d8ae5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,15 +52,10 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - if [[ -n "${{ inputs.run-id }}" ]]; then - echo "Using provided run ID: ${{ inputs.run-id }}" - echo "run-id=${{ inputs.run-id }}" >> "$GITHUB_OUTPUT" - else - echo "Auto-detecting successful tag-triggered run ID for tag: ${{ inputs.git-tag }}" - RUN_ID=$(./ci/tools/lookup-run-id "${{ inputs.git-tag }}" "${{ github.repository }}") - echo "Auto-detected run ID: $RUN_ID" - echo "run-id=$RUN_ID" >> "$GITHUB_OUTPUT" - fi + echo "Auto-detecting successful tag-triggered run ID for tag: ${{ inputs.git-tag }}" + RUN_ID=$(./ci/tools/lookup-run-id "${{ inputs.git-tag }}" "${{ github.repository }}") + echo "Auto-detected run ID: $RUN_ID" + echo "run-id=$RUN_ID" >> "$GITHUB_OUTPUT" check-tag: runs-on: ubuntu-latest