1515 UV_HTTP_TIMEOUT : 60
1616 UV_SYSTEM_PYTHON : " true"
1717 BASE_IMAGE_VERSION : " 2025.11.0"
18+ ARCHITECTURES : ' ["amd64", "aarch64"]'
1819
1920jobs :
2021 init :
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
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