Skip to content
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
1b52124
build cuda-quantum-src image
mitchdz Feb 17, 2026
7100f97
add missing license, add spellcheck exclusion for docker md
mitchdz Feb 17, 2026
ad0039c
Merge remote-tracking branch 'origin/main' into cudaq-src
mitchdz Feb 17, 2026
e8f8795
Merge remote-tracking branch 'origin/main' into cudaq-src
mitchdz Feb 18, 2026
fd22df2
update bad function
mitchdz Feb 18, 2026
1531d44
Merge remote-tracking branch 'origin/main' into cuda-quantum-src
mitchdz Feb 18, 2026
97d0e96
build src build as part of deployments
mitchdz Feb 18, 2026
b14a9d7
make a new workflow for source diff
mitchdz Feb 20, 2026
2903cd9
allow workflow to be ran
mitchdz Feb 20, 2026
f758228
hardcode container values for debugging before workflow is in main
mitchdz Feb 20, 2026
4419915
also on: push:
mitchdz Feb 20, 2026
ae3d6b5
hardcode requirements
mitchdz Feb 20, 2026
1fadd87
modify entrypoint for docker run
mitchdz Feb 20, 2026
a2a8728
remove allowlist
mitchdz Feb 20, 2026
1cab1b8
paralellize apt/pip/tpls pulls; add prefix lines to stdout
mitchdz Feb 20, 2026
60bce40
Merge remote-tracking branch 'origin/main' into cuda-quantum-src
mitchdz Feb 20, 2026
829e58e
use target_image tag_suffix
mitchdz Feb 20, 2026
5ffaa3a
paralellize pip
mitchdz Feb 20, 2026
475a07b
temporary debug
mitchdz Feb 20, 2026
bd8f2f6
update repo owner and failed packages logs
mitchdz Feb 20, 2026
9ced375
DEBUG: arm64 lists as well
mitchdz Feb 20, 2026
3f837e0
add strategy matrix
mitchdz Feb 20, 2026
04c94cc
try full multi-arch build
mitchdz Feb 20, 2026
926c7cd
use only amd64 for auditing
mitchdz Feb 20, 2026
e624876
just use ubuntu-latest for diffing
mitchdz Feb 20, 2026
3d61bfe
only download apt sources
mitchdz Feb 20, 2026
877c3b4
Revert "only download apt sources"
mitchdz Feb 20, 2026
0755ec2
use zip for artifact extension;chmod 777 apt pulls
mitchdz Feb 20, 2026
ba4abfa
use ghcr.io/nvidia/ubuntu:24.04
mitchdz Feb 20, 2026
03c0c80
debug lock file
mitchdz Feb 20, 2026
c9bdd14
include omitted packages
mitchdz Feb 21, 2026
83be27f
fix typo, add TODO
mitchdz Feb 21, 2026
5957828
login to GHCR
mitchdz Feb 21, 2026
aa2121f
login to GHCR
mitchdz Feb 21, 2026
821c2b8
use regular ubuntu
mitchdz Feb 21, 2026
db8698a
also check cu12
mitchdz Feb 21, 2026
f0e1321
use base_image for the source build
mitchdz Feb 23, 2026
a55145e
get EULA of omitted apt packages
mitchdz Feb 23, 2026
73a4fe0
retain build scripts
mitchdz Feb 23, 2026
79dfed0
fix package typo
mitchdz Feb 23, 2026
4324473
use unearth for pip sdist fetching instead of native pip download
mitchdz Feb 23, 2026
47fa446
retrieve license from pip for omitted packages
mitchdz Feb 23, 2026
b610594
update workflow to push to NGC
mitchdz Feb 23, 2026
38ef25b
set up environment properly
mitchdz Feb 23, 2026
1b1451d
rename package_source_diff -> build_package_source
mitchdz Feb 23, 2026
18a7b14
create new workflow with on push
mitchdz Feb 23, 2026
089d2d9
undo unecessary delta
mitchdz Feb 23, 2026
ce32187
remove accidentally added file
mitchdz Feb 23, 2026
415cba3
Revert "undo unecessary delta"
mitchdz Feb 23, 2026
31c6bb3
remove docker README
mitchdz Feb 23, 2026
d87f366
Revert "Revert "undo unecessary delta""
mitchdz Feb 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/Untitled
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ghcr.io
193 changes: 193 additions & 0 deletions .github/workflows/build_package_sources.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# ============================================================================ #
# Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates. #
# All rights reserved. #
# #
# This source code and the accompanying materials are made available under #
# the terms of the Apache License 2.0 which accompanies this distribution. #
# ============================================================================ #
#
# Workflow: diff apt and pip packages between a base and target image, then
# build a source image containing source code for all added packages.
#
# Inputs:
# push_to_NGC: boolean, default false
# Push the built source image to NGC, otherwise push to GHCR.
# environment: string, no default
# The environment to build the source image for.

