Skip to content

Commit 27a82dd

Browse files
committed
feat: add multi-arch image support for linux/amd64 and linux/arm64
1 parent 484f5f7 commit 27a82dd

File tree

11 files changed

+362
-121
lines changed

11 files changed

+362
-121
lines changed

.build/python/src/okdp/extension/tagging/apply_tags.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@
1919

2020
class Tagging:
2121

22-
def __init__(self, short_image_name: str, registry: str, owner: str,):
23-
self.short_image_name, self.tag = short_image_name.split(":")
22+
def __init__(self, image_name: str, registry: str, owner: str, platform: str,):
23+
self.image_name, self.tag = image_name.split(":")
2424
self.registry = registry
2525
self.owner = owner
26+
self.platform = platform
2627

2728
def apply_tags(self) -> None:
2829
"""
29-
Tags <registry>/<owner>/<short_image_name>:tag with the tags reported by all taggers for this image
30+
Tags <registry>/<owner>/<image_name>:tag with the tags reported by all taggers for this image
3031
"""
31-
LOGGER.info(f"Tagging image: {self.short_image_name}")
32+
LOGGER.info(f"Tagging image: {self.image_name}")
3233

33-
image = f"{self.registry}/{self.owner}/{self.short_image_name}:{self.tag}"
34+
image = f"{self.registry}/{self.owner}/{self.image_name}:{self.tag}-{self.platform}"
3435

3536
tags = self.generate_tags()
3637

@@ -40,13 +41,13 @@ def apply_tags(self) -> None:
4041

4142
def generate_tags(self) -> list[str]:
4243
"""
43-
Generate tags for the image <registry>/<owner>/<short_image_name>:latest
44+
Generate tags for the image <registry>/<owner>/<image_name>:latest
4445
"""
45-
LOGGER.info(f"Tagging image: {self.short_image_name}")
46-
taggers, _ = get_taggers_and_manifests(self.short_image_name)
46+
LOGGER.info(f"Tagging image: {self.image_name}")
47+
taggers, _ = get_taggers_and_manifests(self.image_name)
4748

48-
image = f"{self.registry}/{self.owner}/{self.short_image_name}:{ self.tag }"
49-
tags = [f"{self.registry}/{self.owner}/{self.short_image_name}:{ self.tag }"]
49+
image = f"{self.registry}/{self.owner}/{self.image_name}:{self.tag}-{self.platform}"
50+
tags = [f"{self.registry}/{self.owner}/{self.image_name}:{self.tag}-{self.platform}"]
5051
with DockerRunner(image) as container:
5152
for tagger in taggers:
5253
tagger_name = tagger.__class__.__name__
@@ -55,7 +56,7 @@ def generate_tags(self) -> list[str]:
5556
f"Calculated tag, tagger_name: {tagger_name} tag_value: {tag_value}"
5657
)
5758
tags.append(
58-
f"{self.registry}/{self.owner}/{self.short_image_name}:{tag_value}"
59+
f"{self.registry}/{self.owner}/{self.image_name}:{tag_value}-{self.platform}"
5960
)
6061

6162
return tags
@@ -66,9 +67,9 @@ def generate_tags(self) -> list[str]:
6667

6768
arg_parser = argparse.ArgumentParser()
6869
arg_parser.add_argument(
69-
"--short-image-name",
70+
"--image-name",
7071
required=True,
71-
help="Short image name",
72+
help="Image name:tag",
7273
)
7374
arg_parser.add_argument(
7475
"--registry",
@@ -82,8 +83,15 @@ def generate_tags(self) -> list[str]:
8283
required=True,
8384
help="Owner of the image",
8485
)
86+
arg_parser.add_argument(
87+
"--platform",
88+
required=True,
89+
type=str,
90+
choices=["amd64", "arm64"],
91+
help="Platform",
92+
)
8593
args = arg_parser.parse_args()
8694

87-
tagging = Tagging(args.short_image_name, args.registry, args.owner)
95+
tagging = Tagging(args.image_name, args.registry, args.owner, args.platform)
8896

8997
tagging.apply_tags()

.build/python/tests/okdp/extension/tagging/test_tagging.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ def test_long_tagger(mock_container, mock_exec_cmd):
4545
def test_generate_tags(mock_get_taggers_and_manifests, mock_container, mock_exec_cmd):
4646
"""Test generate_tags output."""
4747
expected_tags = [
48-
"ghcr.io/owner/pyspark-notebook:latest",
49-
"ghcr.io/owner/pyspark-notebook:spark-3.4.1-python-3.12-java-17-scala-2.13",
50-
"ghcr.io/owner/pyspark-notebook:spark-3.4.1-python-3.12.3-java-17.0.2-scala-2.13.12-hub-4.0.1-lab-4.0.5",
48+
"ghcr.io/owner/pyspark-notebook:2025-09-22-amd64",
49+
"ghcr.io/owner/pyspark-notebook:spark-3.4.1-python-3.12-java-17-scala-2.13-amd64",
50+
"ghcr.io/owner/pyspark-notebook:spark-3.4.1-python-3.12.3-java-17.0.2-scala-2.13.12-hub-4.0.1-lab-4.0.5-amd64",
5151
]
5252

