Skip to content

Deploy hass-addon-sunsynk-multi to ghcr.io #32

Deploy hass-addon-sunsynk-multi to ghcr.io

Deploy hass-addon-sunsynk-multi to ghcr.io #32

name: Deploy hass-addon-sunsynk-multi to ghcr.io
on:
release:
types:
- published
workflow_run:
workflows: ["CI"]
branches: [main]
types:
- completed
workflow_dispatch: {}
env:
REGISTRY_IMAGE: ghcr.io/${{ github.repository_owner }}/hass-addon-sunsynk-multi
jobs:
ci-failure:
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'failure'
steps:
- run: echo 'CI failed'
- run: exit 1
information:
name: Gather add-on information
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
github.event_name == 'release' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
outputs:
architectures: ${{ steps.information.outputs.architectures }}
build: ${{ steps.information.outputs.build }}
description: ${{ steps.information.outputs.description }}
environment: ${{ steps.release.outputs.environment }}
name: ${{ steps.information.outputs.name }}
slug: ${{ steps.information.outputs.slug }}
target: ${{ steps.information.outputs.target }}
version: ${{ steps.release.outputs.version }}
steps:
- name: ⤵️ Check out code from GitHub
uses: actions/checkout@v4
- name: 🚀 Run add-on information
id: information
uses: frenck/[email protected]
with:
path: ./hass-addon-sunsynk-multi
- name: ℹ️ Gather version and environment
id: release
run: |
sha="${{ github.sha }}"
# Default values
environment="edge"
version="${sha:0:7}"
# Check if it's a release event
if [[ "${{ github.event_name }}" == "release" ]]; then
version="${{ github.event.release.tag_name }}" # Lowercase the tag name
version="${version#v}" # Remove 'v' prefix if present
environment="stable"
# Set environment to beta if it's a prerelease
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
environment="beta"
fi
fi
# Output the environment and version
echo "environment=${environment}" >> "$GITHUB_OUTPUT"
echo "version=${version}" >> "$GITHUB_OUTPUT"
# Log the outputs
cat "$GITHUB_OUTPUT" >> $GITHUB_STEP_SUMMARY
build:
name: 👷 Build & Deploy ${{ matrix.platform }} ${{ needs.information.outputs.environment }}/${{ needs.information.outputs.version }}
runs-on: ubuntu-latest
needs: information
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm/v6
- linux/arm/v7
- linux/arm64
steps:
- name: ⤵️ Check out code from GitHub
uses: actions/checkout@v4
- name: Prepare Files for Build
run: |
mkdir -p hass-addon-sunsynk-multi/sunsynk
cp -r src hass-addon-sunsynk-multi/sunsynk/
cp setup.* README.md hass-addon-sunsynk-multi/sunsynk/
- name: Set up the correct base image based on platform
run: |
case "${{ matrix.platform }}" in
linux/amd64) echo "BUILD_FROM=ghcr.io/home-assistant/amd64-base-python:3.12-alpine3.21" >> $GITHUB_ENV ;;
linux/arm64) echo "BUILD_FROM=ghcr.io/home-assistant/aarch64-base-python:3.12-alpine3.21" >> $GITHUB_ENV ;;
linux/arm/v6) echo "BUILD_FROM=ghcr.io/home-assistant/armhf-base-python:3.12-alpine3.21" >> $GITHUB_ENV ;;
linux/arm/v7) echo "BUILD_FROM=ghcr.io/home-assistant/armv7-base-python:3.12-alpine3.21" >> $GITHUB_ENV ;;
linux/i386) echo "BUILD_FROM=ghcr.io/home-assistant/i386-base-python:3.12-alpine3.21" >> $GITHUB_ENV ;;
esac
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache # Cache path
key: ${{ runner.os }}-buildx-${{ github.sha }} # Unique key for cache
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare sanitized platform name
id: sanitize-platform
run: |
SANITIZED_PLATFORM=$(echo ${{ matrix.platform }} | sed 's/^linux\///; s/\///g')
echo "SANITIZED_PLATFORM=${SANITIZED_PLATFORM}" >> $GITHUB_ENV
echo "Sanitized platform name: ${SANITIZED_PLATFORM}"
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: ./hass-addon-sunsynk-multi # Specify the directory containing your Dockerfile
file: ./hass-addon-sunsynk-multi/Dockerfile # Specify the Dockerfile path within the context
platforms: ${{ matrix.platform }}
cache-from: type=local,src=/tmp/.buildx-cache # Use cache for build
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max # Save cache
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
BUILD_FROM=${{ env.BUILD_FROM }}
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.SANITIZED_PLATFORM }} # Use the sanitized platform name
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
- name: Save platform to file
id: save-platform
run: |
mkdir -p outputs
echo ${{ env.SANITIZED_PLATFORM }} >> outputs/${{ env.SANITIZED_PLATFORM }}.txt
- name: Artifact platform file for later
uses: actions/upload-artifact@v4
with:
name: platform-outputs-${{ env.SANITIZED_PLATFORM }}
path: outputs/*.txt
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- information
- build
if: |
always() &&
needs.information.result == 'success' &&
needs.build.result != 'failure'
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Download platform outputs
uses: actions/download-artifact@v4
with:
pattern: platform-outputs-*
path: outputs
merge-multiple: true
- name: Load outputs to variable
id: platforms_raw
run: |
ls outputs
# Replace newlines with commas, then remove the trailing comma
echo "value=$(cat outputs/*.txt | tr '\n' ',' | sed 's/,$//')" >> "$GITHUB_OUTPUT"
- name: Generate tags for multi-platform image
run: |
# Define base tags
TAGS=(
"-t ${{ env.REGISTRY_IMAGE }}:${{ needs.information.outputs.version }}"
"-t ${{ env.REGISTRY_IMAGE }}:${{ needs.information.outputs.environment }}"
)
# Get the list of platforms from the build job output
platforms="${{ steps.platforms_raw.outputs.value }}"
# Split the platforms and add platform-specific tags
IFS=',' read -ra PLATFORM_ARRAY <<< "$platforms"
for PLATFORM in "${PLATFORM_ARRAY[@]}"; do
TAGS+=("-t ${{ env.REGISTRY_IMAGE }}/${PLATFORM}:${{ needs.information.outputs.version }}")
TAGS+=("-t ${{ env.REGISTRY_IMAGE }}/${PLATFORM}:${{ needs.information.outputs.environment }}")
done
# Join tags into a string
TAGS_STRING=$(printf " %s" "${TAGS[@]}")
# Export the tags string to the environment for later use
echo "TAGS_STRING=${TAGS_STRING}" >> $GITHUB_ENV
# Log the generated tags for debugging
echo "Generated tags: ${TAGS_STRING}"
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
# Function to retry with exponential backoff
function retry_with_backoff() {
local max_attempts=5
local timeout=10 # Start with 10 seconds
local attempt=1
while true; do
echo "Attempt $attempt of $max_attempts"
if "$@"; then
return 0
fi
if [[ $attempt -ge $max_attempts ]]; then
echo "Failed after $attempt attempts"
return 1
fi
echo "Attempt $attempt failed! Retrying in $timeout seconds..."
sleep $timeout
timeout=$((timeout * 2))
attempt=$((attempt + 1))
done
}
# Get all digests
digests=($(ls))
echo "Found digests: ${digests[*]}"
# Create base manifest first
echo "Creating base manifest..."
retry_with_backoff docker buildx imagetools create \
-t ${{ env.REGISTRY_IMAGE }}:${{ needs.information.outputs.version }} \
-t ${{ env.REGISTRY_IMAGE }}:${{ needs.information.outputs.environment }} \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' "${digests[@]}")
echo "Waiting 30 seconds after base manifest creation before processing platform-specific tags..."
sleep 30 # Wait 30 seconds before processing platform-specific tags
# Process each platform separately
platforms="${{ steps.platforms_raw.outputs.value }}"
IFS=',' read -ra PLATFORM_ARRAY <<< "$platforms"
for PLATFORM in "${PLATFORM_ARRAY[@]}"; do
echo "Processing platform: $PLATFORM"
retry_with_backoff docker buildx imagetools create \
-t ${{ env.REGISTRY_IMAGE }}/${PLATFORM}:${{ needs.information.outputs.version }} \
-t ${{ env.REGISTRY_IMAGE }}/${PLATFORM}:${{ needs.information.outputs.environment }} \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' "${digests[@]}")
echo "Completed manifest for $PLATFORM"
echo "Waiting 30 seconds before processing next platform..."
sleep 30 # Wait 30 seconds between platforms
done
- name: Inspect image
run: |
# Function to retry with exponential backoff
function retry_with_backoff() {
local max_attempts=5
local timeout=10
local attempt=1
while true; do
echo "Attempt $attempt of $max_attempts"
if "$@"; then
return 0
fi
if [[ $attempt -ge $max_attempts ]]; then
echo "Failed after $attempt attempts"
return 1
fi
echo "Attempt $attempt failed! Retrying in $timeout seconds..."
sleep $timeout
timeout=$((timeout * 2))
attempt=$((attempt + 1))
done
}
echo "Waiting 30 seconds for manifests to settle before starting inspections..."
sleep 30
# Inspect base image first
echo "Inspecting base image..."
retry_with_backoff docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ needs.information.outputs.version }}
# Inspect each platform
platforms="${{ steps.platforms_raw.outputs.value }}"
IFS=',' read -ra PLATFORM_ARRAY <<< "$platforms"
for PLATFORM in "${PLATFORM_ARRAY[@]}"; do
echo "Inspecting platform: $PLATFORM"
retry_with_backoff docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}/${PLATFORM}:${{ needs.information.outputs.version }}
echo "Waiting 10 seconds before inspecting next platform..."
sleep 10
done
publish_addon:
name: 🆕 Update addon version to ${{ needs.information.outputs.version }}
needs: [information, build, merge]
if: |
always() &&
needs.information.result == 'success' &&
needs.build.result != 'failure' &&
needs.merge.result == 'success'
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main
- name: Configure Git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Update versions and commit changes
run: |
# Pull latest changes with rebase strategy
git pull --rebase origin main
# Make the version updates
sed -i 's/version:.*/version: "${{ needs.information.outputs.version }}"/g' hass-addon-sunsynk-edge/config.yaml
if [[ "${{ needs.information.outputs.environment }}" == "stable" ]]; then
sed -i 's/version:.*/version: "${{ needs.information.outputs.version }}"/g' hass-addon-sunsynk-multi/config.yaml
fi
# Stage and commit changes
git add hass-addon-sunsynk-edge/config.yaml hass-addon-sunsynk-multi/config.yaml
git commit -m "Update addon version to ${{ needs.information.outputs.version }} [no ci]" || echo "No changes to commit"
- name: Push changes
run: |
# Try to push changes
for i in {1..3}; do
if git push origin main; then
echo "Successfully pushed changes"
exit 0
else
echo "Push failed, attempting to rebase and retry..."
git pull --rebase origin main
fi
echo "Waiting 5 seconds before next push attempt..."
sleep 5
done
echo "Failed to push after 3 attempts"
exit 1