on:
push:
workflow_dispatch:
inputs:
push_to_NGC:
required: false
type: boolean
default: false
description: 'Push the built source image to NGC, otherwise push to GHCR.'
environment:
required: false
type: string

name: Build package sources

jobs:
diff-packages:
name: Diff apt/pip packages (CUDA ${{ matrix.cuda }})
runs-on: ubuntu-latest
outputs:
base_image: ${{ steps.images.outputs.base_image }}
strategy:
fail-fast: false
matrix:
cuda: ['12.6', '13.0']
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set image names
id: images
run: |
echo "base_image=nvcr.io/nvidia/cuda:${{ matrix.cuda }}.0-runtime-ubuntu24.04" >> $GITHUB_OUTPUT
cuda_major=$(echo "${{ matrix.cuda }}" | cut -d. -f1)
echo "target_image=nvcr.io/nvidia/nightly/cuda-quantum:cu${cuda_major}-latest" >> $GITHUB_OUTPUT

- name: Log in to container registries
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 2>/dev/null || true
echo "${{ secrets.DOCKERHUB_READONLY_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin 2>/dev/null || true

- name: Pull images
run: |
docker pull "${{ steps.images.outputs.base_image }}"
docker pull "${{ steps.images.outputs.target_image }}"

- name: Diff package lists
run: |
chmod +x scripts/diff_image_packages.sh
./scripts/diff_image_packages.sh \
"${{ steps.images.outputs.base_image }}" \
"${{ steps.images.outputs.target_image }}" \
package-source-diff

- name: Upload package lists
uses: actions/upload-artifact@v4
with:
name: package-source-diff-cu${{ matrix.cuda }}.zip
path: package-source-diff/
retention-days: 1

build-source-image:
name: Build source image (CUDA ${{ matrix.cuda }})
needs: diff-packages
runs-on: linux-amd64-cpu32
strategy:
fail-fast: false
matrix:
cuda: ['12.6', '13.0']
permissions:
contents: read
packages: write

environment:
name: ${{ inputs.environment || 'default' }}

steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive

- name: Download package lists
uses: actions/download-artifact@v4
with:
name: package-source-diff-cu${{ matrix.cuda }}.zip

- name: Restore package-source-diff directory layout
run: |
# Ensure package-source-diff/ exists with list files for Dockerfile COPY
if [ ! -f package-source-diff/apt_packages.txt ]; then
mkdir -p package-source-diff
mv apt_packages.txt pip_packages.txt package-source-diff/ 2>/dev/null || true
fi
touch package-source-diff/apt_packages.txt package-source-diff/pip_packages.txt
ls -la package-source-diff/

- name: Generate tpls lock file
run: |
chmod +x scripts/generate_tpls_lock.sh
./scripts/generate_tpls_lock.sh tpls_commits.lock
cat tpls_commits.lock
wc -l tpls_commits.lock || true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build source image
id: build
uses: docker/build-push-action@v5
with:
context: .
file: docker/build/package_sources.Dockerfile
build-args: |
base_image=${{ needs.diff-packages.outputs.base_image }}
load: true
tags: package-sources:latest
outputs: type=docker,dest=/tmp/package-sources.tar
push: false

- name: Log in to GitHub CR
if: github.event.inputs.push_to_NGC != 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}

- name: Log in to NGC
if: github.event.inputs.push_to_NGC == 'true'
uses: docker/login-action@v3
with:
registry: nvcr.io
username: '$oauthtoken'
password: ${{ secrets.NGC_CREDENTIALS }}