5353
mock_get_taggers_and_manifests.return_value = (
@@ -58,10 +58,10 @@ def test_generate_tags(mock_get_taggers_and_manifests, mock_container, mock_exec
5858
[spark_info_manifest],
5959
)
6060

61-
t = Tagging("pyspark-notebook:latest", "ghcr.io", "owner")
61+
t = Tagging("pyspark-notebook:2025-09-22", "ghcr.io", "owner", "amd64")
6262
tags = t.generate_tags()
6363

64-
assert tags[0] == "ghcr.io/owner/pyspark-notebook:latest"
64+
assert tags[0] == "ghcr.io/owner/pyspark-notebook:2025-09-22-amd64"
6565
for tag in expected_tags:
6666
assert tag in tags
6767
assert len(tags) == len(expected_tags)
@@ -75,7 +75,7 @@ def tag_value(self, container): return "tag1"
7575
mock_apply_tags_docker_runner.return_value.__enter__.return_value = mock_container
7676
mock_apply_tags_docker_runner.return_value.__exit__.return_value = None
7777

78-
t = Tagging("pyspark-notebook:latest", "ghcr.io", "owner")
78+
t = Tagging("pyspark-notebook:2025-09-22", "ghcr.io", "owner", "arm64")
7979
t.apply_tags()
8080

8181
# Check docker["tag", ...] was called with the right args
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: 'Multi-arch Image Tagger'
2+
description: 'Applies tags and prepares amd64 and arm64 images for creating multi-arch manifests'
3+
inputs:
4+
image:
5+
description: Image name with tag (including registry/repo, e.g. docker.io/myuser/myimage:mytag)
6+
required: true
7+
registry:
8+
description: The registry used to push images into
9+
required: true
10+
platform_amd64:
11+
description: Target image platform for AMD64 architecture
12+
required: true
13+
platform_arm64:
14+
description: Target image platform for AMD64 architecture
15+
required: true
16+
17+
runs:
18+
using: "composite"
19+
steps:
20+
21+
- name: Prepare image push 📦
22+
run: |
23+
# The short image name (without tag) is necessary to push to the registry
24+
echo "SHORT_IMAGE_NAME=${{ inputs.image }}" | awk -F: '{print $1}' >> $GITHUB_ENV
25+
shell: bash
26+
27+
# Pull the images locally for the two platforms: amd64 and arm64
28+
- name: Pull ${{ inputs.image }} image for ${{ inputs.platform_amd64 }} platform ⬇️
29+
run: |
30+
docker pull ${{ inputs.registry }}/$OWNER/${{ inputs.image }}-${{ inputs.platform_amd64 }}
31+
shell: bash
32+
33+
- name: Pull ${{ inputs.image }} image for ${{ inputs.platform_arm64 }} platform ⬇️
34+
run: |
35+
docker pull ${{ inputs.registry }}/$OWNER/${{ inputs.image }}-${{ inputs.platform_arm64 }}
36+
shell: bash
37+
38+
- name: Apply tags to ${{ inputs.image }} image and platform ${{ inputs.platform_amd64 }} 🏷
39+
run: |
40+
python3 -m okdp.extension.tagging.apply_tags --image-name ${{ inputs.image }} \
41+
--registry ${{ inputs.registry }} \
42+
--owner $OWNER \
43+
--platform ${{ inputs.platform_amd64 }}
44+
shell: bash
45+
46+
- name: Fetch all tags for ${{ inputs.image }} image and platform ${{ inputs.platform_amd64 }} 🏷
47+
run: |
48+
tags=$(docker images ${{ inputs.registry }}/$OWNER/$SHORT_IMAGE_NAME \
49+
--format "{{.Repository}}:{{.Tag}}" \
50+
| grep -v -- "-${{ inputs.platform_arm64 }}$" \
51+
| sed "s/-${{ inputs.platform_amd64 }}$//" \
52+
| tr '\n' ' ')
53+
echo "IMAGE_TAGS=$tags" >> $GITHUB_ENV
54+
shell: bash
55+
56+
- name: Apply tags to ${{ inputs.image }} image and platform ${{ inputs.platform_arm64 }} 🏷
57+
run: |
58+
for tag in $IMAGE_TAGS
59+
do
60+
docker tag ${{ inputs.registry }}/$OWNER/${{ inputs.image }}-${{ inputs.platform_arm64 }} ${tag}-${{ inputs.platform_arm64 }}
61+
done
62+
shell: bash
63+

.github/workflows/build-base-images-template.yml

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,11 @@ on:
2525
required: false
2626
type: string
2727
default: ""
28-
runs-on:
29-
description: GitHub Actions Runner image
30-
required: true
31-
type: string
3228

3329
jobs:
3430

3531
docker-stacks-foundation:
36-
uses: ./.github/workflows/build-image-template.yml
32+
uses: ./.github/workflows/build-multi-arch-image-template.yml
3733
with:
3834
parent-image: ""
3935
image: docker-stacks-foundation:${{ inputs.python_dev_tag }}
@@ -42,42 +38,38 @@ jobs:
4238
registry: ${{ inputs.registry }}
4339
publish_to_registry: ${{ inputs.publish_to_registry }}
4440
git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
45-
runs-on: ubuntu-24.04
4641
secrets: inherit
4742

4843
base-notebook:
49-
uses: ./.github/workflows/build-image-template.yml
44+
uses: ./.github/workflows/build-multi-arch-image-template.yml
5045
with:
5146
parent-image: docker-stacks-foundation:${{ inputs.python_dev_tag }}
5247
image: base-notebook:${{ inputs.python_dev_tag }}
5348
registry: ${{ inputs.registry }}
5449
publish_to_registry: ${{ inputs.publish_to_registry }}
5550
git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
56-
runs-on: ubuntu-24.04
5751
secrets: inherit
5852
needs: [docker-stacks-foundation]
5953

6054
minimal-notebook:
61-
uses: ./.github/workflows/build-image-template.yml
55+
uses: ./.github/workflows/build-multi-arch-image-template.yml
6256
with:
6357
parent-image: base-notebook:${{ inputs.python_dev_tag }}
6458
image: minimal-notebook:${{ inputs.python_dev_tag }}
6559
registry: ${{ inputs.registry }}
6660
publish_to_registry: ${{ inputs.publish_to_registry }}
6761
git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
68-
runs-on: ubuntu-24.04
6962
secrets: inherit
7063
needs: [base-notebook]
7164

7265
scipy-notebook:
73-
uses: ./.github/workflows/build-image-template.yml
66+
uses: ./.github/workflows/build-multi-arch-image-template.yml
7467
with:
7568
parent-image: minimal-notebook:${{ inputs.python_dev_tag }}
7669
image: scipy-notebook:${{ inputs.python_dev_tag }}
7770
registry: ${{ inputs.registry }}
7871
publish_to_registry: ${{ inputs.publish_to_registry }}
7972
git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
80-
runs-on: ubuntu-24.04
8173
secrets: inherit
8274
needs: [minimal-notebook]
8375

.github/workflows/build-datascience-images-template.yml

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,65 +21,56 @@ on:
2121
required: false
2222
type: string
2323
default: ""
24-
runs-on:
25-
description: GitHub Actions Runner image
26-
required: true
27-
type: string
2824

2925
jobs:
3026
r:
31-
uses: ./.github/workflows/build-image-template.yml
27+
uses: ./.github/workflows/build-multi-arch-image-template.yml
3228
with:
3329
parent-image: minimal-notebook:${{ inputs.python_dev_tag }}
3430
image: r-notebook:${{ inputs.python_dev_tag }}
3531
registry: ${{ inputs.registry }}
3632
publish_to_registry: ${{ inputs.publish_to_registry }}
3733
git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
38-
runs-on: ${{ inputs.runs-on }}
3934
secrets: inherit
4035

4136
datascience:
42-
uses: ./.github/workflows/build-image-template.yml
37+
uses: ./.github/workflows/build-multi-arch-image-template.yml
4338
with:
4439
parent-image: scipy-notebook:${{ inputs.python_dev_tag }}
4540
image: datascience-notebook:${{ inputs.python_dev_tag }}
4641
registry: ${{ inputs.registry }}
4742
publish_to_registry: ${{ inputs.publish_to_registry }}
4843
git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
49-
runs-on: ${{ inputs.runs-on }}
5044
secrets: inherit
5145

5246
# julia:
53-
# uses: ./.github/workflows/build-image-template.yml
47+
# uses: ./.github/workflows/build-multi-arch-image-template.yml
5448
# with:
5549
# parent-image: minimal-notebook:${{ inputs.python_dev_tag }}
5650
# image: julia-notebook
5751
# registry: ${{ inputs.registry }}
5852
# publish_to_registry: ${{ inputs.publish_to_registry }}
5953
# git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
60-
# runs-on: ${{ inputs.runs-on }}
6154
# secrets: inherit
6255

6356
# tensorflow:
64-
# uses: ./.github/workflows/build-image-template.yml
57+
# uses: ./.github/workflows/build-multi-arch-image-template.yml
6558
# with:
6659
# parent-image: scipy-notebook:${{ inputs.python_dev_tag }}
6760
# image: tensorflow-notebook
6861
# registry: ${{ inputs.registry }}
6962
# publish_to_registry: ${{ inputs.publish_to_registry }}
7063
# git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
71-
# runs-on: ${{ inputs.runs-on }}
7264
# secrets: inherit
7365

7466
# pytorch:
75-
# uses: ./.github/workflows/build-image-template.yml
67+
# uses: ./.github/workflows/build-multi-arch-image-template.yml
7668
# with:
7769
# parent-image: scipy-notebook:${{ inputs.python_dev_tag }}
7870
# image: pytorch-notebook
7971
# registry: ${{ inputs.registry }}
8072
# publish_to_registry: ${{ inputs.publish_to_registry }}
8173
# git_latest_release_tag: ${{ inputs.git_latest_release_tag }}
82-
# runs-on: ${{ inputs.runs-on }}
8374
# secrets: inherit
8475

8576

0 commit comments

Comments
 (0)