1111 branches :
1212 - main
1313
14+ env :
15+ IMAGE : docker.io/tinpotnick/drachtio-server
16+
1417permissions :
1518 contents : read
1619
@@ -22,19 +25,30 @@ jobs:
2225 build :
2326 runs-on : ubuntu-latest
2427 strategy :
28+ fail-fast : false
2529 matrix :
2630 platform :
2731 - linux/amd64
2832 - linux/arm64
2933 steps :
34+ - name : Prepare
35+ run : |
36+ platform=${{ matrix.platform }}
37+ echo "PLATFORM_PAIR=${platform//\//-}" >> "$GITHUB_ENV"
38+
3039 - name : Checkout
31- uses : actions/checkout@v6
40+ uses : actions/checkout@v7
41+
42+ - name : Docker meta
43+ id : meta
44+ uses : docker/metadata-action@v6
45+ with :
46+ images : ${{ env.IMAGE }}
3247
3348 - name : Set up QEMU
3449 uses : docker/setup-qemu-action@v4
3550
3651 - name : Set up Docker Buildx
37- id : buildx
3852 uses : docker/setup-buildx-action@v4
3953
4054 - name : Login to Docker Hub
@@ -44,69 +58,77 @@ jobs:
4458 username : ${{ secrets.DOCKERHUB_USERNAME }}
4559 password : ${{ secrets.DOCKERHUB_TOKEN }}
4660
47- - name : Docker meta
48- id : ourdockertags
49- uses : docker/metadata-action@v6
50- with :
51- images : |
52- docker.io/tinpotnick/drachtio-server
53- tags : |
54- type=ref,event=branch
55- type=ref,event=pr
56- type=semver,pattern={{version}}
57- type=semver,pattern={{major}}.{{minor}}
58- type=semver,pattern={{major}}
59- type=sha
60-
61- - name : Build and push
61+ # Build each platform once and push it by digest (no tag). The tagged
62+ # multi-arch manifest is assembled later in the merge job without
63+ # rebuilding. On pull_request we build for validation but do not push.
64+ - name : Build and push by digest
65+ id : build
6266 uses : docker/build-push-action@v7
6367 with :
6468 context : .
6569 platforms : ${{ matrix.platform }}
66- push : ${{ github.event_name != 'pull_request' }}
67- tags : ${{ steps.ourdockertags.outputs.tags }}
68- labels : ${{ steps.ourdockertags.outputs.labels }}
69- cache-from : type=gha
70- cache-to : type=gha,mode=max
70+ labels : ${{ steps.meta.outputs.labels }}
71+ cache-from : type=gha,scope=${{ env.PLATFORM_PAIR }}
72+ cache-to : type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
73+ outputs : type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
74+
75+ - name : Export digest
76+ if : github.event_name != 'pull_request'
77+ run : |
78+ mkdir -p "${{ runner.temp }}/digests"
79+ digest="${{ steps.build.outputs.digest }}"
80+ touch "${{ runner.temp }}/digests/${digest#sha256:}"
81+
82+ - name : Upload digest
83+ if : github.event_name != 'pull_request'
84+ uses : actions/upload-artifact@v7
85+ with :
86+ name : digests-${{ env.PLATFORM_PAIR }}
87+ path : ${{ runner.temp }}/digests/*
88+ if-no-files-found : error
89+ retention-days : 1
7190
72- push-multi-platform :
91+ merge :
7392 needs : build
7493 if : github.event_name != 'pull_request'
7594 runs-on : ubuntu-latest
7695 steps :
77- - name : Checkout
78- uses : actions/checkout@v6
96+ - name : Download digests
97+ uses : actions/download-artifact@v8
98+ with :
99+ path : ${{ runner.temp }}/digests
100+ pattern : digests-*
101+ merge-multiple : true
79102
80103 - name : Set up Docker Buildx
81104 uses : docker/setup-buildx-action@v4
82105
83- - name : Set up QEMU
84- uses : docker/setup-qemu-action@v4
85-
86- - name : Login to Docker Hub
87- uses : docker/login-action@v4
88- with :
89- username : ${{ secrets.DOCKERHUB_USERNAME }}
90- password : ${{ secrets.DOCKERHUB_TOKEN }}
91-
92106 - name : Docker meta
93- id : ourdockertags
107+ id : meta
94108 uses : docker/metadata-action@v6
95109 with :
96- images : |
97- docker.io/tinpotnick/drachtio-server
110+ images : ${{ env.IMAGE }}
98111 tags : |
99112 type=ref,event=branch
100113 type=semver,pattern={{version}}
101114 type=semver,pattern={{major}}.{{minor}}
102115 type=semver,pattern={{major}}
116+ type=sha
103117
104- - name : Build and push multi-platform
105- uses : docker/build-push- action@v7
118+ - name : Login to Docker Hub
119+ uses : docker/login- action@v4
106120 with :
107- context : .
108- platforms : linux/amd64,linux/arm64
109- push : true
110- tags : ${{ steps.ourdockertags.outputs.tags }}
111- labels : ${{ steps.ourdockertags.outputs.labels }}
112- cache-from : type=gha
121+ username : ${{ secrets.DOCKERHUB_USERNAME }}
122+ password : ${{ secrets.DOCKERHUB_TOKEN }}
123+
124+ # Stitch the per-platform digests into a single multi-arch manifest.
125+ # This is a registry-side operation: no image is rebuilt here.
126+ - name : Create manifest list and push
127+ working-directory : ${{ runner.temp }}/digests
128+ run : |
129+ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
130+ $(printf '${{ env.IMAGE }}@sha256:%s ' *)
131+
132+ - name : Inspect image
133+ run : |
134+ docker buildx imagetools inspect ${{ env.IMAGE }}:${{ steps.meta.outputs.version }}
0 commit comments