Release workflow (ruaan-deysel/ha-unraid)
The Release workflow from ruaan-deysel/ha-unraid, explained and optimized by Latchkey.
C
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Release workflow from the ruaan-deysel/ha-unraid 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)
---
# yamllint disable rule:line-length
name: Release
"on":
push:
branches:
- main
paths:
- custom_components/unraid/manifest.json
tags:
- "v*"
permissions: {}
jobs:
auto_tag:
name: Tag release on manifest change
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/heads/')
permissions:
contents: write
outputs:
version: ${{ steps.manifest.outputs.version }}
tag_created: ${{ steps.create_tag.outputs.created }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Read version from manifest
id: manifest
run: |
VERSION=$(python3 -c "import json; print(json.load(open('custom_components/unraid/manifest.json'))['version'])")
if [ -z "$VERSION" ]; then
echo "Version not found in manifest.json"
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Manifest version: $VERSION"
# Only release once the CHANGELOG is ready: the [Unreleased] content
# must have been moved under a "## [VERSION]" heading matching the new
# manifest version. A manifest bump without that section skips tagging
# instead of publishing a release with generic fallback notes. Because
# this workflow only triggers on manifest.json changes, the changelog
# section must land in the same push as (or before) the manifest bump.
- name: Check CHANGELOG has notes for this version
id: changelog
run: |
VERSION="${{ steps.manifest.outputs.version }}"
if [ ! -f "CHANGELOG.md" ]; then
echo "ready=false" >> $GITHUB_OUTPUT
echo "CHANGELOG.md not found"
exit 0
fi
# Escape regex metacharacters (the dots) in the version string
VER_RE=$(printf '%s' "$VERSION" | sed 's/\./\\./g')
NOTES=$(awk -v ver="$VER_RE" '
$0 ~ "^## \\[" ver "\\]" {in_section=1; next}
in_section && $0 ~ /^## \[/ {exit}
in_section {print}
' CHANGELOG.md)
if [ -n "$(echo "$NOTES" | tr -d '[:space:]')" ]; then
echo "ready=true" >> $GITHUB_OUTPUT
echo "Found release notes for $VERSION in CHANGELOG.md"
else
echo "ready=false" >> $GITHUB_OUTPUT
echo "No '## [$VERSION]' section with content found in CHANGELOG.md"
fi
- name: Check if tag already exists
id: check_tag
run: |
TAG="v${{ steps.manifest.outputs.version }}"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Tag $TAG already exists (local)"
exit 0
fi
if git ls-remote --exit-code --tags origin "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Tag $TAG already exists (remote)"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Tag $TAG does not exist"
fi
- name: Create and push tag
id: create_tag
if: >-
steps.check_tag.outputs.exists == 'false' &&
steps.changelog.outputs.ready == 'true'
run: |
TAG="v${{ steps.manifest.outputs.version }}"
echo "Creating tag $TAG"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
echo "created=true" >> $GITHUB_OUTPUT
- name: Summary
run: |
TAG="v${{ steps.manifest.outputs.version }}"
VERSION="${{ steps.manifest.outputs.version }}"
if [ "${{ steps.check_tag.outputs.exists }}" = "true" ]; then
echo "Tag $TAG already existed. No new tag created." >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.changelog.outputs.ready }}" != "true" ]; then
echo "⚠️ Tag $TAG **not** created: CHANGELOG.md has no \`## [$VERSION]\` section with content." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Move the \`[Unreleased]\` notes under a \`## [$VERSION]\` heading and push the change together with a manifest.json update to trigger the release." >> $GITHUB_STEP_SUMMARY
else
echo "Created and pushed tag $TAG from manifest.json version." >> $GITHUB_STEP_SUMMARY
echo "The release job in this workflow run will now build and publish the release." >> $GITHUB_STEP_SUMMARY
fi
# Runs in two ways:
# 1. Same-run after auto_tag creates a tag from a manifest version bump.
# Tags pushed with the default GITHUB_TOKEN do NOT trigger new workflow
# runs (GitHub's recursion guard), so the release must be built here -
# a separate tag-triggered run would never start.
# 2. Directly from a manually pushed v* tag (auto_tag is skipped; the
# `!cancelled()` keeps this job eligible despite the skipped dependency).
release:
name: Build and Release
runs-on: ubuntu-latest
needs: auto_tag
if: >-
!cancelled() && (
startsWith(github.ref, 'refs/tags/') ||
(needs.auto_tag.result == 'success' && needs.auto_tag.outputs.tag_created == 'true')
)
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- name: Determine version
id: version
run: |
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
# Triggered by a manually pushed tag (e.g., v2025.12.0 -> 2025.12.0)
VERSION=${GITHUB_REF#refs/tags/v}
else
# Same-run release after auto_tag created the tag
VERSION="${{ needs.auto_tag.outputs.version }}"
fi
if [ -z "$VERSION" ]; then
echo "ERROR: could not determine release version"
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
# Check if this is a pre-release
if [[ "$VERSION" =~ (alpha|beta|rc) ]]; then
echo "prerelease=true" >> $GITHUB_OUTPUT
echo "This is a pre-release"
else
echo "prerelease=false" >> $GITHUB_OUTPUT
echo "This is a stable release"
fi
- name: Verify manifest.json version matches tag
run: |
MANIFEST_VERSION=$(python3 -c "import json; print(json.load(open('custom_components/unraid/manifest.json'))['version'])")
TAG_VERSION="${{ steps.version.outputs.version }}"
if [ "$MANIFEST_VERSION" != "$TAG_VERSION" ]; then
echo "ERROR: manifest.json version ($MANIFEST_VERSION) does not match tag ($TAG_VERSION)"
exit 1
fi
echo "✅ manifest.json version matches tag: $TAG_VERSION"
- name: Build release package
run: |
echo "Building release package..."
# Create build directory
mkdir -p build
# Create the integration package (zip file for HACS compatibility)
PACKAGE_FILE="build/unraid-${{ steps.version.outputs.version }}.zip"
# Package the custom_components directory
cd custom_components
zip -r "../$PACKAGE_FILE" unraid/ \
-x "*.pyc" \
-x "*__pycache__*" \
-x "*.git*" \
-x "*.DS_Store"
cd ..
# Verify the package was created
if [ ! -f "$PACKAGE_FILE" ]; then
echo "ERROR: Package file not found: $PACKAGE_FILE"
ls -la build/
exit 1
fi
echo "✅ Package created: $PACKAGE_FILE"
ls -lh "$PACKAGE_FILE"
# Show package contents
echo ""
echo "Package contents:"
unzip -l "$PACKAGE_FILE" | head -20
- name: Calculate SHA256 checksum
id: checksum
run: |
PACKAGE_FILE="build/unraid-${{ steps.version.outputs.version }}.zip"
# Calculate SHA256 checksum
SHA256=$(sha256sum "$PACKAGE_FILE" | awk '{print $1}')
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
echo "✅ SHA256 checksum: $SHA256"
# Save checksum to file for release notes
echo "$SHA256" > build/SHA256SUM
- name: Extract release notes from CHANGELOG
id: changelog
run: |
VERSION="${{ steps.version.outputs.version }}"
# Check if CHANGELOG.md exists
if [ ! -f "CHANGELOG.md" ]; then
echo "⚠️ CHANGELOG.md not found, using default release notes"
NOTES="Release v$VERSION of the Unraid Home Assistant integration."
else
# Extract release notes for this version from CHANGELOG.md
# Look for content after "## [VERSION]" until the next version header.
if grep -qF "## [$VERSION]" CHANGELOG.md; then
# Escape regex metacharacters (the dots) in the version string
VER_RE=$(printf '%s' "$VERSION" | sed 's/\./\\./g')
NOTES=$(awk -v ver="$VER_RE" '
$0 ~ "^## \\[" ver "\\]" {in_section=1; next}
in_section && $0 ~ /^## \[/ {exit}
in_section {print}
' CHANGELOG.md)
if [ -z "$NOTES" ]; then
echo "⚠️ No release notes found for version $VERSION in CHANGELOG.md"
NOTES="See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details."
else
echo "✅ Extracted release notes for version $VERSION"
fi
else
echo "⚠️ Version $VERSION not found in CHANGELOG.md"
NOTES="See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details."
fi
fi
# Save to file (GitHub Actions doesn't handle multiline outputs well)
echo "$NOTES" > build/RELEASE_NOTES.md
# Add link to full changelog if not already present in NOTES
if ! grep -q "See \[CHANGELOG.md\](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details\." build/RELEASE_NOTES.md; then
echo "" >> build/RELEASE_NOTES.md
echo "See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." >> build/RELEASE_NOTES.md
fi
# Add installation instructions
echo "" >> build/RELEASE_NOTES.md
echo "---" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
echo "## 📦 Installation" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
echo "### Via HACS (Recommended)" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
echo "1. Open HACS in Home Assistant" >> build/RELEASE_NOTES.md
echo "2. Go to **Integrations**" >> build/RELEASE_NOTES.md
echo "3. Click the **⋮** menu → **Custom repositories**" >> build/RELEASE_NOTES.md
echo "4. Add this repository: \`https://github.com/${{ github.repository }}\`" >> build/RELEASE_NOTES.md
echo "5. Category: **Integration**" >> build/RELEASE_NOTES.md
echo "6. Click **Add**" >> build/RELEASE_NOTES.md
echo "7. Search for **Unraid**" >> build/RELEASE_NOTES.md
echo "8. Click **Download**" >> build/RELEASE_NOTES.md
echo "9. Restart Home Assistant" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
echo "### Manual Installation" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
echo "1. Download \`unraid-$VERSION.zip\`" >> build/RELEASE_NOTES.md
echo "2. Extract to \`config/custom_components/\`" >> build/RELEASE_NOTES.md
echo "3. Restart Home Assistant" >> build/RELEASE_NOTES.md
echo "4. Add the integration via **Settings** → **Devices & Services** → **Add Integration**" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
# Add SHA256 checksum to release notes
echo "---" >> build/RELEASE_NOTES.md
echo "" >> build/RELEASE_NOTES.md
echo "**SHA256 Checksum:**" >> build/RELEASE_NOTES.md
echo "\`\`\`" >> build/RELEASE_NOTES.md
echo "${{ steps.checksum.outputs.sha256 }} unraid-$VERSION.zip" >> build/RELEASE_NOTES.md
echo "\`\`\`" >> build/RELEASE_NOTES.md
cat build/RELEASE_NOTES.md
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="v${{ steps.version.outputs.version }}"
PRE_FLAG=""
if [ "${{ steps.version.outputs.prerelease }}" = "true" ]; then
PRE_FLAG="--prerelease"
fi
gh release create "$TAG" \
build/unraid-${{ steps.version.outputs.version }}.zip \
build/SHA256SUM \
CHANGELOG.md \
--title "$TAG" \
--notes-file build/RELEASE_NOTES.md \
$PRE_FLAG
- name: Release Summary
run: |
echo "## 🎉 v${{ steps.version.outputs.version }} Released Successfully!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Package:** \`unraid-${{ steps.version.outputs.version }}.zip\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**SHA256 Checksum:** \`${{ steps.checksum.outputs.sha256 }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Release URL:** https://github.com/${{ github.repository }}/releases/tag/v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.version.outputs.prerelease }}" == "true" ]; then
echo "**Type:** Pre-release" >> $GITHUB_STEP_SUMMARY
else
echo "**Type:** Stable release" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📋 Next Steps" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Release created and package uploaded" >> $GITHUB_STEP_SUMMARY
echo "- ✅ HACS users can now update to v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Manual installation package available for download" >> $GITHUB_STEP_SUMMARY
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
--- # yamllint disable rule:line-length name: Release "on": push: branches: - main paths: - custom_components/unraid/manifest.json tags: - "v*" permissions: {} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: auto_tag: timeout-minutes: 30 name: Tag release on manifest change runs-on: latchkey-small if: startsWith(github.ref, 'refs/heads/') permissions: contents: write outputs: version: ${{ steps.manifest.outputs.version }} tag_created: ${{ steps.create_tag.outputs.created }} steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 - name: Read version from manifest id: manifest run: | VERSION=$(python3 -c "import json; print(json.load(open('custom_components/unraid/manifest.json'))['version'])") if [ -z "$VERSION" ]; then echo "Version not found in manifest.json" exit 1 fi echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Manifest version: $VERSION" # Only release once the CHANGELOG is ready: the [Unreleased] content # must have been moved under a "## [VERSION]" heading matching the new # manifest version. A manifest bump without that section skips tagging # instead of publishing a release with generic fallback notes. Because # this workflow only triggers on manifest.json changes, the changelog # section must land in the same push as (or before) the manifest bump. - name: Check CHANGELOG has notes for this version id: changelog run: | VERSION="${{ steps.manifest.outputs.version }}" if [ ! -f "CHANGELOG.md" ]; then echo "ready=false" >> $GITHUB_OUTPUT echo "CHANGELOG.md not found" exit 0 fi # Escape regex metacharacters (the dots) in the version string VER_RE=$(printf '%s' "$VERSION" | sed 's/\./\\./g') NOTES=$(awk -v ver="$VER_RE" ' $0 ~ "^## \\[" ver "\\]" {in_section=1; next} in_section && $0 ~ /^## \[/ {exit} in_section {print} ' CHANGELOG.md) if [ -n "$(echo "$NOTES" | tr -d '[:space:]')" ]; then echo "ready=true" >> $GITHUB_OUTPUT echo "Found release notes for $VERSION in CHANGELOG.md" else echo "ready=false" >> $GITHUB_OUTPUT echo "No '## [$VERSION]' section with content found in CHANGELOG.md" fi - name: Check if tag already exists id: check_tag run: | TAG="v${{ steps.manifest.outputs.version }}" if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then echo "exists=true" >> $GITHUB_OUTPUT echo "Tag $TAG already exists (local)" exit 0 fi if git ls-remote --exit-code --tags origin "$TAG" >/dev/null 2>&1; then echo "exists=true" >> $GITHUB_OUTPUT echo "Tag $TAG already exists (remote)" else echo "exists=false" >> $GITHUB_OUTPUT echo "Tag $TAG does not exist" fi - name: Create and push tag id: create_tag if: >- steps.check_tag.outputs.exists == 'false' && steps.changelog.outputs.ready == 'true' run: | TAG="v${{ steps.manifest.outputs.version }}" echo "Creating tag $TAG" git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git tag -a "$TAG" -m "Release $TAG" git push origin "$TAG" echo "created=true" >> $GITHUB_OUTPUT - name: Summary run: | TAG="v${{ steps.manifest.outputs.version }}" VERSION="${{ steps.manifest.outputs.version }}" if [ "${{ steps.check_tag.outputs.exists }}" = "true" ]; then echo "Tag $TAG already existed. No new tag created." >> $GITHUB_STEP_SUMMARY elif [ "${{ steps.changelog.outputs.ready }}" != "true" ]; then echo "⚠️ Tag $TAG **not** created: CHANGELOG.md has no \`## [$VERSION]\` section with content." >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Move the \`[Unreleased]\` notes under a \`## [$VERSION]\` heading and push the change together with a manifest.json update to trigger the release." >> $GITHUB_STEP_SUMMARY else echo "Created and pushed tag $TAG from manifest.json version." >> $GITHUB_STEP_SUMMARY echo "The release job in this workflow run will now build and publish the release." >> $GITHUB_STEP_SUMMARY fi # Runs in two ways: # 1. Same-run after auto_tag creates a tag from a manifest version bump. # Tags pushed with the default GITHUB_TOKEN do NOT trigger new workflow # runs (GitHub's recursion guard), so the release must be built here - # a separate tag-triggered run would never start. # 2. Directly from a manually pushed v* tag (auto_tag is skipped; the # `!cancelled()` keeps this job eligible despite the skipped dependency). release: timeout-minutes: 30 name: Build and Release runs-on: latchkey-small needs: auto_tag if: >- !cancelled() && ( startsWith(github.ref, 'refs/tags/') || (needs.auto_tag.result == 'success' && needs.auto_tag.outputs.tag_created == 'true') ) permissions: contents: write steps: - name: Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 - name: Determine version id: version run: | if [[ "$GITHUB_REF" == refs/tags/v* ]]; then # Triggered by a manually pushed tag (e.g., v2025.12.0 -> 2025.12.0) VERSION=${GITHUB_REF#refs/tags/v} else # Same-run release after auto_tag created the tag VERSION="${{ needs.auto_tag.outputs.version }}" fi if [ -z "$VERSION" ]; then echo "ERROR: could not determine release version" exit 1 fi echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Version: $VERSION" # Check if this is a pre-release if [[ "$VERSION" =~ (alpha|beta|rc) ]]; then echo "prerelease=true" >> $GITHUB_OUTPUT echo "This is a pre-release" else echo "prerelease=false" >> $GITHUB_OUTPUT echo "This is a stable release" fi - name: Verify manifest.json version matches tag run: | MANIFEST_VERSION=$(python3 -c "import json; print(json.load(open('custom_components/unraid/manifest.json'))['version'])") TAG_VERSION="${{ steps.version.outputs.version }}" if [ "$MANIFEST_VERSION" != "$TAG_VERSION" ]; then echo "ERROR: manifest.json version ($MANIFEST_VERSION) does not match tag ($TAG_VERSION)" exit 1 fi echo "✅ manifest.json version matches tag: $TAG_VERSION" - name: Build release package run: | echo "Building release package..." # Create build directory mkdir -p build # Create the integration package (zip file for HACS compatibility) PACKAGE_FILE="build/unraid-${{ steps.version.outputs.version }}.zip" # Package the custom_components directory cd custom_components zip -r "../$PACKAGE_FILE" unraid/ \ -x "*.pyc" \ -x "*__pycache__*" \ -x "*.git*" \ -x "*.DS_Store" cd .. # Verify the package was created if [ ! -f "$PACKAGE_FILE" ]; then echo "ERROR: Package file not found: $PACKAGE_FILE" ls -la build/ exit 1 fi echo "✅ Package created: $PACKAGE_FILE" ls -lh "$PACKAGE_FILE" # Show package contents echo "" echo "Package contents:" unzip -l "$PACKAGE_FILE" | head -20 - name: Calculate SHA256 checksum id: checksum run: | PACKAGE_FILE="build/unraid-${{ steps.version.outputs.version }}.zip" # Calculate SHA256 checksum SHA256=$(sha256sum "$PACKAGE_FILE" | awk '{print $1}') echo "sha256=$SHA256" >> $GITHUB_OUTPUT echo "✅ SHA256 checksum: $SHA256" # Save checksum to file for release notes echo "$SHA256" > build/SHA256SUM - name: Extract release notes from CHANGELOG id: changelog run: | VERSION="${{ steps.version.outputs.version }}" # Check if CHANGELOG.md exists if [ ! -f "CHANGELOG.md" ]; then echo "⚠️ CHANGELOG.md not found, using default release notes" NOTES="Release v$VERSION of the Unraid Home Assistant integration." else # Extract release notes for this version from CHANGELOG.md # Look for content after "## [VERSION]" until the next version header. if grep -qF "## [$VERSION]" CHANGELOG.md; then # Escape regex metacharacters (the dots) in the version string VER_RE=$(printf '%s' "$VERSION" | sed 's/\./\\./g') NOTES=$(awk -v ver="$VER_RE" ' $0 ~ "^## \\[" ver "\\]" {in_section=1; next} in_section && $0 ~ /^## \[/ {exit} in_section {print} ' CHANGELOG.md) if [ -z "$NOTES" ]; then echo "⚠️ No release notes found for version $VERSION in CHANGELOG.md" NOTES="See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." else echo "✅ Extracted release notes for version $VERSION" fi else echo "⚠️ Version $VERSION not found in CHANGELOG.md" NOTES="See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." fi fi # Save to file (GitHub Actions doesn't handle multiline outputs well) echo "$NOTES" > build/RELEASE_NOTES.md # Add link to full changelog if not already present in NOTES if ! grep -q "See \[CHANGELOG.md\](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details\." build/RELEASE_NOTES.md; then echo "" >> build/RELEASE_NOTES.md echo "See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." >> build/RELEASE_NOTES.md fi # Add installation instructions echo "" >> build/RELEASE_NOTES.md echo "---" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md echo "## 📦 Installation" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md echo "### Via HACS (Recommended)" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md echo "1. Open HACS in Home Assistant" >> build/RELEASE_NOTES.md echo "2. Go to **Integrations**" >> build/RELEASE_NOTES.md echo "3. Click the **⋮** menu → **Custom repositories**" >> build/RELEASE_NOTES.md echo "4. Add this repository: \`https://github.com/${{ github.repository }}\`" >> build/RELEASE_NOTES.md echo "5. Category: **Integration**" >> build/RELEASE_NOTES.md echo "6. Click **Add**" >> build/RELEASE_NOTES.md echo "7. Search for **Unraid**" >> build/RELEASE_NOTES.md echo "8. Click **Download**" >> build/RELEASE_NOTES.md echo "9. Restart Home Assistant" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md echo "### Manual Installation" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md echo "1. Download \`unraid-$VERSION.zip\`" >> build/RELEASE_NOTES.md echo "2. Extract to \`config/custom_components/\`" >> build/RELEASE_NOTES.md echo "3. Restart Home Assistant" >> build/RELEASE_NOTES.md echo "4. Add the integration via **Settings** → **Devices & Services** → **Add Integration**" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md # Add SHA256 checksum to release notes echo "---" >> build/RELEASE_NOTES.md echo "" >> build/RELEASE_NOTES.md echo "**SHA256 Checksum:**" >> build/RELEASE_NOTES.md echo "\`\`\`" >> build/RELEASE_NOTES.md echo "${{ steps.checksum.outputs.sha256 }} unraid-$VERSION.zip" >> build/RELEASE_NOTES.md echo "\`\`\`" >> build/RELEASE_NOTES.md cat build/RELEASE_NOTES.md - name: Create GitHub Release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="v${{ steps.version.outputs.version }}" PRE_FLAG="" if [ "${{ steps.version.outputs.prerelease }}" = "true" ]; then PRE_FLAG="--prerelease" fi gh release create "$TAG" \ build/unraid-${{ steps.version.outputs.version }}.zip \ build/SHA256SUM \ CHANGELOG.md \ --title "$TAG" \ --notes-file build/RELEASE_NOTES.md \ $PRE_FLAG - name: Release Summary run: | echo "## 🎉 v${{ steps.version.outputs.version }} Released Successfully!" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Package:** \`unraid-${{ steps.version.outputs.version }}.zip\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**SHA256 Checksum:** \`${{ steps.checksum.outputs.sha256 }}\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Release URL:** https://github.com/${{ github.repository }}/releases/tag/v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ "${{ steps.version.outputs.prerelease }}" == "true" ]; then echo "**Type:** Pre-release" >> $GITHUB_STEP_SUMMARY else echo "**Type:** Stable release" >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "### 📋 Next Steps" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "- ✅ Release created and package uploaded" >> $GITHUB_STEP_SUMMARY echo "- ✅ HACS users can now update to v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY echo "- ✅ Manual installation package available for download" >> $GITHUB_STEP_SUMMARY
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
This workflow runs 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.