Skip to content

fix(code): trust Hub host for code-server links #289

fix(code): trust Hub host for code-server links

fix(code): trust Hub host for code-server links #289

Workflow file for this run

# Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved.
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
name: Build Docker Images
on:
push:
branches: [main, develop]
tags: ['v*']
paths:
- 'dockerfiles/**'
- 'runtime/hub/**'
- 'runtime/notebook/**'
- 'runtime/shared/**'
- 'runtime/code-server/**'
- 'projects/**'
- '.github/workflows/docker-build.yml'
- '.github/build-config.json'
pull_request:
branches: [main, develop]
paths:
- 'dockerfiles/**'
- 'runtime/hub/**'
- 'runtime/notebook/**'
- 'runtime/shared/**'
- 'runtime/code-server/**'
- 'projects/**'
- '.github/workflows/docker-build.yml'
- '.github/build-config.json'
workflow_dispatch:
inputs:
version:
description: 'Version tag for images (e.g. v1.2.0)'
required: false
default: ''
images:
description: 'Which images to build'
required: true
default: 'all'
type: choice
options:
- all
- hub
- code
- code-cpu
- code-gpu
- courses
- base-cpu
- base-gpu
gpu_target:
description: 'GPU target (all = build every target)'
required: false
default: 'all'
type: choice
options:
- all
- gfx110x
- gfx1150
- gfx1151
- gfx1152
- gfx120x
# Build matrix is defined in .github/build-config.json
# Prevent duplicate builds on the same branch / PR
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: write
jobs:
# ──────────────────────────────────────────────
# Detect which files changed to decide what to build
# ──────────────────────────────────────────────
changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
hub: ${{ steps.filter.outputs.hub }}
code: ${{ steps.filter.outputs.code }}
courses: ${{ steps.filter.outputs.courses }}
base-cpu: ${{ steps.filter.outputs.base-cpu }}
base-gpu: ${{ steps.filter.outputs.base-gpu }}
registry: ${{ steps.registry.outputs.prefix }}
is-tag: ${{ steps.check.outputs.is-tag }}
gpu-targets: ${{ steps.config.outputs.targets }}
gpu-map: ${{ steps.config.outputs.gpu-map }}
default-gpu-target: ${{ steps.config.outputs.default }}
course-list: ${{ steps.config.outputs.courses }}
image-name-suffix: ${{ steps.names.outputs.suffix }}
hub-image: ${{ steps.names.outputs.hub }}
code-cpu-image: ${{ steps.names.outputs.code-cpu }}
code-gpu-image: ${{ steps.names.outputs.code-gpu }}
base-cpu-image: ${{ steps.names.outputs.base-cpu }}
base-gpu-image: ${{ steps.names.outputs.base-gpu }}
steps:
- uses: actions/checkout@v4
- name: Set registry prefix
id: registry
run: echo "prefix=ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
- name: Resolve image names
id: names
env:
# Repo-level variable. Leave unset in upstream (public) repo → "" → names
# stay as auplc-base / auplc-cv / … Set to e.g. "-dev" in a fork so every
# published image (and the BASE_IMAGE consumed by course builds) flips to
# auplc-base-dev / auplc-cv-dev / … Keeps forks from colliding with
# upstream packages and lets dev branches self-reference their own base.
SUFFIX: ${{ vars.IMAGE_NAME_SUFFIX }}
REGISTRY: ${{ steps.registry.outputs.prefix }}
run: |
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "hub=${REGISTRY}/auplc-hub${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "code-cpu=${REGISTRY}/auplc-code-cpu${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "code-gpu=${REGISTRY}/auplc-code-gpu${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "base-cpu=${REGISTRY}/auplc-default${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "base-gpu=${REGISTRY}/auplc-base${SUFFIX}" >> "$GITHUB_OUTPUT"
- name: Check if tag push
id: check
run: |
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
echo "is-tag=true" >> "$GITHUB_OUTPUT"
else
echo "is-tag=false" >> "$GITHUB_OUTPUT"
fi
- name: Load build config
id: config
run: |
CONFIG='.github/build-config.json'
ALL_NAMES=$(jq -c '[.gpu_targets[].name]' "$CONFIG")
DEFAULT=$(jq -r '.default_gpu_target' "$CONFIG")
COURSES=$(jq -c '.courses' "$CONFIG")
GPU_MAP=$(jq -c '.gpu_targets' "$CONFIG")
INPUT="${{ github.event.inputs.gpu_target }}"
if [[ -n "$INPUT" && "$INPUT" != "all" ]]; then
echo "targets=[\"${INPUT}\"]" >> "$GITHUB_OUTPUT"
else
echo "targets=${ALL_NAMES}" >> "$GITHUB_OUTPUT"
fi
echo "default=${DEFAULT}" >> "$GITHUB_OUTPUT"
echo "courses=${COURSES}" >> "$GITHUB_OUTPUT"
echo "gpu-map=${GPU_MAP}" >> "$GITHUB_OUTPUT"
- uses: dorny/paths-filter@v3
if: github.ref_type != 'tag' && github.event_name != 'workflow_dispatch'
id: filter
with:
filters: |
hub:
- 'dockerfiles/Hub/**'
- 'runtime/hub/**'
code:
- 'dockerfiles/Code/**'
- 'runtime/code-server/**'
- 'runtime/shared/**'
courses:
- 'dockerfiles/Courses/**'
- 'dockerfiles/Base/**'
- 'projects/**'
base-cpu:
- 'dockerfiles/Base/Dockerfile.cpu'
- 'runtime/notebook/**'
- 'runtime/shared/**'
- 'runtime/package.json'
- 'runtime/.npmrc'
- 'runtime/pnpm-lock.yaml'
- 'runtime/pnpm-workspace.yaml'
- 'runtime/hub/frontend/tsconfig.base.json'
base-gpu:
- 'dockerfiles/Base/Dockerfile.rocm'
- 'runtime/notebook/**'
- 'runtime/shared/**'
- 'runtime/package.json'
- 'runtime/.npmrc'
- 'runtime/pnpm-lock.yaml'
- 'runtime/pnpm-workspace.yaml'
- 'runtime/hub/frontend/tsconfig.base.json'
- '.github/build-config.json'
# ──────────────────────────────────────────────
# Hub Image (CPU only, runs on GitHub Actions)
# ──────────────────────────────────────────────
build-hub:
name: Build Hub Image
needs: changes
if: |
needs.changes.outputs.is-tag == 'true' ||
(github.event_name != 'workflow_dispatch' && needs.changes.outputs.hub == 'true') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.images == 'all' || github.event.inputs.images == 'hub'))
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.hub-image }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=pr
- name: Build and push Hub image
uses: docker/build-push-action@v6
with:
context: .
file: dockerfiles/Hub/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=hub
cache-to: type=gha,mode=max,scope=hub
provenance: false
# ──────────────────────────────────────────────
# Code CPU Image (code-server environment)
# ──────────────────────────────────────────────
build-code-cpu:
name: Build Code CPU Image
needs: [changes, build-base-cpu]
if: |
always() &&
needs.changes.result == 'success' &&
(needs.build-base-cpu.result == 'success' || needs.build-base-cpu.result == 'skipped') &&
(
needs.changes.outputs.is-tag == 'true' ||
(github.event_name != 'workflow_dispatch' && (needs.changes.outputs.code == 'true' || needs.changes.outputs.base-cpu == 'true')) ||
(
github.event_name == 'workflow_dispatch' &&
(
github.event.inputs.images == 'all' ||
github.event.inputs.images == 'code' ||
github.event.inputs.images == 'code-cpu' ||
github.event.inputs.images == 'base-cpu'
)
)
)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- uses: pnpm/action-setup@v4
with:
version: 10.27.0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: pnpm
cache-dependency-path: runtime/pnpm-lock.yaml
- name: Package Hub link extension
working-directory: runtime
run: |
pnpm install --frozen-lockfile --filter auplc-hub-link...
rm -f code-server/extensions/auplc-hub-link/auplc-hub-link-*.vsix
HUB_LINK_VERSION=$(node -p "require('./code-server/extensions/auplc-hub-link/package.json').version")
pnpm --filter @auplc/runtime-status run build
pnpm --filter auplc-hub-link run build
test -f "code-server/extensions/auplc-hub-link/auplc-hub-link-${HUB_LINK_VERSION}.vsix"
- name: Clean Hub link build artifacts
if: always()
run: |
rm -rf runtime/code-server/extensions/auplc-hub-link/node_modules
rm -rf runtime/code-server/extensions/auplc-hub-link/out
rm -f runtime/code-server/extensions/auplc-hub-link/auplc-hub-link-*.vsix
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.code-cpu-image }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=pr
- name: Build and push Code CPU image
uses: docker/build-push-action@v6
with:
context: .
file: dockerfiles/Code/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
BASE_IMAGE=${{ needs.changes.outputs.base-cpu-image }}:latest
IMAGE_FLAVOR=cpu
cache-from: type=gha,scope=code-cpu
cache-to: type=gha,mode=max,scope=code-cpu
provenance: false
# ──────────────────────────────────────────────
# Base CPU Image (runs on GitHub Actions)
# ──────────────────────────────────────────────
build-base-cpu:
name: Build Base CPU Image
needs: changes
if: |
needs.changes.outputs.is-tag == 'true' ||
(github.event_name != 'workflow_dispatch' && needs.changes.outputs.base-cpu == 'true') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.images == 'all' || github.event.inputs.images == 'base-cpu'))
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.base-cpu-image }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=pr
- name: Build and push Base CPU image
uses: docker/build-push-action@v6
with:
context: .
file: dockerfiles/Base/Dockerfile.cpu
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=base-cpu
cache-to: type=gha,mode=max,scope=base-cpu
provenance: false
# ──────────────────────────────────────────────
# Code GPU Image (code-server environment)
# ──────────────────────────────────────────────
build-code-gpu:
name: "Build Code GPU Image (${{ matrix.gpu_target }})"
needs: [changes, build-base-gpu]
if: |
always() &&
needs.changes.result == 'success' &&
(needs.build-base-gpu.result == 'success' || needs.build-base-gpu.result == 'skipped') &&
(
needs.changes.outputs.is-tag == 'true' ||
(github.event_name != 'workflow_dispatch' && (needs.changes.outputs.code == 'true' || needs.changes.outputs.base-gpu == 'true')) ||
(
github.event_name == 'workflow_dispatch' &&
(
github.event.inputs.images == 'all' ||
github.event.inputs.images == 'code' ||
github.event.inputs.images == 'code-gpu' ||
github.event.inputs.images == 'base-gpu'
)
)
)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
gpu_target: ${{ fromJSON(needs.changes.outputs.gpu-targets) }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- uses: pnpm/action-setup@v4
with:
version: 10.27.0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: pnpm
cache-dependency-path: runtime/pnpm-lock.yaml
- name: Package Hub link extension
working-directory: runtime
run: |
pnpm install --frozen-lockfile --filter auplc-hub-link...
rm -f code-server/extensions/auplc-hub-link/auplc-hub-link-*.vsix
HUB_LINK_VERSION=$(node -p "require('./code-server/extensions/auplc-hub-link/package.json').version")
pnpm --filter @auplc/runtime-status run build
pnpm --filter auplc-hub-link run build
test -f "code-server/extensions/auplc-hub-link/auplc-hub-link-${HUB_LINK_VERSION}.vsix"
- name: Clean Hub link build artifacts
if: always()
run: |
rm -rf runtime/code-server/extensions/auplc-hub-link/node_modules
rm -rf runtime/code-server/extensions/auplc-hub-link/out
rm -f runtime/code-server/extensions/auplc-hub-link/auplc-hub-link-*.vsix
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata (target-suffixed tags)
id: meta-suffixed
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.code-gpu-image }}
flavor: |
suffix=-${{ matrix.gpu_target }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Docker metadata (unsuffixed tags — default target only)
if: matrix.gpu_target == needs.changes.outputs.default-gpu-target
id: meta-default
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.code-gpu-image }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Merge tags
id: tags
run: |
TAGS="${{ steps.meta-suffixed.outputs.tags }}"
if [ -n "${{ steps.meta-default.outputs.tags }}" ]; then
TAGS="${TAGS}
${{ steps.meta-default.outputs.tags }}"
fi
TAGS=$(echo "$TAGS" | sort -u)
{
echo "tags<<EOF"
echo "$TAGS"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Build and push Code GPU image
uses: docker/build-push-action@v6
with:
context: .
file: dockerfiles/Code/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.tags.outputs.tags }}
labels: ${{ steps.meta-suffixed.outputs.labels }}
build-args: |
BASE_IMAGE=${{ needs.changes.outputs.base-gpu-image }}:latest-${{ matrix.gpu_target }}
IMAGE_FLAVOR=gpu
cache-from: type=gha,scope=code-gpu-${{ matrix.gpu_target }}
cache-to: type=gha,mode=max,scope=code-gpu-${{ matrix.gpu_target }}
provenance: false
# ──────────────────────────────────────────────
# Base GPU Image (multi-target via GPU_TARGET)
#
# No GPU hardware required — only installs ROCm apt packages + pip wheels.
# Uses free-disk-space action to reclaim runner disk for the large image.
# Builds GPU targets in parallel via matrix strategy.
# ──────────────────────────────────────────────
build-base-gpu:
name: "Build Base GPU Image (${{ matrix.gpu_target }})"
needs: changes
if: |
needs.changes.outputs.is-tag == 'true' ||
(github.event_name != 'workflow_dispatch' && needs.changes.outputs.base-gpu == 'true') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.images == 'all' || github.event.inputs.images == 'base-gpu'))
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
gpu_target: ${{ fromJSON(needs.changes.outputs.gpu-targets) }}
outputs:
image: ${{ steps.out.outputs.image }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve GPU config
id: suffix
run: |
echo "tag_suffix=${{ matrix.gpu_target }}" >> "$GITHUB_OUTPUT"
# Look up target-specific build metadata; fall back to gpu_target if not found
WHL=$(echo '${{ needs.changes.outputs.gpu-map }}' | jq -r --arg t "${{ matrix.gpu_target }}" '.[] | select(.name == $t) | .pytorch_whl // .name')
SDK=$(echo '${{ needs.changes.outputs.gpu-map }}' | jq -r --arg t "${{ matrix.gpu_target }}" '.[] | select(.name == $t) | .rocm_sdk // .name')
echo "pytorch_whl=${WHL}" >> "$GITHUB_OUTPUT"
echo "rocm_sdk=${SDK}" >> "$GITHUB_OUTPUT"
- name: Docker metadata (target-suffixed tags)
id: meta-suffixed
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.base-gpu-image }}
flavor: |
suffix=-${{ steps.suffix.outputs.tag_suffix }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Docker metadata (unsuffixed tags — default target only)
if: matrix.gpu_target == needs.changes.outputs.default-gpu-target
id: meta-default
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.base-gpu-image }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Merge tags
id: tags
run: |
TAGS="${{ steps.meta-suffixed.outputs.tags }}"
if [ -n "${{ steps.meta-default.outputs.tags }}" ]; then
TAGS="${TAGS}
${{ steps.meta-default.outputs.tags }}"
fi
# Deduplicate
TAGS=$(echo "$TAGS" | sort -u)
{
echo "tags<<EOF"
echo "$TAGS"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Build and push Base GPU image
uses: docker/build-push-action@v6
with:
context: .
file: dockerfiles/Base/Dockerfile.rocm
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.tags.outputs.tags }}
labels: ${{ steps.meta-suffixed.outputs.labels }}
build-args: |
GPU_TARGET=${{ matrix.gpu_target }}
ROCM_SDK_TARGET=${{ steps.suffix.outputs.rocm_sdk }}
PYTORCH_WHL_TARGET=${{ steps.suffix.outputs.pytorch_whl }}
cache-from: type=gha,scope=base-gpu-${{ matrix.gpu_target }}
cache-to: type=gha,mode=max,scope=base-gpu-${{ matrix.gpu_target }}
provenance: false
- name: Export first image tag for downstream jobs
if: github.event_name != 'pull_request'
id: out
run: echo "image=$(echo '${{ steps.tags.outputs.tags }}' | head -1)" >> "$GITHUB_OUTPUT"
# ──────────────────────────────────────────────
# Course Images (CV, DL, LLM, PhySim)
#
# These images use `FROM ghcr.io/amdresearch/auplc-base:latest`
# which is pre-built and pushed to GHCR. The build itself only copies
# files and installs pip packages — no GPU required.
#
# BASE_IMAGE build-arg overrides the default so course images
# can track the latest GPU base image version from CI.
#
# Each course is built for every configured GPU target (course × gpu_target matrix).
# ──────────────────────────────────────────────
build-courses:
name: "Build Course: ${{ matrix.course }} (${{ matrix.gpu_target }})"
needs: [changes, build-base-gpu]
if: |
always() &&
needs.changes.result == 'success' &&
(needs.build-base-gpu.result == 'success' || needs.build-base-gpu.result == 'skipped') &&
(
needs.changes.outputs.is-tag == 'true' ||
(github.event_name != 'workflow_dispatch' && needs.changes.outputs.courses == 'true') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.images == 'all' || github.event.inputs.images == 'courses' || github.event.inputs.images == 'base-gpu'))
)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
course: ${{ fromJSON(needs.changes.outputs.course-list) }}
gpu_target: ${{ fromJSON(needs.changes.outputs.gpu-targets) }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare build context
run: cp -r projects/${{ matrix.course }} dockerfiles/Courses/${{ matrix.course }}/course_data
- name: Derive image name and tag suffix
id: meta-inputs
env:
NAME_SUFFIX: ${{ needs.changes.outputs.image-name-suffix }}
run: |
COURSE_LC=$(echo '${{ matrix.course }}' | tr '[:upper:]' '[:lower:]')
echo "image_name=auplc-${COURSE_LC}${NAME_SUFFIX}" >> "$GITHUB_OUTPUT"
echo "tag_suffix=${{ matrix.gpu_target }}" >> "$GITHUB_OUTPUT"
- name: Determine base image tag
if: github.event_name != 'pull_request'
id: base
run: |
SUFFIX="${{ steps.meta-inputs.outputs.tag_suffix }}"
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
BRANCH="${GITHUB_REF##*/}"
elif [[ "$GITHUB_REF" == refs/heads/main ]]; then
BRANCH="latest"
else
BRANCH=$(echo "${GITHUB_REF##refs/heads/}" | tr '/' '-')
fi
# Pull the base image from THIS repo's package (auplc-base or auplc-base-dev)
# at the tag built by this same workflow run for the current branch —
# never fall back to upstream auplc-base.
echo "image=${{ needs.changes.outputs.base-gpu-image }}:${BRANCH}-${SUFFIX}" >> "$GITHUB_OUTPUT"
- name: Docker metadata (target-suffixed tags)
id: meta-suffixed
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.registry }}/${{ steps.meta-inputs.outputs.image_name }}
flavor: |
suffix=-${{ steps.meta-inputs.outputs.tag_suffix }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Docker metadata (unsuffixed tags — default target only)
if: matrix.gpu_target == needs.changes.outputs.default-gpu-target
id: meta-default
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.registry }}/${{ steps.meta-inputs.outputs.image_name }}
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ github.event.inputs.version }},enable=${{ github.event.inputs.version != '' }}
type=sha,prefix=sha-
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Merge tags
id: tags
run: |
TAGS="${{ steps.meta-suffixed.outputs.tags }}"
if [ -n "${{ steps.meta-default.outputs.tags }}" ]; then
TAGS="${TAGS}
${{ steps.meta-default.outputs.tags }}"
fi
# Deduplicate
TAGS=$(echo "$TAGS" | sort -u)
{
echo "tags<<EOF"
echo "$TAGS"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Build and push ${{ matrix.course }} (${{ matrix.gpu_target }}) image
uses: docker/build-push-action@v6
with:
context: dockerfiles/Courses/${{ matrix.course }}
file: dockerfiles/Courses/${{ matrix.course }}/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.tags.outputs.tags }}
labels: ${{ steps.meta-suffixed.outputs.labels }}
build-args: |
${{ steps.base.outputs.image && format('BASE_IMAGE={0}', steps.base.outputs.image) || '' }}
cache-from: type=gha,scope=course-${{ matrix.course }}-${{ matrix.gpu_target }}
cache-to: type=gha,mode=max,scope=course-${{ matrix.course }}-${{ matrix.gpu_target }}
provenance: false
- name: Clean course build context
if: always()
run: rm -rf dockerfiles/Courses/${{ matrix.course }}/course_data