Skip to content

Commit 550716a

Browse files
edenhausCopilot
andauthored
Optimize docker container publish job (home-assistant#157076)
Co-authored-by: Copilot <[email protected]>
1 parent 56a71e6 commit 550716a

File tree

1 file changed

+81
-75
lines changed

1 file changed

+81
-75
lines changed

.github/workflows/builder.yml

Lines changed: 81 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ env:
1515
UV_HTTP_TIMEOUT: 60
1616
UV_SYSTEM_PYTHON: "true"
1717
BASE_IMAGE_VERSION: "2025.11.0"
18+
ARCHITECTURES: '["amd64", "aarch64"]'
1819

1920
jobs:
2021
init:
@@ -25,6 +26,7 @@ jobs:
2526
version: ${{ steps.version.outputs.version }}
2627
channel: ${{ steps.version.outputs.channel }}
2728
publish: ${{ steps.version.outputs.publish }}
29+
architectures: ${{ env.ARCHITECTURES }}
2830
steps:
2931
- name: Checkout the repository
3032
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
@@ -85,7 +87,7 @@ jobs:
8587
strategy:
8688
fail-fast: false
8789
matrix:
88-
arch: ["amd64", "aarch64"]
90+
arch: ${{ fromJson(needs.init.outputs.architectures) }}
8991
include:
9092
- arch: amd64
9193
os: ubuntu-latest
@@ -227,6 +229,7 @@ jobs:
227229
file: ./Dockerfile
228230
platforms: ${{ steps.vars.outputs.platform }}
229231
push: true
232+
provenance: false
230233
cache-from: ${{ steps.cache.outcome == 'success' && steps.vars.outputs.cache_image || '' }}
231234
build-args: |
232235
BUILD_FROM=${{ steps.vars.outputs.base_image }}
@@ -350,9 +353,6 @@ jobs:
350353
matrix:
351354
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
352355
steps:
353-
- name: Checkout the repository
354-
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
355-
356356
- name: Install Cosign
357357
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
358358
with:
@@ -373,81 +373,87 @@ jobs:
373373
username: ${{ github.repository_owner }}
374374
password: ${{ secrets.GITHUB_TOKEN }}
375375

376-
- name: Build Meta Image
376+
- name: Verify architecture image signatures
377377
shell: bash
378378
run: |
379-
export DOCKER_CLI_EXPERIMENTAL=enabled
380-
381-
function create_manifest() {
382-
local tag_l=${1}
383-
local tag_r=${2}
384-
local registry=${{ matrix.registry }}
385-
386-
docker manifest create "${registry}/home-assistant:${tag_l}" \
387-
"${registry}/amd64-homeassistant:${tag_r}" \
388-
"${registry}/aarch64-homeassistant:${tag_r}"
389-
390-
docker manifest annotate "${registry}/home-assistant:${tag_l}" \
391-
"${registry}/amd64-homeassistant:${tag_r}" \
392-
--os linux --arch amd64
393-
394-
docker manifest annotate "${registry}/home-assistant:${tag_l}" \
395-
"${registry}/aarch64-homeassistant:${tag_r}" \
396-
--os linux --arch arm64 --variant=v8
397-
398-
docker manifest push --purge "${registry}/home-assistant:${tag_l}"
399-
cosign sign --yes "${registry}/home-assistant:${tag_l}"
400-
}
401-
402-
function validate_image() {
403-
local image=${1}
404-
if ! cosign verify --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp https://github.com/home-assistant/core/.* "${image}"; then
405-
echo "Invalid signature!"
406-
exit 1
407-
fi
408-
}
409-
410-
function push_dockerhub() {
411-
local image=${1}
412-
local tag=${2}
413-
414-
docker tag "ghcr.io/home-assistant/${image}:${tag}" "docker.io/homeassistant/${image}:${tag}"
415-
docker push "docker.io/homeassistant/${image}:${tag}"
416-
cosign sign --yes "docker.io/homeassistant/${image}:${tag}"
417-
}
418-
419-
# Pull images from github container registry and verify signature
420-
docker pull "ghcr.io/home-assistant/amd64-homeassistant:${{ needs.init.outputs.version }}"
421-
docker pull --platform linux/arm64 "ghcr.io/home-assistant/aarch64-homeassistant:${{ needs.init.outputs.version }}"
422-
423-
validate_image "ghcr.io/home-assistant/amd64-homeassistant:${{ needs.init.outputs.version }}"
424-
validate_image "ghcr.io/home-assistant/aarch64-homeassistant:${{ needs.init.outputs.version }}"
425-
426-
if [[ "${{ matrix.registry }}" == "docker.io/homeassistant" ]]; then
427-
# Upload images to dockerhub
428-
push_dockerhub "amd64-homeassistant" "${{ needs.init.outputs.version }}"
429-
push_dockerhub "aarch64-homeassistant" "${{ needs.init.outputs.version }}"
430-
fi
379+
ARCHS=$(echo '${{ needs.init.outputs.architectures }}' | jq -r '.[]')
380+
for arch in $ARCHS; do
381+
echo "Verifying ${arch} image signature..."
382+
cosign verify \
383+
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
384+
--certificate-identity-regexp https://github.com/home-assistant/core/.* \
385+
"ghcr.io/home-assistant/${arch}-homeassistant:${{ needs.init.outputs.version }}"
386+
done
387+
echo "✓ All images verified successfully"
388+
389+
# Generate all Docker tags based on version string
390+
# Version format: YYYY.MM.PATCH, YYYY.MM.PATCHbN (beta), or YYYY.MM.PATCH.devYYYYMMDDHHMM (dev)
391+
# Examples:
392+
# 2025.12.1 (stable) -> tags: 2025.12.1, 2025.12, stable, latest, beta, rc
393+
# 2025.12.0b3 (beta) -> tags: 2025.12.0b3, beta, rc
394+
# 2025.12.0.dev202511250240 -> tags: 2025.12.0.dev202511250240, dev
395+
- name: Generate Docker metadata
396+
id: meta
397+
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
398+
with:
399+
images: ${{ matrix.registry }}/home-assistant
400+
sep-tags: ","
401+
tags: |
402+
type=raw,value=${{ needs.init.outputs.version }},priority=9999
403+
type=raw,value=dev,enable=${{ contains(needs.init.outputs.version, 'd') }}
404+
type=raw,value=beta,enable=${{ !contains(needs.init.outputs.version, 'd') }}
405+
type=raw,value=rc,enable=${{ !contains(needs.init.outputs.version, 'd') }}
406+
type=raw,value=stable,enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
407+
type=raw,value=latest,enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
408+
type=semver,pattern={{major}}.{{minor}},value=${{ needs.init.outputs.version }},enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
409+
410+
- name: Push architecture images to DockerHub
411+
if: matrix.registry == 'docker.io/homeassistant'
412+
shell: bash
413+
run: |
414+
ARCHS=$(echo '${{ needs.init.outputs.architectures }}' | jq -r '.[]')
415+
for arch in $ARCHS; do
416+
echo "Pushing ${arch} to DockerHub..."
417+
docker pull "ghcr.io/home-assistant/${arch}-homeassistant:${{ needs.init.outputs.version }}"
418+
docker tag "ghcr.io/home-assistant/${arch}-homeassistant:${{ needs.init.outputs.version }}" \
419+
"docker.io/homeassistant/${arch}-homeassistant:${{ needs.init.outputs.version }}"
420+
docker push "docker.io/homeassistant/${arch}-homeassistant:${{ needs.init.outputs.version }}"
421+
cosign sign --yes "docker.io/homeassistant/${arch}-homeassistant:${{ needs.init.outputs.version }}"
422+
done
431423
432-
# Create version tag
433-
create_manifest "${{ needs.init.outputs.version }}" "${{ needs.init.outputs.version }}"
424+
- name: Set up Docker Buildx
425+
uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.7.1
434426

435-
# Create general tags
436-
if [[ "${{ needs.init.outputs.version }}" =~ d ]]; then
437-
create_manifest "dev" "${{ needs.init.outputs.version }}"
438-
elif [[ "${{ needs.init.outputs.version }}" =~ b ]]; then
439-
create_manifest "beta" "${{ needs.init.outputs.version }}"
440-
create_manifest "rc" "${{ needs.init.outputs.version }}"
441-
else
442-
create_manifest "stable" "${{ needs.init.outputs.version }}"
443-
create_manifest "latest" "${{ needs.init.outputs.version }}"
444-
create_manifest "beta" "${{ needs.init.outputs.version }}"
445-
create_manifest "rc" "${{ needs.init.outputs.version }}"
446-
447-
# Create series version tag (e.g. 2021.6)
448-
v="${{ needs.init.outputs.version }}"
449-
create_manifest "${v%.*}" "${{ needs.init.outputs.version }}"
450-
fi
427+
- name: Create and push multi-arch manifests
428+
shell: bash
429+
run: |
430+
# Build list of architecture images dynamically
431+
ARCHS=$(echo '${{ needs.init.outputs.architectures }}' | jq -r '.[]')
432+
ARCH_IMAGES=()
433+
for arch in $ARCHS; do
434+
ARCH_IMAGES+=("${{ matrix.registry }}/${arch}-homeassistant:${{ needs.init.outputs.version }}")
435+
done
436+
437+
# Build list of all tags for single manifest creation
438+
# Note: Using sep-tags=',' in metadata-action for easier parsing
439+
TAG_ARGS=()
440+
IFS=',' read -ra TAGS <<< "${{ steps.meta.outputs.tags }}"
441+
for tag in "${TAGS[@]}"; do
442+
TAG_ARGS+=("--tag" "${tag}")
443+
done
444+
445+
# Create manifest with ALL tags in a single operation (much faster!)
446+
echo "Creating multi-arch manifest with tags: ${TAGS[*]}"
447+
docker buildx imagetools create "${TAG_ARGS[@]}" "${ARCH_IMAGES[@]}"
448+
449+
# Sign each tag separately (signing requires individual tag names)
450+
echo "Signing all tags..."
451+
for tag in "${TAGS[@]}"; do
452+
echo "Signing ${tag}"
453+
cosign sign --yes "${tag}"
454+
done
455+
456+
echo "All manifests created and signed successfully"
451457
452458
build_python:
453459
name: Build PyPi package

0 commit comments

Comments
 (0)