Skip to content

Commit a9b2019

Browse files
committed
feat(ci): split docker platforms speed up docker build time
1 parent 495f6ac commit a9b2019

File tree

1 file changed

+130
-41
lines changed

1 file changed

+130
-41
lines changed

.github/workflows/operator-ci.yaml

Lines changed: 130 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,60 @@ jobs:
177177
file: operator/reporting/cover.out
178178
format: golang
179179

180-
# Build multi-platform container image and push to registry
181-
build-and-push-operator:
180+
# Compute image tags and version metadata once for reuse
181+
compute-metadata:
182182
runs-on: ubuntu-latest
183-
needs: [tests] # Don't run the build and push if tests fail
184-
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
183+
needs: [tests]
184+
outputs:
185+
git-sha: ${{ steps.meta.outputs.git-sha }}
186+
version: ${{ steps.meta.outputs.version }}
187+
tags: ${{ steps.meta.outputs.tags }}
188+
steps:
189+
- uses: actions/checkout@v4
190+
- name: Fetch all tags
191+
run: git fetch --tags --force
192+
- name: Compute metadata
193+
id: meta
194+
run: |
195+
export GIT_SHA=$(git rev-parse --short ${{ github.sha }})
196+
echo "git-sha=${GIT_SHA}" >> $GITHUB_OUTPUT
197+
198+
case ${{ github.ref_type }} in
199+
branch)
200+
export VERSION=$(git tag --list 'operator*' --sort=-v:refname | head -n 1 | cut -d/ -f2)+${GIT_SHA}
201+
TAGS="${GIT_SHA} $(echo "${VERSION}" | tr + -)"
202+
;;
203+
tag)
204+
export VERSION=$(echo "${{ github.ref_name }}" | cut -f 2 -d /)
205+
TAGS="${GIT_SHA} ${VERSION} latest"
206+
;;
207+
*)
208+
echo "Unknown ref type: ${{ github.ref_type }}"
209+
exit 1
210+
;;
211+
esac
212+
213+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
214+
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
215+
echo "📦 Version: ${VERSION}"
216+
echo "🏷️ Tags: ${TAGS}"
217+
218+
# Build container images on native architecture runners (much faster than QEMU)
219+
build-operator:
220+
runs-on: ${{ matrix.runner }}
221+
needs: [compute-metadata]
222+
strategy:
223+
matrix:
224+
include:
225+
- platform: linux/amd64
226+
runner: ubuntu-latest
227+
- platform: linux/arm64
228+
runner: ubuntu-24.04-arm
185229
permissions:
186230
contents: read
187231
packages: write
188232
attestations: write
189233
id-token: write
190-
#
191234
steps:
192235
- name: Checkout repository
193236
uses: actions/checkout@v4
@@ -199,60 +242,106 @@ jobs:
199242
registry: ${{ env.REGISTRY }}
200243
username: ${{ github.actor }}
201244
password: ${{ secrets.GITHUB_TOKEN }}
202-
203-
# Setup for multi-platform builds (linux/amd64, linux/arm64)
204-
- name: Set up QEMU
205-
uses: docker/setup-qemu-action@v3
206245

207246
- name: Set up Docker Buildx
208247
uses: docker/setup-buildx-action@v3
209248

