44 push :
55 # Only build Docker images when a tag is pushed
66 tags : [ '*.*.*' ]
7+ # Also allow manual triggering for testing
8+ workflow_dispatch :
79
810env :
911 REGISTRY : ghcr.io
10- # We'll set the image name in the steps
1112
1213jobs :
13- build :
14+ setup :
15+ runs-on : ubuntu-latest
16+ outputs :
17+ lowercase_image_name : ${{ steps.image-name.outputs.lowercase_image_name }}
18+ tags : ${{ steps.meta.outputs.tags }}
19+ labels : ${{ steps.meta.outputs.labels }}
20+ platforms : ${{ steps.set-platforms.outputs.platforms }}
21+
22+ steps :
23+ - name : Checkout repository
24+ uses : actions/checkout@v3
25+
26+ - name : Set lowercase image name
27+ id : image-name
28+ run : echo "lowercase_image_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
29+
30+ # Extract metadata (tags, labels) for Docker
31+ - name : Extract Docker metadata
32+ id : meta
33+ uses : docker/metadata-action@v5
34+ with :
35+ images : ${{ env.REGISTRY }}/${{ steps.image-name.outputs.lowercase_image_name }}
1436
37+ - name : Set platforms
38+ id : set-platforms
39+ run : |
40+ echo "platforms=[\"linux/amd64\", \"linux/arm64\", \"linux/arm/v7\"]" >> $GITHUB_OUTPUT
41+
42+ build :
43+ needs : setup
1544 runs-on : ubuntu-latest
1645 permissions :
1746 contents : read
1847 packages : write
1948 id-token : write
49+ strategy :
50+ fail-fast : false # Allow other platforms to continue building if one fails
51+ matrix :
52+ platform : ${{ fromJson(needs.setup.outputs.platforms) }}
2053
2154 steps :
2255 - name : Checkout repository
2356 uses : actions/checkout@v3
2457
25- - name : Set lowercase image name
26- id : image-name
27- run : echo "LOWERCASE_IMAGE_NAME=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
28-
2958 - name : Setup Docker buildx
3059 uses : docker/setup-buildx-action@v3
3160
@@ -38,19 +67,99 @@ jobs:
3867 username : ${{ github.actor }}
3968 password : ${{ secrets.GITHUB_TOKEN }}
4069
41- # Extract metadata (tags, labels) for Docker
42- - name : Extract Docker metadata
43- id : meta
44- uses : docker/metadata-action@v5
45- with :
46- images : ${{ env.REGISTRY }}/${{ env.LOWERCASE_IMAGE_NAME }}
70+ - name : Get platform short name
71+ id : platform-name
72+ run : |
73+ PLATFORM="${{ matrix.platform }}"
74+ SHORT_NAME=$(echo $PLATFORM | sed -e 's/linux\///' -e 's/\//-/')
75+ echo "short_name=${SHORT_NAME}" >> $GITHUB_OUTPUT
4776
4877 - name : Build and push Docker image
4978 id : build-and-push
5079 uses : docker/build-push-action@v5
5180 with :
52- platforms : linux/amd64,linux/arm64,linux/arm/v7
81+ platforms : ${{ matrix.platform }}
5382 context : .
5483 push : ${{ github.event_name != 'pull_request' }}
55- tags : ${{ steps.meta.outputs.tags }},${{ env.REGISTRY }}/${{ env.LOWERCASE_IMAGE_NAME }}:latest
56- labels : ${{ steps.meta.outputs.labels }}
84+ tags : ${{ env.REGISTRY }}/${{ needs.setup.outputs.lowercase_image_name }}-${{ steps.platform-name.outputs.short_name }}:${{ github.ref_name || 'latest' }}
85+ labels : ${{ needs.setup.outputs.labels }}
86+ cache-from : type=gha
87+ cache-to : type=gha,mode=max
88+ outputs : type=docker,dest=/tmp/${{ steps.platform-name.outputs.short_name }}-image.tar
89+
90+ - name : Upload image artifact
91+ uses : actions/upload-artifact@v3
92+ with :
93+ name : ${{ steps.platform-name.outputs.short_name }}-image
94+ path : /tmp/${{ steps.platform-name.outputs.short_name }}-image.tar
95+ retention-days : 1
96+
97+ create-manifest :
98+ needs : [setup, build]
99+ runs-on : ubuntu-latest
100+ if : github.event_name != 'pull_request'
101+ permissions :
102+ contents : read
103+ packages : write
104+ id-token : write
105+
106+ steps :
107+ - name : Setup Docker buildx
108+ uses : docker/setup-buildx-action@v3
109+
110+ # Login against registry
111+ - name : Log into registry ${{ env.REGISTRY }}
112+ uses : docker/login-action@v3
113+ with :
114+ registry : ${{ env.REGISTRY }}
115+ username : ${{ github.actor }}
116+ password : ${{ secrets.GITHUB_TOKEN }}
117+
118+ # Generate short names for platforms
119+ - name : Generate platform short names
120+ id : platform-names
121+ run : |
122+ platforms='${{ needs.setup.outputs.platforms }}'
123+ short_names=()
124+ for platform in $(echo $platforms | jq -r '.[]'); do
125+ short_name=$(echo $platform | sed -e 's/linux\///' -e 's/\//-/')
126+ short_names+=("$short_name")
127+ done
128+ echo "short_names=${short_names[@]}" >> $GITHUB_OUTPUT
129+
130+ # Download platform artifacts and load images
131+ - name : Download and load platform images
132+ run : |
133+ platforms='${{ needs.setup.outputs.platforms }}'
134+ for platform in $(echo $platforms | jq -r '.[]'); do
135+ short_name=$(echo $platform | sed -e 's/linux\///' -e 's/\//-/')
136+ echo "Downloading $short_name image"
137+ mkdir -p /tmp/$short_name
138+ curl -sSL -o /tmp/$short_name-image.tar $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/artifacts/$short_name-image
139+ echo "Loading $short_name image"
140+ docker load --input /tmp/$short_name-image.tar
141+ done
142+
143+ # Create and push manifest
144+ - name : Create and push multi-platform manifest
145+ run : |
146+ IMAGE_NAME="${{ env.REGISTRY }}/${{ needs.setup.outputs.lowercase_image_name }}"
147+ TAG="${{ github.ref_name || 'latest' }}"
148+
149+ # Push individual images if not already pushed
150+ platforms='${{ needs.setup.outputs.platforms }}'
151+ manifest_args=()
152+
153+ for platform in $(echo $platforms | jq -r '.[]'); do
154+ short_name=$(echo $platform | sed -e 's/linux\///' -e 's/\//-/')
155+ platform_tag="${IMAGE_NAME}-${short_name}:${TAG}"
156+ docker push "${platform_tag}"
157+ manifest_args+=("${platform_tag}")
158+ done
159+
160+ # Create and push the manifest
161+ docker manifest create "${IMAGE_NAME}:${TAG}" "${manifest_args[@]}"
162+ docker manifest create "${IMAGE_NAME}:latest" "${manifest_args[@]}"
163+
164+ docker manifest push "${IMAGE_NAME}:${TAG}"
165+ docker manifest push "${IMAGE_NAME}:latest"
0 commit comments