name: Deploy to Home Assistant on: push: branches: - main paths: - 'config/**' jobs: deploy: name: Deploy Configuration runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install CIFS utilities run: | sudo apt-get update sudo apt-get install -y cifs-utils - name: Mount HA config via SMB env: SMB_HOST: ${{ secrets.SMB_HOST }} SMB_SHARE: ${{ secrets.SMB_SHARE }} SMB_USER: ${{ secrets.SMB_USER }} SMB_PASSWORD: ${{ secrets.SMB_PASSWORD }} run: | sudo mkdir -p /mnt/ha-config sudo mkdir -p /mnt/ha-config-backup sudo mount -t cifs "//${SMB_HOST}/${SMB_SHARE}" /mnt/ha-config \ -o username=${SMB_USER},password=${SMB_PASSWORD},uid=$(id -u),gid=$(id -g) echo "✓ HA config mounted at /mnt/ha-config" - name: Generate secrets.yaml env: DB_USER: ${{ secrets.DB_USER }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }} POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }} DB_NAME: ${{ secrets.DB_NAME }} run: | python3 << 'EOF' import os from urllib.parse import quote_plus # URL-encode password for PostgreSQL connection string db_user = os.environ['DB_USER'] db_password = quote_plus(os.environ['DB_PASSWORD']) db_host = os.environ['POSTGRES_HOST'] db_port = os.environ['POSTGRES_PORT'] db_name = os.environ['DB_NAME'] # Generate secrets.yaml secrets_content = f"""# Auto-generated secrets.yaml recorder_db: "postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" """ with open('config/secrets.yaml', 'w') as f: f.write(secrets_content) print("✓ Generated secrets.yaml") EOF - name: Replace notification service placeholder env: HA_NOTIFY_SERVICE: ${{ secrets.HA_NOTIFY_SERVICE }} run: | # Replace {{HA_NOTIFY_SERVICE}} placeholder in automations.yaml sed -i "s/{{HA_NOTIFY_SERVICE}}/$HA_NOTIFY_SERVICE/g" config/automations.yaml echo "✓ Replaced notification service placeholder" - name: Pre-deployment validation run: | docker run --rm \ -v $(pwd)/config:/config \ homeassistant/home-assistant:latest \ python -m homeassistant --script check_config -c /config - name: Backup current config run: | echo "Creating backup of current configuration..." rsync -a --delete /mnt/ha-config/ /mnt/ha-config-backup/ echo "✓ Backup created at /mnt/ha-config-backup" - name: Deploy config files run: | echo "Deploying configuration files..." rsync -av --checksum --delete \ --exclude='.git*' \ --exclude='secrets.yaml' \ --exclude='home-assistant.log*' \ --exclude='*.db' \ --exclude='*.db-*' \ config/ /mnt/ha-config/ echo "✓ Configuration files deployed" - name: Deploy secrets.yaml run: | echo "Deploying secrets.yaml..." rsync -av --checksum \ config/secrets.yaml /mnt/ha-config/secrets.yaml echo "✓ Secrets file deployed" - name: Reload Home Assistant configuration env: HA_HOST: ${{ secrets.HA_HOST }} HA_TOKEN: ${{ secrets.HA_TOKEN }} run: | echo "Reloading Home Assistant configuration..." curl -X POST \ -H "Authorization: Bearer ${HA_TOKEN}" \ -H "Content-Type: application/json" \ http://${HA_HOST}:8123/api/services/homeassistant/reload_all echo "✓ Reload triggered" - name: Health check with retry env: HA_HOST: ${{ secrets.HA_HOST }} HA_TOKEN: ${{ secrets.HA_TOKEN }} run: | echo "Waiting for Home Assistant to become ready..." MAX_ATTEMPTS=12 # 60 seconds total (12 * 5 seconds) ATTEMPT=1 while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do echo "Health check attempt $ATTEMPT/$MAX_ATTEMPTS..." if curl -f -s \ -H "Authorization: Bearer ${HA_TOKEN}" \ http://${HA_HOST}:8123/api/ > /dev/null 2>&1; then echo "✓ Home Assistant is healthy!" exit 0 fi if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then echo " Not ready yet, waiting 5 seconds..." sleep 5 fi ATTEMPT=$((ATTEMPT + 1)) done echo "✗ Health check failed after ${MAX_ATTEMPTS} attempts" exit 1 - name: Cleanup backup on success if: success() run: | echo "Cleaning up backup..." rm -rf /mnt/ha-config-backup echo "✓ Backup removed" - name: Unmount SMB share if: always() run: | sudo umount /mnt/ha-config 2>/dev/null || true echo "✓ SMB share unmounted" - name: Send success notification if: success() env: HA_HOST: ${{ secrets.HA_HOST }} run: | echo "Sending success notification..." curl -X POST \ http://${HA_HOST}:8123/api/webhook/gitea_deploy echo "✓ Success notification sent" - name: Rollback on failure if: failure() env: HA_HOST: ${{ secrets.HA_HOST }} HA_TOKEN: ${{ secrets.HA_TOKEN }} run: | echo "⚠ Deployment failed, rolling back..." # Restore backup rsync -a --delete /mnt/ha-config-backup/ /mnt/ha-config/ echo "✓ Configuration restored from backup" # Reload with old config curl -X POST \ -H "Authorization: Bearer ${HA_TOKEN}" \ -H "Content-Type: application/json" \ http://${HA_HOST}:8123/api/services/homeassistant/reload_all echo "✓ Home Assistant reloaded with previous configuration" # Send rollback notification curl -X POST \ http://${HA_HOST}:8123/api/webhook/gitea_rollback echo "✓ Rollback notification sent" # Cleanup backup rm -rf /mnt/ha-config-backup echo "✓ Backup cleaned up"