Inject version commmit to .cpp to avoid cache misses and recompiles o… #30
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
| on: | ||
| workflow_call: | ||
| inputs: | ||
| platforms: | ||
| type: string | ||
| required: false | ||
| default: linux/amd64 | ||
| devdeps_image: | ||
| required: false | ||
| type: string | ||
| devdeps_cache: | ||
| required: false | ||
| type: string | ||
| devdeps_archive: | ||
| required: false | ||
| type: string | ||
| ompidev_image: | ||
| required: false | ||
| type: string | ||
| build_docs: | ||
| required: false | ||
| type: string | ||
| cuda_version: | ||
| required: false | ||
| type: string | ||
| description: The CUDA version with which the development dependencies were built. | ||
| environment: | ||
| required: false | ||
| type: string | ||
| secrets: | ||
| NGC_CREDENTIALS: | ||
| description: 'Credentials for deployments to NGC.' | ||
| required: false | ||
| DOCKERHUB_USERNAME: | ||
| required: true | ||
| DOCKERHUB_READONLY_TOKEN: | ||
| required: true | ||
| name: Docker images | ||
| jobs: | ||
| metadata: | ||
| name: Metadata | ||
| runs-on: ubuntu-latest | ||
| permissions: {} | ||
| outputs: | ||
| runner: ${{ steps.info.outputs.runner }} | ||
| platform_tag: ${{ steps.info.outputs.platform_tag }} | ||
| build_docs: ${{ steps.info.outputs.build_docs }} | ||
| push_to_ngc: ${{ steps.info.outputs.push_to_ngc }} | ||
| is_versioned: ${{ steps.info.outputs.is_versioned }} | ||
| # Needed to define metadata depending on whether an environment secret is defined. | ||
| environment: | ||
| name: ${{ inputs.environment || 'default' }} | ||
| url: ${{ vars.deployment_url || format('https://github.com/{0}', github.repository) }} | ||
| steps: | ||
| - id: info | ||
| run: | | ||
| is_versioned=${{ github.ref_type == 'tag' || startsWith(github.ref_name, 'releases/') || startsWith(github.ref_name, 'staging/') }} | ||
| echo "is_versioned=$is_versioned" >> $GITHUB_OUTPUT | ||
| if [ -n "$(echo ${{ inputs.platforms }} | grep ',')" ]; then | ||
| # multi-platform builds get no platform tag | ||
| echo "runner=linux-amd64-cpu16" >> $GITHUB_OUTPUT | ||
| echo "build_docs=${{ inputs.build_docs != 'false' }}" >> $GITHUB_OUTPUT | ||
| has_continuous_deployment=${{ startsWith(github.ref_name, 'experimental/') || github.ref_name == 'main' }} | ||
| push_to_ngc=`${{ inputs.environment && secrets.NGC_CREDENTIALS != '' }} && ($is_versioned || $has_continuous_deployment) && echo true || echo` | ||
| echo "push_to_ngc=$push_to_ngc" >> $GITHUB_OUTPUT | ||
| elif [ -n "$(echo ${{ inputs.platforms }} | grep -i arm)" ]; then | ||
| platform_tag=`echo ${{ inputs.platforms }} | sed 's/linux\///g' | tr -d ' '` | ||
| echo "platform_tag=$platform_tag" >> $GITHUB_OUTPUT | ||
| echo "runner=linux-arm64-cpu16" >> $GITHUB_OUTPUT | ||
| echo "build_docs=${{ inputs.build_docs == 'true' }}" >> $GITHUB_OUTPUT | ||
| else | ||
| platform_tag=`echo ${{ inputs.platforms }} | sed 's/linux\///g' | tr -d ' '` | ||
| echo "platform_tag=$platform_tag" >> $GITHUB_OUTPUT | ||
| echo "runner=linux-amd64-cpu16" >> $GITHUB_OUTPUT | ||
| echo "build_docs=${{ inputs.build_docs != 'false' }}" >> $GITHUB_OUTPUT | ||
| fi | ||
| ompi_image: | ||
| name: open-mpi | ||
| needs: metadata | ||
| if: inputs.ompidev_image | ||
| runs-on: ${{ needs.metadata.outputs.runner }} | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| id-token: write | ||
| outputs: | ||
| tar_cache: ${{ steps.build_info.outputs.tar_cache }} | ||
| tar_archive: ${{ steps.build_info.outputs.tar_archive }} | ||
| image_hash: ${{ steps.build_info.outputs.image_name }}@${{ steps.docker_build.outputs.digest }} | ||
| environment: | ||
| name: ${{ inputs.environment || 'default' }} | ||
| url: ${{ vars.deployment_url || format('https://github.com/{0}', github.repository) }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Log in to DockerHub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_READONLY_TOKEN }} | ||
| - name: Log in to the container registry | ||
| if: inputs.environment && vars.registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ vars.registry }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Determine build arguments | ||
| id: build_info | ||
| run: | | ||
| repo_owner=${{ github.repository_owner }} | ||
| registry=${{ vars.registry || 'localhost:5000' }} | ||
| image_name=$registry/${repo_owner,,}/${{ vars.packages_prefix }}open-mpi | ||
| echo "image_name=$image_name" >> $GITHUB_OUTPUT | ||
| docker pull ${{ inputs.ompidev_image }} # to get the tag | ||
| dev_tag=`docker inspect ${{ inputs.ompidev_image }} --format='{{json .Config.Labels}}' | jq -r '."org.opencontainers.image.version"'` | ||
| echo "image_tag=${dev_tag#ompi-}" >> $GITHUB_OUTPUT | ||
| docker image rm ${{ inputs.ompidev_image }} | ||
| docker image prune --force | ||
| if ${{ inputs.environment == '' }}; then | ||
| tar_archive=/tmp/open-mpi.tar | ||
| echo "tar_cache=tar-ompi-${{ needs.metadata.outputs.platform_tag }}-${{ github.sha }}" >> $GITHUB_OUTPUT | ||
| echo "tar_archive=$tar_archive" >> $GITHUB_OUTPUT | ||
| echo "docker_output=type=docker,dest=$tar_archive" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Extract metadata | ||
| id: metadata | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ steps.build_info.outputs.image_name }} | ||
| flavor: | ||
| latest=false | ||
| tags: | | ||
| type=raw,value=${{ steps.build_info.outputs.image_tag }} | ||
| labels: | | ||
| org.opencontainers.image.title=open-mpi | ||
| org.opencontainers.image.description=Open MPI dependencies of CUDA Quantum | ||
| - name: Set up context for buildx | ||
| run: | | ||
| docker context create builder_context | ||
| - name: Set up buildx runner | ||
| uses: docker/setup-buildx-action@v3 | ||
| with: | ||
| endpoint: builder_context | ||
| version: v0.19.0 | ||
| buildkitd-config: ${{ needs.metadata.outputs.runner != 'ubuntu-latest' && '/etc/buildkit/buildkitd.toml' || null }} | ||
| driver-opts: | | ||
| network=host | ||
| image=moby/buildkit:v0.19.0 | ||
| - name: Setup proxy cache | ||
| if: needs.metadata.outputs.runner != 'ubuntu-latest' | ||
| uses: nv-gha-runners/setup-proxy-cache@main | ||
| with: | ||
| enable-apt: true | ||
| - name: Build Open MPI | ||
| id: docker_build | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| file: ./docker/build/devcontainer.Dockerfile | ||
| build-args: | | ||
| ompidev_image=${{ inputs.ompidev_image }} | ||
| base_image=nvcr.io/nvidia/cuda:${{ inputs.cuda_version }}.0-runtime-ubuntu24.04 | ||
| cuda_version=${{ inputs.cuda_version }} | ||
| cuda_packages= | ||
| tags: ${{ steps.metadata.outputs.tags }} | ||
| labels: ${{ steps.metadata.outputs.labels }} | ||
| platforms: ${{ inputs.platforms }} | ||
| push: ${{ inputs.environment != '' }} | ||
| outputs: ${{ steps.build_info.outputs.docker_output }} | ||
| - name: Install Cosign | ||
| if: inputs.environment | ||
| uses: sigstore/cosign-installer@v3.3.0 | ||
| with: | ||
| cosign-release: 'v2.2.2' | ||
| - name: Sign image with GitHub OIDC Token | ||
| if: inputs.environment && false # Signing is disabled as long as the package is private, since we can't clean up signatures in that case | ||
| env: | ||
| DIGEST: ${{ steps.docker_build.outputs.digest }} | ||
| TAGS: ${{ steps.metadata.outputs.tags }} | ||
| run: cosign sign --yes --recursive "${TAGS}@${DIGEST}" | ||
| - name: Cache cuda-quantum image | ||
| if: steps.build_info.outputs.tar_cache && steps.build_info.outputs.tar_archive | ||
| uses: actions/cache/save@v4 | ||
| with: | ||
| path: ${{ steps.build_info.outputs.tar_archive }} | ||
| key: ${{ steps.build_info.outputs.tar_cache }} | ||
| cudaqdev_image: | ||
| name: cuda-quantum-dev (debug) | ||
| needs: metadata | ||
| runs-on: ${{ needs.metadata.outputs.runner }} | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| id-token: write | ||
| outputs: | ||
| tar_cache: ${{ steps.prereqs.outputs.tar_cache }} | ||
| tar_archive: ${{ steps.prereqs.outputs.tar_archive }} | ||
| image_hash: ${{ steps.prereqs.outputs.image_name }}@${{ steps.docker_build.outputs.digest }} | ||
| environment: | ||
| name: ${{ inputs.environment || 'default' }} | ||
| url: ${{ vars.deployment_url || format('https://github.com/{0}', github.repository) }} | ||
| # Needed for making local images available to the docker/build-push-action. | ||
| # See also https://stackoverflow.com/a/63927832. | ||
| services: | ||
| registry: | ||
| image: registry:2 | ||
| ports: | ||
| - 5000:5000 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Log in to DockerHub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_READONLY_TOKEN }} | ||
| - name: Log in to GitHub CR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Log in to the container registry | ||
| if: inputs.environment && vars.registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ vars.registry }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Restore build environment | ||
| if: inputs.devdeps_cache && inputs.devdeps_archive | ||
| id: restore | ||
| uses: actions/cache/restore@v4 | ||
| with: | ||
| path: ${{ inputs.devdeps_archive }} | ||
| key: ${{ inputs.devdeps_cache }} | ||
| fail-on-cache-miss: true | ||
| - name: Load prerequisites | ||
| id: prereqs | ||
| run: | | ||
| if ${{ steps.restore.outcome != 'skipped' }}; then | ||
| load_output=`docker load --input "${{ inputs.devdeps_archive }}"` | ||
| base_image=`echo "$load_output" | grep -o 'Loaded image: \S*:\S*' | head -1 | cut -d ' ' -f 3` | ||
| echo "Base image: $base_image" >> $GITHUB_STEP_SUMMARY | ||
| # Push the image to the local registry to make it available within | ||
| # the containered environment that docker/build-push-action uses. | ||
| docker push $base_image | ||
| rm -rf "${{ inputs.devdeps_archive }}" | ||
| elif ${{ inputs.devdeps_image != '' }}; then | ||
| base_image=${{ inputs.devdeps_image }} | ||
| echo "Base image: $base_image" >> $GITHUB_STEP_SUMMARY | ||
| docker pull $base_image | ||
| else | ||
| echo "::error file=docker_images.yml::Missing configuration for development dependencies. Either specify the image (i.e. provide devdeps_image) or cache (i.e. provide devdeps_cache and devdeps_archive) that should be used for the build." | ||
| exit 1 | ||
| fi | ||
| repo_owner=${{ github.repository_owner }} | ||
| registry=${{ vars.registry || 'localhost:5000' }} | ||
| image_name=$registry/${repo_owner,,}/${{ vars.packages_prefix }}cuda-quantum-dev | ||
| image_tag=`docker inspect $base_image --format='{{json .Config.Labels}}' | jq -r '."org.opencontainers.image.version"'` | ||
| docker image rm $base_image | ||
| docker image prune --force | ||
| echo "image_name=$image_name" >> $GITHUB_OUTPUT | ||
| echo "image_tag=$image_tag" >> $GITHUB_OUTPUT | ||
| echo "base_image=$base_image" >> $GITHUB_OUTPUT | ||
| if ${{ inputs.environment == '' }}; then | ||
| tar_archive=/tmp/cuda-quantum-dev.tar | ||
| echo "tar_cache=tar-cudaqdev-$image_tag-${{ needs.metadata.outputs.platform_tag }}-${{ github.sha }}" >> $GITHUB_OUTPUT | ||
| echo "tar_archive=$tar_archive" >> $GITHUB_OUTPUT | ||
| echo "docker_output=type=docker,dest=$tar_archive" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Set up context for buildx | ||
| run: | | ||
| docker context create builder_context | ||
| - name: Set up buildx runner | ||
| uses: docker/setup-buildx-action@v3 | ||
| with: | ||
| endpoint: builder_context | ||
| version: v0.19.0 | ||
| buildkitd-config: ${{ needs.metadata.outputs.runner != 'ubuntu-latest' && '/etc/buildkit/buildkitd.toml' || null }} | ||
| driver-opts: | | ||
| network=host | ||
| image=moby/buildkit:v0.19.0 | ||
| - name: Setup proxy cache | ||
| if: needs.metadata.outputs.runner != 'ubuntu-latest' | ||
| uses: nv-gha-runners/setup-proxy-cache@main | ||
| with: | ||
| enable-apt: true | ||
| - name: Set up ccache and ORAS | ||
| uses: ./.github/actions/ccache-setup | ||
| - name: Restore ccache from GHCR | ||
| uses: ./.github/actions/ccache-restore | ||
| with: | ||
| cache-key: ${{ needs.metadata.outputs.platform_tag }}-cudaqdev | ||
| registry-token: ${{ github.token }} | ||
| - name: Extract metadata | ||
| id: metadata | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ steps.prereqs.outputs.image_name }} | ||
| flavor: latest=false | ||
| tags: type=raw,value=${{ steps.prereqs.outputs.image_tag }} | ||
| labels: | | ||
| org.opencontainers.image.title=cuda-quantum-dev | ||
| org.opencontainers.image.description=Dev environment for CUDA Quantum (debug build) | ||
| - name: Build cuda-quantum-dev image (debug) | ||
| id: docker_build | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| file: ./docker/build/cudaq.dev.Dockerfile | ||
| build-contexts: | | ||
| ccache-data=/tmp/ccache-data | ||
| build-args: | | ||
| base_image=${{ steps.prereqs.outputs.base_image }} | ||
| install="CMAKE_BUILD_TYPE=Debug" | ||
| git_source_sha=${{ github.sha }} | ||
| tags: ${{ steps.metadata.outputs.tags }} | ||
| labels: ${{ steps.metadata.outputs.labels }} | ||
| platforms: ${{ inputs.platforms }} | ||
| push: ${{ inputs.environment != '' }} | ||
| outputs: ${{ steps.prereqs.outputs.docker_output }} | ||
| - name: Extract ccache from build | ||
| run: | | ||
| mkdir -p /tmp/ccache-export | ||
| docker buildx build \ | ||
| -f ./docker/build/cudaq.dev.Dockerfile . \ | ||
| --build-context ccache-data=/tmp/ccache-data \ | ||
| --build-arg base_image=${{ steps.prereqs.outputs.base_image }} \ | ||
| --build-arg install="CMAKE_BUILD_TYPE=Debug" \ | ||
| --build-arg git_source_sha=${{ github.sha }} \ | ||
| --target ccache-export \ | ||
| --output type=local,dest=/tmp/ccache-export \ | ||
| 2>/dev/null || true | ||
| - name: Push ccache to GHCR | ||
| uses: ./.github/actions/ccache-push | ||
| with: | ||
| cache-key: ${{ needs.metadata.outputs.platform_tag }}-cudaqdev | ||
| cache-path: /tmp/ccache-export/ccache | ||
| registry-token: ${{ github.token }} | ||
| - name: Write matrix outputs | ||
| uses: cloudposse/github-action-matrix-outputs-write@1.0.0 | ||
| with: | ||
| matrix-step-name: docker_images | ||
| matrix-key: ${{ needs.metadata.outputs.platform_tag }}-cu${{ inputs.cuda_version }}-dev-image | ||
| outputs: | | ||
| digest: ${{ steps.docker_build.outputs.digest }} | ||
| image_tag: ${{ steps.metadata.outputs.tags }} | ||
| - name: Install Cosign | ||
| if: inputs.environment | ||
| uses: sigstore/cosign-installer@v3.3.0 | ||
| with: | ||
| cosign-release: 'v2.2.2' | ||
| - name: Sign image with GitHub OIDC Token | ||
| if: inputs.environment | ||
| env: | ||
| DIGEST: ${{ steps.docker_build.outputs.digest }} | ||
| TAGS: ${{ steps.metadata.outputs.tags }} | ||
| run: cosign sign --yes --recursive "${TAGS}@${DIGEST}" | ||
| - name: Cache cuda-quantum-dev image | ||
| if: steps.prereqs.outputs.tar_cache && steps.prereqs.outputs.tar_archive | ||
| uses: actions/cache/save@v4 | ||
| with: | ||
| path: ${{ steps.prereqs.outputs.tar_archive }} | ||
| key: ${{ steps.prereqs.outputs.tar_cache }} | ||
| cudaq_image: | ||
| name: cuda-quantum (release) | ||
| needs: [metadata, ompi_image] | ||
| # Force this job to run even when some of the dependencies above are skipped. | ||
| if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') | ||
| runs-on: ${{ needs.metadata.outputs.runner }} | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| id-token: write | ||
| outputs: | ||
| tar_cache: ${{ steps.prereqs.outputs.tar_cache }} | ||
| tar_archive: ${{ steps.prereqs.outputs.tar_archive }} | ||
| image_hash: ${{ steps.prereqs.outputs.image_name }}@${{ steps.cudaq_build.outputs.digest }} | ||
| environment: | ||
| name: ${{ inputs.environment || 'default' }} | ||
| url: ${{ vars.deployment_url || format('https://github.com/{0}', github.repository) }} | ||
| # Needed for making local images available to the docker/build-push-action. | ||
| # See also https://stackoverflow.com/a/63927832. | ||
| services: | ||
| registry: | ||
| image: registry:2 | ||
| ports: | ||
| - 5000:5000 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Log in to DockerHub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_READONLY_TOKEN }} | ||
| - name: Log in to GitHub CR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Log in to the container registry | ||
| if: inputs.environment && vars.registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ vars.registry }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Restore build environment | ||
| if: inputs.devdeps_cache && inputs.devdeps_archive | ||
| id: restore_devdeps | ||
| uses: actions/cache/restore@v4 | ||
| with: | ||
| path: ${{ inputs.devdeps_archive }} | ||
| key: ${{ inputs.devdeps_cache }} | ||
| fail-on-cache-miss: true | ||
| - name: Restore Open MPI dependencies | ||
| id: restore_openmpi | ||
| if: needs.ompi_image.outputs.tar_cache && needs.ompi_image.outputs.tar_archive | ||
| uses: actions/cache/restore@v4 | ||
| with: | ||
| path: ${{ needs.ompi_image.outputs.tar_archive }} | ||
| key: ${{ needs.ompi_image.outputs.tar_cache }} | ||
| fail-on-cache-miss: true | ||
| - name: Load prerequisites | ||
| id: prereqs | ||
| run: | | ||
| if ${{ needs.ompi_image.result == 'skipped' }}; then | ||
| base_image=ghcr.io/nvidia/ubuntu:24.04 | ||
| elif ${{ steps.restore_openmpi.outcome != 'skipped' }}; then | ||
| load_output=`docker load --input "${{ needs.ompi_image.outputs.tar_archive }}"` | ||
| base_image=`echo "$load_output" | grep -o 'Loaded image: \S*:\S*' | head -1 | cut -d ' ' -f 3` | ||
| echo "Base image: $base_image" >> $GITHUB_STEP_SUMMARY | ||
| # Push the image to the local registry to make it available within | ||
| # the containered environment that docker/build-push-action uses. | ||
| docker push $base_image | ||
| docker image rm $base_image | ||
| rm -rf "${{ needs.ompi_image.outputs.tar_archive }}" | ||
| else | ||
| base_image=${{ needs.ompi_image.outputs.image_hash }} | ||
| echo "Base image: $base_image" >> $GITHUB_STEP_SUMMARY | ||
| fi | ||
| if ${{ steps.restore_devdeps.outcome != 'skipped' }}; then | ||
| load_output=`docker load --input "${{ inputs.devdeps_archive }}"` | ||
| echo "$load_output" | ||
| devdeps_image=`echo "$load_output" | grep -o 'Loaded image: \S*:\S*' | head -1 | cut -d ' ' -f 3` | ||
| echo "Devdeps image: $devdeps_image" >> $GITHUB_STEP_SUMMARY | ||
| # Push the image to the local registry to make it available within | ||
| # the containered environment that docker/build-push-action uses. | ||
| docker push $devdeps_image | ||
| docker image rm $devdeps_image | ||
| rm -rf "${{ inputs.devdeps_archive }}" | ||
| elif ${{ inputs.devdeps_image != '' }}; then | ||
| devdeps_image=${{ inputs.devdeps_image }} | ||
| echo "Devdeps image: $devdeps_image" >> $GITHUB_STEP_SUMMARY | ||
| else | ||
| echo "::error file=docker_images.yml::Missing configuration for development dependencies. Either specify the image (i.e. provide devdeps_image) or cache (i.e. provide devdeps_cache and devdeps_archive) that should be used for the build." | ||
| exit 1 | ||
| fi | ||
| docker image prune --force | ||
| repo_owner=${{ github.repository_owner }} | ||
| registry=${{ vars.registry || 'localhost:5000' }}/${repo_owner,,} | ||
| ngc_registry=`${{ needs.metadata.outputs.push_to_ngc == 'true' }} && echo nvcr.io/${repo_owner,,}/nightly || echo ''` | ||
| dev_image_name=${registry}/${{ vars.packages_prefix }}cuda-quantum-dev | ||
| image_name=${ngc_registry:-$registry}/${{ vars.packages_prefix }}cuda-quantum | ||
| image_description="CUDA Quantum toolkit for heterogeneous quantum-classical workflows" | ||
| platform_tag=${{ needs.metadata.outputs.platform_tag }} | ||
| cuda_major=`echo ${{ inputs.cuda_version }} | cut -d . -f1` | ||
| deprecation_notice="" | ||
| image_tag=${platform_tag:+$platform_tag-}${cuda_major:+cu${cuda_major}-} | ||
| if ${{ github.event.pull_request.number != '' }} || [ -n "$(echo ${{ github.ref_name }} | grep pull-request/)" ]; then | ||
| pr_number=`echo ${{ github.ref_name }} | grep -o [0-9]*` | ||
| image_tag+=pr-${pr_number:-${{ github.event.pull_request.number }}} | ||
| elif ${{ github.ref_type == 'branch' && github.ref_name == 'main' }}; then | ||
| image_tag+=latest | ||
| elif ${{ needs.metadata.outputs.is_versioned == 'true' }}; then | ||
| image_tag+=`echo ${{ github.ref_name }} | egrep -o "([0-9]{1,}\.)+[0-9]{1,}"` | ||
| else | ||
| image_tag+=`echo ${{ github.ref_name }} | tr '/' '-'` | ||
| fi | ||
| echo "image_name=$image_name" >> $GITHUB_OUTPUT | ||
| echo "image_tag=$image_tag" >> $GITHUB_OUTPUT | ||
| echo "image_tag_suffix=-base" >> $GITHUB_OUTPUT | ||
| echo "image_description=$image_description" >> $GITHUB_OUTPUT | ||
| echo "base_image=$base_image" >> $GITHUB_OUTPUT | ||
| echo "devdeps_image=$devdeps_image" >> $GITHUB_OUTPUT | ||
| echo "dev_image_name=$dev_image_name" >> $GITHUB_OUTPUT | ||
| echo "deprecation_notice=$deprecation_notice" >> $GITHUB_OUTPUT | ||
| if ${{ inputs.environment == '' }}; then | ||
| tar_archive=/tmp/cuda-quantum.tar | ||
| echo "tar_cache=tar-cudaq-${image_tag}-${{ github.sha }}" >> $GITHUB_OUTPUT | ||
| echo "tar_archive=$tar_archive" >> $GITHUB_OUTPUT | ||
| echo "docker_output=type=docker,dest=$tar_archive" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Set up context for buildx | ||
| run: | | ||
| docker context create builder_context | ||
| - name: Set up buildx runner | ||
| uses: docker/setup-buildx-action@v3 | ||
| with: | ||
| endpoint: builder_context | ||
| version: v0.19.0 | ||
| buildkitd-config: ${{ needs.metadata.outputs.runner != 'ubuntu-latest' && '/etc/buildkit/buildkitd.toml' || null }} | ||
| driver-opts: | | ||
| network=host | ||
| image=moby/buildkit:v0.19.0 | ||
| - name: Setup proxy cache | ||
| if: needs.metadata.outputs.runner != 'ubuntu-latest' | ||
| uses: nv-gha-runners/setup-proxy-cache@main | ||
| with: | ||
| enable-apt: true | ||
| - name: Set up ccache and ORAS | ||
| uses: ./.github/actions/ccache-setup | ||
| - name: Restore ccache from GHCR | ||
| uses: ./.github/actions/ccache-restore | ||
| with: | ||
| cache-key: ${{ needs.metadata.outputs.platform_tag }}-cudaq-release | ||
| registry-token: ${{ github.token }} | ||
| - name: Extract cuda-quantum-dev metadata | ||
| id: cudaqdev_metadata | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ steps.prereqs.outputs.dev_image_name }} | ||
| flavor: | | ||
| latest=false | ||
| suffix=${{ steps.prereqs.outputs.image_tag_suffix }},onlatest=true | ||
| tags: type=raw,value=${{ steps.prereqs.outputs.image_tag }} | ||
| labels: | | ||
| org.opencontainers.image.title=cuda-quantum-dev | ||
| org.opencontainers.image.description=Dev environment for CUDA Quantum (release build) | ||
| - name: Build cuda-quantum-dev image (release) | ||
| id: release_build | ||
| if: success() && !cancelled() | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| file: ./docker/build/cudaq.dev.Dockerfile | ||
| build-contexts: | | ||
| ccache-data=/tmp/ccache-data | ||
| build-args: | | ||
| base_image=${{ steps.prereqs.outputs.devdeps_image }} | ||
| install="CMAKE_BUILD_TYPE=Release CUDA_QUANTUM_VERSION=${{ steps.prereqs.outputs.image_tag }}" | ||
| git_source_sha=${{ github.sha }} | ||
| tags: ${{ steps.cudaqdev_metadata.outputs.tags }} | ||
| labels: ${{ steps.cudaqdev_metadata.outputs.labels }} | ||
| platforms: ${{ inputs.platforms }} | ||
| push: true | ||
| g - name: Extract ccache from release build | ||
| run: | | ||
| mkdir -p /tmp/ccache-export | ||
| docker buildx build \ | ||
| -f ./docker/build/cudaq.dev.Dockerfile . \ | ||
| --build-context ccache-data=/tmp/ccache-data \ | ||
| --build-arg base_image=${{ steps.prereqs.outputs.devdeps_image }} \ | ||
| --build-arg install="CMAKE_BUILD_TYPE=Release CUDA_QUANTUM_VERSION=${{ steps.prereqs.outputs.image_tag }}" \ | ||
| --build-arg git_source_sha=${{ github.sha }} \ | ||
| --target ccache-export \ | ||
| --output type=local,dest=/tmp/ccache-export \ | ||
| 2>/dev/null || true | ||
| - name: Push ccache to GHCR | ||
| uses: ./.github/actions/ccache-push | ||
| with: | ||
| cache-key: ${{ needs.metadata.outputs.platform_tag }}-cudaq-release | ||
| cache-path: /tmp/ccache-export/ccache | ||
| registry-token: ${{ github.token }} | ||
| - name: Install Cosign | ||
| if: inputs.environment | ||
| uses: sigstore/cosign-installer@v3.3.0 | ||
| with: | ||
| cosign-release: 'v2.2.2' | ||
| - name: Sign image with GitHub OIDC Token | ||
| if: inputs.environment | ||
| env: | ||
| DIGEST: ${{ steps.release_build.outputs.digest }} | ||
| TAGS: ${{ steps.cudaqdev_metadata.outputs.tags }} | ||
| run: cosign sign --yes --recursive "${TAGS}@${DIGEST}" | ||
| - name: Log in to NGC | ||
| if: needs.metadata.outputs.push_to_ngc == 'true' | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: nvcr.io | ||
| username: '$oauthtoken' | ||
| password: ${{ secrets.NGC_CREDENTIALS }} | ||
| - name: Extract cuda-quantum metadata | ||
| id: cudaq_metadata | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ steps.prereqs.outputs.image_name }} | ||
| flavor: | | ||
| latest=false | ||
| suffix=${{ steps.prereqs.outputs.image_tag_suffix }},onlatest=true | ||
| tags: type=raw,value=${{ steps.prereqs.outputs.image_tag }} | ||
| labels: | | ||
| org.opencontainers.image.title=cuda-quantum | ||
| org.opencontainers.image.description=${{ steps.prereqs.outputs.image_description }} | ||
| - name: Configure credentials | ||
| if: needs.metadata.outputs.platform_tag == '' | ||
| run: | | ||
| docker run --privileged multiarch/qemu-user-static:latest --reset -p yes --credential yes | ||
| - name: Build cuda-quantum image | ||
| id: cudaq_build | ||
| if: success() && !cancelled() | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| file: ./docker/release/cudaq.Dockerfile | ||
| build-args: | | ||
| cudaqdev_image=${{ steps.prereqs.outputs.dev_image_name }}@${{ steps.release_build.outputs.digest }} | ||
| base_image=${{ steps.prereqs.outputs.base_image }} | ||
| release_version=${{ steps.prereqs.outputs.image_tag }} | ||
| deprecation_notice=${{ steps.prereqs.outputs.deprecation_notice }} | ||
| tags: ${{ steps.cudaq_metadata.outputs.tags }} | ||
| labels: ${{ steps.cudaq_metadata.outputs.labels }} | ||
| platforms: ${{ inputs.platforms }} | ||
| provenance: false | ||
| push: ${{ inputs.environment != '' }} | ||
| outputs: ${{ steps.prereqs.outputs.docker_output }} | ||
| - name: Write matrix outputs | ||
| uses: cloudposse/github-action-matrix-outputs-write@1.0.0 | ||
| with: | ||
| matrix-step-name: docker_images | ||
| matrix-key: ${{ needs.metadata.outputs.platform_tag }}-cu${{ inputs.cuda_version }}-image | ||
| outputs: | | ||
| digest: ${{ steps.cudaq_build.outputs.digest }} | ||
| image_tag: ${{ steps.cudaq_metadata.outputs.tags }} | ||
| - name: Cache cuda-quantum image | ||
| if: steps.prereqs.outputs.tar_cache && steps.prereqs.outputs.tar_archive | ||
| uses: actions/cache/save@v4 | ||
| with: | ||
| path: ${{ steps.prereqs.outputs.tar_archive }} | ||
| key: ${{ steps.prereqs.outputs.tar_cache }} | ||
| - name: Sign image with GitHub OIDC Token | ||
| if: inputs.environment && needs.metadata.outputs.push_to_ngc != 'true' | ||
| env: | ||
| DIGEST: ${{ steps.cudaq_build.outputs.digest }} | ||
| TAGS: ${{ steps.cudaq_metadata.outputs.tags }} | ||
| run: cosign sign --yes --recursive "${TAGS}@${DIGEST}" | ||
| - name: Install NGC CLI | ||
| if: inputs.environment && needs.metadata.outputs.push_to_ngc == 'true' | ||
| uses: ./.github/actions/install-ngc-cli | ||
| with: | ||
| version: 3.31.0 | ||
| checksum: b715e503e2c0b44814a51f330eafd605f5d240ea0987bf615700d359c993f138 | ||
| - name: Sign image with NGC CLI | ||
| if: inputs.environment && needs.metadata.outputs.push_to_ngc == 'true' | ||
| env: | ||
| TAGS: ${{ steps.cudaq_metadata.outputs.tags }} | ||
| NGC_CLI_API_KEY: ${{ secrets.NGC_CREDENTIALS }} | ||
| NGC_CLI_ORG: ${{ github.repository_owner }} | ||
| NGC_CLI_TEAM: 'nightly' | ||
| run: | | ||
| echo "Signing ${TAGS}" | ||
| ngc-cli/ngc registry image publish --source ${TAGS} ${TAGS} --sign | ||
| documentation: | ||
| name: Documentation | ||
| needs: [metadata, cudaqdev_image, cudaq_image] | ||
| # Unfortunately, we basically inherit this from the cudaq_image job; | ||
| # all jobs that depend on it will need to add the same condition. | ||
| if: always() && !cancelled() && needs.metadata.outputs.build_docs == 'true' && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') | ||
| runs-on: ${{ needs.metadata.outputs.runner }} | ||
| permissions: | ||
| contents: read | ||
| packages: read | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Log in to GitHub CR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Log in to the container registry | ||
| if: inputs.environment && vars.registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ vars.registry }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Restore CUDA Quantum build | ||
| id: restore_cudaqdev | ||
| if: needs.cudaqdev_image.outputs.tar_cache && needs.cudaqdev_image.outputs.tar_archive | ||
| uses: actions/cache/restore@v4 | ||
| with: | ||
| path: ${{ needs.cudaqdev_image.outputs.tar_archive }} | ||
| key: ${{ needs.cudaqdev_image.outputs.tar_cache }} | ||
| fail-on-cache-miss: true | ||
| - name: Restore CUDA Quantum image | ||
| id: restore_cudaq | ||
| if: needs.cudaq_image.outputs.tar_cache && needs.cudaq_image.outputs.tar_archive | ||
| uses: actions/cache/restore@v4 | ||
| with: | ||
| path: ${{ needs.cudaq_image.outputs.tar_archive }} | ||
| key: ${{ needs.cudaq_image.outputs.tar_cache }} | ||
| fail-on-cache-miss: true | ||
| - name: Build documentation | ||
| id: docs_build | ||
| run: | | ||
| if ${{ steps.restore_cudaq.outcome != 'skipped' }}; then | ||
| load_output=`docker load --input "${{ needs.cudaq_image.outputs.tar_archive }}"` | ||
| cudaq_image=`echo "$load_output" | grep -o 'Loaded image: \S*:\S*' | head -1 | cut -d ' ' -f 3` | ||
| else | ||
| cudaq_image=${{ needs.cudaq_image.outputs.image_hash }} | ||
| docker pull $cudaq_image | ||
| fi | ||
| image_tag=`docker inspect $cudaq_image --format='{{json .Config.Labels}}' | jq -r '."org.opencontainers.image.version"' | sed -E 's/^(arm64-|amd64-)//'` | ||
| docs_version="CUDA_QUANTUM_VERSION=$(echo $image_tag | sed -re 's/^(cu[0-9]+-)?(.*)-base$/\2/')" | ||
| docker image rm $cudaq_image | ||
| docker image prune --force | ||
| if ${{ steps.restore_cudaqdev.outcome != 'skipped' }}; then | ||
| load_output=`docker load --input "${{ needs.cudaqdev_image.outputs.tar_archive }}"` | ||
| cudaqdev_image=`echo "$load_output" | grep -o 'Loaded image: \S*:\S*' | head -1 | cut -d ' ' -f 3` | ||
| else | ||
| cudaqdev_image=${{ needs.cudaqdev_image.outputs.image_hash }} | ||
| docker pull $cudaqdev_image | ||
| fi | ||
| docker run --rm -dit --network host --name cuda-quantum-dev $cudaqdev_image | ||
| (docker exec cuda-quantum-dev bash -c "export $docs_version && bash scripts/build_docs.sh" && built=true) || built=false | ||
| if $built; then docker cp cuda-quantum-dev:"/usr/local/cudaq/docs/." docs; \ | ||
| else docker cp cuda-quantum-dev:"/workspaces/cuda-quantum/build/." /tmp/build; fi | ||
| docker stop cuda-quantum-dev | ||
| if $built; then `exit 0`; else `exit 1`; fi | ||
| html_files=`find docs/api/ -type f -name "*.html"` | ||
| json="{\"html_files\":[]}" | ||
| for file in $html_files; do | ||
| file=\'$file\' | ||
| json=`echo $json | jq ".html_files |= . + [\"$file\"]"` | ||
| done | ||
| echo "json=$(echo $json)" >> $GITHUB_OUTPUT | ||
| - name: Upload build artifacts | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: build | ||
| path: /tmp/build | ||
| retention-days: 1 | ||
| - name: Upload documentation | ||
| if: success() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: cuda_quantum_docs # changing the artifact name requires updating other workflows | ||
| path: docs | ||
| retention-days: 30 | ||
| if-no-files-found: error | ||
| - name: Spell check HTML documentation | ||
| if: success() | ||
| continue-on-error: true # to be removed once we update all docs for this check to pass | ||
| uses: rojopolis/spellcheck-github-actions@0.30.0 | ||
| with: | ||
| config_path: '.github/pre-commit/spellcheck_config.yml' | ||
| task_name: html | ||
| source_files: ${{ join(fromJSON(steps.docs_build.outputs.json).html_files, ' ') }} | ||
| validation: | ||
| name: Validation | ||
| needs: [metadata, cudaq_image] | ||
| # Unfortunately, we basically inherit this from the cudaq_image job; | ||
| # all jobs that depend on it will need to add the same condition. | ||
| if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') | ||
| runs-on: ${{ needs.metadata.outputs.runner }} | ||
| permissions: | ||
| contents: read | ||
| packages: read | ||
| outputs: | ||
| artifact_id: "${{ steps.job_summary.outputs.artifact_id }}" | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Log in to GitHub CR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Log in to the container registry | ||
| if: inputs.environment && vars.registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ vars.registry }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ github.token }} | ||
| - name: Load cuda-quantum image | ||
| id: restore | ||
| if: needs.cudaq_image.outputs.tar_cache && needs.cudaq_image.outputs.tar_archive | ||
| uses: actions/cache/restore@v4 | ||
| with: | ||
| path: ${{ needs.cudaq_image.outputs.tar_archive }} | ||
| key: ${{ needs.cudaq_image.outputs.tar_cache }} | ||
| fail-on-cache-miss: true | ||
| - name: Validate cuda-quantum image | ||
| run: | | ||
| if ${{ steps.restore.outcome != 'skipped' }}; then | ||
| load_output=`docker load --input "${{ needs.cudaq_image.outputs.tar_archive }}"` | ||
| cudaq_image=`echo "$load_output" | grep -o 'Loaded image: \S*:\S*' | head -1 | cut -d ' ' -f 3` | ||
| else | ||
| cudaq_image=${{ needs.cudaq_image.outputs.image_hash }} | ||
| docker pull $cudaq_image | ||
| fi | ||
| docker run --rm -dit --name cuda-quantum $cudaq_image | ||
| docker cp scripts/validate_installation.sh cuda-quantum:"/home/cudaq/validate_installation.sh" | ||
| docker cp docs/notebook_validation.py cuda-quantum:"/home/cudaq/notebook_validation.py" | ||
| # In containers without GPU support, UCX does not work properly since it is configured to work with GPU-support. | ||
| # Hence, don't enforce UCX when running these tests. | ||
| docker exec cuda-quantum bash -c "python3 -m pip install --break-system-packages pandas scipy seaborn h5py contfrac" | ||
| docker exec cuda-quantum bash -c "sudo apt install -y python3-venv" | ||
| (docker exec cuda-quantum bash -c "unset OMPI_MCA_pml && set -o pipefail && bash validate_installation.sh | tee /tmp/validation.out") && passed=true || passed=false | ||
| docker cp cuda-quantum:"/tmp/validation.out" /tmp/validation.out | ||
| docker stop cuda-quantum | ||
| if ! $passed; then | ||
| echo "::error::Validation failed; see job summary for more details." | ||
| exit 1 | ||
| elif ${{ inputs.ompidev_image != '' }} && [ "$(cat /tmp/validation.out | grep '^nvidia$')" != "nvidia" ]; then | ||
| echo "Missing installation for nvidia backend." | ||
| exit 1 | ||
| fi | ||
| - name: Create job summary | ||
| id: job_summary | ||
| if: always() && !cancelled() | ||
| run: | | ||
| cuda_major=`echo ${{ inputs.cuda_version }} | cut -d . -f1` | ||
| platform_id=${{ needs.metadata.outputs.platform_tag }} | ||
| artifact_id=image${platform_id:+_$platform_id}${cuda_major:+_cu$cuda_major} # changing the artifact name requires updating other workflows | ||
| echo "artifact_id=$artifact_id" >> $GITHUB_OUTPUT | ||
| if [ -f /tmp/validation.out ]; then | ||
| summary=/tmp/summary.txt | ||
| echo "## Validation" > $summary | ||
| echo "The validation of the cuda-quantum image produced the following output:" >> $summary | ||
| echo '```text' >> $summary | ||
| cat /tmp/validation.out >> $summary | ||
| echo '```' >> $summary | ||
| echo "validation_summary=$summary" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Upload job summary | ||
| if: steps.job_summary.outputs.validation_summary != '' | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: ${{ steps.job_summary.outputs.artifact_id }}_validation | ||
| path: ${{ steps.job_summary.outputs.validation_summary }} | ||
| retention-days: 1 | ||
| if-no-files-found: warn | ||
| staging: | ||
| name: Staging | ||
| needs: [metadata, cudaqdev_image, cudaq_image, validation] | ||
| # Unfortunately, we basically inherit this from the cudaq_image job; | ||
| # all jobs that depend on it will need to add the same condition. | ||
| if: always() && !cancelled() && inputs.environment && needs.metadata.outputs.platform_tag == '' && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Create build info | ||
| id: staging | ||
| run: | | ||
| cudaqdev_hash=${{ needs.cudaqdev_image.outputs.image_hash }} | ||
| cudaq_hash=${{ needs.cudaq_image.outputs.image_hash }} | ||
| artifact_name=${{ needs.validation.outputs.artifact_id }}_publishing # changing the artifact name requires updating other workflows | ||
| echo "artifact_name=$artifact_name" >> $GITHUB_OUTPUT | ||
| info_file="$artifact_name.txt" | ||
| echo "info_file=$info_file" >> $GITHUB_OUTPUT | ||
| mkdir -p "$(dirname "$info_file")" && rm -rf "$info_file" | ||
| echo "source-sha: ${{ github.sha }}" >> "$info_file" | ||
| echo "cuda-quantum-image: $cudaq_hash" >> "$info_file" | ||
| echo "cuda-quantum-dev-image: $cudaqdev_hash" >> "$info_file" | ||
| echo "cuda-quantum-devdeps-image: ${{ inputs.devdeps_image }}" >> "$info_file" | ||
| echo "platforms: ${{ inputs.platforms }}" >> "$info_file" | ||
| echo "cuda-version: ${{ inputs.cuda_version }}" >> "$info_file" | ||
| cat .github/workflows/config/gitlab_commits.txt >> "$info_file" | ||
| - name: Upload build info | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: ${{ steps.staging.outputs.artifact_name }} # changing the artifact name requires updating other workflows | ||
| path: ${{ steps.staging.outputs.info_file }} | ||
| retention-days: 30 | ||
| if-no-files-found: error | ||
| clean_up: | ||
| name: Prepare cache clean-up | ||
| needs: [metadata, cudaqdev_image, cudaq_image, ompi_image, documentation, validation] | ||
| # We need to clean up even if the workflow is cancelled or fails. | ||
| if: always() | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Save cache keys | ||
| id: workflow_inputs | ||
| run: | | ||
| keys=${{ needs.cudaqdev_image.outputs.tar_cache }} | ||
| keys+=" ${{ needs.cudaq_image.outputs.tar_cache }}" | ||
| keys+=" ${{ needs.ompi_image.outputs.tar_cache }}" | ||
| echo "$keys" >> cache_keys.txt | ||
| echo "artifact_name=${{ needs.validation.outputs.artifact_id }}_cache_keys" >> $GITHUB_OUTPUT | ||
| - uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: ${{ steps.workflow_inputs.outputs.artifact_name }} | ||
| path: cache_keys.txt | ||
| retention-days: 1 | ||
| if-no-files-found: error | ||