diff --git a/.github/workflows/build-app.yaml b/.github/workflows/build-app.yaml new file mode 100644 index 00000000..1f89611b --- /dev/null +++ b/.github/workflows/build-app.yaml @@ -0,0 +1,100 @@ +name: Build app + +on: + workflow_call: + inputs: + app: + required: true + type: string + publish: + required: true + type: boolean + +jobs: + prepare: + name: Prepare + if: github.repository == 'home-assistant/apps-example' + runs-on: ubuntu-latest + outputs: + architectures: ${{ steps.info.outputs.architectures }} + build_matrix: ${{ steps.matrix.outputs.matrix }} + image_name: ${{ steps.normalize.outputs.image_name }} + registry_prefix: ${{ steps.normalize.outputs.registry_prefix }} + version: ${{ steps.normalize.outputs.version }} + steps: + - name: Check out the repository + uses: actions/checkout@v6.0.2 + + - name: Get app information + id: info + uses: home-assistant/actions/helpers/info@master + with: + path: "./${{ inputs.app }}" + + - name: Normalize app information + id: normalize + run: | + image="${{ steps.info.outputs.image }}" + echo "image_name=${image##*/}" >> "$GITHUB_OUTPUT" + echo "registry_prefix=${image%/*}" >> "$GITHUB_OUTPUT" + # Strip surrounding quotes that the info action includes from YAML string values + version="${{ steps.info.outputs.version }}" + echo "version=${version//[\"\']/}" >> "$GITHUB_OUTPUT" + + - name: Prepare build matrix + id: matrix + uses: home-assistant/builder/actions/prepare-multi-arch-matrix@2026.03.2 + with: + architectures: ${{ steps.info.outputs.architectures }} + image-name: ${{ steps.normalize.outputs.image_name }} + registry-prefix: ${{ steps.normalize.outputs.registry_prefix }} + + build: + name: Build ${{ matrix.arch }} image + needs: prepare + runs-on: ${{ matrix.os }} + permissions: + contents: read + id-token: write + packages: write + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }} + steps: + - name: Check out the repository + uses: actions/checkout@v6.0.2 + with: + persist-credentials: false + + - name: Build image + uses: home-assistant/builder/actions/build-image@2026.03.2 + with: + arch: ${{ matrix.arch }} + container-registry-password: ${{ secrets.GITHUB_TOKEN }} + context: "./${{ inputs.app }}" + image: ${{ matrix.image }} + image-tags: | + ${{ needs.prepare.outputs.version }} + latest + push: ${{ inputs.publish }} + version: ${{ needs.prepare.outputs.version }} + + manifest: + name: Publish multi-arch manifest + needs: [prepare, build] + if: inputs.publish + runs-on: ubuntu-latest + permissions: + id-token: write + packages: write + steps: + - name: Publish multi-arch manifest + uses: home-assistant/builder/actions/publish-multi-arch-manifest@2026.03.2 + with: + architectures: ${{ needs.prepare.outputs.architectures }} + container-registry-password: ${{ secrets.GITHUB_TOKEN }} + image-name: ${{ needs.prepare.outputs.image_name }} + image-tags: | + ${{ needs.prepare.outputs.version }} + latest + registry-prefix: ${{ needs.prepare.outputs.registry_prefix }} diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index cef45c92..5ba31ebf 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -1,117 +1,81 @@ name: Builder env: - BUILD_ARGS: "--test" - MONITORED_FILES: "build.yaml config.yaml Dockerfile rootfs" + MONITORED_FILES: "config.json config.yaml config.yml Dockerfile rootfs" on: - push: + pull_request: branches: - main - pull_request: + push: branches: - main +permissions: + contents: read + jobs: init: - runs-on: ubuntu-latest name: Initialize builds + runs-on: ubuntu-latest outputs: - changed_addons: ${{ steps.changed_addons.outputs.addons }} - changed: ${{ steps.changed_addons.outputs.changed }} + changed: ${{ steps.filter.outputs.changed }} + changed_apps: ${{ steps.filter.outputs.changed_apps }} steps: - name: Check out the repository uses: actions/checkout@v6.0.2 - name: Get changed files id: changed_files - uses: jitterbit/get-changed-files@v1 + uses: tj-actions/changed-files@v47 - name: Find app directories - id: addons + id: apps uses: home-assistant/actions/helpers/find-addons@master - - name: Get changed apps - id: changed_addons + - name: Filter changed apps + id: filter + env: + APPS: ${{ steps.apps.outputs.addons }} + CHANGED_FILES: ${{ steps.changed_files.outputs.all_changed_files }} run: | - declare -a changed_addons - for addon in ${{ steps.addons.outputs.addons }}; do - if [[ "${{ steps.changed_files.outputs.all }}" =~ $addon ]]; then - for file in ${{ env.MONITORED_FILES }}; do - if [[ "${{ steps.changed_files.outputs.all }}" =~ $addon/$file ]]; then - if [[ ! "${changed_addons[@]}" =~ $addon ]]; then - changed_addons+=("\"${addon}\","); - fi - fi - done - fi - done + changed_apps=() - changed=$(echo ${changed_addons[@]} | rev | cut -c 2- | rev) + # If the workflow file itself changed, rebuild all apps + if [[ "${CHANGED_FILES}" =~ \.github/workflows/(builder|build-app)\.yaml ]]; then + changed_apps=(${APPS}) + else + for app in ${APPS}; do + for file in ${MONITORED_FILES}; do + if [[ "${CHANGED_FILES}" =~ ${app}/${file} ]]; then + changed_apps+=("${app}") + break + fi + done + done + fi - if [[ -n ${changed} ]]; then - echo "Changed apps: $changed"; - echo "changed=true" >> $GITHUB_OUTPUT; - echo "addons=[$changed]" >> $GITHUB_OUTPUT; + if [[ ${#changed_apps[@]} -gt 0 ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "changed_apps=$(jq -nc '$ARGS.positional' --args "${changed_apps[@]}")" >> "$GITHUB_OUTPUT" else - echo "No app had any monitored files changed (${{ env.MONITORED_FILES }})"; + echo "changed=false" >> "$GITHUB_OUTPUT" fi - build: + + build-app: + name: Build ${{ matrix.app }} needs: init - runs-on: ubuntu-latest if: needs.init.outputs.changed == 'true' - name: Build ${{ matrix.arch }} ${{ matrix.addon }} app - strategy: - matrix: - addon: ${{ fromJson(needs.init.outputs.changed_addons) }} - arch: ["aarch64", "amd64"] permissions: contents: read + id-token: write packages: write - - steps: - - name: Check out repository - uses: actions/checkout@v6.0.2 - - - name: Get information - id: info - uses: home-assistant/actions/helpers/info@master - with: - path: "./${{ matrix.addon }}" - - - name: Check if app should be built - id: check - run: | - if [[ "${{ steps.info.outputs.image }}" == "null" ]]; then - echo "Image property is not defined, skipping build" - echo "build_arch=false" >> $GITHUB_OUTPUT; - elif [[ "${{ steps.info.outputs.architectures }}" =~ ${{ matrix.arch }} ]]; then - echo "build_arch=true" >> $GITHUB_OUTPUT; - echo "image=$(echo ${{ steps.info.outputs.image }} | cut -d'/' -f3)" >> $GITHUB_OUTPUT; - if [[ -z "${{ github.head_ref }}" ]] && [[ "${{ github.event_name }}" == "push" ]]; then - echo "BUILD_ARGS=" >> $GITHUB_ENV; - fi - else - echo "${{ matrix.arch }} is not a valid arch for ${{ matrix.addon }}, skipping build"; - echo "build_arch=false" >> $GITHUB_OUTPUT; - fi - - - name: Login to GitHub Container Registry - if: env.BUILD_ARGS != '--test' - uses: docker/login-action@v4.0.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build ${{ matrix.addon }} app - if: steps.check.outputs.build_arch == 'true' - uses: home-assistant/builder@2026.02.1 - with: - args: | - ${{ env.BUILD_ARGS }} \ - --${{ matrix.arch }} \ - --target /data/${{ matrix.addon }} \ - --image "${{ steps.check.outputs.image }}" \ - --docker-hub "ghcr.io/${{ github.repository_owner }}" \ - --addon + strategy: + fail-fast: false + matrix: + app: ${{ fromJSON(needs.init.outputs.changed_apps) }} + uses: ./.github/workflows/build-app.yaml + with: + app: ${{ matrix.app }} + publish: ${{ github.event_name == 'push' }} + secrets: inherit diff --git a/README.md b/README.md index 7e0dadec..761b8177 100644 --- a/README.md +++ b/README.md @@ -20,18 +20,20 @@ _Example app to use as a blueprint for new apps._ diff --git a/example/Dockerfile b/example/Dockerfile index caea653f..2496e677 100644 --- a/example/Dockerfile +++ b/example/Dockerfile @@ -1,12 +1,28 @@ # https://developers.home-assistant.io/docs/apps/configuration#app-dockerfile -ARG BUILD_FROM -FROM $BUILD_FROM +ARG BUILD_FROM=ghcr.io/home-assistant/base:3.23 +FROM ${BUILD_FROM} # Execute during the build of the image -ARG TEMPIO_VERSION BUILD_ARCH +ARG TEMPIO_VERSION=2021.09.0 +ARG TARGETARCH RUN \ - curl -sSLf -o /usr/bin/tempio \ - "https://github.com/home-assistant/tempio/releases/download/${TEMPIO_VERSION}/tempio_${BUILD_ARCH}" + if [ -z "${TARGETARCH}" ]; then \ + echo "TARGETARCH is not set, please use Docker BuildKit for the build." && exit 1; \ + fi \ + && case "${TARGETARCH}" in \ + amd64) tempio_arch="amd64" ;; \ + arm64) tempio_arch="aarch64" ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \ + esac \ + && curl -sSLf -o /usr/bin/tempio \ + "https://github.com/home-assistant/tempio/releases/download/${TEMPIO_VERSION}/tempio_${tempio_arch}" # Copy root filesystem COPY rootfs / + +LABEL \ + io.hass.type="addon" \ + org.opencontainers.image.title="Home Assistant App: Example app" \ + org.opencontainers.image.description="Example app to use as a blueprint for new apps." \ + org.opencontainers.image.source="https://github.com/home-assistant/apps-example" \ + org.opencontainers.image.licenses="Apache License 2.0" diff --git a/example/build.yaml b/example/build.yaml deleted file mode 100644 index b80f868d..00000000 --- a/example/build.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# https://developers.home-assistant.io/docs/apps/configuration#app-dockerfile -build_from: - aarch64: "ghcr.io/home-assistant/aarch64-base:3.23" - amd64: "ghcr.io/home-assistant/amd64-base:3.23" -labels: - org.opencontainers.image.title: "Home Assistant App: Example app" - org.opencontainers.image.description: "Example app to use as a blueprint for new apps." - org.opencontainers.image.source: "https://github.com/home-assistant/apps-example" - org.opencontainers.image.licenses: "Apache License 2.0" -args: - TEMPIO_VERSION: "2021.09.0" diff --git a/example/config.yaml b/example/config.yaml index a97e4f28..0a03bc90 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -14,4 +14,4 @@ options: message: "Hello world..." schema: message: "str?" -image: "ghcr.io/home-assistant/{arch}-app-example" +image: "ghcr.io/home-assistant/app-example"