- name: Tag and push to GHCR or NGC
id: push
run: |
docker load --input /tmp/package-sources.tar
cuda_major=$(echo "${{ matrix.cuda }}" | cut -d. -f1)
target_image="nvcr.io/nvidia/nightly/cuda-quantum:cu${cuda_major}-latest"
tag_suffix="${target_image##*:}"
if [ "${{ github.event.inputs.push_to_NGC }}" = "true" ]; then
# Derive push destination from target_image: .../cuda-quantum:tag -> .../cuda-quantum-src:tag
repo_part="${target_image%:*}"
tag="${repo_part}-src:${tag_suffix}"
else
owner_lower=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
tag="ghcr.io/${owner_lower}/cuda-quantum-src:${tag_suffix}"
fi
docker tag package-sources:latest "$tag"
docker push "$tag"
echo "image_tag=$tag" | tee -a $GITHUB_OUTPUT

- name: Summary
run: |
base_image="${{ needs.diff-packages.outputs.base_image }}"
cuda_major=$(echo "${{ matrix.cuda }}" | cut -d. -f1); target_image="nvcr.io/nvidia/nightly/cuda-quantum:cu${cuda_major}-latest"
echo "## Package source diff (CUDA ${{ matrix.cuda }})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Base image:** \`${base_image}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Target image:** \`${target_image}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Apt packages added:** $(wc -l < package-source-diff/apt_packages.txt 2>/dev/null || echo 0)" >> $GITHUB_STEP_SUMMARY
echo "- **Pip packages added:** $(wc -l < package-source-diff/pip_packages.txt 2>/dev/null || echo 0)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Built image \`package-sources:latest\` (ubuntu:24.04 with sources under \`/sources/apt\`, \`/sources/pip\`, and \`/sources/tpls\`)." >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.push.outputs.image_tag }}" ]; then
echo "- **Pushed:** \`${{ steps.push.outputs.image_tag }}\`" >> $GITHUB_STEP_SUMMARY
fi
154 changes: 154 additions & 0 deletions docker/build/package_sources.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# ============================================================================ #
# Copyright (c) 2022 - 2026 NVIDIA Corporation & Affiliates. #
# All rights reserved. #
# #
# This source code and the accompanying materials are made available under #
# the terms of the Apache License 2.0 which accompanies this distribution. #
# ============================================================================ #
#
# Builds an Ubuntu 24.04 image containing source code for a set of apt and pip
# packages plus the repo's tpls/ (third-party library) source. Tpls are cloned
# at build time using .gitmodules and a lock file (commit + path per line) via
# git clone --no-checkout --filter=tree:0 + fetch + checkout.
#
# Build from repo root with package-source-diff/ and tpls_commits.lock (or generate with scripts/generate_tpls_lock.sh):
# docker build -t package-sources:latest -f docker/build/package_sources.Dockerfile .
#
# base_image is the base image to use for the build.
#
# Expects in build context:
# package-source-diff/apt_packages.txt - one apt package name per line
# package-source-diff/pip_packages.txt - one pip package==version per line
# tpls_commits.lock - "<commit> <path>" per submodule (same as install_prerequisites.sh -l)
# .gitmodules - submodule paths and URLs
# scripts/clone_tpls_from_lock.sh - clone script
# NOTICE, LICENSE - attribution

ARG base_image=ubuntu:24.04
FROM ${base_image}

SHELL ["/bin/bash", "-c"]
ARG DEBIAN_FRONTEND=noninteractive

# Install deps for fetching apt source, pip sdists, and cloning tpls
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
build-essential \
curl \
dpkg-dev \
git \
jq \
python3 \
python3-pip \
unzip \
&& python3 -m pip install --upgrade unearth --break-system-packages \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

# Install necessary repository for librdmac1
RUN apt-get update && apt-get install -y --no-install-recommends gnupg wget \
&& wget -qO - "https://www.mellanox.com/downloads/ofed/RPM-GPG-KEY-Mellanox" | apt-key add - \
&& mkdir -p /etc/apt/sources.list.d && wget -q -nc --no-check-certificate -P /etc/apt/sources.list.d "https://linux.mellanox.com/public/repo/mlnx_ofed/5.3-1.0.0.1/ubuntu20.04/mellanox_mlnx_ofed.list" \
&& echo 'deb-src http://linux.mellanox.com/public/repo/mlnx_ofed/5.3-1.0.0.1/ubuntu20.04/$(ARCH) ./' >> /etc/apt/sources.list.d/mellanox_mlnx_ofed.list \
&& apt-get update -y

# Enable source repositories (Ubuntu 24.04 DEB822 format)
RUN if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then \
sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources; \
else \
sed -i '/^# deb-src/s/^# //' /etc/apt/sources.list 2>/dev/null || true; \
fi
RUN apt-get update

