diff --git a/.github/actions/get-cudaq-wheels/action.yaml b/.github/actions/get-cudaq-wheels/action.yaml new file mode 100644 index 0000000..b27450a --- /dev/null +++ b/.github/actions/get-cudaq-wheels/action.yaml @@ -0,0 +1,142 @@ +name: Get CUDAQ wheels +description: 'Either restore CUDAQ wheels from cache or build them' + +inputs: + repo: + description: 'CUDAQ repository.' + required: true + ref: + description: 'The branch, tag or SHA to checkout.' + required: true + token: + description: 'CUDAQ repository access token.' + default: '' + required: false + pr-number: + description: 'Unique pull request identifier.' + default: '' + required: false + save-build: + description: 'Indicates whether to save the build' + default: 'false' + required: false + lookup-only: + description: 'Check if a cache entry exists without downloading the cache' + default: 'false' + required: false + platform: + description: 'Platform (amd64 or arm64)' + default: '' + required: true +outputs: + found-cache: + description: 'A boolean value to indicate that a cache entry was found.' + value: ${{ steps.check-cache.outputs.valid }} + +runs: + using: "composite" + steps: + # ========================================================================== + # Try to restore from cache + # ========================================================================== + + - name: Create CUDAQ wheel cache key + id: cudaq-wheels-key + env: + # This are a list of files that when changed should require a new cudaq build + to_hash: | + .github/actions/get-cudaq-wheels/** + .cudaq_version + run: | + hash=${{ hashFiles(format('{0}', env.to_hash)) }} + echo "main=cudaq-wheels-${{ matrix.platform }}-${{ inputs.ref }}-$hash" >> $GITHUB_OUTPUT + if [[ -n "${{ inputs.pr-number }}" ]]; then + echo "pr=-pr${{ inputs.pr-number }}" >> $GITHUB_OUTPUT + fi + shell: bash --noprofile --norc -euo pipefail {0} + + - name: Try to restoring CUDAQ wheels from cache + id: restore-cudaq-wheels + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: false + path: /cudaq-wheels + key: ${{ steps.cudaq-wheels-key.outputs.main }}${{ steps.cudaq-wheels-key.outputs.pr }} + restore-keys: ${{ steps.cudaq-wheels-key.outputs.main }} + lookup-only: ${{ inputs.lookup-only }} + + # The restore action could find a partial match using the `restore-keys`. In such cases + # it would still report `cache-hit` as false, but would load the cache from the partial + # one. Thus, we need to check whether the cache is valid by other means. + - name: Check if cache is valid + id: check-cache + run: | + if [[ "${{ steps.restore-cudaq-wheels.outputs.cache-matched-key }}" == "" ]]; then + echo "valid=false" >> $GITHUB_OUTPUT + else + echo "valid=true" >> $GITHUB_OUTPUT + fi + shell: bash --noprofile --norc -euo pipefail {0} + + # ========================================================================== + # Build CUDAQ wheels + # ========================================================================== + + - name: Login to GitHub CR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Get CUDAQ code + if: steps.check-cache.outputs.valid == 'false' && inputs.lookup-only == 'false' + uses: actions/checkout@v4 + with: + repository: ${{ inputs.repo }} + ref: ${{ inputs.ref }} + path: cudaq + set-safe-directory: true + + - name: Set up context for buildx + run: | + docker context create builder_context + shell: bash --noprofile --norc -euo pipefail {0} + + - name: Set up buildx runner + uses: docker/setup-buildx-action@v3 + with: + endpoint: builder_context + driver-opts: network=host + + # FIXME handle multiple versions (search for 3.10) + - name: Build wheel + id: wheel_build + uses: docker/build-push-action@v5 + with: + context: cudaq + file: ./cudaq/docker/release/cudaq.wheel.Dockerfile + build-args: | + base_image=ghcr.io/nvidia/cuda-quantum-devdeps:manylinux-${{ inputs.platform }}-cu12.0-gcc11-main + release_version=0.0.0 + python_version=3.10 + outputs: /cudaq-wheels + + - name: Upload wheel + uses: actions/upload-artifact@v4 + with: + name: pycudaq-${{ inputs.platform }}-3.10 + path: /cudaq-wheels + retention-days: 1 + if-no-files-found: error + + # ========================================================================== + # Store CUDAQ wheels cache + # ========================================================================== + + - name: Store CUDAQ wheels in the cache + if: steps.check-cache.outputs.valid == 'false' && inputs.save-build == 'true' && inputs.lookup-only == 'false' + uses: actions/cache/save@v4 + with: + path: /cudaq-wheels + key: ${{ steps.cudaq-wheels-key.outputs.main }}${{ steps.cudaq-wheels-key.outputs.pr }} diff --git a/.github/workflows/pr_workflow.yaml b/.github/workflows/pr_workflow.yaml index a946910..46d1ca5 100644 --- a/.github/workflows/pr_workflow.yaml +++ b/.github/workflows/pr_workflow.yaml @@ -122,6 +122,48 @@ jobs: save-ccache: false platform: ${{ matrix.platform }} + # This is similar to build-cudaq, but it builds the CUDA-Q wheels. It uses a + # CUDA-Q docker file to perform the build and therefore cannot run in a + # container context, so it cannot be done in the same build-cudaq job. + build-cudaq-wheels: + name: Build CUDAQ wheels + needs: [check-changes] + if: needs.check-changes.outputs.build-cudaq == 'true' + strategy: + fail-fast: false + matrix: + platform: ['amd64', 'arm64'] + runs-on: ${{ startsWith(github.repository, 'NVIDIA/cudaqx') && format('linux-{0}-cpu32', matrix.platform) || 'ubuntu-latest' }} + permissions: + actions: write + contents: read + pull-requests: read + steps: + - name: Get code + uses: actions/checkout@v4 + with: + set-safe-directory: true + + - name: Lookup PR info + id: get-pr-info + env: + GH_TOKEN: ${{ github.token }} + uses: nv-gha-runners/get-pr-info@main + + - name: Get required CUDAQ version + id: get-cudaq-version + uses: ./.github/actions/get-cudaq-version + + - name: Get CUDAQ wheels + uses: ./.github/actions/get-cudaq-wheels + with: + repo: ${{ steps.get-cudaq-version.outputs.repo }} + ref: ${{ steps.get-cudaq-version.outputs.ref }} + token: ${{ secrets.CUDAQ_ACCESS_TOKEN }} + pr-number: ${{ fromJSON(steps.get-pr-info.outputs.pr-info).number }} + save-build: true + platform: ${{ matrix.platform }} + build-docs: name: Docs needs: [check-changes, build-cudaq]