Skip to content

Commit d3fe605

Browse files
maleadtgiordano
andcommitted
Merge docker images across platforms, and simplify the action.
With the current setup, images for different platforms are pushed with the same tag, overwriting each other. This refactors the workflow into two jobs: a per-platform `build` job that pushes single-arch images by digest (no tag) and a `merge` job that combines the digests into one manifest list per variant. [skip tests] Co-Authored-By: Mosè Giordano <mose@gnu.org>
1 parent 04f29f7 commit d3fe605

2 files changed

Lines changed: 153 additions & 75 deletions

File tree

.github/workflows/Container.yml

Lines changed: 142 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,191 @@
1+
# Based on:
2+
# * <https://docs.github.com/en/actions/publishing-packages/publishing-docker-images>
3+
# * <https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners>
4+
15
name: Publish Docker container
26

37
on:
4-
workflow_dispatch:
5-
inputs:
6-
tag:
7-
description: 'Tag to build instead'
8-
required: false
9-
default: ''
10-
mark_as_latest:
11-
description: 'Mark as latest'
12-
type: boolean
13-
required: false
14-
default: false
158
push:
169
tags:
1710
- 'v*'
1811
branches:
1912
- main
2013

14+
env:
15+
REGISTRY: ghcr.io
16+
IMAGE_NAME: ${{ github.repository }}
17+
JULIA_VERSION: "1.12"
18+
2119
jobs:
22-
push_to_registry:
23-
name: Container for ${{ matrix.platform }} - Julia ${{ matrix.julia }} - CUDA ${{ matrix.cuda }}
20+
build:
21+
name: Container for ${{ matrix.platform }} - CUDA ${{ matrix.cuda }}
2422
permissions:
2523
contents: read
2624
packages: write
2725

2826
strategy:
2927
matrix:
30-
julia: ["1.11", "1.12"]
31-
cuda: ["11.8", "12.9", "13.2"]
28+
cuda: ["12.9", "13.2"]
3229
platform: ["linux/amd64"]
3330
os: ["ubuntu-24.04"]
3431
include:
35-
- julia: "1.12"
36-
cuda: "13.2"
37-
platform: "linux/amd64"
38-
os: "ubuntu-24.04"
39-
default: true
40-
- julia: "1.12"
41-
cuda: "13.2"
32+
# Additional arm64 build for the latest CUDA version.
33+
- cuda: "13.2"
4234
platform: "linux/arm64"
4335
os: "ubuntu-24.04-arm"
44-
default: true
4536

4637
runs-on: ${{ matrix.os }}
4738

4839
steps:
4940
- name: Check out the repo
5041
uses: actions/checkout@v6
51-
with:
52-
ref: ${{ inputs.tag || github.ref_name }}
5342

54-
- name: Get package spec
55-
id: pkg
43+
- name: Compute build variables
44+
id: vars
5645
run: |
57-
if [[ -n "${{ inputs.tag }}" ]]; then
58-
echo "ref=${{ inputs.tag }}" >> $GITHUB_OUTPUT
59-
echo "name=${{ inputs.tag }}" >> $GITHUB_OUTPUT
60-
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
61-
echo "ref=${{ github.ref_name }}" >> $GITHUB_OUTPUT
62-
echo "name=${{ github.ref_name }}" >> $GITHUB_OUTPUT
63-
else
64-
echo "ref=${{ github.sha }}" >> $GITHUB_OUTPUT
65-
echo "name=dev" >> $GITHUB_OUTPUT
66-
fi
46+
# Docker rejects uppercase image names.
47+
image=$(echo "${IMAGE_NAME}" | tr 'A-Z' 'a-z')
48+
echo "image=${image}" >> $GITHUB_OUTPUT
6749
68-
VERSION=$(grep "^version = " Project.toml | cut -d'"' -f2)
69-
echo "version=$VERSION" >> $GITHUB_OUTPUT
50+
cuda_major=$(echo ${{ matrix.cuda }} | cut -d'.' -f1)
51+
echo "variant=cuda${cuda_major}" >> $GITHUB_OUTPUT
7052
71-
- name: Get CUDA major version
72-
id: cuda
73-
run: |
74-
CUDA_MAJOR=$(echo ${{ matrix.cuda }} | cut -d'.' -f1)
75-
echo "major=${CUDA_MAJOR}" >> $GITHUB_OUTPUT
53+
# platform is e.g. linux/amd64 — use a slug for filenames and artifact names.
54+
echo "platform_slug=$(echo "${{ matrix.platform }}" | tr / -)" >> $GITHUB_OUTPUT
55+
56+
version=$(grep "^version = " Project.toml | cut -d'"' -f2)
57+
echo "version=${version}" >> $GITHUB_OUTPUT
7658
77-
- name: Set CPU target
78-
id: cpu_target
79-
run: |
8059
if [[ "${{ matrix.platform }}" == "linux/amd64" ]]; then
81-
echo "target=generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)" >> $GITHUB_OUTPUT
60+
echo "cpu_target=generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)" >> $GITHUB_OUTPUT
8261
elif [[ "${{ matrix.platform }}" == "linux/arm64" ]]; then
83-
echo "target=generic;cortex-a57;thunderx2t99;carmel,clone_all;apple-m1,base(3);neoverse-512tvb,base(3)" >> $GITHUB_OUTPUT
62+
echo "cpu_target=generic;cortex-a57;thunderx2t99;carmel,clone_all;apple-m1,base(3);neoverse-512tvb,base(3)" >> $GITHUB_OUTPUT
8463
fi
8564
65+
- name: Set up Docker Buildx
66+
uses: docker/setup-buildx-action@v4
67+
8668
- name: Log in to registry
8769
uses: docker/login-action@v4
8870
with:
89-
registry: ghcr.io
71+
registry: ${{ env.REGISTRY }}
9072
username: ${{ github.actor }}
9173
password: ${{ secrets.GITHUB_TOKEN }}
9274

