Skip to content

Release

Release #55

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
tag:
description: "Git tag to release (e.g., v1.0.0) - Leave empty to auto-calculate from GitVersion"
required: false
type: string
create_docker:
description: "Also create Docker image"
required: false
default: true
type: boolean
env:
DOTNET_VERSION: "9.0.x"
PROJECT_NAME: "KnxMonitor"
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
calculate-version:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
version: ${{ steps.gitversion.outputs.semVer }}
tag: ${{ steps.determine-tag.outputs.tag }}
informational-version: ${{ steps.gitversion.outputs.informationalVersion }}
assembly-version: ${{ steps.gitversion.outputs.assemblySemVer }}
is-prerelease: ${{ steps.gitversion.outputs.preReleaseLabel != '' }}
steps:
- name: Checkout code
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5.0.0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.1.0
with:
versionSpec: "6.x"
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v4.1.0
with:
configFilePath: GitVersion.yml
- name: Determine Tag
id: determine-tag
run: |
if [[ -n "${{ needs.calculate-version.outputs.tag }}" ]]; then
# Use provided tag
TAG="${{ needs.calculate-version.outputs.tag }}"
echo "Using provided tag: $TAG"
else
# Auto-calculate tag from GitVersion
TAG="v${{ steps.gitversion.outputs.semVer }}"
echo "Auto-calculated tag: $TAG"
fi
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Display GitVersion outputs
run: |
echo "SemVer: ${{ steps.gitversion.outputs.semVer }}"
echo "FullSemVer: ${{ steps.gitversion.outputs.fullSemVer }}"
echo "InformationalVersion: ${{ steps.gitversion.outputs.informationalVersion }}"
echo "AssemblySemVer: ${{ steps.gitversion.outputs.assemblySemVer }}"
echo "PreReleaseLabel: ${{ steps.gitversion.outputs.preReleaseLabel }}"
echo "Final Tag: ${{ steps.determine-tag.outputs.tag }}"
build:
needs: calculate-version
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- runtime: win-x64
os: ubuntu-latest
- runtime: linux-x64
os: ubuntu-latest
- runtime: linux-arm64
os: ubuntu-latest
- runtime: osx-x64
os: macos-15-intel
- runtime: osx-arm64
os: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5.0.0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.1.0
with:
versionSpec: "6.x"
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v4.1.0
with:
configFilePath: GitVersion.yml
- name: Cache NuGet packages
uses: actions/cache@v4.3.0
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore dependencies
run: dotnet restore --verbosity minimal
- name: Run tests
run: dotnet test --configuration Release --verbosity normal --no-restore
- name: Build and publish
run: |
echo "DEBUG: Building for runtime: ${{ matrix.runtime }}"
echo "DEBUG: .NET SDK Version: $(dotnet --version)"
echo "DEBUG: Available SDKs: $(dotnet --list-sdks)"
# Ensure clean build environment
dotnet clean --configuration Release --verbosity minimal
if [[ "${{ matrix.runtime }}" == "linux-x64" || "${{ matrix.runtime }}" == "linux-arm64" ]]; then
echo "DEBUG: Using enterprise-grade framework-dependent deployment for Docker (Linux)"
# Framework-dependent deployment optimized for Docker containers
# This creates KnxMonitor.dll for Docker and KnxMonitor executable for packaging
dotnet publish ${{ env.PROJECT_NAME }} \
--configuration Release \
--runtime ${{ matrix.runtime }} \
--self-contained false \
--output ./publish/${{ matrix.runtime }} \
--verbosity normal \
--nologo
# Also create a self-contained executable for Linux packaging
dotnet publish ${{ env.PROJECT_NAME }} \
--configuration Release \
--runtime ${{ matrix.runtime }} \
--self-contained true \
--property:PublishSingleFile=true \
--output ./publish-packaging/${{ matrix.runtime }} \
--verbosity normal \
--nologo
echo "DEBUG: Self-contained deployment for packaging completed"
echo "DEBUG: Framework-dependent deployment completed"
# Verify both DLL and executable are created
if [[ ! -f "./publish/${{ matrix.runtime }}/KnxMonitor.dll" ]]; then
echo "ERROR: KnxMonitor.dll not found in framework-dependent deployment"
echo "Available files:"
ls -la ./publish/${{ matrix.runtime }}/
exit 1
fi
# Ensure executable has proper permissions
if [[ -f "./publish/${{ matrix.runtime }}/KnxMonitor" ]]; then
chmod +x "./publish/${{ matrix.runtime }}/KnxMonitor"
echo "DEBUG: Made KnxMonitor executable"
fi
else
echo "DEBUG: Using enterprise-grade self-contained deployment for native platforms"
# Self-contained deployment with conservative settings for compatibility
dotnet publish ${{ env.PROJECT_NAME }} \
--configuration Release \
--runtime ${{ matrix.runtime }} \
--self-contained true \
--property:PublishSingleFile=true \
--property:IncludeNativeLibrariesForSelfExtract=true \
--property:EnableCompressionInSingleFile=true \
--property:PublishTrimmed=false \
--property:PublishReadyToRun=false \
--output ./publish/${{ matrix.runtime }} \
--verbosity normal \
--nologo
echo "DEBUG: Self-contained deployment completed"
# Verify executable is created
EXPECTED_EXE="KnxMonitor"
if [[ "${{ matrix.runtime }}" == win-* ]]; then
EXPECTED_EXE="KnxMonitor.exe"
fi
if [[ ! -f "./publish/${{ matrix.runtime }}/$EXPECTED_EXE" ]]; then
echo "ERROR: $EXPECTED_EXE not found in self-contained deployment"
echo "Available files:"
ls -la ./publish/${{ matrix.runtime }}/
exit 1
fi
# Ensure executable has proper permissions (Unix-like systems)
if [[ "${{ matrix.runtime }}" != win-* ]]; then
chmod +x "./publish/${{ matrix.runtime }}/$EXPECTED_EXE"
echo "DEBUG: Made $EXPECTED_EXE executable"
fi
fi
echo "DEBUG: Build validation completed successfully"
echo "DEBUG: Final output verification:"
ls -la ./publish/${{ matrix.runtime }}/ | head -20
# Additional validation: verify the application can show version
echo "DEBUG: Testing application startup..."
cd ./publish/${{ matrix.runtime }}/
if [[ -f "KnxMonitor.dll" ]] && command -v dotnet >/dev/null 2>&1; then
echo "DEBUG: Testing DLL version..."
timeout 10 dotnet KnxMonitor.dll --version || echo "DLL version test failed (non-critical)"
fi
if [[ -f "KnxMonitor" && -x "KnxMonitor" ]]; then
echo "DEBUG: Testing executable version..."
timeout 10 ./KnxMonitor --version || echo "Executable version test failed (non-critical)"
elif [[ -f "KnxMonitor.exe" ]]; then
echo "DEBUG: Testing Windows executable version..."
timeout 10 ./KnxMonitor.exe --version || echo "Windows executable version test failed (non-critical)"
fi
echo "DEBUG: Application startup test completed"
- name: Code sign macOS binaries
if: startsWith(matrix.runtime, 'osx-')
env:
APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_DEVELOPER_ID: ${{ secrets.APPLE_DEVELOPER_ID }}
run: |
# Create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
# Create keychain and set as default
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH"
# Import certificate
if [ -z "$APPLE_CERTIFICATE_P12" ]; then
echo "Error: APPLE_CERTIFICATE_P12 secret is empty"
exit 1
fi
echo "$APPLE_CERTIFICATE_P12" | base64 -D > certificate.p12
if [ ! -f certificate.p12 ] || [ ! -s certificate.p12 ]; then
echo "Error: Failed to decode certificate"
exit 1
fi
security import certificate.p12 -k "$KEYCHAIN_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Sign the binary
BINARY_PATH="./publish/${{ matrix.runtime }}/${{ env.PROJECT_NAME }}"
codesign --sign "Developer ID Application: Fabian Schmieder ($APPLE_DEVELOPER_ID)" \
--verbose \
--force \
--options runtime \
--entitlements entitlements.plist \
--timestamp \
"$BINARY_PATH"
# Verify signature
codesign --verify --verbose "$BINARY_PATH"
# Check Gatekeeper status (non-blocking - notarization not required for CLI tools)
echo "Checking Gatekeeper status (informational only):"
spctl --assess --type execute --verbose "$BINARY_PATH" || echo "Note: Binary is signed but not notarized (expected for CLI tools)"
# Clean up
rm certificate.p12
security delete-keychain "$KEYCHAIN_PATH"
- name: Create archive
run: |
# Create a staging directory with all files for the archive
mkdir -p ./archive-staging
# Copy the binary (handle Windows .exe extension and Linux packaging)
if [[ "${{ matrix.runtime }}" == "linux-x64" || "${{ matrix.runtime }}" == "linux-arm64" ]]; then
# Use self-contained executable for Linux packaging
cp ./publish-packaging/${{ matrix.runtime }}/KnxMonitor ./archive-staging/
elif [[ "${{ matrix.runtime }}" == win-* ]]; then
cp ./publish/${{ matrix.runtime }}/KnxMonitor.exe ./archive-staging/
else
cp ./publish/${{ matrix.runtime }}/KnxMonitor ./archive-staging/
fi
# Copy documentation files
cp README.md ./archive-staging/ 2>/dev/null || echo "README.md not found"
cp LICENSE ./archive-staging/ 2>/dev/null || echo "LICENSE not found"
# Copy man page
if [ -f "docs/knxmonitor.1" ]; then
mkdir -p ./archive-staging/docs
cp docs/knxmonitor.1 ./archive-staging/docs/
fi
# Copy example CSV file
if [ -f "knx-addresses.csv" ]; then
cp knx-addresses.csv ./archive-staging/
fi
# Create the archive
cd ./archive-staging
if [[ "${{ matrix.runtime }}" == win-* ]]; then
zip -r ../${{ env.PROJECT_NAME }}-${{ needs.calculate-version.outputs.version }}-${{ matrix.runtime }}.zip .
else
tar -czf ../${{ env.PROJECT_NAME }}-${{ needs.calculate-version.outputs.version }}-${{ matrix.runtime }}.tar.gz .
fi
- name: Upload artifacts
uses: actions/upload-artifact@v5.0.0
with:
name: ${{ env.PROJECT_NAME }}-${{ matrix.runtime }}
path: |
*.zip
*.tar.gz
retention-days: 1
compression-level: 9
- name: Upload Docker artifacts (framework-dependent)
if: matrix.runtime == 'linux-x64' || matrix.runtime == 'linux-arm64'
uses: actions/upload-artifact@v5.0.0
with:
name: ${{ env.PROJECT_NAME }}-docker-${{ matrix.runtime }}
path: ./publish/${{ matrix.runtime }}/
retention-days: 1
compression-level: 9
docker:
needs: [calculate-version, build]
if: inputs.create_docker
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.7.1
- name: Login to GitHub Container Registry
uses: docker/login-action@v3.3.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5.5.1
with:
images: ghcr.io/${{ github.repository_owner }}/${{ env.PROJECT_NAME }}
tags: |
type=raw,value=${{ needs.calculate-version.outputs.version }}
type=raw,value=latest,enable=${{ needs.calculate-version.outputs.is-prerelease == 'false' }}
labels: |
org.opencontainers.image.title=KNX Monitor
org.opencontainers.image.description=KNX/EIB bus monitoring and debugging tool
org.opencontainers.image.vendor=metaneutrons
org.opencontainers.image.version=${{ needs.calculate-version.outputs.version }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.documentation=${{ github.server_url }}/${{ github.repository }}/blob/main/README.md
org.opencontainers.image.licenses=GPL-3.0
- name: Download Linux binaries for Docker
uses: actions/download-artifact@v6.0.0
with:
pattern: ${{ env.PROJECT_NAME }}-docker-linux-*
path: ./docker-artifacts
merge-multiple: false
- name: Prepare Docker context for multi-arch build
run: |
echo "INFO: Preparing enterprise-grade Docker build context..."
# Create clean build directories
rm -rf docker-context
mkdir -p docker-context/publish-amd64 docker-context/publish-arm64
echo "INFO: Processing AMD64 (x64) artifacts..."
cd docker-artifacts/${{ env.PROJECT_NAME }}-docker-linux-x64
# Validate required files for AMD64 (direct directory, no archive)
if [[ ! -f "KnxMonitor.dll" ]]; then
echo "ERROR: KnxMonitor.dll not found in AMD64 deployment"
echo "Available files:"
ls -la
exit 1
fi
# Copy AMD64 deployment with validation
cp -r * ../../docker-context/publish-amd64/
echo "INFO: AMD64 deployment copied to Docker context"
echo "INFO: Processing ARM64 artifacts..."
cd ../${{ env.PROJECT_NAME }}-docker-linux-arm64
# Validate required files for ARM64 (direct directory, no archive)
if [[ ! -f "KnxMonitor.dll" ]]; then
echo "ERROR: KnxMonitor.dll not found in ARM64 deployment"
echo "Available files:"
ls -la
exit 1
fi
# Copy ARM64 deployment with validation
cp -r * ../../docker-context/publish-arm64/
echo "INFO: ARM64 deployment copied to Docker context"
# Copy and validate Dockerfile
cd ../..
if [[ ! -f "Dockerfile.ci" ]]; then
echo "ERROR: Dockerfile.ci not found"
exit 1
fi
cp Dockerfile.ci docker-context/Dockerfile
echo "INFO: Dockerfile copied to build context"
# Comprehensive validation of Docker context
echo "INFO: Validating Docker build context..."
# Validate AMD64 deployment
echo "INFO: AMD64 deployment validation:"
if [[ ! -f "docker-context/publish-amd64/KnxMonitor.dll" ]]; then
echo "ERROR: AMD64 KnxMonitor.dll missing"
exit 1
fi
AMD64_DLL_SIZE=$(stat -c%s "docker-context/publish-amd64/KnxMonitor.dll" 2>/dev/null || stat -f%z "docker-context/publish-amd64/KnxMonitor.dll")
if [[ "$AMD64_DLL_SIZE" -lt 1000 ]]; then
echo "ERROR: AMD64 KnxMonitor.dll appears to be too small ($AMD64_DLL_SIZE bytes)"
exit 1
fi
echo " ✓ KnxMonitor.dll: $AMD64_DLL_SIZE bytes"
# Validate ARM64 deployment
echo "INFO: ARM64 deployment validation:"
if [[ ! -f "docker-context/publish-arm64/KnxMonitor.dll" ]]; then
echo "ERROR: ARM64 KnxMonitor.dll missing"
exit 1
fi
ARM64_DLL_SIZE=$(stat -c%s "docker-context/publish-arm64/KnxMonitor.dll" 2>/dev/null || stat -f%z "docker-context/publish-arm64/KnxMonitor.dll")
if [[ "$ARM64_DLL_SIZE" -lt 1000 ]]; then
echo "ERROR: ARM64 KnxMonitor.dll appears to be too small ($ARM64_DLL_SIZE bytes)"
exit 1
fi
echo " ✓ KnxMonitor.dll: $ARM64_DLL_SIZE bytes"
# Validate Dockerfile
if [[ ! -f "docker-context/Dockerfile" ]]; then
echo "ERROR: Dockerfile missing from build context"
exit 1
fi
# Check Dockerfile syntax
if ! grep -q "ENTRYPOINT.*entrypoint.sh" docker-context/Dockerfile; then
echo "ERROR: Dockerfile does not contain expected entrypoint configuration"
exit 1
fi
echo " ✓ Dockerfile syntax validated"
# Final context summary
echo "INFO: Docker build context prepared successfully:"
echo " ✓ AMD64 deployment: $(ls docker-context/publish-amd64/ | wc -l) files"
echo " ✓ ARM64 deployment: $(ls docker-context/publish-arm64/ | wc -l) files"
echo " ✓ Dockerfile: $(wc -l < docker-context/Dockerfile) lines"
# Show key files for debugging
echo "INFO: Key files in build context:"
echo "AMD64 files:"
ls -la docker-context/publish-amd64/ | head -10
echo "ARM64 files:"
ls -la docker-context/publish-arm64/ | head -10
echo "INFO: Docker context preparation completed successfully"
- name: Build and push Docker image
uses: docker/build-push-action@v6.18.0
with:
context: ./docker-context
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ needs.calculate-version.outputs.version }}
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
VCS_REF=${{ github.sha }}
provenance: true
sbom: true
- name: Validate Docker image
run: |
echo "INFO: Validating built Docker images..."
# Convert project name to lowercase for Docker image name
IMAGE_NAME=$(echo "${{ env.PROJECT_NAME }}" | tr '[:upper:]' '[:lower:]')
# Test AMD64 image
echo "INFO: Testing AMD64 image..."
docker run --rm --platform linux/amd64 \
ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}:${{ needs.calculate-version.outputs.version }} \
--version
# Test ARM64 image (if running on compatible hardware)
echo "INFO: Testing ARM64 image..."
if docker run --rm --platform linux/arm64 \
ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}:${{ needs.calculate-version.outputs.version }} \
--version 2>/dev/null; then
echo " ✓ ARM64 image validation successful"
else
echo " ⚠ ARM64 image validation skipped (incompatible host or emulation issues)"
fi
# Test help command
echo "INFO: Testing help command..."
docker run --rm --platform linux/amd64 \
ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}:${{ needs.calculate-version.outputs.version }} \
--help | head -5
echo "INFO: Docker image validation completed successfully"
release:
needs: [calculate-version, build, docker]
if: always() && needs.calculate-version.result == 'success' && needs.build.result == 'success' && (needs.docker.result == 'success' || needs.docker.result == 'skipped')
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
- name: Download release artifacts
uses: actions/download-artifact@v6.0.0
with:
pattern: KnxMonitor-*
path: ./artifacts
merge-multiple: false
- name: Download AUR package
uses: actions/download-artifact@v6.0.0
with:
pattern: aur-package-*
path: ./artifacts
merge-multiple: false
- name: Prepare release assets
run: |
mkdir -p ./release-assets
find ./artifacts -name "*.zip" -o -name "*.tar.gz" | xargs -I {} cp {} ./release-assets/
ls -la ./release-assets/
- name: Create and push Git tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Check if tag already exists
if git rev-parse "${{ needs.calculate-version.outputs.tag }}" >/dev/null 2>&1; then
echo "Tag ${{ needs.calculate-version.outputs.tag }} already exists, skipping tag creation"
else
echo "Creating new tag ${{ needs.calculate-version.outputs.tag }}"
git tag -a ${{ needs.calculate-version.outputs.tag }} -m "Release ${{ needs.calculate-version.outputs.tag }}"
git push origin ${{ needs.calculate-version.outputs.tag }}
fi
- name: Generate release notes
id: release_notes
run: |
cat << EOF > release_notes.md
## 🚀 KNX Monitor ${{ needs.calculate-version.outputs.version }}
### 📋 Version Information
- **Version**: ${{ needs.calculate-version.outputs.version }}
- **GitVersion**: ${{ needs.calculate-version.outputs.informational-version }}
- **Assembly Version**: ${{ needs.calculate-version.outputs.assembly-version }}
### 📦 Installation
#### Homebrew (macOS/Linux)
\`\`\`bash
brew install metaneutrons/tap/knxmonitor
\`\`\`
#### Docker
\`\`\`bash
docker run --rm -it ghcr.io/metaneutrons/knxmonitor:${{ needs.calculate-version.outputs.version }} --help
\`\`\`
#### Manual Installation
Download the appropriate binary for your platform from the assets below.
### 🏗️ Build Information
- **.NET Version**: 9.0
- **Build Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
- **Commit**: ${{ github.sha }}
- **Tag**: ${{ needs.calculate-version.outputs.tag }}
### 📋 Supported Platforms
- **Windows x64** (knxmonitor-${{ needs.calculate-version.outputs.version }}-win-x64.zip)
- **Linux x64** (knxmonitor-${{ needs.calculate-version.outputs.version }}-linux-x64.tar.gz)
- **macOS x64** (knxmonitor-${{ needs.calculate-version.outputs.version }}-osx-x64.tar.gz)
- **macOS ARM64** (knxmonitor-${{ needs.calculate-version.outputs.version }}-osx-arm64.tar.gz)
- **Docker** (ghcr.io/metaneutrons/knxmonitor:${{ needs.calculate-version.outputs.version }})
### 🔧 Quick Start
\`\`\`bash
# Show version
knxmonitor --version
# Monitor KNX bus via IP routing
knxmonitor --connection-type routing
# Monitor with IP tunneling
knxmonitor --connection-type tunneling --host 192.168.1.100
\`\`\`
---
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ needs.calculate-version.outputs.tag }}...HEAD
EOF
- name: Create GitHub Release
uses: softprops/action-gh-release@v2.0.8
with:
tag_name: ${{ needs.calculate-version.outputs.tag }}
name: "KNX Monitor ${{ needs.calculate-version.outputs.version }}"
body_path: release_notes.md
files: ./release-assets/*
draft: false
prerelease: ${{ needs.calculate-version.outputs.is-prerelease == 'true' }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package-linux:
name: Package for Linux distributions
needs: [calculate-version, build]
runs-on: ubuntu-latest
if: needs.calculate-version.outputs.is-prerelease == 'false'
permissions:
contents: write
packages: read
steps:
- name: Checkout code
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
- name: Download Linux artifacts
uses: actions/download-artifact@v6.0.0
with:
pattern: KnxMonitor-linux-*
path: ./artifacts
merge-multiple: false
- name: Extract Linux artifacts
run: |
echo "INFO: Extracting Linux artifacts for packaging..."
# Extract AMD64 artifacts
if [ -d "artifacts/KnxMonitor-linux-x64" ]; then
cd artifacts/KnxMonitor-linux-x64
if [ -f "KnxMonitor-${{ needs.calculate-version.outputs.version }}-linux-x64.tar.gz" ]; then
tar -xzf "KnxMonitor-${{ needs.calculate-version.outputs.version }}-linux-x64.tar.gz"
echo "INFO: AMD64 artifacts extracted"
ls -la
else
echo "ERROR: AMD64 archive not found"
exit 1
fi
cd ../..
fi
# Extract ARM64 artifacts
if [ -d "artifacts/KnxMonitor-linux-arm64" ]; then
cd artifacts/KnxMonitor-linux-arm64
if [ -f "KnxMonitor-${{ needs.calculate-version.outputs.version }}-linux-arm64.tar.gz" ]; then
tar -xzf "KnxMonitor-${{ needs.calculate-version.outputs.version }}-linux-arm64.tar.gz"
echo "INFO: ARM64 artifacts extracted"
ls -la
else
echo "ERROR: ARM64 archive not found"
exit 1
fi
cd ../..
fi
echo "INFO: All Linux artifacts extracted successfully"
- name: Install packaging tools
run: |
sudo apt-get update
sudo apt-get install -y dpkg-dev
- name: Make packaging scripts executable
run: |
chmod +x packaging/build-deb.sh
- name: Build Debian packages
run: |
# Build for amd64
./packaging/build-deb.sh ${{ needs.calculate-version.outputs.version }} amd64
# Build for arm64
./packaging/build-deb.sh ${{ needs.calculate-version.outputs.version }} arm64
- name: Upload Debian packages to release
uses: softprops/action-gh-release@v2.0.8
with:
tag_name: ${{ needs.calculate-version.outputs.tag }}
files: "*.deb"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create AUR package files
run: |
VERSION="${{ needs.calculate-version.outputs.version }}"
# Calculate SHA256 for source tarball
TARBALL_URL="https://github.com/metaneutrons/KnxMonitor/archive/refs/tags/v${VERSION}.tar.gz"
TARBALL_SHA=$(curl -sL "$TARBALL_URL" | sha256sum | cut -d' ' -f1)
# Update PKGBUILD with correct version and SHA
sed -i "s/pkgver=.*/pkgver=${VERSION}/" packaging/arch/PKGBUILD
sed -i "s/sha256sums=('SKIP')/sha256sums=('${TARBALL_SHA}')/" packaging/arch/PKGBUILD
# Create .SRCINFO file (requires makepkg, so we'll create a basic one)
cd packaging/arch
cat > .SRCINFO << EOF
pkgbase = knxmonitor
pkgdesc = KNX/EIB bus monitoring and debugging tool
pkgver = ${VERSION}
pkgrel = 1
url = https://github.com/metaneutrons/KnxMonitor
arch = x86_64
arch = aarch64
license = GPL3
makedepends = dotnet-sdk>=9.0
depends = glibc
depends = gcc-libs
depends = openssl
optdepends = knxd: KNX daemon for local KNX/IP gateway
optdepends = docker: For containerized deployment
source = knxmonitor-${VERSION}.tar.gz::https://github.com/metaneutrons/KnxMonitor/archive/refs/tags/v${VERSION}.tar.gz
sha256sums = ${TARBALL_SHA}
pkgname = knxmonitor
EOF
- name: Upload AUR package files
uses: actions/upload-artifact@v5.0.0
with:
name: aur-package-${{ needs.calculate-version.outputs.version }}
path: |
packaging/arch/PKGBUILD
packaging/arch/.SRCINFO
retention-days: 90
- name: Create packaging summary
run: |
echo "## 📦 Linux Packaging Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Debian Packages" >> $GITHUB_STEP_SUMMARY
echo "- ✅ knxmonitor_${{ needs.calculate-version.outputs.version }}_amd64.deb" >> $GITHUB_STEP_SUMMARY
echo "- ✅ knxmonitor_${{ needs.calculate-version.outputs.version }}_arm64.deb" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Arch Linux AUR" >> $GITHUB_STEP_SUMMARY
echo "- ✅ PKGBUILD updated with version ${{ needs.calculate-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- ✅ .SRCINFO generated" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Installation Instructions" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Debian/Ubuntu:**" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "wget https://github.com/metaneutrons/KnxMonitor/releases/download/v${{ needs.calculate-version.outputs.version }}/knxmonitor_${{ needs.calculate-version.outputs.version }}_amd64.deb" >> $GITHUB_STEP_SUMMARY
echo "sudo dpkg -i knxmonitor_${{ needs.calculate-version.outputs.version }}_amd64.deb" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Arch Linux:**" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "yay -S knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "# or" >> $GITHUB_STEP_SUMMARY
echo "git clone https://aur.archlinux.org/knxmonitor.git" >> $GITHUB_STEP_SUMMARY
echo "cd knxmonitor && makepkg -si" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
publish-aur:
needs: [calculate-version, package-linux, release]
if: always() && needs.calculate-version.outputs.is-prerelease == 'false' && needs.release.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 15
continue-on-error: false
steps:
- name: Checkout repository
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0
- name: Download AUR package artifact
uses: actions/download-artifact@v6.0.0
with:
pattern: aur-package-*
path: ./aur-artifacts
merge-multiple: false
- name: Validate AUR package files
run: |
echo "INFO: Validating AUR package files..."
# Find the AUR package directory
AUR_DIR=$(find ./aur-artifacts -type d -name "aur-package-*" | head -1)
if [ -z "$AUR_DIR" ]; then
echo "ERROR: AUR package directory not found"
exit 1
fi
echo "INFO: Found AUR package directory: $AUR_DIR"
# Validate PKGBUILD exists
if [ ! -f "$AUR_DIR/PKGBUILD" ]; then
echo "ERROR: PKGBUILD not found in $AUR_DIR"
exit 1
fi
# Validate .SRCINFO exists
if [ ! -f "$AUR_DIR/.SRCINFO" ]; then
echo "ERROR: .SRCINFO not found in $AUR_DIR"
exit 1
fi
# Validate PKGBUILD syntax
echo "INFO: Validating PKGBUILD syntax..."
if ! bash -n "$AUR_DIR/PKGBUILD"; then
echo "ERROR: PKGBUILD has syntax errors"
exit 1
fi
# Check required PKGBUILD variables
echo "INFO: Checking required PKGBUILD variables..."
for var in pkgname pkgver pkgrel pkgdesc arch url license source sha256sums; do
if ! grep -q "^${var}=" "$AUR_DIR/PKGBUILD"; then
echo "ERROR: Required variable '$var' not found in PKGBUILD"
exit 1
fi
done
# Validate version matches
PKGBUILD_VERSION=$(grep "^pkgver=" "$AUR_DIR/PKGBUILD" | cut -d'=' -f2)
EXPECTED_VERSION="${{ needs.calculate-version.outputs.version }}"
if [ "$PKGBUILD_VERSION" != "$EXPECTED_VERSION" ]; then
echo "ERROR: PKGBUILD version ($PKGBUILD_VERSION) does not match expected version ($EXPECTED_VERSION)"
exit 1
fi
echo "✅ AUR package files validated successfully"
echo " - PKGBUILD: $(wc -l < "$AUR_DIR/PKGBUILD") lines"
echo " - .SRCINFO: $(wc -l < "$AUR_DIR/.SRCINFO") lines"
echo " - Version: $PKGBUILD_VERSION"
- name: Prepare AUR package for publishing
run: |
echo "INFO: Preparing AUR package for publishing..."
# Find and copy AUR files to workspace root
AUR_DIR=$(find ./aur-artifacts -type d -name "aur-package-*" | head -1)
cp "$AUR_DIR/PKGBUILD" ./PKGBUILD
cp "$AUR_DIR/.SRCINFO" ./.SRCINFO
echo "INFO: AUR package files prepared"
echo "PKGBUILD content:"
cat ./PKGBUILD
echo ""
echo ".SRCINFO content:"
cat ./.SRCINFO
- name: Publish to AUR
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
with:
pkgname: knxmonitor
pkgbuild: ./PKGBUILD
assets: ./.SRCINFO
commit_username: metaneutrons
commit_email: wohlhabend_gasherd_2r@icloud.com
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message: "Update to version ${{ needs.calculate-version.outputs.version }}"
ssh_keyscan_types: rsa,ecdsa,ed25519
updpkgsums: false
test: false
force_push: false
allow_empty_commits: false
- name: Create AUR publish summary
if: always()
run: |
echo "## 📦 AUR Package Publishing" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ job.status }}" = "success" ]; then
echo "✅ **Successfully published to AUR**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Package**: knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: ${{ needs.calculate-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **AUR URL**: https://aur.archlinux.org/packages/knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Installation" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "# Using yay" >> $GITHUB_STEP_SUMMARY
echo "yay -S knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "# Using paru" >> $GITHUB_STEP_SUMMARY
echo "paru -S knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "# Manual installation" >> $GITHUB_STEP_SUMMARY
echo "git clone https://aur.archlinux.org/knxmonitor.git" >> $GITHUB_STEP_SUMMARY
echo "cd knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "makepkg -si" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
else
echo "❌ **AUR publishing failed**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Manual Publishing" >> $GITHUB_STEP_SUMMARY
echo "If automatic publishing fails, you can manually update the AUR package:" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "git clone ssh://aur@aur.archlinux.org/knxmonitor.git" >> $GITHUB_STEP_SUMMARY
echo "cd knxmonitor" >> $GITHUB_STEP_SUMMARY
echo "# Download PKGBUILD and .SRCINFO from artifacts" >> $GITHUB_STEP_SUMMARY
echo "makepkg --printsrcinfo > .SRCINFO" >> $GITHUB_STEP_SUMMARY
echo "git add PKGBUILD .SRCINFO" >> $GITHUB_STEP_SUMMARY
echo "git commit -m 'Update to version ${{ needs.calculate-version.outputs.version }}'" >> $GITHUB_STEP_SUMMARY
echo "git push" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
fi
update-homebrew:
needs: [calculate-version, release]
if: always() && needs.calculate-version.outputs.is-prerelease == 'false' && needs.release.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 10
continue-on-error: false # Don't fail the entire release if Homebrew update fails
steps:
- name: Download macOS artifacts
uses: actions/download-artifact@v6.0.0
with:
pattern: ${{ env.PROJECT_NAME }}-osx-*
path: ./macos-artifacts
- name: Calculate checksums
id: checksums
run: |
# Calculate SHA256 for macOS x64
MACOS_X64_SHA=$(sha256sum ./macos-artifacts/${{ env.PROJECT_NAME }}-osx-x64/${{ env.PROJECT_NAME }}-${{ needs.calculate-version.outputs.version }}-osx-x64.tar.gz | cut -d' ' -f1)
echo "macos_x64_sha=${MACOS_X64_SHA}" >> $GITHUB_OUTPUT
# Calculate SHA256 for macOS ARM64
MACOS_ARM64_SHA=$(sha256sum ./macos-artifacts/${{ env.PROJECT_NAME }}-osx-arm64/${{ env.PROJECT_NAME }}-${{ needs.calculate-version.outputs.version }}-osx-arm64.tar.gz | cut -d' ' -f1)
echo "macos_arm64_sha=${MACOS_ARM64_SHA}" >> $GITHUB_OUTPUT
echo "macOS x64 SHA256: ${MACOS_X64_SHA}"
echo "macOS ARM64 SHA256: ${MACOS_ARM64_SHA}"
- name: Update Homebrew tap
if: needs.calculate-version.outputs.is-prerelease == 'false'
env:
TAP_TOKEN: ${{ secrets.HOMEBREW_UPDATE_TOKEN }}
run: |
VERSION=${{ needs.calculate-version.outputs.version }}
X64_SHA=${{ steps.checksums.outputs.macos_x64_sha }}
ARM64_SHA=${{ steps.checksums.outputs.macos_arm64_sha }}
git clone https://x-access-token:${TAP_TOKEN}@github.com/metaneutrons/homebrew-tap.git
cd homebrew-tap
mkdir -p Formula
cat > Formula/knxmonitor.rb << EOF
class Knxmonitor < Formula
desc "KNX/EIB bus monitoring and debugging tool"
homepage "https://github.com/metaneutrons/KnxMonitor"
version "${VERSION}"
license "GPL-3.0"
on_macos do
if Hardware::CPU.intel?
url "https://github.com/metaneutrons/KnxMonitor/releases/download/v${VERSION}/KnxMonitor-${VERSION}-osx-x64.tar.gz"
sha256 "${X64_SHA}"
else
url "https://github.com/metaneutrons/KnxMonitor/releases/download/v${VERSION}/KnxMonitor-${VERSION}-osx-arm64.tar.gz"
sha256 "${ARM64_SHA}"
end
end
def install
bin.install "KnxMonitor" => "knxmonitor"
man1.install "docs/knxmonitor.1" if File.exist?("docs/knxmonitor.1")
end
test do
assert_match version.to_s, shell_output("#{bin}/knxmonitor --version")
end
end
EOF
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/knxmonitor.rb
git commit -m "Update knxmonitor to ${VERSION}"
git push
- name: Create Homebrew update summary
if: needs.calculate-version.outputs.is-prerelease == 'false'
run: |
echo "## 🍺 Homebrew Update Information" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version**: ${{ needs.calculate-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**macOS x64 SHA256**: \`${{ steps.checksums.outputs.macos_x64_sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "**macOS ARM64 SHA256**: \`${{ steps.checksums.outputs.macos_arm64_sha }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ Homebrew formula updated automatically at Formula/knxmonitor.rb" >> $GITHUB_STEP_SUMMARY
- name: Release Summary
run: |
echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version**: ${{ needs.calculate-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Tag**: ${{ needs.calculate-version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "**GitVersion**: ${{ needs.calculate-version.outputs.informational-version }}" >> $GITHUB_STEP_SUMMARY
echo "**Prerelease**: ${{ needs.calculate-version.outputs.is-prerelease }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Assets" >> $GITHUB_STEP_SUMMARY
echo "- $(ls -1 ./release-assets/ | wc -l) platform binaries" >> $GITHUB_STEP_SUMMARY
if [[ "${{ inputs.create_docker }}" == "true" ]]; then
echo "- Docker image: \`ghcr.io/metaneutrons/knxmonitor:${{ needs.calculate-version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.calculate-version.outputs.is-prerelease }}" == "false" ]]; then
echo "- Homebrew formula will be updated automatically" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🔗 Links" >> $GITHUB_STEP_SUMMARY
echo "- [Release Page](https://github.com/${{ github.repository }}/releases/tag/${{ needs.calculate-version.outputs.tag }})" >> $GITHUB_STEP_SUMMARY
echo "- [Docker Image](https://github.com/${{ github.repository }}/pkgs/container/knxmonitor)" >> $GITHUB_STEP_SUMMARY
echo "- [Homebrew Tap](https://github.com/metaneutrons/homebrew-tap)" >> $GITHUB_STEP_SUMMARY
# Summary job to show overall status
summary:
needs: [calculate-version, build, docker, release, package-linux, update-homebrew, publish-aur]
if: always()
runs-on: ubuntu-latest
steps:
- name: Release Summary
run: |
echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version**: ${{ needs.calculate-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Tag**: ${{ needs.calculate-version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "**GitVersion**: ${{ needs.calculate-version.outputs.informational-version }}" >> $GITHUB_STEP_SUMMARY
echo "**Prerelease**: ${{ needs.calculate-version.outputs.is-prerelease }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📊 Job Status" >> $GITHUB_STEP_SUMMARY
echo "- **Version Calculation**: ${{ needs.calculate-version.result == 'success' && '✅' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Build**: ${{ needs.build.result == 'success' && '✅' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Linux Packaging**: ${{ needs.package-linux.result == 'success' && '✅' || needs.package-linux.result == 'skipped' && '⏭️' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Docker**: ${{ needs.docker.result == 'success' && '✅' || needs.docker.result == 'skipped' && '⏭️' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Release**: ${{ needs.release.result == 'success' && '✅' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Homebrew**: ${{ needs.update-homebrew.result == 'success' && '✅' || needs.update-homebrew.result == 'skipped' && '⏭️' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "- **AUR**: ${{ needs.publish-aur.result == 'success' && '✅' || needs.publish-aur.result == 'skipped' && '⏭️' || '❌' }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Assets" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.build.result }}" == "success" ]]; then
echo "- ✅ Platform binaries created" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.package-linux.result }}" == "success" ]]; then
echo "- ✅ Linux packages (.deb, AUR) created" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.package-linux.result }}" == "failure" ]]; then
echo "- ❌ Linux packaging failed" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.docker.result }}" == "success" ]]; then
echo "- ✅ Docker image: \`ghcr.io/metaneutrons/knxmonitor:${{ needs.calculate-version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.docker.result }}" == "failure" ]]; then
echo "- ❌ Docker image build failed" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.update-homebrew.result }}" == "success" ]]; then
echo "- ✅ Homebrew formula updated automatically" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.update-homebrew.result }}" == "failure" ]]; then
echo "- ❌ Homebrew formula update failed (check artifacts for manual instructions)" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.publish-aur.result }}" == "success" ]]; then
echo "- ✅ AUR package published automatically" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.publish-aur.result }}" == "failure" ]]; then
echo "- ❌ AUR package publishing failed (check artifacts for manual instructions)" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🔗 Links" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.release.result }}" == "success" ]]; then
echo "- [Release Page](https://github.com/${{ github.repository }}/releases/tag/${{ needs.calculate-version.outputs.tag }})" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.docker.result }}" == "success" ]]; then
echo "- [Docker Image](https://github.com/${{ github.repository }}/pkgs/container/knxmonitor)" >> $GITHUB_STEP_SUMMARY
fi
echo "- [Homebrew Tap](https://github.com/metaneutrons/homebrew-tap)" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.publish-aur.result }}" == "success" ]]; then
echo "- [AUR Package](https://aur.archlinux.org/packages/knxmonitor)" >> $GITHUB_STEP_SUMMARY
fi
# Set overall workflow status
if [[ "${{ needs.build.result }}" != "success" || "${{ needs.release.result }}" != "success" ]]; then
echo "❌ Release failed - critical components failed" >> $GITHUB_STEP_SUMMARY
exit 1
elif [[ "${{ needs.docker.result }}" == "failure" && "${{ inputs.create_docker }}" == "true" ]]; then
echo "⚠️ Release completed with warnings - Docker build failed" >> $GITHUB_STEP_SUMMARY
exit 1
else
echo "✅ Release completed successfully" >> $GITHUB_STEP_SUMMARY
fi