210-
# Build and tag container image based on git ref type
211-
- name: Build the operator container image
249+
# Build and tag container image for single platform on native hardware
250+
- name: Build the operator container image (${{ matrix.platform }})
212251
id: build
213252
env:
214-
platforms: ${{ env.PLATFORMS }}
253+
GIT_SHA: ${{ needs.compute-metadata.outputs.git-sha }}
254+
VERSION: ${{ needs.compute-metadata.outputs.version }}
215255
run: |
216-
apt-get update && apt-get install -y make git jq
256+
sudo apt-get update && sudo apt-get install -y jq
217257
cd operator
218-
# if this is a tag build, use the tag as the version, otherwise use the sha
219-
git fetch --all
220-
export GIT_SHA=$(git rev-parse --short ${{ github.sha }})
221-
TAGS="-t ${REGISTRY@L}/${{env.IMAGE_NAME}}/operator:${GIT_SHA}"
222-
case ${{ github.ref_type }} in
223-
branch)
224-
# The last tag + current git sha
225-
export OPERATOR_VERSION=$(git tag --list 'operator*' --sort=-v:refname | head -n 1 | cut -d/ -f2)+${GIT_SHA}
226-
TAGS="$TAGS -t ${REGISTRY@L}/${{env.IMAGE_NAME}}/operator:$(echo "${OPERATOR_VERSION}" | tr + -)"
227-
;;
228-
tag)
229-
# The version part of the tag
230-
export OPERATOR_VERSION=$(echo "${{ github.ref_name }}" | cut -f 2 -d /)
231-
TAGS="$TAGS -t ${REGISTRY@L}/${{env.IMAGE_NAME}}/operator:${OPERATOR_VERSION} -t ${REGISTRY@L}/${{env.IMAGE_NAME}}/operator:latest"
232-
;;
233-
*)
234-
echo "Unkown type ${{ github.ref_type }}"
235-
exit 1
236-
;;
237-
esac
258+
PLATFORM_TAG=$(echo "${{ matrix.platform }}" | tr '/' '-')
259+
260+
# Lowercase for Docker compliance
261+
IMAGE_NAME=$(echo "${{env.IMAGE_NAME}}" | tr '[:upper:]' '[:lower:]')
262+
REGISTRY=$(echo "${{env.REGISTRY}}" | tr '[:upper:]' '[:lower:]')
263+
264+
# Build platform-specific tags for all target tags
265+
TAGS=""
266+
for TAG in ${{ needs.compute-metadata.outputs.tags }}; do
267+
TAGS="$TAGS -t ${REGISTRY}/${IMAGE_NAME}/operator:${TAG}-${PLATFORM_TAG}"
268+
done
269+
238270
set -x
239271
docker buildx build \
240272
--build-arg GIT_SHA=${GIT_SHA} \
241-
--build-arg VERSION=${OPERATOR_VERSION} \
242-
--build-arg GO_VERSION=${GO_VERSION} \
273+
--build-arg VERSION=${VERSION} \
274+
--build-arg GO_VERSION=${{ env.GO_VERSION }} \
243275
--push \
244-
--platform ${{ env.PLATFORMS }} \
276+
--platform ${{ matrix.platform }} \
277+
--provenance=false \
245278
${TAGS@L} \
246279
--metadata-file=metadata.json \
247280
-f ../containers/operator.Dockerfile .
248-
cat metadata.json
281+
249282
echo "digest=$(cat metadata.json | jq -r .\"containerimage.digest\")" >> $GITHUB_OUTPUT
250-
cat $GITHUB_OUTPUT
283+
284+
# Create multi-platform manifest from individual architecture builds
285+
create-manifest:
286+
runs-on: ubuntu-latest
287+
needs: [compute-metadata, build-operator]
288+
permissions:
289+
contents: read
290+
packages: write
291+
attestations: write
292+
id-token: write
293+
steps:
294+
- name: Log in to the Container registry
295+
uses: docker/login-action@v3
296+
with:
297+
registry: ${{ env.REGISTRY }}
298+
username: ${{ github.actor }}
299+
password: ${{ secrets.GITHUB_TOKEN }}
300+
301+
# Create and push multi-platform manifests, then delete platform-specific tags
302+
- name: Create manifests and cleanup
303+
id: manifest
304+
run: |
305+
sudo apt-get update && sudo apt-get install -y jq
306+
307+
# Lowercase for Docker compliance
308+
IMAGE_NAME=$(echo "${{env.IMAGE_NAME}}" | tr '[:upper:]' '[:lower:]')
309+
REGISTRY=$(echo "${{env.REGISTRY}}" | tr '[:upper:]' '[:lower:]')
310+
311+
# Create manifest for each tag combining amd64 and arm64 images
312+
for TAG in ${{ needs.compute-metadata.outputs.tags }}; do
313+
FULL_TAG="${REGISTRY}/${IMAGE_NAME}/operator:${TAG}"
314+
echo "📦 Creating manifest for $FULL_TAG"
315+
docker manifest create $FULL_TAG \
316+
${FULL_TAG}-linux-amd64 \
317+
${FULL_TAG}-linux-arm64
318+
docker manifest push $FULL_TAG
319+
echo "✅ Pushed $FULL_TAG"
320+
done
321+
322+
# Get digest of the main tag (git sha) for attestation
323+
MAIN_TAG="${REGISTRY}/${IMAGE_NAME}/operator:${{ needs.compute-metadata.outputs.git-sha }}"
324+
DIGEST=$(docker manifest inspect $MAIN_TAG | jq -r '.manifests[0].digest')
325+
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
326+
echo "subject-name=${REGISTRY}/${IMAGE_NAME}/operator" >> $GITHUB_OUTPUT
327+
328+
# Clean up platform-specific tags to avoid confusion
329+
echo "🗑️ Cleaning up platform-specific tags..."
330+
for TAG in ${{ needs.compute-metadata.outputs.tags }}; do
331+
for PLATFORM in linux-amd64 linux-arm64; do
332+
PLATFORM_TAG="${REGISTRY}/${IMAGE_NAME}/operator:${TAG}-${PLATFORM}"
333+
echo "Deleting ${PLATFORM_TAG}"
334+
docker run --rm quay.io/skopeo/stable:latest delete \
335+
--creds ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} \
336+
docker://${PLATFORM_TAG} 2>&1 || echo " (tag may not exist)"
337+
done
338+
done
339+
echo "✅ Cleanup complete"
251340
252-
# Generate supply chain security attestation
341+
# Generate supply chain security attestation for the multi-platform manifest
253342
- name: Generate artifact attestation
254343
uses: actions/attest-build-provenance@v2
255344
with:
256-
subject-name: ${{ env.REGISTRY }}/${{env.IMAGE_NAME}}/operator
257-
subject-digest: ${{ steps.build.outputs.digest }}
345+
subject-name: ${{ steps.manifest.outputs.subject-name }}
346+
subject-digest: ${{ steps.manifest.outputs.digest }}
258347
push-to-registry: true

0 commit comments

Comments
 (0)