name: Build images # yamllint disable-line rule:truthy on: workflow_dispatch: release: types: ["published"] schedule: - cron: "0 2 * * *" env: BUILD_TYPE: core DEFAULT_PYTHON: "3.12" PIP_TIMEOUT: 60 jobs: init: name: Initialize build if: github.repository_owner == 'home-assistant' runs-on: ubuntu-latest outputs: architectures: ${{ steps.info.outputs.architectures }} version: ${{ steps.version.outputs.version }} channel: ${{ steps.version.outputs.channel }} publish: ${{ steps.version.outputs.publish }} steps: - name: Checkout the repository uses: actions/checkout@v4.1.2 with: fetch-depth: 0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} uses: actions/setup-python@v5.0.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Get information id: info uses: home-assistant/actions/helpers/info@master - name: Get version id: version uses: home-assistant/actions/helpers/version@master with: type: ${{ env.BUILD_TYPE }} - name: Verify version uses: home-assistant/actions/helpers/verify-version@master with: ignore-dev: true build_python: name: Build PyPi package environment: ${{ needs.init.outputs.channel }} needs: ["init", "build_base"] runs-on: ubuntu-latest if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true' steps: - name: Checkout the repository uses: actions/checkout@v4.1.2 - name: Set up Python ${{ env.DEFAULT_PYTHON }} uses: actions/setup-python@v5.0.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Download Translations run: python3 -m script.translations download env: LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} - name: Build package shell: bash run: | # Remove dist, build, and homeassistant.egg-info # when build locally for testing! pip install twine build python -m build - name: Upload package shell: bash run: | export TWINE_USERNAME="__token__" export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" twine upload dist/* --skip-existing build_base: name: Build ${{ matrix.arch }} base core image if: github.repository_owner == 'home-assistant' needs: init runs-on: ubuntu-latest permissions: contents: read packages: write id-token: write strategy: fail-fast: false matrix: arch: ${{ fromJson(needs.init.outputs.architectures) }} steps: - name: Checkout the repository uses: actions/checkout@v4.1.2 - name: Download nightly wheels of frontend if: needs.init.outputs.channel == 'dev' uses: dawidd6/action-download-artifact@v3.1.4 with: github_token: ${{secrets.GITHUB_TOKEN}} repo: home-assistant/frontend branch: dev workflow: nightly.yaml workflow_conclusion: success name: wheels - name: Download nightly wheels of intents if: needs.init.outputs.channel == 'dev' uses: dawidd6/action-download-artifact@v3.1.4 with: github_token: ${{secrets.GITHUB_TOKEN}} repo: home-assistant/intents-package branch: main workflow: nightly.yaml workflow_conclusion: success name: package - name: Set up Python ${{ env.DEFAULT_PYTHON }} if: needs.init.outputs.channel == 'dev' uses: actions/setup-python@v5.0.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Adjust nightly version if: needs.init.outputs.channel == 'dev' shell: bash run: | python3 -m pip install packaging tomli python3 -m pip install . version="$(python3 script/version_bump.py nightly)" if [[ "$(ls home_assistant_frontend*.whl)" =~ ^home_assistant_frontend-(.*)-py3-none-any.whl$ ]]; then echo "Found frontend wheel, setting version to: ${BASH_REMATCH[1]}" frontend_version="${BASH_REMATCH[1]}" yq \ --inplace e -o json \ '.requirements = ["home-assistant-frontend=="+env(frontend_version)]' \ homeassistant/components/frontend/manifest.json sed -i "s|home-assistant-frontend==.*|home-assistant-frontend==${BASH_REMATCH[1]}|" \ homeassistant/package_constraints.txt sed -i "s|home-assistant-frontend==.*||" requirements_all.txt fi if [[ "$(ls home_assistant_intents*.whl)" =~ ^home_assistant_intents-(.*)-py3-none-any.whl$ ]]; then echo "Found intents wheel, setting version to: ${BASH_REMATCH[1]}" yq \ --inplace e -o json \ 'del(.requirements[] | select(contains("home-assistant-intents")))' \ homeassistant/components/conversation/manifest.json intents_version="${BASH_REMATCH[1]}" yq \ --inplace e -o json \ '.requirements += ["home-assistant-intents=="+env(intents_version)]' \ homeassistant/components/conversation/manifest.json sed -i "s|home-assistant-intents==.*|home-assistant-intents==${BASH_REMATCH[1]}|" \ homeassistant/package_constraints.txt sed -i "s|home-assistant-intents==.*||" requirements_all.txt fi - name: Adjustments for armhf if: matrix.arch == 'armhf' run: | # Pandas has issues building on armhf, it is expected they # will drop the platform in the near future (they consider it # "flimsy" on 386). The following packages depend on pandas, # so we comment them out. sed -i "s|env-canada|# env-canada|g" requirements_all.txt sed -i "s|noaa-coops|# noaa-coops|g" requirements_all.txt sed -i "s|pyezviz|# pyezviz|g" requirements_all.txt sed -i "s|pykrakenapi|# pykrakenapi|g" requirements_all.txt - name: Adjustments for 64-bit if: matrix.arch == 'amd64' || matrix.arch == 'aarch64' run: | # Some speedups are only available on 64-bit, and since # we build 32bit images on 64bit hosts, we only enable # the speed ups on 64bit since the wheels for 32bit # are not available. sed -i "s|aiohttp-zlib-ng|aiohttp-zlib-ng\[isal\]|g" requirements_all.txt - name: Download Translations run: python3 -m script.translations download env: LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} - name: Write meta info file shell: bash run: | echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE - name: Login to GitHub Container Registry uses: docker/login-action@v3.1.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build base image uses: home-assistant/builder@2024.03.5 with: args: | $BUILD_ARGS \ --${{ matrix.arch }} \ --cosign \ --target /data \ --generic ${{ needs.init.outputs.version }} - name: Archive translations shell: bash run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T - - name: Upload translations uses: actions/upload-artifact@v3 with: name: translations path: translations.tar.gz if-no-files-found: error build_machine: name: Build ${{ matrix.machine }} machine core image if: github.repository_owner == 'home-assistant' needs: ["init", "build_base"] runs-on: ubuntu-latest permissions: contents: read packages: write id-token: write strategy: matrix: machine: - generic-x86-64 - intel-nuc - khadas-vim3 - odroid-c2 - odroid-c4 - odroid-m1 - odroid-n2 - odroid-xu - qemuarm - qemuarm-64 - qemux86 - qemux86-64 - raspberrypi - raspberrypi2 - raspberrypi3 - raspberrypi3-64 - raspberrypi4 - raspberrypi4-64 - raspberrypi5-64 - tinker - yellow - green steps: - name: Checkout the repository uses: actions/checkout@v4.1.2 - name: Set build additional args run: | # Create general tags if [[ "${{ needs.init.outputs.version }}" =~ d ]]; then echo "BUILD_ARGS=--additional-tag dev" >> $GITHUB_ENV elif [[ "${{ needs.init.outputs.version }}" =~ b ]]; then echo "BUILD_ARGS=--additional-tag beta" >> $GITHUB_ENV else echo "BUILD_ARGS=--additional-tag stable" >> $GITHUB_ENV fi - name: Login to GitHub Container Registry uses: docker/login-action@v3.1.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build base image uses: home-assistant/builder@2024.03.5 with: args: | $BUILD_ARGS \ --target /data/machine \ --cosign \ --machine "${{ needs.init.outputs.version }}=${{ matrix.machine }}" publish_ha: name: Publish version files environment: ${{ needs.init.outputs.channel }} if: github.repository_owner == 'home-assistant' needs: ["init", "build_machine"] runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@v4.1.2 - name: Initialize git uses: home-assistant/actions/helpers/git-init@master with: name: ${{ secrets.GIT_NAME }} email: ${{ secrets.GIT_EMAIL }} token: ${{ secrets.GIT_TOKEN }} - name: Update version file uses: home-assistant/actions/helpers/version-push@master with: key: "homeassistant[]" key-description: "Home Assistant Core" version: ${{ needs.init.outputs.version }} channel: ${{ needs.init.outputs.channel }} - name: Update version file (stable -> beta) if: needs.init.outputs.channel == 'stable' uses: home-assistant/actions/helpers/version-push@master with: key: "homeassistant[]" key-description: "Home Assistant Core" version: ${{ needs.init.outputs.version }} channel: beta publish_container: name: Publish meta container for ${{ matrix.registry }} environment: ${{ needs.init.outputs.channel }} if: github.repository_owner == 'home-assistant' needs: ["init", "build_base"] runs-on: ubuntu-latest permissions: contents: read packages: write id-token: write strategy: matrix: registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"] steps: - name: Checkout the repository uses: actions/checkout@v4.1.2 - name: Install Cosign uses: sigstore/cosign-installer@v3.4.0 with: cosign-release: "v2.2.3" - name: Login to DockerHub if: matrix.registry == 'docker.io/homeassistant' uses: docker/login-action@v3.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry if: matrix.registry == 'ghcr.io/home-assistant' uses: docker/login-action@v3.1.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build Meta Image shell: bash run: | export DOCKER_CLI_EXPERIMENTAL=enabled function create_manifest() { local tag_l=${1} local tag_r=${2} local registry=${{ matrix.registry }} docker manifest create "${registry}/home-assistant:${tag_l}" \ "${registry}/amd64-homeassistant:${tag_r}" \ "${registry}/i386-homeassistant:${tag_r}" \ "${registry}/armhf-homeassistant:${tag_r}" \ "${registry}/armv7-homeassistant:${tag_r}" \ "${registry}/aarch64-homeassistant:${tag_r}" docker manifest annotate "${registry}/home-assistant:${tag_l}" \ "${registry}/amd64-homeassistant:${tag_r}" \ --os linux --arch amd64 docker manifest annotate "${registry}/home-assistant:${tag_l}" \ "${registry}/i386-homeassistant:${tag_r}" \ --os linux --arch 386 docker manifest annotate "${registry}/home-assistant:${tag_l}" \ "${registry}/armhf-homeassistant:${tag_r}" \ --os linux --arch arm --variant=v6 docker manifest annotate "${registry}/home-assistant:${tag_l}" \ "${registry}/armv7-homeassistant:${tag_r}" \ --os linux --arch arm --variant=v7 docker manifest annotate "${registry}/home-assistant:${tag_l}" \ "${registry}/aarch64-homeassistant:${tag_r}" \ --os linux --arch arm64 --variant=v8 docker manifest push --purge "${registry}/home-assistant:${tag_l}" cosign sign --yes "${registry}/home-assistant:${tag_l}" } function validate_image() { local image=${1} if ! cosign verify --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp https://github.com/home-assistant/core/.* "${image}"; then echo "Invalid signature!" exit 1 fi } function push_dockerhub() { local image=${1} local tag=${2} docker tag "ghcr.io/home-assistant/${image}:${tag}" "docker.io/homeassistant/${image}:${tag}" docker push "docker.io/homeassistant/${image}:${tag}" cosign sign --yes "docker.io/homeassistant/${image}:${tag}" } # Pull images from github container registry and verify signature docker pull "ghcr.io/home-assistant/amd64-homeassistant:${{ needs.init.outputs.version }}" docker pull "ghcr.io/home-assistant/i386-homeassistant:${{ needs.init.outputs.version }}" docker pull "ghcr.io/home-assistant/armhf-homeassistant:${{ needs.init.outputs.version }}" docker pull "ghcr.io/home-assistant/armv7-homeassistant:${{ needs.init.outputs.version }}" docker pull "ghcr.io/home-assistant/aarch64-homeassistant:${{ needs.init.outputs.version }}" validate_image "ghcr.io/home-assistant/amd64-homeassistant:${{ needs.init.outputs.version }}" validate_image "ghcr.io/home-assistant/i386-homeassistant:${{ needs.init.outputs.version }}" validate_image "ghcr.io/home-assistant/armhf-homeassistant:${{ needs.init.outputs.version }}" validate_image "ghcr.io/home-assistant/armv7-homeassistant:${{ needs.init.outputs.version }}" validate_image "ghcr.io/home-assistant/aarch64-homeassistant:${{ needs.init.outputs.version }}" if [[ "${{ matrix.registry }}" == "docker.io/homeassistant" ]]; then # Upload images to dockerhub push_dockerhub "amd64-homeassistant" "${{ needs.init.outputs.version }}" push_dockerhub "i386-homeassistant" "${{ needs.init.outputs.version }}" push_dockerhub "armhf-homeassistant" "${{ needs.init.outputs.version }}" push_dockerhub "armv7-homeassistant" "${{ needs.init.outputs.version }}" push_dockerhub "aarch64-homeassistant" "${{ needs.init.outputs.version }}" fi # Create version tag create_manifest "${{ needs.init.outputs.version }}" "${{ needs.init.outputs.version }}" # Create general tags if [[ "${{ needs.init.outputs.version }}" =~ d ]]; then create_manifest "dev" "${{ needs.init.outputs.version }}" elif [[ "${{ needs.init.outputs.version }}" =~ b ]]; then create_manifest "beta" "${{ needs.init.outputs.version }}" create_manifest "rc" "${{ needs.init.outputs.version }}" else create_manifest "stable" "${{ needs.init.outputs.version }}" create_manifest "latest" "${{ needs.init.outputs.version }}" create_manifest "beta" "${{ needs.init.outputs.version }}" create_manifest "rc" "${{ needs.init.outputs.version }}" # Create series version tag (e.g. 2021.6) v="${{ needs.init.outputs.version }}" create_manifest "${v%.*}" "${{ needs.init.outputs.version }}" fi