Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions .github/workflows/container-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ jobs:
- name: Check Dockerfile
run: docker buildx build --check --file images/generic/Dockerfile .
- name: Check image target
run: docker buildx bake --file docker-bake.hcl --print runtime
run: >-
docker buildx bake --file docker-bake.hcl --print
runtime runtime-gpu-nvidia
- name: Build the no-weight image and run the isolated OpenHands stack
env:
DOCKER_DEFAULT_PLATFORM: ${{ matrix.platform }}
Expand Down Expand Up @@ -134,7 +136,9 @@ jobs:
- name: Check platform Dockerfile
run: docker buildx build --check --platform linux/amd64 --file images/platform/Dockerfile .
- name: Check Terra image targets
run: docker buildx bake --file docker-bake.hcl --print terra-runtime terra-ci
run: >-
docker buildx bake --file docker-bake.hcl --print
terra-runtime terra-runtime-gpu-nvidia terra-ci
- name: Build Terra-compatible CI base
run: >-
docker buildx build --load --platform linux/amd64
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@ jobs:
persist-credentials: false
- name: Review dependencies
uses: actions/dependency-review-action@v5.0.0
with:
# vLLM 0.25.0 directly requires diskcache 5.6.3 and torch 2.11.0;
# neither advisory has a patched release. Heartwood does not load
# caches across trust domains or expose torch.jit.script to input.
# A repository compliance test confines both packages to the hashed
# GPU lock. Revisit these exact exceptions with every vLLM update.
allow-ghsas: GHSA-w8v5-vhqr-4h9v, GHSA-rrmf-rvhw-rf47
239 changes: 239 additions & 0 deletions .github/workflows/gpu-container-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
# This source file is part of the Heartwood open-source project
#
# SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT

name: GPU Container Image

on:
pull_request:
paths:
- ".github/workflows/gpu-container-image.yml"
- "docker-bake.hcl"
- "images/generic/Dockerfile"
- "images/platform/Dockerfile"
- "images/gpu/**"
- "images/generic/image-flavors.toml"
- "images/platforms.toml"
push:
branches: [main]
workflow_dispatch:

concurrency:
group: gpu-container-image-${{ github.ref }}
cancel-in-progress: false

jobs:
contract:
name: Validate GPU image contracts
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Validate build graph
run: >-
docker buildx bake --file docker-bake.hcl --print
runtime-gpu-nvidia terra-runtime-gpu-nvidia
> /tmp/gpu-build-graph.json
- name: Validate runtime lock and launcher
run: |
grep --fixed-strings 'vllm==0.25.0' images/gpu/vllm-requirements.txt
grep --fixed-strings 'torch==2.11.0' images/gpu/vllm-requirements.txt
grep --fixed-strings -- '--hash=sha256:' images/gpu/vllm-requirements.txt
bash -n images/gpu/start_vllm.sh
test -x images/gpu/start_vllm.sh

pull-request-build:
name: Build GPU candidate ${{ matrix.target }}
if: github.event_name == 'pull_request'
needs: contract
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
fail-fast: false
matrix:
target: [runtime-gpu-nvidia, terra-runtime-gpu-nvidia]
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
persist-credentials: false
- name: Reclaim runner space for local image verification
run: |
df --human-readable /
sudo rm -rf \
/opt/ghc \
/usr/local/.ghcup \
/usr/local/lib/android \
/usr/share/dotnet \
/usr/share/swift
df --human-readable /
- name: Build candidate locally
env:
TARGET: ${{ matrix.target }}
run: |
docker buildx bake --file docker-bake.hcl \
--set "${TARGET}.tags=heartwood-gpu-pr:${TARGET}" \
--set "${TARGET}.output=type=docker" \
--set "${TARGET}.attest=type=sbom,disabled=true" \
--set "${TARGET}.attest=type=provenance,disabled=true" \
"${TARGET}"
- name: Verify candidate contents
env:
TARGET: ${{ matrix.target }}
run: |
image="heartwood-gpu-pr:${TARGET}"
docker run --rm --platform linux/amd64 --entrypoint /opt/heartwood-vllm/bin/python \
"${image}" -c 'from importlib.metadata import version; print(version("vllm"))'
test -z "$(docker run --rm --platform linux/amd64 --entrypoint find "${image}" \
/opt /home -type f -size +10M \
\( -name '*.gguf' -o -name '*.safetensors' -o -name '*.bin' \) -print)"

