2525permissions :
2626 contents : read
2727
28+ env :
29+ IMAGE : manifestdotbuild/manifest
30+
2831jobs :
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
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