Skip to content
Latchkey

Build Home Assistant Turbo workflow (ha-china/HAOS-CN)

The Build Home Assistant Turbo workflow from ha-china/HAOS-CN, explained and optimized by Latchkey.

B

CI health: B - good

Point runs-on at Latchkey and get job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: ha-china/HAOS-CN.github/workflows/Builder.ymlLicense Apache-2.0View source

What it does

This is the Build Home Assistant Turbo workflow from the ha-china/HAOS-CN repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Build Home Assistant Turbo
on:
  workflow_dispatch:
    inputs:
      targets:
        description: 'Targets to build (comma separated)'
        required: false
        default: 'rpi3_64,rpi4_64,rpi5_64,ova,generic_x86_64,generic_aarch64,green,yellow,panther_x2,orangepi_cm4'

#  schedule:
#    - cron: '0 18 26 * *'
#  watch:
#    types: [started]

#env:
#  RUN_VALIDATION: true

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            TARGETS="${{ github.event.inputs.targets }}"
          else
            TARGETS="rpi3_64,rpi4_64,rpi5_64,ova,generic_x86_64,generic_aarch64,green,yellow,panther_x2,orangepi_cm4"
          fi
          JSON=$(echo "$TARGETS" | jq -R -c 'split(",")')
          echo "matrix=$JSON" >> $GITHUB_OUTPUT
    permissions:
      contents: read
      pull-requests: read
      packages: write

  build:
    needs: prepare
    runs-on: ubuntu-latest
    permissions:
      contents: read
    name: Build ${{ matrix.target }}
    strategy:
      fail-fast: false
      matrix:
        target: ${{ fromJson(needs.prepare.outputs.matrix) }}
    steps:
      - name: Echo target
        run: echo "Building for ${{ matrix.target }}"
        #target: [generic_aarch64,generic_x86_64]

    #steps:
      - name: Checkout
        uses: actions/checkout@v7

      - name: Checkout private repo
        uses: actions/checkout@v7
        with:
          repository: desmond-dong/haos-cn-scripts
          token: ${{ secrets.GH_PAT }}
          path: private

      - name: Run only on main
        if: github.ref_name == 'main'
        id: get_haos_version
        run: |
          ver=$(curl -s https://api.github.com/repos/home-assistant/operating-system/releases/latest | jq -r '.tag_name')
          echo "CURRENT_VERSION=${ver}" >> $GITHUB_ENV
  
      - name: Run only on dev
        if: github.ref_name == 'dev'
        id: get_haos_version_dev
        run: |
          ver=$(curl -s https://version.home-assistant.io/dev.json | jq -r '.hassos.ova')
          echo "CURRENT_VERSION=${ver}"
          echo "CURRENT_VERSION=${ver}" >> $GITHUB_ENV
        
      - name: Initialization environment and Free up disk space
        run: |
          sudo timedatectl set-timezone "Asia/Shanghai"
          chmod +x private/*.sh
          sudo private/release_space.sh
          sudo rm -rf "$AGENT_TOOLSDIRECTORY"


      - name: Clone source code and Change Settings.
        run: |
          if [ "${GITHUB_REF_NAME}" = "main" ]; then
            git clone https://github.com/home-assistant/operating-system.git -b main
          else
            git clone https://github.com/home-assistant/operating-system.git
          fi
          cp -r buildroot-external operating-system
          cd operating-system
          git submodule update --init
          
      - name: Run private build script
        run: |
          chmod +x private/*.sh
          private/haos.sh
        env:
          MATRIX_TARGET: ${{matrix.target}}
          RAUC_CERTIFICATE: ${{ secrets.RAUC_CERTIFICATE }}
          RAUC_PRIVATE_KEY: ${{ secrets.RAUC_PRIVATE_KEY }}

 
      - name: Log in to Docker Hub
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build 
        shell: 'script -q -e -c "bash {0}"'
        id: build
        run: |
          sleep $((RANDOM % 600))    
          cd operating-system
          scripts/enter.sh make ${{matrix.target}}

          

      - name: Set tag and body for release
        if: github.ref_name == 'main'
        run: |
          echo "RELEASE_TAG=${CURRENT_VERSION}" >> $GITHUB_ENV
          echo "RELEASE_BODY=为中国地区专门加速的Home Assistant OS,不修改任何系统内的其它任何信息,所有的修改都以加速为目的。公众号:老王杂谈说,欢迎关注" >> $GITHUB_ENV

      - name: Set tag and body for dev release
        if: github.ref_name != 'main'
        run: |
          echo "RELEASE_TAG=dev-${CURRENT_VERSION}" >> $GITHUB_ENV
          echo "RELEASE_BODY=此为开发版,请不要当正式版使用" >> $GITHUB_ENV

      - name: Upload images to release
        id: upload-release
        uses: svenstaro/upload-release-action@v2
        with:
          repo_token: ${{ secrets.UPLOAD_TOKEN }}
          file: operating-system/output/images/haos_*
          tag: ${{ env.RELEASE_TAG }}
          overwrite: true
          file_glob: true
          body: ${{ env.RELEASE_BODY }}
          prerelease: ${{ github.ref != 'refs/heads/main' }}

  trigger-full-images:
    needs: build
    if: github.ref_name == 'main'
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Dispatch full image workflow
        env:
          GH_TOKEN: ${{ secrets.GH_PAT }}
          CHANNEL: stable
        run: |
          curl -L -X POST \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${GH_TOKEN}" \
            https://api.github.com/repos/ha-china/operating-system-full-images/actions/workflows/build-channel.yml/dispatches \
            -d "{\"ref\":\"main\",\"inputs\":{\"channel\":\"${CHANNEL}\"}}"

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: Build Home Assistant Turbo
on:
  workflow_dispatch:
    inputs:
      targets:
        description: 'Targets to build (comma separated)'
        required: false
        default: 'rpi3_64,rpi4_64,rpi5_64,ova,generic_x86_64,generic_aarch64,green,yellow,panther_x2,orangepi_cm4'
 
#  schedule:
#    - cron: '0 18 26 * *'
#  watch:
#    types: [started]
 
#env:
#  RUN_VALIDATION: true
 
jobs:
  prepare:
    timeout-minutes: 30
    runs-on: latchkey-small
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            TARGETS="${{ github.event.inputs.targets }}"
          else
            TARGETS="rpi3_64,rpi4_64,rpi5_64,ova,generic_x86_64,generic_aarch64,green,yellow,panther_x2,orangepi_cm4"
          fi
          JSON=$(echo "$TARGETS" | jq -R -c 'split(",")')
          echo "matrix=$JSON" >> $GITHUB_OUTPUT
    permissions:
      contents: read
      pull-requests: read
      packages: write
 
  build:
    timeout-minutes: 30
    needs: prepare
    runs-on: latchkey-small
    permissions:
      contents: read
    name: Build ${{ matrix.target }}
    strategy:
      fail-fast: false
      matrix:
        target: ${{ fromJson(needs.prepare.outputs.matrix) }}
    steps:
      - name: Echo target
        run: echo "Building for ${{ matrix.target }}"
        #target: [generic_aarch64,generic_x86_64]
 
    #steps:
      - name: Checkout
        uses: actions/checkout@v7
 
      - name: Checkout private repo
        uses: actions/checkout@v7
        with:
          repository: desmond-dong/haos-cn-scripts
          token: ${{ secrets.GH_PAT }}
          path: private
 
      - name: Run only on main
        if: github.ref_name == 'main'
        id: get_haos_version
        run: |
          ver=$(curl -s https://api.github.com/repos/home-assistant/operating-system/releases/latest | jq -r '.tag_name')
          echo "CURRENT_VERSION=${ver}" >> $GITHUB_ENV
  
      - name: Run only on dev
        if: github.ref_name == 'dev'
        id: get_haos_version_dev
        run: |
          ver=$(curl -s https://version.home-assistant.io/dev.json | jq -r '.hassos.ova')
          echo "CURRENT_VERSION=${ver}"
          echo "CURRENT_VERSION=${ver}" >> $GITHUB_ENV
        
      - name: Initialization environment and Free up disk space
        run: |
          sudo timedatectl set-timezone "Asia/Shanghai"
          chmod +x private/*.sh
          sudo private/release_space.sh
          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
 
 
      - name: Clone source code and Change Settings.
        run: |
          if [ "${GITHUB_REF_NAME}" = "main" ]; then
            git clone https://github.com/home-assistant/operating-system.git -b main
          else
            git clone https://github.com/home-assistant/operating-system.git
          fi
          cp -r buildroot-external operating-system
          cd operating-system
          git submodule update --init
          
      - name: Run private build script
        run: |
          chmod +x private/*.sh
          private/haos.sh
        env:
          MATRIX_TARGET: ${{matrix.target}}
          RAUC_CERTIFICATE: ${{ secrets.RAUC_CERTIFICATE }}
          RAUC_PRIVATE_KEY: ${{ secrets.RAUC_PRIVATE_KEY }}
 
 
      - name: Log in to Docker Hub
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
 
      - name: Build 
        shell: 'script -q -e -c "bash {0}"'
        id: build
        run: |
          sleep $((RANDOM % 600))    
          cd operating-system
          scripts/enter.sh make ${{matrix.target}}
 
          
 
      - name: Set tag and body for release
        if: github.ref_name == 'main'
        run: |
          echo "RELEASE_TAG=${CURRENT_VERSION}" >> $GITHUB_ENV
          echo "RELEASE_BODY=为中国地区专门加速的Home Assistant OS,不修改任何系统内的其它任何信息,所有的修改都以加速为目的。公众号:老王杂谈说,欢迎关注" >> $GITHUB_ENV
 
      - name: Set tag and body for dev release
        if: github.ref_name != 'main'
        run: |
          echo "RELEASE_TAG=dev-${CURRENT_VERSION}" >> $GITHUB_ENV
          echo "RELEASE_BODY=此为开发版,请不要当正式版使用" >> $GITHUB_ENV
 
      - name: Upload images to release
        id: upload-release
        uses: svenstaro/upload-release-action@v2
        with:
          repo_token: ${{ secrets.UPLOAD_TOKEN }}
          file: operating-system/output/images/haos_*
          tag: ${{ env.RELEASE_TAG }}
          overwrite: true
          file_glob: true
          body: ${{ env.RELEASE_BODY }}
          prerelease: ${{ github.ref != 'refs/heads/main' }}
 
  trigger-full-images:
    timeout-minutes: 30
    needs: build
    if: github.ref_name == 'main'
    runs-on: latchkey-small
    permissions:
      contents: read
    steps:
      - name: Dispatch full image workflow
        env:
          GH_TOKEN: ${{ secrets.GH_PAT }}
          CHANNEL: stable
        run: |
          curl -L -X POST \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${GH_TOKEN}" \
            https://api.github.com/repos/ha-china/operating-system-full-images/actions/workflows/build-channel.yml/dispatches \
            -d "{\"ref\":\"main\",\"inputs\":{\"channel\":\"${CHANNEL}\"}}"
 

What changed

2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 3 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow