Skip to content

Merge pull request #41 from AMDResearch/fix/batch-operations-performance #60

Merge pull request #41 from AMDResearch/fix/batch-operations-performance

Merge pull request #41 from AMDResearch/fix/batch-operations-performance #60

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/**'
- 'projects/**'
- '.github/workflows/docker-build.yml'
- '.github/build-config.json'
pull_request:
branches: [main, develop]
paths:
- 'dockerfiles/**'
- 'runtime/hub/**'
- '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
- courses
- base-cpu
- base-gpu
gpu_target:
description: 'GPU target (all = build every target)'
required: false
default: 'all'
type: choice
options:
- all
- gfx110x
- gfx1151
# 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 }}
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 }}
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: 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/**'
courses:
- 'dockerfiles/Courses/**'
- 'dockerfiles/Base/**'
- 'projects/**'
base-cpu:
- 'dockerfiles/Base/Dockerfile.cpu'
base-gpu:
- 'dockerfiles/Base/Dockerfile.rocm'
# ──────────────────────────────────────────────
# 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: 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.GH_PACKAGES_TOKEN || secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.registry }}/auplc-hub
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
# ──────────────────────────────────────────────
# 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: 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.GH_PACKAGES_TOKEN || secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.registry }}/auplc-default
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: dockerfiles/Base
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
# ──────────────────────────────────────────────
# Base GPU Image (multi-target via GPU_TARGET)
#
# No GPU hardware required — only downloads ROCm tarball + pip wheels.
# Uses free-disk-space action to reclaim runner disk for the large image.
# Builds 5 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.GH_PACKAGES_TOKEN || secrets.GITHUB_TOKEN }}
- name: Resolve GPU config
id: suffix
run: |
echo "tag_suffix=${{ matrix.gpu_target }}" >> "$GITHUB_OUTPUT"
# Look up pytorch_whl from gpu-map; 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')
echo "pytorch_whl=${WHL}" >> "$GITHUB_OUTPUT"
- name: Docker metadata (target-suffixed tags)
id: meta-suffixed
uses: docker/metadata-action@v5
with:
images: ${{ needs.changes.outputs.registry }}/auplc-base
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.registry }}/auplc-base
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: dockerfiles/Base
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 }}
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 all 5 GPU targets (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: 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.GH_PACKAGES_TOKEN || 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
run: |
echo "image_name=auplc-$(echo '${{ matrix.course }}' | tr '[:upper:]' '[:lower:]')" >> "$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
echo "image=${{ needs.changes.outputs.registry }}/auplc-base:${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