diff --git a/.dockerignore b/.dockerignore index 9f14b98059..3b4e95f016 100644 --- a/.dockerignore +++ b/.dockerignore @@ -111,4 +111,5 @@ config/ examples/ Dockerfile .git/ -tests/build/ +tests/ +.* diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index 56be20bd87..c10bef7a9f 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -7,11 +7,11 @@ inputs: target: description: "Target to build" required: true - example: "docker" - baseimg: - description: "Base image type" + example: "final / lint" + build_type: + description: "Image type to build" required: true - example: "docker" + example: "docker / hassio" suffix: description: "Suffix to add to tags" required: true @@ -55,7 +55,7 @@ runs: cache-from: type=gha cache-to: ${{ steps.cache-to.outputs.value }} build-args: | - BASEIMGTYPE=${{ inputs.baseimg }} + BUILD_TYPE=${{ inputs.build_type }} BUILD_VERSION=${{ inputs.version }} outputs: | type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true @@ -78,7 +78,7 @@ runs: cache-from: type=gha cache-to: ${{ steps.cache-to.outputs.value }} build-args: | - BASEIMGTYPE=${{ inputs.baseimg }} + BUILD_TYPE=${{ inputs.build_type }} BUILD_VERSION=${{ inputs.version }} outputs: | type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7895e7624a..21c461720e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -111,8 +111,8 @@ jobs: uses: ./.github/actions/build-image with: platform: ${{ matrix.platform }} - target: docker - baseimg: docker + target: final + build_type: docker suffix: "" version: ${{ needs.init.outputs.tag }} @@ -120,8 +120,8 @@ jobs: uses: ./.github/actions/build-image with: platform: ${{ matrix.platform }} - target: hassio - baseimg: hassio + target: final + build_type: hassio suffix: "hassio" version: ${{ needs.init.outputs.tag }} @@ -130,7 +130,7 @@ jobs: with: platform: ${{ matrix.platform }} target: lint - baseimg: docker + build_type: docker suffix: lint version: ${{ needs.init.outputs.tag }} diff --git a/docker/Dockerfile b/docker/Dockerfile index 4393d5a447..11ed5e492a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,214 +1,73 @@ -# Build these with the build.py script -# Example: -# python3 docker/build.py --tag dev --arch amd64 --build-type docker build +ARG BUILD_TYPE=docker -# One of "docker", "hassio" -ARG BASEIMGTYPE=docker +FROM ghcr.io/esphome/docker-base:2024.09.0-pre0 AS base +RUN git config --system --add safe.directory "*" -# https://github.com/hassio-addons/addon-debian-base/releases -FROM ghcr.io/hassio-addons/debian-base:7.2.0 AS base-hassio -# https://hub.docker.com/_/debian?tab=tags&page=1&name=bookworm -FROM debian:12.2-slim AS base-docker - -FROM base-${BASEIMGTYPE} AS base - - -ARG TARGETARCH -ARG TARGETVARIANT - - -# Note that --break-system-packages is used below because -# https://peps.python.org/pep-0668/ added a safety check that prevents -# installing packages with the same name as a system package. This is -# not a problem for us because we are not concerned about overwriting -# system packages because we are running in an isolated container. +COPY requirements.txt / RUN \ - apt-get update \ - # Use pinned versions so that we get updates with build caching - && apt-get install -y --no-install-recommends \ - python3-pip=23.0.1+dfsg-1 \ - python3-setuptools=66.1.1-1 \ - python3-venv=3.11.2-1+b1 \ - python3-wheel=0.38.4-2 \ - iputils-ping=3:20221126-1 \ - git=1:2.39.2-1.1 \ - curl=7.88.1-10+deb12u7 \ - openssh-client=1:9.2p1-2+deb12u3 \ - python3-cffi=1.15.1-5 \ - libcairo2=1.16.0-7 \ - libmagic1=1:5.44-3 \ - patch=2.7.6-7 \ - && ( \ - ( \ - [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] && \ - apt-get install -y --no-install-recommends \ - build-essential=12.9 \ - python3-dev=3.11.2-1+b1 \ - zlib1g-dev=1:1.2.13.dfsg-1 \ - libjpeg-dev=1:2.1.5-2 \ - libfreetype-dev=2.12.1+dfsg-5+deb12u3 \ - libssl-dev=3.0.14-1~deb12u1 \ - libffi-dev=3.4.4-1 \ - libopenjp2-7=2.5.0-2 \ - libtiff6=4.5.0-6+deb12u1 \ - cargo=0.66.0+ds1-1 \ - pkg-config=1.8.1-1 \ - gcc-arm-linux-gnueabihf=4:12.2.0-3 \ - ) \ - || [ "$TARGETARCH$TARGETVARIANT" != "armv7" ] \ - ) \ - && rm -rf \ - /tmp/* \ - /var/{cache,log}/* \ - /var/lib/apt/lists/* - -ENV \ - # Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/ - LANG=C.UTF-8 LC_ALL=C.UTF-8 \ - # Store globally installed pio libs in /piolibs - PLATFORMIO_GLOBALLIB_DIR=/piolibs - -# Support legacy binaries on Debian multiarch system. There is no "correct" way -# to do this, other than using properly built toolchains... -# See: https://unix.stackexchange.com/questions/553743/correct-way-to-add-lib-ld-linux-so-3-in-debian -RUN \ - if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - ln -s /lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 /lib/ld-linux.so.3; \ - fi + uv pip install --no-cache-dir \ + -r /requirements.txt RUN \ - # Ubuntu python3-pip is missing wheel - if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ - pip3 install \ - --break-system-packages --no-cache-dir \ - # Keep platformio version in sync with requirements.txt - platformio==6.1.15 \ # Change some platformio settings - && platformio settings set enable_telemetry No \ + platformio settings set enable_telemetry No \ && platformio settings set check_platformio_interval 1000000 \ && mkdir -p /piolibs +COPY script/platformio_install_deps.py platformio.ini / -# First install requirements to leverage caching when requirements don't change -# tmpfs is for https://github.com/rust-lang/cargo/issues/8719 +RUN /platformio_install_deps.py /platformio.ini --libraries -COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini / -RUN --mount=type=tmpfs,target=/root/.cargo if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ - CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo \ - pip3 install \ - --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ - && /platformio_install_deps.py /platformio.ini --libraries +FROM base AS base-docker -# Avoid unsafe git error when container user and file config volume permissions don't match -RUN git config --system --add safe.directory '*' - - -# ======================= docker-type image ======================= -FROM base AS docker - -# Copy esphome and install -COPY . /esphome -RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ - pip3 install \ - --break-system-packages --no-cache-dir -e /esphome - -# Settings for dashboard ENV USERNAME="" PASSWORD="" - -# Expose the dashboard to Docker EXPOSE 6052 -# Run healthcheck (heartbeat) HEALTHCHECK --interval=30s --timeout=30s \ - CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1 + CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1 COPY docker/docker_entrypoint.sh /entrypoint.sh -# The directory the user should mount their configuration files to VOLUME /config WORKDIR /config -# Set entrypoint to esphome (via a script) so that the user doesn't have to type 'esphome' -# in every docker command twice ENTRYPOINT ["/entrypoint.sh"] -# When no arguments given, start the dashboard in the workdir CMD ["dashboard", "/config"] +FROM base AS base-hassio - - -# ======================= hassio-type image ======================= -FROM base AS hassio +# TODO: Install s6-overlay RUN \ - apt-get update \ - # Use pinned versions so that we get updates with build caching + set -x \ + && apt-get update \ && apt-get install -y --no-install-recommends \ nginx-light=1.22.1-9 \ && rm -rf \ /tmp/* \ /var/{cache,log}/* \ - /var/lib/apt/lists/* + /var/lib/apt/lists/* \ + /usr/src/* ARG BUILD_VERSION=dev -# Copy root filesystem COPY docker/ha-addon-rootfs/ / -# Copy esphome and install -COPY . /esphome -RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ - pip3 install \ - --break-system-packages --no-cache-dir -e /esphome - -# Labels LABEL \ io.hass.name="ESPHome" \ - io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + io.hass.description="Manage and program microcontrollers through YAML configuration files" \ io.hass.type="addon" \ io.hass.version="${BUILD_VERSION}" - # io.hass.arch is inherited from addon-debian-base +FROM base-${BUILD_TYPE} AS final + +COPY . /esphome + +RUN uv pip install --no-cache-dir -e /esphome -# ======================= lint-type image ======================= FROM base AS lint -ENV \ - PLATFORMIO_CORE_DIR=/esphome/.temp/platformio - -RUN \ - apt-get update \ - # Use pinned versions so that we get updates with build caching - && apt-get install -y --no-install-recommends \ - clang-format-13=1:13.0.1-11+b2 \ - clang-tidy-14=1:14.0.6-12 \ - patch=2.7.6-7 \ - software-properties-common=0.99.30-4.1~deb12u1 \ - nano=7.2-1+deb12u1 \ - build-essential=12.9 \ - python3-dev=3.11.2-1+b1 \ - && rm -rf \ - /tmp/* \ - /var/{cache,log}/* \ - /var/lib/apt/lists/* - -COPY requirements_test.txt / -RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ - pip3 install \ - --break-system-packages --no-cache-dir -r /requirements_test.txt - -VOLUME ["/esphome"] -WORKDIR /esphome +# TODO: diff --git a/docker/build.py b/docker/build.py index 47461ddf97..25719351dd 100755 --- a/docker/build.py +++ b/docker/build.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 -from dataclasses import dataclass -import subprocess import argparse -from platform import machine -import shlex +from dataclasses import dataclass import re +import shlex +import subprocess import sys - CHANNEL_DEV = "dev" CHANNEL_BETA = "beta" CHANNEL_RELEASE = "release" @@ -57,7 +55,7 @@ manifest_parser = subparsers.add_parser( class DockerParams: build_to: str manifest_to: str - baseimgtype: str + build_type: str platform: str target: str @@ -69,7 +67,7 @@ class DockerParams: TYPE_LINT: "esphome/esphome-lint", }[build_type] build_to = f"{prefix}-{arch}" - baseimgtype = { + _build_type = { TYPE_DOCKER: "docker", TYPE_HA_ADDON: "hassio", TYPE_LINT: "docker", @@ -80,14 +78,14 @@ class DockerParams: ARCH_AARCH64: "linux/arm64", }[arch] target = { - TYPE_DOCKER: "docker", - TYPE_HA_ADDON: "hassio", + TYPE_DOCKER: "final", + TYPE_HA_ADDON: "final", TYPE_LINT: "lint", }[build_type] return cls( build_to=build_to, manifest_to=prefix, - baseimgtype=baseimgtype, + build_type=_build_type, platform=platform, target=target, ) @@ -149,7 +147,7 @@ def main(): "buildx", "build", "--build-arg", - f"BASEIMGTYPE={params.baseimgtype}", + f"BUILD_TYPE={params.build_type}", "--build-arg", f"BUILD_VERSION={args.tag}", "--cache-from", diff --git a/requirements.txt b/requirements.txt index 3e658de8ad..3dfa1fa802 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ tornado==6.4 tzlocal==5.2 # from time tzdata>=2021.1 # from time pyserial==3.5 -platformio==6.1.15 # When updating platformio, also update Dockerfile +platformio==6.1.15 esptool==4.7.0 click==8.1.7 esphome-dashboard==20240620.0 @@ -17,6 +17,9 @@ aioesphomeapi==24.6.2 zeroconf==0.132.2 python-magic==0.4.27 ruamel.yaml==0.18.6 # dashboard_import +pillow==10.2.0 +cairosvg==2.7.1 + # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 diff --git a/requirements_optional.txt b/requirements_optional.txt index c984d41332..e69de29bb2 100644 --- a/requirements_optional.txt +++ b/requirements_optional.txt @@ -1,2 +0,0 @@ -pillow==10.2.0 -cairosvg==2.7.1