Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/workflows/build-app.yaml
Original file line number Diff line number Diff line change
@@ -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 }}
130 changes: 47 additions & 83 deletions .github/workflows/builder.yaml
Original file line number Diff line number Diff line change
@@ -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
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ _Example app to use as a blueprint for new apps._
<!--

Notes to developers after forking or using the github template feature:
- While developing comment out the 'image' key from 'example/config.yaml' to make the supervisor build the app
- While developing comment out the 'image' key from 'example/config.yaml' to make the supervisor build the app locally.
- Remember to put this back when pushing up your changes.
- When you merge to the 'main' branch of your repository a new build will be triggered.
- Make sure you adjust the 'version' key in 'example/config.yaml' when you do that.
- Make sure you update 'example/CHANGELOG.md' when you do that.
- The first time this runs you might need to adjust the image configuration on github container registry to make it public
- You may also need to adjust the GitHub Actions configuration (Settings > Actions > General > Workflow > Read & Write)
- Adjust the 'image' key in 'example/config.yaml' so it points to your username instead of 'home-assistant'.
- This is where the build images will be published to.
- The first time this runs you might need to adjust the image configuration on github container registry to make it public.
- You may also need to adjust the GitHub Actions configuration (Settings > Actions > General > Workflow > Read & Write).
- Update the repository check in '.github/workflows/build-app.yaml' to match your repository name
(the 'github.repository' condition in the 'prepare' job).
- Adjust the 'image' key in 'example/config.yaml' so it points to your username instead of 'home-assistant'
(e.g., 'ghcr.io/my-username/my-app').
- Rename the example directory.
- The 'slug' key in 'example/config.yaml' should match the directory name.
- Adjust all keys/url's that points to 'home-assistant' to now point to your user/fork.
- Adjust all keys/urls that point to 'home-assistant' to now point to your user/fork.
- Share your repository on the forums https://community.home-assistant.io/c/projects/9
- Do awesome stuff!
-->
Expand Down
26 changes: 21 additions & 5 deletions example/Dockerfile
Original file line number Diff line number Diff line change
@@ -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 link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, not really new, but tempio is actually in the base image, so this is somewhat a bad example 🤔 😅

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, right 😅 It does no harm as it works and just displays some patterns but we can come up with something more practical.


# Copy root filesystem
COPY rootfs /

LABEL \
io.hass.type="addon" \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we maybe introduce app?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC Supervisor doesn't really consume it but defines it amongst its consts. So we should at least add it there (and maybe make some use of it?). Let's do it in a follow-up there and here before we update the docs.

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"
11 changes: 0 additions & 11 deletions example/build.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Loading