Skip to content

Commit e1e07e3

Browse files
authored
Merge pull request #1549 from mnfst/fix/docker-native-arm64-runners
ci(docker): build arm64 on native runners, drop QEMU emulation
2 parents 4cc69c3 + d57ee30 commit e1e07e3

File tree

1 file changed

+100
-25
lines changed

1 file changed

+100
-25
lines changed

.github/workflows/docker.yml

Lines changed: 100 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,93 @@ on:
2525
permissions:
2626
contents: read
2727

28+
env:
29+
IMAGE: manifestdotbuild/manifest
30+
2831
jobs:
2932
validate:
30-
name: Build (validate)
33+
name: Build (validate, ${{ matrix.platform.arch }})
3134
if: github.event_name == 'pull_request'
32-
runs-on: ubuntu-latest
35+
strategy:
36+
fail-fast: false
37+
matrix:
38+
platform:
39+
- { os: ubuntu-latest, arch: amd64 }
40+
- { os: ubuntu-24.04-arm, arch: arm64 }
41+
runs-on: ${{ matrix.platform.os }}
3342
steps:
3443
- uses: actions/checkout@v4
3544

36-
- uses: docker/setup-qemu-action@v3
37-
3845
- uses: docker/setup-buildx-action@v3
3946

4047
- uses: docker/build-push-action@v6
4148
with:
4249
context: .
4350
file: docker/Dockerfile
4451
push: false
45-
platforms: linux/amd64,linux/arm64
46-
cache-from: type=gha
47-
cache-to: type=gha,mode=max
52+
platforms: linux/${{ matrix.platform.arch }}
53+
cache-from: type=gha,scope=${{ matrix.platform.arch }}
54+
cache-to: type=gha,mode=max,scope=${{ matrix.platform.arch }}
55+
56+
build:
57+
name: Build (${{ matrix.platform.arch }})
58+
if: github.event_name == 'workflow_dispatch'
59+
strategy:
60+
fail-fast: true
61+
matrix:
62+
platform:
63+
- { os: ubuntu-latest, arch: amd64 }
64+
- { os: ubuntu-24.04-arm, arch: arm64 }
65+
runs-on: ${{ matrix.platform.os }}
66+
permissions:
67+
contents: read
68+
id-token: write
69+
steps:
70+
- uses: actions/checkout@v4
71+
72+
- uses: docker/setup-buildx-action@v3
73+
74+
- uses: docker/login-action@v3
75+
with:
76+
username: ${{ secrets.DOCKERHUB_USERNAME }}
77+
password: ${{ secrets.DOCKERHUB_TOKEN }}
78+
79+
- uses: docker/metadata-action@v5
80+
id: meta
81+
with:
82+
images: ${{ env.IMAGE }}
83+
84+
- id: build
85+
uses: docker/build-push-action@v6
86+
with:
87+
context: .
88+
file: docker/Dockerfile
89+
platforms: linux/${{ matrix.platform.arch }}
90+
labels: ${{ steps.meta.outputs.labels }}
91+
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
92+
cache-from: type=gha,scope=${{ matrix.platform.arch }}
93+
cache-to: type=gha,mode=max,scope=${{ matrix.platform.arch }}
94+
sbom: true
95+
provenance: mode=max
4896

49-
publish:
50-
name: Build & Publish
97+
- name: Export digest
98+
run: |
99+
mkdir -p ${{ runner.temp }}/digests
100+
digest="${{ steps.build.outputs.digest }}"
101+
touch "${{ runner.temp }}/digests/${digest#sha256:}"
102+
103+
- name: Upload digest
104+
uses: actions/upload-artifact@v4
105+
with:
106+
name: digests-${{ matrix.platform.arch }}
107+
path: ${{ runner.temp }}/digests/*
108+
if-no-files-found: error
109+
retention-days: 1
110+
111+
merge:
112+
name: Merge & Publish
51113
if: github.event_name == 'workflow_dispatch'
114+
needs: build
52115
runs-on: ubuntu-latest
53116
permissions:
54117
contents: read
@@ -68,7 +131,12 @@ jobs:
68131
fi
69132
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
70133
71-
- uses: docker/setup-qemu-action@v3
134+
- name: Download digests
135+
uses: actions/download-artifact@v4
136+
with:
137+
path: ${{ runner.temp }}/digests
138+
pattern: digests-*
139+
merge-multiple: true
72140

73141
- uses: docker/setup-buildx-action@v3
74142

@@ -80,7 +148,7 @@ jobs:
80148
- uses: docker/metadata-action@v5
81149
id: meta
82150
with:
83-
images: manifestdotbuild/manifest
151+
images: ${{ env.IMAGE }}
84152
flavor: |
85153
latest=true
86154
tags: |
@@ -89,27 +157,34 @@ jobs:
89157
type=semver,pattern={{major}},value=${{ steps.version.outputs.version }}
90158
type=sha
91159
92-
- uses: docker/build-push-action@v6
93-
id: build
94-
with:
95-
context: .
96-
file: docker/Dockerfile
97-
push: true
98-
platforms: linux/amd64,linux/arm64
99-
tags: ${{ steps.meta.outputs.tags }}
100-
labels: ${{ steps.meta.outputs.labels }}
101-
cache-from: type=gha
102-
cache-to: type=gha,mode=max
103-
sbom: true
104-
provenance: mode=max
160+
- name: Create manifest list and push
161+
working-directory: ${{ runner.temp }}/digests
162+
run: |
163+
docker buildx imagetools create \
164+
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
165+
$(printf '${{ env.IMAGE }}@sha256:%s ' *)
166+
167+
- name: Inspect image
168+
run: |
169+
docker buildx imagetools inspect ${{ env.IMAGE }}:${{ steps.meta.outputs.version }}
105170
106171
- uses: sigstore/cosign-installer@v3
107172

108173
- name: Sign published image
109174
env:
110175
TAGS: ${{ steps.meta.outputs.tags }}
111-
DIGEST: ${{ steps.build.outputs.digest }}
176+
VERSION: ${{ steps.meta.outputs.version }}
112177
run: |
178+
# Extract the manifest-list digest from the freshly-pushed tag.
179+
# Uses the pattern established by the dagger project and others: format
180+
# the full inspect output as JSON and pull .manifest.digest out with jq.
181+
DIGEST=$(docker buildx imagetools inspect "${{ env.IMAGE }}:${VERSION}" --format '{{json .}}' | jq -r '.manifest.digest')
182+
if [ -z "$DIGEST" ] || [ "$DIGEST" = "null" ]; then
183+
echo "::error::Failed to extract manifest-list digest for ${{ env.IMAGE }}:${VERSION}"
184+
docker buildx imagetools inspect "${{ env.IMAGE }}:${VERSION}" --format '{{json .}}'
185+
exit 1
186+
fi
187+
echo "Signing manifest list digest: $DIGEST"
113188
for tag in ${TAGS}; do
114189
cosign sign --yes "${tag}@${DIGEST}"
115190
done

0 commit comments

Comments
 (0)