diff --git a/.gitattributes b/.gitattributes index 6a18819be9d..606d6837628 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,6 +16,7 @@ Dockerfile.dev linguist-language=Dockerfile CODEOWNERS linguist-generated=true Dockerfile linguist-generated=true homeassistant/generated/*.py linguist-generated=true +machine/* linguist-generated=true mypy.ini linguist-generated=true requirements.txt linguist-generated=true requirements_all.txt linguist-generated=true diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index d68440fc90b..d68a2396bcf 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -35,6 +35,7 @@ jobs: channel: ${{ steps.version.outputs.channel }} publish: ${{ steps.version.outputs.publish }} architectures: ${{ env.ARCHITECTURES }} + base_image_version: ${{ env.BASE_IMAGE_VERSION }} steps: - name: Checkout the repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -100,7 +101,7 @@ jobs: arch: ${{ fromJson(needs.init.outputs.architectures) }} include: - arch: amd64 - os: ubuntu-latest + os: ubuntu-24.04 - arch: aarch64 os: ubuntu-24.04-arm steps: @@ -195,77 +196,20 @@ jobs: run: | echo "${GITHUB_SHA};${GITHUB_REF};${GITHUB_EVENT_NAME};${GITHUB_ACTOR}" > rootfs/OFFICIAL_IMAGE - - name: Login to GitHub Container Registry - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Install Cosign - uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0 - with: - cosign-release: "v2.5.3" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - - - name: Build variables - id: vars - shell: bash - env: - ARCH: ${{ matrix.arch }} - run: | - echo "base_image=ghcr.io/home-assistant/${ARCH}-homeassistant-base:${BASE_IMAGE_VERSION}" >> "$GITHUB_OUTPUT" - echo "cache_image=ghcr.io/home-assistant/${ARCH}-homeassistant:latest" >> "$GITHUB_OUTPUT" - echo "created=$(date --rfc-3339=seconds --utc)" >> "$GITHUB_OUTPUT" - - - name: Verify base image signature - env: - BASE_IMAGE: ${{ steps.vars.outputs.base_image }} - run: | - cosign verify \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - --certificate-identity-regexp "https://github.com/home-assistant/docker/.*" \ - "${BASE_IMAGE}" - - - name: Verify cache image signature - id: cache - continue-on-error: true - env: - CACHE_IMAGE: ${{ steps.vars.outputs.cache_image }} - run: | - cosign verify \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - --certificate-identity-regexp "https://github.com/home-assistant/core/.*" \ - "${CACHE_IMAGE}" - - name: Build base image - id: build - uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 + uses: home-assistant/builder/actions/build-image@62a1597b84b3461abad9816d9cd92862a2b542c3 # 2026.03.2 with: - context: . - file: ./Dockerfile - platforms: ${{ steps.vars.outputs.platform }} - push: true - cache-from: ${{ steps.cache.outcome == 'success' && steps.vars.outputs.cache_image || '' }} + arch: ${{ matrix.arch }} build-args: | - BUILD_FROM=${{ steps.vars.outputs.base_image }} - tags: ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant:${{ needs.init.outputs.version }} - outputs: type=image,push=true,compression=zstd,compression-level=9,force-compression=true,oci-mediatypes=true - labels: | - io.hass.arch=${{ matrix.arch }} - io.hass.version=${{ needs.init.outputs.version }} - org.opencontainers.image.created=${{ steps.vars.outputs.created }} - org.opencontainers.image.version=${{ needs.init.outputs.version }} - - - name: Sign image - env: - ARCH: ${{ matrix.arch }} - VERSION: ${{ needs.init.outputs.version }} - DIGEST: ${{ steps.build.outputs.digest }} - run: | - cosign sign --yes "ghcr.io/home-assistant/${ARCH}-homeassistant:${VERSION}@${DIGEST}" + BUILD_FROM=ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant-base:${{ needs.init.outputs.base_image_version }} + cache-gha: false + container-registry-password: ${{ secrets.GITHUB_TOKEN }} + cosign-base-identity: "https://github.com/home-assistant/docker/.*" + cosign-base-verify: ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant-base:${{ needs.init.outputs.base_image_version }} + image: ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant + image-tags: ${{ needs.init.outputs.version }} + push: true + version: ${{ needs.init.outputs.version }} build_machine: name: Build ${{ matrix.machine }} machine core image @@ -314,35 +258,38 @@ jobs: with: persist-credentials: false - - name: Set build additional args + - name: Compute extra tags + id: tags + shell: bash env: VERSION: ${{ needs.init.outputs.version }} run: | - # Create general tags if [[ "${VERSION}" =~ d ]]; then - echo "BUILD_ARGS=--additional-tag dev" >> $GITHUB_ENV + echo "extra_tags=dev" >> "$GITHUB_OUTPUT" elif [[ "${VERSION}" =~ b ]]; then - echo "BUILD_ARGS=--additional-tag beta" >> $GITHUB_ENV + echo "extra_tags=beta" >> "$GITHUB_OUTPUT" else - echo "BUILD_ARGS=--additional-tag stable" >> $GITHUB_ENV + echo "extra_tags=stable" >> "$GITHUB_OUTPUT" fi - - name: Login to GitHub Container Registry - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 + - name: Build machine image + uses: home-assistant/builder/actions/build-image@62a1597b84b3461abad9816d9cd92862a2b542c3 # 2026.03.2 with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build base image - uses: home-assistant/builder@6cb4fd3d1338b6e22d0958a4bcb53e0965ea63b4 # 2026.02.1 - with: - image: ${{ matrix.arch }} - args: | - $BUILD_ARGS \ - --target /data/machine \ - --cosign \ - --machine "${{ needs.init.outputs.version }}=${{ matrix.machine }}" + arch: ${{ matrix.arch }} + build-args: | + BUILD_FROM=ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant:${{ needs.init.outputs.version }} + cache-gha: false + container-registry-password: ${{ secrets.GITHUB_TOKEN }} + context: machine/ + cosign-base-identity: "https://github.com/home-assistant/core/.*" + cosign-base-verify: ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant:${{ needs.init.outputs.version }} + file: machine/${{ matrix.machine }} + image: ghcr.io/home-assistant/${{ matrix.machine }}-homeassistant + image-tags: | + ${{ needs.init.outputs.version }} + ${{ steps.tags.outputs.extra_tags }} + push: true + version: ${{ needs.init.outputs.version }} publish_ha: name: Publish version files diff --git a/Dockerfile b/Dockerfile index a64cb7c8276..55919f9fccf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,6 @@ LABEL \ org.opencontainers.image.description="Open-source home automation platform running on Python 3" \ org.opencontainers.image.documentation="https://www.home-assistant.io/docs/" \ org.opencontainers.image.licenses="Apache-2.0" \ - org.opencontainers.image.source="https://github.com/home-assistant/core" \ org.opencontainers.image.title="Home Assistant" \ org.opencontainers.image.url="https://www.home-assistant.io/" diff --git a/machine/build.yaml b/machine/build.yaml deleted file mode 100644 index 2f03369e52f..00000000000 --- a/machine/build.yaml +++ /dev/null @@ -1,10 +0,0 @@ -image: ghcr.io/home-assistant/{machine}-homeassistant -build_from: - aarch64: "ghcr.io/home-assistant/aarch64-homeassistant:" - amd64: "ghcr.io/home-assistant/amd64-homeassistant:" -cosign: - base_identity: https://github.com/home-assistant/core/.* - identity: https://github.com/home-assistant/core/.* -labels: - io.hass.type: core - org.opencontainers.image.source: https://github.com/home-assistant/core diff --git a/machine/generic-x86-64 b/machine/generic-x86-64 index 874f0aecc10..6bb19cbb487 100644 --- a/machine/generic-x86-64 +++ b/machine/generic-x86-64 @@ -1,7 +1,10 @@ -ARG \ - BUILD_FROM - -FROM $BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/amd64-homeassistant:latest +FROM ${BUILD_FROM} RUN apk --no-cache add \ libva-intel-driver + +LABEL io.hass.machine="generic-x86-64" diff --git a/machine/green b/machine/green index c1d74d3528e..e643b729937 100644 --- a/machine/green +++ b/machine/green @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="green" diff --git a/machine/intel-nuc b/machine/intel-nuc index 750b49727be..5d5de80a9c5 100644 --- a/machine/intel-nuc +++ b/machine/intel-nuc @@ -1,10 +1,10 @@ -ARG \ - BUILD_FROM - -FROM $BUILD_FROM - -# NOTE: intel-nuc will be replaced by generic-x86-64. Make sure to apply -# changes in generic-x86-64 as well. +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/amd64-homeassistant:latest +FROM ${BUILD_FROM} RUN apk --no-cache add \ libva-intel-driver + +LABEL io.hass.machine="intel-nuc" diff --git a/machine/khadas-vim3 b/machine/khadas-vim3 index c1d74d3528e..525bce6cef8 100644 --- a/machine/khadas-vim3 +++ b/machine/khadas-vim3 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="khadas-vim3" diff --git a/machine/odroid-c2 b/machine/odroid-c2 index c1d74d3528e..825fa8ff1c9 100644 --- a/machine/odroid-c2 +++ b/machine/odroid-c2 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="odroid-c2" diff --git a/machine/odroid-c4 b/machine/odroid-c4 index c1d74d3528e..b4b047c4c98 100644 --- a/machine/odroid-c4 +++ b/machine/odroid-c4 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="odroid-c4" diff --git a/machine/odroid-m1 b/machine/odroid-m1 index c1d74d3528e..9261d92177b 100644 --- a/machine/odroid-m1 +++ b/machine/odroid-m1 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="odroid-m1" diff --git a/machine/odroid-n2 b/machine/odroid-n2 index c1d74d3528e..6fba7a579a0 100644 --- a/machine/odroid-n2 +++ b/machine/odroid-n2 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="odroid-n2" diff --git a/machine/qemuarm-64 b/machine/qemuarm-64 index c1d74d3528e..a0d29ac1d3f 100644 --- a/machine/qemuarm-64 +++ b/machine/qemuarm-64 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="qemuarm-64" diff --git a/machine/qemux86-64 b/machine/qemux86-64 index c1d74d3528e..aba11244864 100644 --- a/machine/qemux86-64 +++ b/machine/qemux86-64 @@ -1,4 +1,7 @@ -ARG \ - BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/amd64-homeassistant:latest +FROM ${BUILD_FROM} -FROM $BUILD_FROM +LABEL io.hass.machine="qemux86-64" diff --git a/machine/raspberrypi3-64 b/machine/raspberrypi3-64 index 8232d3398a7..dbb7409c52c 100644 --- a/machine/raspberrypi3-64 +++ b/machine/raspberrypi3-64 @@ -1,7 +1,10 @@ -ARG \ - BUILD_FROM - -FROM $BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} RUN apk --no-cache add \ - raspberrypi-utils + raspberrypi-utils + +LABEL io.hass.machine="raspberrypi3-64" diff --git a/machine/raspberrypi4-64 b/machine/raspberrypi4-64 index 8232d3398a7..f966fe36dee 100644 --- a/machine/raspberrypi4-64 +++ b/machine/raspberrypi4-64 @@ -1,7 +1,10 @@ -ARG \ - BUILD_FROM - -FROM $BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} RUN apk --no-cache add \ - raspberrypi-utils + raspberrypi-utils + +LABEL io.hass.machine="raspberrypi4-64" diff --git a/machine/raspberrypi5-64 b/machine/raspberrypi5-64 index 8232d3398a7..7ae62479748 100644 --- a/machine/raspberrypi5-64 +++ b/machine/raspberrypi5-64 @@ -1,7 +1,10 @@ -ARG \ - BUILD_FROM - -FROM $BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} RUN apk --no-cache add \ - raspberrypi-utils + raspberrypi-utils + +LABEL io.hass.machine="raspberrypi5-64" diff --git a/machine/yellow b/machine/yellow index 8232d3398a7..1a69e5dab8e 100644 --- a/machine/yellow +++ b/machine/yellow @@ -1,7 +1,10 @@ -ARG \ - BUILD_FROM - -FROM $BUILD_FROM +# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/aarch64-homeassistant:latest +FROM ${BUILD_FROM} RUN apk --no-cache add \ - raspberrypi-utils + raspberrypi-utils + +LABEL io.hass.machine="yellow" diff --git a/script/hassfest/docker.py b/script/hassfest/docker.py index 83604411944..c99543ac507 100644 --- a/script/hassfest/docker.py +++ b/script/hassfest/docker.py @@ -25,7 +25,6 @@ LABEL \ org.opencontainers.image.description="Open-source home automation platform running on Python 3" \ org.opencontainers.image.documentation="https://www.home-assistant.io/docs/" \ org.opencontainers.image.licenses="Apache-2.0" \ - org.opencontainers.image.source="https://github.com/home-assistant/core" \ org.opencontainers.image.title="Home Assistant" \ org.opencontainers.image.url="https://www.home-assistant.io/" @@ -77,6 +76,59 @@ RUN \ WORKDIR /config """ + +@dataclass(frozen=True) +class _MachineConfig: + """Machine-specific Dockerfile configuration.""" + + arch: str + packages: tuple[str, ...] = () + + +_MACHINES = { + "generic-x86-64": _MachineConfig(arch="amd64", packages=("libva-intel-driver",)), + "green": _MachineConfig(arch="aarch64"), + "intel-nuc": _MachineConfig(arch="amd64", packages=("libva-intel-driver",)), + "khadas-vim3": _MachineConfig(arch="aarch64"), + "odroid-c2": _MachineConfig(arch="aarch64"), + "odroid-c4": _MachineConfig(arch="aarch64"), + "odroid-m1": _MachineConfig(arch="aarch64"), + "odroid-n2": _MachineConfig(arch="aarch64"), + "qemuarm-64": _MachineConfig(arch="aarch64"), + "qemux86-64": _MachineConfig(arch="amd64"), + "raspberrypi3-64": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)), + "raspberrypi4-64": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)), + "raspberrypi5-64": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)), + "yellow": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)), +} + +_MACHINE_DOCKERFILE_TEMPLATE = r"""# Automatically generated by hassfest. +# +# To update, run python3 -m script.hassfest -p docker +ARG BUILD_FROM=ghcr.io/home-assistant/{arch}-homeassistant:latest +FROM ${{BUILD_FROM}} +{extra_packages} +LABEL io.hass.machine="{machine}" +""" + + +def _generate_machine_dockerfile( + machine_name: str, machine_config: _MachineConfig +) -> str: + """Generate a machine Dockerfile from configuration.""" + if machine_config.packages: + pkg_lines = " \\\n ".join(machine_config.packages) + extra_packages = f"\nRUN apk --no-cache add \\\n {pkg_lines}\n" + else: + extra_packages = "" + + return _MACHINE_DOCKERFILE_TEMPLATE.format( + arch=machine_config.arch, + extra_packages=extra_packages, + machine=machine_name, + ) + + _HASSFEST_TEMPLATE = r"""# Automatically generated by hassfest. # # To update, run python3 -m script.hassfest -p docker @@ -174,7 +226,7 @@ def _generate_files(config: Config) -> list[File]: config.root / "requirements_test_pre_commit.txt", {"ruff"} ) - return [ + files = [ File( DOCKERFILE_TEMPLATE.format( timeout=timeout, @@ -192,6 +244,16 @@ def _generate_files(config: Config) -> list[File]: ), ] + for machine_name, machine_config in sorted(_MACHINES.items()): + files.append( + File( + _generate_machine_dockerfile(machine_name, machine_config), + config.root / "machine" / machine_name, + ) + ) + + return files + def validate(integrations: dict[str, Integration], config: Config) -> None: """Validate dockerfile."""