Skip to content

Deployments

Deployments #3866

Workflow file for this run

on:
# We update the build caches during the pull_request_target event,
# and deploy the images via workflow dispatch on branches with deployment permissions.
# A dispatch event is automatically triggered when the pull_request_target event
# triggers the workflow.
workflow_dispatch:
inputs:
update_registry_cache:
type: boolean
description: Create or update the build caches on the container registry. The build terminates after that and no images are pushed.
required: false
default: false
cache_base:
required: false
type: string
description: 'The name of the branch to use as cache base.'
default: main
platforms:
type: string
description: The platforms to build the Docker images for. A semicolon separated list creates a separate list for each platform, whereas a comma separate list creates a multi-platform build.
required: false
default: default
create_release:
type: string
description: Create a GitHub release with the given version number (e.g. 0.3.0). A GitHub release with that version must not exist already.
required: false
default: ''
# We need write permissions for packages to update the build caches of GHCR.
# Make sure no external code is checked out as part of this workflow.
# This trigger replaces the pull_request_target trigger in prepare_deployment.yml
# so that we can use the NVIDIA runners to update the build caches.
workflow_run:
workflows:
- PR merged
types:
- completed
# Whenever we create a release branch, we want to populate the caches.
# Unfortunately, it is not currently possible to filter by branch,
# see also: https://github.com/orgs/community/discussions/26286
push:
branches:
- 'releases/*'
- 'staging/*'
- 'experimental/*'
- 'features/*'
name: Deployments # do not change name without updating workflow_run triggers
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.ref_name }}
cancel-in-progress: false
jobs:
metadata:
name: Prepare build
if: github.event_name != 'push' || github.event.created
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
outputs:
pull_request_number: ${{ steps.pr_info.outputs.pr_number }}
pull_request_base: ${{ steps.pr_info.outputs.pr_base }}
pull_request_commit: ${{ steps.pr_info.outputs.merge_commit }}
llvm_commit: ${{ steps.build_config.outputs.llvm_commit }}
pybind11_commit: ${{ steps.build_config.outputs.pybind11_commit }}
cache_base: ${{ steps.build_info.outputs.cache_base }}
cache_target: ${{ steps.build_info.outputs.cache_target }}
multi_platform: ${{ steps.build_info.outputs.multi_platform }}
platforms: ${{ steps.build_info.outputs.platforms }}
build_dependencies: ${{ steps.build_config.outputs.build_dependencies }}
create_packages: ${{ steps.build_config.outputs.create_packages }}
environment: ${{ steps.build_info.outputs.environment }}
steps:
- name: Retrieve PR info
if: github.event_name == 'workflow_run'
id: pr_info
run: |
artifacts_url=${{ github.event.workflow_run.artifacts_url }}
artifacts=$(gh api $artifacts_url -q '.artifacts[] | {name: .name, url: .archive_download_url}')
for artifact in `echo "$artifacts"`; do
name=`echo $artifact | jq -r '.name'`
if [ "$name" == "metadata_pr" ]; then
url=`echo $artifact | jq -r '.url'`
gh api $url > metadata.zip
unzip -d metadata metadata.zip
for file in `find metadata/ -type f`; do
cat "$file" >> metadata.txt
done
pr_number=`cat metadata.txt | grep -o 'pr-number: \S*' | cut -d ' ' -f 2`
pr_base=`cat metadata.txt | grep -o 'pr-base: \S*' | cut -d ' ' -f 2`
merge_commit=`cat metadata.txt | grep -o 'source-sha: \S*' | cut -d ' ' -f 2`
rm -rf metadata.zip metadata.txt metadata
echo "pr_number=$pr_number" >> $GITHUB_OUTPUT
echo "pr_base=$pr_base" >> $GITHUB_OUTPUT
echo "merge_commit=$merge_commit" >> $GITHUB_OUTPUT
fi
done
env:
GH_TOKEN: ${{ github.token }}
- name: Determine build info
id: build_info
run: |
if ${{ github.event_name == 'workflow_dispatch' }}; then
defaults="${{ (inputs.update_registry_cache && 'linux/arm64 linux/amd64') || 'linux/amd64,linux/arm64' }}"
requested_platforms=`echo "${{ inputs.platforms }}" | tr + , | sed "s#default#$defaults#g"`
else
requested_platforms="linux/arm64 linux/amd64"
fi
if [ -n "$(echo "$requested_platforms" | egrep ',')" ]; then
multi_platform="{\"ids\":[]}"
platform_flag=`echo "$requested_platforms" | tr -d ' '`
multi_platform=`echo $multi_platform | jq ".ids |= . + [\"multiplat\"]"`
platform={\"multiplat\":{\"docker_flag\":\"$platform_flag\"}}
multi_platform=`echo $multi_platform | jq ". |= . + $platform"`
fi
platforms="{\"ids\":[]}"
for input in `echo "$requested_platforms" | tr , ' ' | tr ';' ' '`; do
platform_id=`echo $input | sed 's/linux\///g' | tr -d ' '`
minimal_base_image=`([ "$platform_id" == "amd64" ] && echo amd64/almalinux:8) || ([ "$platform_id" == "arm64" ] && echo arm64v8/almalinux:8) || echo`
platforms=`echo $platforms | jq ".ids |= . + [\"$platform_id\"]"`
platform={\"$platform_id\":{\"docker_flag\":\"$input\",\"minimal_base_image\":\"$minimal_base_image\"}}
platforms=`echo $platforms | jq ". |= . + $platform"`
done
cache_base=${{ (github.event_name == 'push' && 'main') || inputs.cache_base || steps.pr_info.outputs.pr_base }}
cache_target=${{ (github.event_name != 'workflow_dispatch' && (steps.pr_info.outputs.pr_base || github.ref_name)) || (inputs.update_registry_cache && github.ref_name) || '' }}
environment=${{ (github.event_name != 'workflow_run' && 'ghcr-deployment') || '' }}
# Store deployment info
release_title="${{ inputs.create_release }}"
release_version=`echo "$release_title" | (egrep -o "([0-9]{1,}\.)+[0-9]{1,}([A-Za-z0-9_\-\.]*)" || true)`
echo "source-sha: ${{ github.sha }}" >> deployment_info.txt
echo "release-title: $release_title" >> deployment_info.txt
echo "release-version: $release_version" >> deployment_info.txt
echo "cache_base=$cache_base" >> $GITHUB_OUTPUT
echo "cache_target=$cache_target" >> $GITHUB_OUTPUT
echo "multi_platform=$(echo $multi_platform)" >> $GITHUB_OUTPUT
echo "platforms=$(echo $platforms)" >> $GITHUB_OUTPUT
echo "environment=$environment" >> $GITHUB_OUTPUT
- name: Upload deployment info
uses: actions/upload-artifact@v4
with:
name: deployment_info # changing the artifact name requires updating other workflows
path: deployment_info.txt
retention-days: 30
if-no-files-found: error
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: "${{ steps.pr_info.outputs.merge_commit }}"
- name: Configure build
id: build_config
run: |
echo "llvm_commit=$(git rev-parse @:./tpls/llvm)" >> $GITHUB_OUTPUT
echo "pybind11_commit=$(git rev-parse @:./tpls/pybind11)" >> $GITHUB_OUTPUT
if ${{ github.event_name != 'workflow_run' || steps.pr_info.outputs.pr_number != '' }}; then
echo "build_dependencies=true" >> $GITHUB_OUTPUT
fi
if ${{ github.event_name == 'workflow_dispatch' && ! inputs.update_registry_cache }}; then
echo "create_packages=true" >> $GITHUB_OUTPUT
fi
devdeps:
name: Build dev dependencies
needs: metadata
if: needs.metadata.outputs.build_dependencies == 'true'
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms).ids }}
toolchain: [clang16, gcc11, gcc12]
fail-fast: false
uses: ./.github/workflows/dev_environment.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platforms: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
dockerfile: build/devdeps.Dockerfile
build_config_id: ${{ matrix.toolchain }}
build_args: |
toolchain=${{ matrix.toolchain }}
registry_cache_from: ${{ needs.metadata.outputs.cache_base }}
update_registry_cache: ${{ needs.metadata.outputs.cache_target }}
pull_request_number: ${{ needs.metadata.outputs.pull_request_number }}
pull_request_commit: ${{ needs.metadata.outputs.pull_request_commit }}
checkout_submodules: true
environment: ${{ needs.metadata.outputs.environment }}
# needed only for the cloudposse GitHub action
matrix_key: ${{ matrix.platform }}-${{ matrix.toolchain }}
wheeldeps:
name: Build wheel dependencies
needs: metadata
if: needs.metadata.outputs.build_dependencies == 'true'
strategy:
matrix:
# There are currently no multi-platform manylinux images available.
# See https://github.com/pypa/manylinux/issues/1306.
platform: ${{ fromJson(needs.metadata.outputs.platforms).ids }}
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/dev_environment.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platforms: ${{ fromJson(needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
dockerfile: build/devdeps.manylinux.Dockerfile
build_config_id: cu${{ matrix.cuda_version }}-gcc11
build_args: |
base_image=ghcr.io/nvidia/pypa/manylinux_2_28${{ (matrix.platform == 'arm64' && '_aarch64') || (matrix.platform == 'amd64' && '_x86_64') || '' }}:latest
cuda_version=${{ matrix.cuda_version }}
toolchain=gcc11
distro=rhel8
llvm_commit=${{ needs.metadata.outputs.llvm_commit }}
pybind11_commit=${{ needs.metadata.outputs.pybind11_commit }}
registry_cache_from: ${{ needs.metadata.outputs.cache_base }}
update_registry_cache: ${{ needs.metadata.outputs.cache_target }}
pull_request_number: ${{ needs.metadata.outputs.pull_request_number }}
pull_request_commit: ${{ needs.metadata.outputs.pull_request_commit }}
environment: ${{ needs.metadata.outputs.environment }}
# needed only for the cloudposse GitHub action
matrix_key: ${{ matrix.platform }}-cu${{ matrix.cuda_version }}-python
source_build:
name: Build cross-platform dependencies
needs: metadata
if: needs.metadata.outputs.build_dependencies == 'true'
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.platforms).ids }}
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/dev_environment.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platforms: ${{ fromJson(needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
dockerfile: build/assets.Dockerfile
build_config_id: cu${{ matrix.cuda_version }}-llvm
build_target: prereqs
build_args: |
base_image=${{ fromJson(needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].minimal_base_image }}
cuda_version=${{ matrix.cuda_version }}
toolchain=llvm
registry_cache_from: ${{ needs.metadata.outputs.cache_base }}
update_registry_cache: ${{ needs.metadata.outputs.cache_target }}
pull_request_number: ${{ needs.metadata.outputs.pull_request_number }}
pull_request_commit: ${{ needs.metadata.outputs.pull_request_commit }}
checkout_submodules: true
environment: ${{ needs.metadata.outputs.environment }}
# needed only for the cloudposse GitHub action
matrix_key: ${{ matrix.platform }}-cu${{ matrix.cuda_version }}-installer
openmpi:
name: Build Open MPI
needs: metadata
if: needs.metadata.outputs.build_dependencies == 'true'
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms).ids }}
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/dev_environment.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platforms: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
dockerfile: build/devdeps.ompi.Dockerfile
build_config_id: cu${{ matrix.cuda_version }}
build_args: |
cuda_version=${{ matrix.cuda_version }}
registry_cache_from: ${{ needs.metadata.outputs.cache_base }}
update_registry_cache: ${{ needs.metadata.outputs.cache_target }}
environment: ${{ needs.metadata.outputs.environment }}
# needed only for the cloudposse GitHub action
matrix_key: ${{ matrix.platform }}-cu${{ matrix.cuda_version }}-ompi
dispatch:
name: Dispatch deployments
needs: [metadata, devdeps, wheeldeps, openmpi]
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
# We want this dispatch to trigger additional workflows.
github-token: ${{ secrets.REPO_BOT_ACCESS_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'deployments.yml',
ref: '${{ needs.metadata.outputs.pull_request_base || github.ref_name }}',
inputs: {
platforms: 'linux/arm64,linux/amd64',
cache_base: '${{ needs.metadata.outputs.cache_base }}',
},
})
# This job is needed only when using the cloudposse GitHub action to read
# the output of a matrix job. This is a workaround due to current GitHub
# limitations that may not be needed if the work started here concludes:
# https://github.com/actions/runner/pull/2477
config:
name: Configure build
needs: [metadata, devdeps, wheeldeps, source_build, openmpi]
if: needs.metadata.outputs.create_packages == 'true'
runs-on: ubuntu-latest
outputs:
json: "${{ steps.read_json.outputs.result }}"
steps:
- uses: cloudposse/[email protected]
id: read_json
with:
matrix-step-name: dev_environment
coverage:
name: Update code coverage
needs: [metadata, config]
if: needs.metadata.outputs.multi_platform != ''
strategy:
matrix:
platform: [amd64]
toolchain: [clang16]
fail-fast: false
uses: ./.github/workflows/generate_cc.yml
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
platform: linux/${{ matrix.platform }}
devdeps_image: ${{ fromJson(needs.config.outputs.json).image_hash[format('{0}-{1}', fromJson(needs.metadata.outputs.multi_platform).ids[0], matrix.toolchain)] }}
export_environment: false
extdevdeps:
name: Create dev environment
needs: [metadata, config, openmpi]
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms).ids }}
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/dev_environment.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platforms: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
dockerfile: build/devdeps.ext.Dockerfile
build_config_id: cu${{ matrix.cuda_version }}-gcc11
build_args: |
cuda_version=${{ matrix.cuda_version }}
base_image=${{ fromJson(needs.config.outputs.json).image_hash[format('{0}-gcc11', matrix.platform)] }}
ompidev_image=${{ fromJson(needs.config.outputs.json).image_hash[format('{0}-cu{1}-ompi', matrix.platform, matrix.cuda_version)] }}
${{ matrix.cuda_version != '11.8' && 'cuda_packages=cuda-cudart cuda-nvrtc cuda-compiler libcublas-dev libcusolver libnvjitlink' || '' }}
registry_cache_from: ${{ needs.metadata.outputs.cache_base }}
update_registry_cache: ${{ needs.metadata.outputs.cache_target }}
environment: ${{ needs.metadata.outputs.environment }}
# needed only for the cloudposse GitHub action
matrix_key: ${{ matrix.platform }}-cu${{ matrix.cuda_version }}-ext
# This job is needed only when using the cloudposse GitHub action to read
# the output of a matrix job. This is a workaround due to current GitHub
# limitations that may not be needed if the work started here concludes:
# https://github.com/actions/runner/pull/2477
ext_config:
name: Configure build
needs: extdevdeps
runs-on: ubuntu-latest
outputs:
json: "${{ steps.read_json.outputs.result }}"
steps:
- uses: cloudposse/[email protected]
id: read_json
with:
matrix-step-name: dev_environment
docker_images:
name: Create Docker images
needs: [metadata, config, ext_config]
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms).ids }}
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/docker_images.yml
secrets:
NGC_CREDENTIALS: ${{ secrets.NGC_CREDENTIALS }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platforms: ${{ fromJson(needs.metadata.outputs.multi_platform || needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
cuda_version: ${{ matrix.cuda_version }}
ompidev_image: ${{ fromJson(needs.config.outputs.json).image_hash[format('{0}-cu{1}-ompi', matrix.platform, matrix.cuda_version)] }}
devdeps_image: ${{ fromJson(needs.ext_config.outputs.json).image_hash[format('{0}-cu{1}-ext', matrix.platform, matrix.cuda_version)] }}
build_docs: ${{ matrix.cuda_version == '11.8' }}
environment: ghcr-deployment
python_wheels:
name: Create Python wheels
needs: [metadata, config]
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.platforms).ids }}
python_version: ['3.10', '3.11', '3.12']
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/python_wheels.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platform: ${{ fromJson(needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
python_version: ${{ matrix.python_version }}
cuda_version: ${{ matrix.cuda_version }}
devdeps_image: ${{ fromJson(needs.config.outputs.json).image_hash[format('{0}-cu{1}-python', matrix.platform, matrix.cuda_version)] }}
create_staging_info: ${{ matrix.python_version == '3.11' }} # staging info is the same for all python versions
binaries:
name: Create CUDA Quantum installer
needs: [metadata, config]
strategy:
matrix:
platform: ${{ fromJson(needs.metadata.outputs.platforms).ids }}
cuda_version: ["11.8", "12.0"]
fail-fast: false
uses: ./.github/workflows/prebuilt_binaries.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_READONLY_TOKEN: ${{ secrets.DOCKERHUB_READONLY_TOKEN }}
with:
platform: ${{ fromJson(needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].docker_flag }}
platform_base_image: ${{ fromJson(needs.metadata.outputs.platforms)[format('{0}', matrix.platform)].minimal_base_image }}
build_config_id: cu${{ matrix.cuda_version }}-llvm
cuda_version: ${{ matrix.cuda_version }}
build_cache: ${{ fromJson(needs.config.outputs.json).build_cache[format('{0}-cu{1}-installer', matrix.platform, matrix.cuda_version)] }}
environment: ghcr-deployment