8888 # Advanced Docker builder with cache support
8989 uses : docker/setup-buildx-action@v3
9090
91+ - name : Login to GitHub Container Registry
92+ # Login to GHCR for multi-platform builds that need to be pushed to registry
93+ # Single-platform builds for PRs don't need registry push
94+ if : inputs.save-artifact && contains(inputs.platforms, ',')
95+ uses : docker/login-action@v3
96+ with :
97+ registry : ghcr.io
98+ username : ${{ github.actor }}
99+ password : ${{ secrets.GITHUB_TOKEN }}
100+
91101 # =============================================================================
92102 # DOCKER BUILD
93103 # Build image with layer caching for efficiency
97107 id : meta
98108 uses : docker/metadata-action@v5
99109 with :
100- images : ${{ inputs.image-name }}
110+ # Use GHCR for multi-platform artifact builds, local name otherwise
111+ images : |
112+ ${{ (inputs.save-artifact && contains(inputs.platforms, ',')) && format('ghcr.io/{0}/{1}', github.repository_owner, inputs.image-name) || inputs.image-name }}
101113 tags : |
102114 type=raw,value=${{ inputs.version }},enable=${{ inputs.version != '' }}
103115 type=raw,value=latest,enable=${{ inputs.version != '' }}
@@ -109,37 +121,53 @@ jobs:
109121 id : build-config
110122 run : |
111123 # Determine if we're building for multiple platforms
124+ IS_MULTI_PLATFORM="false"
112125 if echo "${{ inputs.platforms }}" | grep -q ','; then
113- echo "is_multi_platform=true" >> $GITHUB_OUTPUT
114- else
115- echo "is_multi_platform=false" >> $GITHUB_OUTPUT
126+ IS_MULTI_PLATFORM="true"
116127 fi
117128
118- # Determine if we can load the image (only for single-platform non-artifact builds)
129+ # For multi-platform builds with save-artifact, push to GHCR
130+ # For single-platform builds or PR builds, load locally or save to tar
119131 SAVE_ARTIFACT="${{ inputs.save-artifact }}"
120- if [ "$SAVE_ARTIFACT" != "true" ] && ! echo "${{ inputs.platforms }}" | grep -q ','; then
121- echo "can_load=true" >> $GITHUB_OUTPUT
122- echo "output_type=" >> $GITHUB_OUTPUT
132+ SHOULD_PUSH="false"
133+ CAN_LOAD="false"
134+ OUTPUT_TYPE=""
135+
136+ if [ "$SAVE_ARTIFACT" = "true" ] && [ "$IS_MULTI_PLATFORM" = "true" ]; then
137+ # Multi-platform artifact build: push to GHCR
138+ SHOULD_PUSH="true"
139+ CAN_LOAD="false"
140+ elif [ "$SAVE_ARTIFACT" != "true" ] && [ "$IS_MULTI_PLATFORM" = "false" ]; then
141+ # Single-platform PR build: load locally
142+ CAN_LOAD="true"
123143 else
124- # Need to output to tar for artifact or multi-platform builds
125- echo "can_load=false" >> $GITHUB_OUTPUT
126- # Use tag SHA if provided, otherwise use github.sha
144+ # Single-platform artifact build: save to tar
145+ CAN_LOAD="false"
127146 SHA_TO_USE="${{ inputs.tag_sha || github.sha }}"
128- # Use OCI format for multi-platform builds (docker format doesn't support manifest lists)
129- if echo "${{ inputs.platforms }}" | grep -q ','; then
130- echo "output_type=type=oci,dest=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar" >> $GITHUB_OUTPUT
131- else
132- echo "output_type=type=docker,dest=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar" >> $GITHUB_OUTPUT
133- fi
147+ OUTPUT_TYPE="type=docker,dest=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar"
134148 fi
135149
150+ {
151+ echo "is_multi_platform=$IS_MULTI_PLATFORM"
152+ echo "should_push=$SHOULD_PUSH"
153+ echo "can_load=$CAN_LOAD"
154+ echo "output_type=$OUTPUT_TYPE"
155+ } >> $GITHUB_OUTPUT
156+
157+ echo "📋 Build configuration:"
158+ echo " Multi-platform: $IS_MULTI_PLATFORM"
159+ echo " Save artifact: $SAVE_ARTIFACT"
160+ echo " Should push: $SHOULD_PUSH"
161+ echo " Can load: $CAN_LOAD"
162+ echo " Output type: $OUTPUT_TYPE"
163+
136164 - name : Build Docker image
137165 id : build
138166 uses : docker/build-push-action@v6
139167 with :
140168 context : .
141169 platforms : ${{ inputs.platforms }}
142- push : false # Never push from this reusable workflow
170+ push : ${{ steps.build-config.outputs.should_push == 'true' }}
143171 load : ${{ steps.build-config.outputs.can_load == 'true' }}
144172 tags : ${{ steps.meta.outputs.tags }}
145173 labels : ${{ steps.meta.outputs.labels }}
@@ -163,14 +191,18 @@ jobs:
163191 if [ "$CAN_LOAD" = "true" ]; then
164192 # For loaded single-platform images, scan by image reference
165193 FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1)
166- echo "scan_input=" >> $GITHUB_OUTPUT
167- echo "scan_image_ref=$FIRST_TAG" >> $GITHUB_OUTPUT
194+ {
195+ echo "scan_input="
196+ echo "scan_image_ref=$FIRST_TAG"
197+ } >> $GITHUB_OUTPUT
168198 echo "Using image reference for scanning: $FIRST_TAG"
169199 else
170200 # For multi-platform or artifact builds, scan the tar file
171201 SHA_TO_USE="${{ inputs.tag_sha || github.sha }}"
172- echo "scan_input=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar" >> $GITHUB_OUTPUT
173- echo "scan_image_ref=" >> $GITHUB_OUTPUT
202+ {
203+ echo "scan_input=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar"
204+ echo "scan_image_ref="
205+ } >> $GITHUB_OUTPUT
174206 echo "Using tar file for scanning: ${{ inputs.artifact-name }}-${SHA_TO_USE}.tar"
175207 fi
176208
@@ -231,21 +263,24 @@ jobs:
231263
232264 # =============================================================================
233265 # ARTIFACT STORAGE
234- # Save Docker image for reuse in publish workflow
266+ # Save Docker image tar files for single-platform builds
267+ # Multi-platform builds are pushed to GHCR instead
235268 # =============================================================================
236269
237270 - name : Compress Docker image artifact
238271 # Compress the tar file to reduce storage costs
239- if : inputs.save-artifact
272+ # Only for single-platform builds (multi-platform builds pushed to GHCR)
273+ if : inputs.save-artifact && !contains(inputs.platforms, ',')
240274 run : |
241275 SHA_TO_USE="${{ inputs.tag_sha || github.sha }}"
242276 echo "Compressing Docker image artifact..."
243277 gzip -9 ${{ inputs.artifact-name }}-${SHA_TO_USE}.tar
244278 ls -lh ${{ inputs.artifact-name }}-${SHA_TO_USE}.tar.gz
245279
246280 - name : Upload Docker image artifact
247- # Store image for deterministic publishing
248- if : inputs.save-artifact
281+ # Store single-platform image tar for deterministic publishing
282+ # Multi-platform images are stored in GHCR registry
283+ if : inputs.save-artifact && !contains(inputs.platforms, ',')
249284 uses : actions/upload-artifact@v4
250285 with :
251286 name : ${{ inputs.artifact-name }}-${{ inputs.tag_sha || github.sha }}
@@ -258,10 +293,19 @@ jobs:
258293 # Generate attestations for build provenance (main builds only)
259294 # =============================================================================
260295
261- - name : Generate attestations
262- # Creates cryptographic proof of build provenance (SLSA Level 3)
263- # Only runs when id-token permission is available (required for attestations)
264- if : inputs.save-artifact && inputs.version != '' && env.ACTIONS_ID_TOKEN_REQUEST_URL != ''
296+ - name : Generate attestations for GHCR images
297+ # Creates cryptographic proof of build provenance for multi-platform images
298+ # Multi-platform images are stored in GHCR registry
299+ if : inputs.save-artifact && contains(inputs.platforms, ',') && inputs.version != '' && env.ACTIONS_ID_TOKEN_REQUEST_URL != ''
300+ uses : actions/attest-build-provenance@v2
301+ with :
302+ subject-name : ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }}
303+ subject-digest : ${{ steps.build.outputs.digest }}
304+ push-to-registry : true
305+
306+ - name : Generate attestations for tar artifacts
307+ # Creates cryptographic proof of build provenance for single-platform tar files
308+ if : inputs.save-artifact && !contains(inputs.platforms, ',') && inputs.version != '' && env.ACTIONS_ID_TOKEN_REQUEST_URL != ''
265309 uses : actions/attest-build-provenance@v2
266310 with :
267311 subject-path : ${{ inputs.artifact-name }}-${{ inputs.tag_sha || github.sha }}.tar.gz
0 commit comments