build:
name: Build And Validate ${{ matrix.target }}
if: github.ref == 'refs/heads/main'
needs: contract
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- target: runtime-gpu-nvidia
commit_suffix: gpu-nvidia
flatten: "false"
media_output: type=image,push-by-digest=true,name-canonical=true,push=true
- target: terra-runtime-gpu-nvidia
commit_suffix: terra-gpu-nvidia
flatten: "true"
media_output: type=image,push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=false
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set image name
id: image
run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "${GITHUB_OUTPUT}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build candidate by digest
id: candidate
env:
IMAGE_NAME: ${{ steps.image.outputs.name }}
TARGET: ${{ matrix.target }}
MEDIA_OUTPUT: ${{ matrix.media_output }}
run: |
docker buildx bake --file docker-bake.hcl \
--set "${TARGET}.tags=${IMAGE_NAME}" \
--set "${TARGET}.output=${MEDIA_OUTPUT},name=${IMAGE_NAME}" \
--metadata-file /tmp/gpu-metadata.json \
"${TARGET}"
digest="$(python3 images/scripts/read_buildx_digest.py /tmp/gpu-metadata.json "${TARGET}")"
echo "digest=${digest}" >> "${GITHUB_OUTPUT}"
echo "reference=${IMAGE_NAME}@${digest}" >> "${GITHUB_OUTPUT}"
- name: Verify candidate contents
env:
CANDIDATE: ${{ steps.candidate.outputs.reference }}
run: |
docker pull --platform linux/amd64 "${CANDIDATE}"
docker run --rm --platform linux/amd64 --entrypoint /opt/heartwood-vllm/bin/python \
"${CANDIDATE}" -c 'from importlib.metadata import version; print(version("vllm"))'
test -z "$(docker run --rm --platform linux/amd64 --entrypoint find "${CANDIDATE}" \
/opt /home -type f -size +10M \
\( -name '*.gguf' -o -name '*.safetensors' -o -name '*.bin' \) -print)"
- name: Create and verify immutable commit tag
env:
CANDIDATE: ${{ steps.candidate.outputs.reference }}
CANDIDATE_DIGEST: ${{ steps.candidate.outputs.digest }}
COMMIT_SUFFIX: ${{ matrix.commit_suffix }}
FLATTEN: ${{ matrix.flatten }}
GIT_SHA: ${{ github.sha }}
IMAGE_NAME: ${{ steps.image.outputs.name }}
run: |
commit_ref="${IMAGE_NAME}:sha-${GIT_SHA}-${COMMIT_SUFFIX}"
if inspect="$(docker buildx imagetools inspect "${commit_ref}" 2>/dev/null)"; then
commit_digest="$(awk '$1 == "Digest:" {print $2; exit}' <<<"${inspect}")"
else
create_args=()
if [ "${FLATTEN}" = "true" ]; then
create_args+=(--prefer-index=false)
fi
docker buildx imagetools create "${create_args[@]}" \
--tag "${commit_ref}" "${CANDIDATE}"
inspect="$(docker buildx imagetools inspect "${commit_ref}")"
commit_digest="$(awk '$1 == "Digest:" {print $2; exit}' <<<"${inspect}")"
fi
if [ "${commit_digest}" != "${CANDIDATE_DIGEST}" ]; then
echo "immutable GPU commit tag does not match the validated candidate" >&2
exit 1
fi

promote:
name: Promote GPU Channel Tags
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set image name
id: image
run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "${GITHUB_OUTPUT}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Promote validated generic and Terra tags
env:
GIT_SHA: ${{ github.sha }}
IMAGE_NAME: ${{ steps.image.outputs.name }}
run: |
current_main="$(git ls-remote "https://github.com/${GITHUB_REPOSITORY}.git" refs/heads/main | awk '{print $1}')"
if [ "${current_main}" != "${GIT_SHA}" ]; then
echo "refusing to move GPU channel tags from a stale main workflow" >&2
exit 1
fi
for tuple in \
"gpu-nvidia edge-gpu-nvidia false" \
"terra-gpu-nvidia edge-terra-gpu-nvidia true"; do
read -r suffix channel flatten <<<"${tuple}"
commit_ref="${IMAGE_NAME}:sha-${GIT_SHA}-${suffix}"
commit_digest="$(docker buildx imagetools inspect "${commit_ref}" | awk '$1 == "Digest:" {print $2; exit}')"
create_args=()
if [ "${flatten}" = "true" ]; then
create_args+=(--prefer-index=false)
fi
docker buildx imagetools create "${create_args[@]}" \
--tag "${IMAGE_NAME}:${channel}" "${commit_ref}"
channel_digest="$(docker buildx imagetools inspect "${IMAGE_NAME}:${channel}" | awk '$1 == "Digest:" {print $2; exit}')"
if [ "${channel_digest}" != "${commit_digest}" ]; then
echo "promoted ${channel} digest does not match ${commit_ref}" >&2
exit 1
fi
done
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ Do not add protected health information, credentials, live-platform identifiers,
| [Container Images](docs/container-images.md) | Tags, model storage, providers, and CI |
| [Platform Image Extension Guide](docs/platform-images.md) | Thin platform image mechanism |
| [Terra Jupyter Demo](docs/terra-jupyter-demo.md) | Synthetic Terra workflow |
| [Carina CLI Pilot](docs/carina-cli.md) | Synthetic native GPU and Stanford AI API Gateway workflow |

