CI: Release #178
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: "CI: Release" | |
| # 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. The dry-run mode validates the release path | |
| # without publishing to external release surfaces. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| component: | |
| description: "Component to release" | |
| required: true | |
| type: choice | |
| options: | |
| - cuda-core | |
| - cuda-bindings | |
| - cuda-pathfinder | |
| - cuda-python | |
| release-action: | |
| description: "What to run" | |
| required: true | |
| type: choice | |
| options: | |
| - dry-run | |
| - full-release | |
| default: dry-run | |
| git-tag: | |
| description: "The release git tag" | |
| required: true | |
| type: string | |
| backport-git-tag: | |
| description: "Mainline cuda-bindings/cuda-python only: planned backport tag, or 'not planned'. Leave blank for backport releases." | |
| required: false | |
| type: string | |
| default: "" | |
| run-id: | |
| description: "The GHA run ID that generated validated artifacts (optional - auto-detects successful tag-triggered CI run for git-tag)" | |
| required: false | |
| type: string | |
| default: "" | |
| dry-run-docs-branch: | |
| description: "Dry-run only: optional gh-pages-* branch to receive generated docs, for example gh-pages-dry-run" | |
| required: false | |
| type: string | |
| default: "" | |
| defaults: | |
| run: | |
| shell: bash --noprofile --norc -xeuo pipefail {0} | |
| jobs: | |
| determine-run-id: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| run-id: ${{ steps.lookup-run-id.outputs.run-id }} | |
| steps: | |
| - name: Checkout Source | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| # fetch-depth: 0 is required so the lookup-run-id script can access all git tags | |
| fetch-depth: 0 | |
| - name: Determine Run ID | |
| id: lookup-run-id | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if [[ -n "${{ inputs.run-id }}" ]]; then | |
| echo "Using provided run ID: ${{ inputs.run-id }}" | |
| RUN_ID="${{ inputs.run-id }}" | |
| 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" | |
| fi | |
| echo "run-id=$RUN_ID" >> "$GITHUB_OUTPUT" | |
| check-tag: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Source | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check release tag and draft state | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_ACTION: ${{ inputs.release-action }} | |
| RELEASE_GIT_TAG: ${{ inputs.git-tag }} | |
| DRY_RUN_DOCS_BRANCH: ${{ inputs.dry-run-docs-branch }} | |
| run: | | |
| if [[ "$RELEASE_ACTION" == "full-release" && -n "$DRY_RUN_DOCS_BRANCH" ]]; then | |
| echo "error: dry-run-docs-branch is only valid with release-action=dry-run" >&2 | |
| exit 1 | |
| fi | |
| if [[ "$RELEASE_ACTION" == "dry-run" && -n "$DRY_RUN_DOCS_BRANCH" && ! "$DRY_RUN_DOCS_BRANCH" =~ ^gh-pages-[[:alnum:]._/-]+$ ]]; then | |
| echo "error: dry-run-docs-branch must be a non-production gh-pages-* branch" >&2 | |
| exit 1 | |
| fi | |
| if [[ "$RELEASE_ACTION" == "dry-run" ]]; then | |
| git rev-parse --verify "${RELEASE_GIT_TAG}^{commit}" | |
| echo "Dry-run selected; not checking or creating a GitHub release draft." | |
| exit 0 | |
| fi | |
| 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 | |
| if [[ "${tags[$idx]}" == "${{ inputs.git-tag }}" ]]; then | |
| 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" | |
| exit 1 | |
| fi | |
| break | |
| fi | |
| done | |
| if [[ "$found" == 0 ]]; then | |
| 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 | |
| check-release-notes: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Source | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| # - name: Self-test release-notes checker | |
| # run: | | |
| # pip install pytest | |
| # pytest ci/tools/tests | |
| - name: Check versioned release notes exist | |
| run: | | |
| python ci/tools/check_release_notes.py \ | |
| --git-tag "${{ inputs.git-tag }}" \ | |
| --component "${{ inputs.component }}" \ | |
| --backport-git-tag "${{ inputs.backport-git-tag }}" | |
| doc: | |
| name: Build release docs | |
| if: ${{ github.repository_owner == 'nvidia' }} | |
| # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages | |
| permissions: | |
| id-token: write | |
| contents: write | |
| pull-requests: write | |
| needs: | |
| - check-tag | |
| - check-release-notes | |
| - determine-run-id | |
| secrets: inherit | |
| uses: ./.github/workflows/build-docs.yml | |
| with: | |
| component: ${{ inputs.component }} | |
| git-tag: ${{ inputs.git-tag }} | |
| run-id: ${{ needs.determine-run-id.outputs.run-id }} | |
| is-release: true | |
| deploy-docs: ${{ inputs.release-action == 'full-release' || inputs.dry-run-docs-branch != '' }} | |
| docs-branch: ${{ (inputs.release-action == 'dry-run' && inputs.dry-run-docs-branch) || 'gh-pages' }} | |
| upload-archive: | |
| name: Validate release artifacts | |
| permissions: | |
| contents: write | |
| needs: | |
| - check-tag | |
| - check-release-notes | |
| - determine-run-id | |
| - doc | |
| secrets: inherit | |
| uses: ./.github/workflows/release-upload.yml | |
| with: | |
| git-tag: ${{ inputs.git-tag }} | |
| run-id: ${{ needs.determine-run-id.outputs.run-id }} | |
| component: ${{ inputs.component }} | |
| dry-run: ${{ inputs.release-action == 'dry-run' }} | |
| publish-testpypi: | |
| name: Publish wheels to TestPyPI | |
| if: ${{ inputs.release-action == 'full-release' }} | |
| runs-on: ubuntu-latest | |
| needs: | |
| - check-tag | |
| - check-release-notes | |
| - determine-run-id | |
| - doc | |
| environment: | |
| name: testpypi | |
| url: https://test.pypi.org/p/${{ 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 TestPyPI | |
| uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| publish-pypi: | |
| name: Publish wheels to PyPI | |
| if: ${{ inputs.release-action == 'full-release' }} | |
| runs-on: ubuntu-latest | |
| needs: | |
| - determine-run-id | |
| - publish-testpypi | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/${{ 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@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 | |
| # TODO: add another job to make the release leave the draft state? |