93-
- name: Extract metadata
94-
id: meta
95-
uses: docker/metadata-action@v6
75+
- name: Build and push image by digest
76+
id: build
77+
uses: docker/build-push-action@v7
9678
with:
97-
images: ghcr.io/${{ github.repository }}
98-
tags: |
99-
type=raw,value=${{ steps.pkg.outputs.name }}-julia${{ matrix.julia }}-cuda${{ steps.cuda.outputs.major }}
100-
type=raw,value=${{ steps.pkg.outputs.name }},enable=${{ matrix.default == true && (github.ref_type == 'tag' || inputs.tag != '') }}
101-
type=raw,value=latest,enable=${{ matrix.default == true && (github.ref_type == 'tag' || (inputs.tag != '' && inputs.mark_as_latest)) }}
102-
type=raw,value=dev,enable=${{ matrix.default == true && github.ref_type == 'branch' && inputs.tag == '' }}
79+
context: .
80+
platforms: ${{ matrix.platform }}
81+
provenance: false
10382
labels: |
104-
org.opencontainers.image.version=${{ steps.pkg.outputs.version }}
83+
org.opencontainers.image.version=${{ steps.vars.outputs.version }}
84+
outputs: type=image,name=${{ env.REGISTRY }}/${{ steps.vars.outputs.image }},push-by-digest=true,name-canonical=true,push=true
85+
build-args: |
86+
JULIA_VERSION=${{ env.JULIA_VERSION }}
87+
CUDA_VERSION=${{ matrix.cuda }}
88+
PACKAGE_REF=${{ github.ref_type == 'tag' && github.ref_name || github.sha }}
89+
JULIA_CPU_TARGET=${{ steps.vars.outputs.cpu_target }}
90+
91+
- name: Export digest
92+
run: |
93+
mkdir -p /tmp/digests
94+
# "__" separator avoids collisions with hyphens in either part.
95+
echo "${{ steps.build.outputs.digest }}" \
96+
> "/tmp/digests/${{ steps.vars.outputs.variant }}__${{ steps.vars.outputs.platform_slug }}"
97+
98+
- name: Upload digest
99+
uses: actions/upload-artifact@v7
100+
with:
101+
name: digest-${{ steps.vars.outputs.variant }}-${{ steps.vars.outputs.platform_slug }}
102+
path: /tmp/digests/*
103+
if-no-files-found: error
104+
retention-days: 1
105+
106+
merge:
107+
name: Merge multi-arch manifests
108+
needs: build
109+
runs-on: ubuntu-24.04
110+
permissions:
111+
contents: read
112+
packages: write
113+
114+
steps:
115+
- name: Lowercase image name
116+
id: image
117+
run: |
118+
echo "name=$(echo "${IMAGE_NAME}" | tr 'A-Z' 'a-z')" >> $GITHUB_OUTPUT
119+
120+
- name: Classify ref
121+
id: ref
122+
run: |
123+
# base_name: the version-like prefix used in `<base>-cuda<c>` tags.
124+
# is_stable: whether to additionally publish the bare `cuda<c>` alias
125+
# (i.e. on non-prerelease release tag pushes).
126+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
127+
echo "base_name=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
128+
if [[ "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
129+
echo "is_stable=true" >> $GITHUB_OUTPUT
130+
else
131+
echo "is_stable=false" >> $GITHUB_OUTPUT
132+
fi
133+
else
134+
echo "base_name=dev" >> $GITHUB_OUTPUT
135+
echo "is_stable=false" >> $GITHUB_OUTPUT
136+
fi
137+
138+
- name: Download digests
139+
uses: actions/download-artifact@v8
140+
with:
141+
path: /tmp/digests
142+
pattern: digest-*
143+
merge-multiple: true
105144

106145
- name: Set up Docker Buildx
107146
uses: docker/setup-buildx-action@v4
108147

109-
- name: Build and push image
110-
uses: docker/build-push-action@v7
148+
- name: Log in to registry
149+
uses: docker/login-action@v4
111150
with:
112-
context: .
113-
push: true
114-
provenance: false
115-
platforms: ${{ matrix.platform }}
116-
tags: ${{ steps.meta.outputs.tags }}
117-
labels: ${{ steps.meta.outputs.labels }}
118-
build-args: |
119-
JULIA_VERSION=${{ matrix.julia }}
120-
CUDA_VERSION=${{ matrix.cuda }}
121-
PACKAGE_REF=${{ steps.pkg.outputs.ref }}
122-
JULIA_CPU_TARGET=${{ steps.cpu_target.outputs.target }}
151+
registry: ${{ env.REGISTRY }}
152+
username: ${{ github.actor }}
153+
password: ${{ secrets.GITHUB_TOKEN }}
154+
155+
- name: Create manifest lists
156+
env:
157+
IMAGE: ${{ env.REGISTRY }}/${{ steps.image.outputs.name }}
158+
BASE_NAME: ${{ steps.ref.outputs.base_name }}
159+
IS_STABLE: ${{ steps.ref.outputs.is_stable }}
160+
run: |
161+
set -euo pipefail
162+
163+
# Group digest files by variant (everything before "__").
164+
declare -A variants
165+
for file in /tmp/digests/*; do
166+
name=${file##*/}
167+
variants[${name%%__*}]=1
168+
done
169+
170+
for variant in "${!variants[@]}"; do
171+
sources=()
172+
for file in /tmp/digests/"${variant}"__*; do
173+
sources+=("${IMAGE}@$(cat "${file}")")
174+
done
175+
176+
# Always: <base>-<variant> (e.g. v6.1.0-cuda13, dev-cuda13)
177+
tags=("${IMAGE}:${BASE_NAME}-${variant}")
178+
# On stable releases, additionally publish the bare <variant>
179+
# alias (e.g. cuda13) pointing at the latest stable release.
180+
if [[ "${IS_STABLE}" == "true" ]]; then
181+
tags+=("${IMAGE}:${variant}")
182+
fi
183+
184+
tag_args=()
185+
for tag in "${tags[@]}"; do
186+
tag_args+=(--tag "${tag}")
187+
done
188+
189+
echo "Creating manifest ${tags[*]} from ${#sources[@]} platforms"
190+
docker buildx imagetools create "${tag_args[@]}" "${sources[@]}"
191+
done

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,19 @@ command that initializes the toolkit) will issue a warning.
6262
For quick testing, you can also use [the `juliagpu/cuda.jl` container
6363
image](https://github.com/JuliaGPU/CUDA.jl/pkgs/container/cuda.jl/versions) from the GitHub
6464
Container Registry, which provides Julia, a precompiled version of CUDA.jl, and a matching
65-
CUDA toolkit:
65+
CUDA toolkit. Tags are named after the CUDA major version (`cuda12` or `cuda13`); the
66+
bare tag tracks the latest stable release, while `<version>-cuda<major>` pins to a
67+
specific release and `dev-cuda<major>` tracks the latest `main` build:
6668

6769
```sh
68-
docker run -it --rm --gpus=all ghcr.io/juliagpu/cuda.jl:latest # other tags available too
70+
# latest stable release, CUDA 13
71+
docker run -it --rm --gpus=all ghcr.io/juliagpu/cuda.jl:cuda13
72+
73+
# a specific release, CUDA 12
74+
docker run -it --rm --gpus=all ghcr.io/juliagpu/cuda.jl:v6.1.0-cuda12
75+
76+
# latest main build, CUDA 13
77+
docker run -it --rm --gpus=all ghcr.io/juliagpu/cuda.jl:dev-cuda13
6978
```
7079

7180
For more usage instructions and other information, please refer to [the

0 commit comments

Comments
 (0)