## Future Work

Expand Down
1 change: 1 addition & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ path = [
"LICENSE",
".python-version",
"uv.lock",
"images/gpu/vllm-requirements.txt",
"**/py.typed",
"**/__pycache__/**",
"fixtures/**/*.csv",
Expand Down
42 changes: 42 additions & 0 deletions deploy/carina/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# This source file is part of the Heartwood open-source project
#
# SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT

set -euo pipefail

root=""
while (($#)); do
case "$1" in
--environment-root) root="${2:?missing environment root}"; shift 2 ;;
*) echo "unknown argument: $1" >&2; exit 64 ;;
esac
done
: "${root:?--environment-root is required}"

if ! command -v micromamba >/dev/null 2>&1; then
echo "micromamba is required; load the supported Carina module first" >&2
exit 69
fi
if [[ ! -f pyproject.toml || ! -f uv.lock ]]; then
echo "run this command from the Heartwood repository root" >&2
exit 66
fi

mkdir -p "${root}"
if [[ -x "${root}/bootstrap/bin/uv" && -d "${root}/bootstrap/conda-meta" ]]; then
micromamba install --yes --prefix "${root}/bootstrap" --file deploy/carina/environment.yml
else
rm -rf "${root}/bootstrap"
micromamba create --yes --prefix "${root}/bootstrap" --file deploy/carina/environment.yml
fi
export UV_PROJECT_ENVIRONMENT="${root}/heartwood"
"${root}/bootstrap/bin/uv" sync --locked --no-dev --all-extras --python 3.12
"${root}/bootstrap/bin/uv" venv "${root}/vllm" --python 3.12
"${root}/bootstrap/bin/uv" pip sync \
--require-hashes --python "${root}/vllm/bin/python" images/gpu/vllm-requirements.txt

printf 'Heartwood: %s\n' "$("${root}/heartwood/bin/heartwood" --version)"
printf 'vLLM: %s\n' "$("${root}/vllm/bin/python" -c 'from importlib.metadata import version; print(version("vllm"))')"
13 changes: 13 additions & 0 deletions deploy/carina/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This source file is part of the Heartwood open-source project
#
# SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT

name: heartwood-carina-bootstrap
channels:
- conda-forge
dependencies:
- python=3.12
- uv=0.11.28
- git
26 changes: 26 additions & 0 deletions deploy/carina/interactive.sbatch
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# This source file is part of the Heartwood open-source project
#
# SPDX-FileCopyrightText: 2026 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT

#SBATCH --job-name=heartwood-interactive
#SBATCH --partition=gpu
#SBATCH --gres=gpu:1
#SBATCH --cpus-per-task=8
#SBATCH --mem=64G
#SBATCH --time=02:00:00

set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

: "${HEARTWOOD_ENVIRONMENT_ROOT:?set HEARTWOOD_ENVIRONMENT_ROOT}"
: "${HEARTWOOD_MODEL_ROOT:?set HEARTWOOD_MODEL_ROOT}"
: "${HEARTWOOD_STATE_ROOT:?set HEARTWOOD_STATE_ROOT}"

exec "${script_dir}/launch-interactive.sh" \
--environment-root "${HEARTWOOD_ENVIRONMENT_ROOT}" \
--model-root "${HEARTWOOD_MODEL_ROOT}" \
--state-root "${HEARTWOOD_STATE_ROOT}"
Loading
Loading