ENV SOURCES_ROOT=/sources
RUN mkdir -p "${SOURCES_ROOT}/apt" "${SOURCES_ROOT}/pip" "${SOURCES_ROOT}/tpls" "${SOURCES_ROOT}/scripts"

ENV SCRIPTS_DIR=${SOURCES_ROOT}/scripts

# Copy .gitmodules, tpls lock file, clone script, package lists, and pip sdist fetcher
COPY .gitmodules "${SCRIPTS_DIR}"/.gitmodules
COPY tpls_commits.lock "${SCRIPTS_DIR}"/tpls_commits.lock
COPY scripts/clone_tpls_from_lock.sh "${SCRIPTS_DIR}"/clone_tpls_from_lock.sh
COPY package-source-diff/apt_packages.txt "${SCRIPTS_DIR}"/apt_packages.txt
COPY package-source-diff/pip_packages.txt "${SCRIPTS_DIR}"/pip_packages.txt

# Copy attribution
COPY NOTICE LICENSE "${SOURCES_ROOT}/"

# Fetch apt source, pip sdists, and clone tpls in parallel (prefix lines so logs stay readable)
RUN apt-get update && set -o pipefail && \
( set -o pipefail; cd "${SOURCES_ROOT}/apt" && \
chmod 777 . && \
: > "${SOURCES_ROOT}/apt/apt_omitted_packages.txt" && \
while IFS= read -r pkg || [ -n "$pkg" ]; do \
[ -z "$pkg" ] && continue; \
apt-get source -y "$pkg" || echo "$pkg" >> "${SOURCES_ROOT}/apt/apt_omitted_packages.txt"; \
done < "${SCRIPTS_DIR}"/apt_packages.txt; \
) 2>&1 | sed 's/^/[apt] /' & \
( set -o pipefail; : > "${SOURCES_ROOT}/pip/pip_omitted_packages.txt" && \
cd "${SOURCES_ROOT}/pip" && \
while IFS= read -r package || [ -n "$package" ]; do \
[ -z "$package" ] && continue; \
url=$(unearth --no-binary "$package" 2>/dev/null | jq -r '.link.url'); \
if [ -n "$url" ] && [ "$url" != "null" ]; then \
curl -fsSL -O "$url" || echo "$package" >> pip_omitted_packages.txt; \
else \
echo "$package" >> pip_omitted_packages.txt; \
fi; \
done < "${SCRIPTS_DIR}"/pip_packages.txt; \
) 2>&1 | sed 's/^/[pip] /' & \
( set -o pipefail; SOURCES_ROOT="${SOURCES_ROOT}" GITMODULES="${SCRIPTS_DIR}"/.gitmodules lock_file="${SCRIPTS_DIR}"/tpls_commits.lock \
bash "${SCRIPTS_DIR}"/clone_tpls_from_lock.sh ) 2>&1 | sed 's/^/[tpls] /' & \
wait

RUN echo -e "apt_omitted_packages.txt:\n$(cat ${SOURCES_ROOT}/apt/apt_omitted_packages.txt)"
RUN echo -e "pip_omitted_packages.txt:\n$(cat ${SOURCES_ROOT}/pip/pip_omitted_packages.txt)"

# For omitted apt packages (no source available), extract license/copyright/EULA from the .deb
RUN echo "Retrieving EULA/copyright for omitted apt packages..." && \
mkdir -p "${SOURCES_ROOT}/apt/licenses" /tmp/deb_extract && \
while IFS= read -r pkg || [ -n "$pkg" ]; do \
[ -z "$pkg" ] && continue; \
( cd /tmp/deb_extract && apt-get download "$pkg" 2>/dev/null ) || true; \
deb=$(ls /tmp/deb_extract/*.deb 2>/dev/null | head -1); \
if [ -n "$deb" ]; then \
dpkg-deb -R "$deb" "/tmp/deb_extract/${pkg}_pkg" 2>/dev/null || true; \
dest="${SOURCES_ROOT}/apt/licenses/${pkg}"; \
mkdir -p "$dest"; \
find "/tmp/deb_extract/${pkg}_pkg" \( -iname "*license*" -o -iname "*eula*" -o -iname "*copyright*" \) -exec cp -a {} "$dest/" \; 2>/dev/null || true; \
rm -rf "/tmp/deb_extract/${pkg}_pkg" /tmp/deb_extract/*.deb; \
fi; \
done < "${SOURCES_ROOT}/apt/apt_omitted_packages.txt"; \
rm -rf /tmp/deb_extract

# For omitted pip packages (no sdist), get EULA/license from the wheel: fetch wheel from PyPI, extract, copy license/EULA/copyright files
RUN echo "Retrieving EULA/license for omitted pip packages..." && \
mkdir -p "${SOURCES_ROOT}/pip/licenses" /tmp/wheel_extract && \
while IFS= read -r package || [ -n "$package" ]; do \
[ -z "$package" ] && continue; \
name="${package%%==*}"; \
version="${package#*==}"; \
[ -z "$name" ] || [ -z "$version" ] || [ "$version" = "$package" ] && continue; \
url=$(curl -sS "https://pypi.org/pypi/${name}/${version}/json" 2>/dev/null | jq -r '.urls[] | select(.packagetype=="bdist_wheel") | select(.filename | test("manylinux.*x86_64|manylinux_2.*x86_64")) | .url' 2>/dev/null | head -1); \
if [ -n "$url" ] && [ "$url" != "null" ]; then \
if curl -fsSL -o /tmp/pip_wheel.whl "$url" 2>/dev/null; then \
(cd /tmp/wheel_extract && unzip -o -q /tmp/pip_wheel.whl 2>/dev/null) || true; \
dest="${SOURCES_ROOT}/pip/licenses/${name}"; \
mkdir -p "$dest"; \
find /tmp/wheel_extract -type f \( -iname "*license*" -o -iname "*eula*" -o -iname "*copyright*" \) -exec cp -an {} "$dest/" \; 2>/dev/null || true; \
if [ -z "$(ls -A "$dest" 2>/dev/null)" ]; then \
license_text=$(curl -sS "https://pypi.org/pypi/${name}/${version}/json" 2>/dev/null | jq -r '.info.license // .info.license_expression // empty'); \
[ -n "$license_text" ] && [ "$license_text" != "null" ] && echo "$license_text" > "$dest/LICENSE_from_PyPI.txt"; \
fi; \
find /tmp/wheel_extract -mindepth 1 -delete 2>/dev/null || rm -rf /tmp/wheel_extract/*; \
fi; \
rm -f /tmp/pip_wheel.whl; \
fi; \
done < "${SOURCES_ROOT}/pip/pip_omitted_packages.txt"; \
rm -rf /tmp/wheel_extract

# Summary
RUN echo "apt: $(find ${SOURCES_ROOT}/apt -maxdepth 1 -type d 2>/dev/null | wc -l) dirs" && \
echo "pip: $(find ${SOURCES_ROOT}/pip -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.zip' \) 2>/dev/null | wc -l) sdists" && \
echo "tpls: $(find ${SOURCES_ROOT}/tpls -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l) libraries"

WORKDIR ${SOURCES_ROOT}
21 changes: 21 additions & 0 deletions docker/release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,24 @@ The CUDA-Q installation in this image is licensed under [Apache License
2.0](https://www.apache.org/licenses/LICENSE-2.0). More information about the
license and third party libraries can be found in the LICENSE and NOTICE files
of the CUDA-Q installation folder defined by `CUDA_QUANTUM_PATH`.

## Source image (cuda-quantum-src)

The ``cudaq-src.Dockerfile`` builds an image that extends the CUDA-Q base image
with the source code of the third-party libraries (tpls) used by CUDA-Q, e.g.
fmt, spdlog, qpp, Crow, Stim, LLVM, Eigen, etc. (see the repository ``NOTICE``
file). Source is placed under ``/opt/nvidia/cudaq-tpls-src`` (environment
variable ``CUDAQ_TPL_SRC_ROOT``). NOTICE and LICENSE are included there for
attribution.

Build from the repository root with submodules initialized::

git submodule update --init --recursive
docker build -t nvcr.io/nvidia/nightly/cuda-quantum-src:cu12-latest-base \\
-f docker/release/cudaq-src.Dockerfile .

To use a different base image::

docker build -t nvcr.io/nvidia/nightly/cuda-quantum-src:cu12-latest-base \\
--build-arg base_image=nvcr.io/nvidia/nightly/cuda-quantum:cu12-latest-base \\
-f docker/release/cudaq-src.Dockerfile .
Loading
Loading