hass.tibber_prices/.github/workflows/release.yml
Julian Pawlowski bb176135f6 fix(ci): ensure release notes end with newline before heredoc delimiter
cliff.toml has trim=true which strips git-cliff's trailing newline.
When written to GITHUB_OUTPUT via heredoc, the closing delimiter was
appended to the last content line instead of its own line, causing
"Matching delimiter not found" error.

Added printf '\n' in the workflow and echo "" in generate-notes to
guarantee a newline before the heredoc closing delimiter.

Impact: Release workflow no longer fails when generating release notes.
2026-04-07 15:16:03 +00:00

284 lines
12 KiB
YAML

name: Generate Release Notes
on:
push:
tags:
- 'v*.*.*' # Triggers on version tags like v1.0.0, v2.1.3, etc.
workflow_dispatch:
inputs:
tag:
description: 'Tag version to release (e.g., v0.3.0)'
required: true
type: string
permissions:
contents: write # Needed to create/update releases and push commits
jobs:
# Note: We trust that validate.yml and lint.yml have already run on the
# main branch commit before the tag was created (either by prepare-release
# script or auto-tag workflow). This avoids duplicate workflow runs.
sync-manifest:
name: Sync manifest.json with tag version
runs-on: ubuntu-latest
outputs:
updated: ${{ steps.update.outputs.updated }}
version: ${{ steps.tag.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
id: tag
run: |
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
echo "version=$TAG_VERSION" >> $GITHUB_OUTPUT
echo "Tag version: v$TAG_VERSION"
- name: Check manifest.json version
id: manifest
run: |
MANIFEST_VERSION=$(grep -o '"version": "[^"]*"' custom_components/tibber_prices/manifest.json | cut -d'"' -f4)
echo "version=$MANIFEST_VERSION" >> $GITHUB_OUTPUT
echo "Manifest version: $MANIFEST_VERSION"
- name: Update manifest.json if needed
id: update
run: |
TAG_VERSION="${{ steps.tag.outputs.version }}"
MANIFEST_VERSION="${{ steps.manifest.outputs.version }}"
if [ "$TAG_VERSION" != "$MANIFEST_VERSION" ]; then
echo "::notice::Version mismatch detected - auto-syncing"
echo " Tag: v$TAG_VERSION"
echo " Manifest: $MANIFEST_VERSION"
# Update manifest.json
sed -i "s/\"version\": \".*\"/\"version\": \"$TAG_VERSION\"/" custom_components/tibber_prices/manifest.json
# Verify update
NEW_VERSION=$(grep -o '"version": "[^"]*"' custom_components/tibber_prices/manifest.json | cut -d'"' -f4)
if [ "$NEW_VERSION" = "$TAG_VERSION" ]; then
echo "✓ Successfully updated manifest.json to $TAG_VERSION"
echo "updated=true" >> $GITHUB_OUTPUT
else
echo "::error::Failed to update manifest.json"
exit 1
fi
else
echo "✓ Versions match - no update needed"
echo "updated=false" >> $GITHUB_OUTPUT
fi
- name: Commit and push manifest.json update
if: steps.update.outputs.updated == 'true'
run: |
TAG_VERSION="v${{ steps.tag.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add custom_components/tibber_prices/manifest.json
git commit -m "chore(release): sync manifest.json with tag ${TAG_VERSION}"
# Push to main branch
git push origin HEAD:main
# Delete and recreate tag on new commit
echo "::notice::Recreating tag ${TAG_VERSION} on updated commit"
git tag -d "${TAG_VERSION}"
git push origin :refs/tags/"${TAG_VERSION}"
git tag -a "${TAG_VERSION}" -m "Release ${TAG_VERSION}"
git push origin "${TAG_VERSION}"
# Delete existing release if present (will be recreated by release-notes job)
gh release delete "${TAG_VERSION}" --yes --cleanup-tag=false || echo "No existing release to delete"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-notes:
name: Generate and publish release notes
runs-on: ubuntu-latest
needs: sync-manifest # Wait for manifest sync to complete
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0 # Fetch all history for git-cliff
ref: main # Use updated main branch if manifest was synced
- name: Get previous tag
id: previoustag
run: |
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
echo "previous_tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT
echo "Previous tag: ${PREVIOUS_TAG}"
- name: Check version appropriateness
id: version_check
run: |
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
PREV_TAG="${{ steps.previoustag.outputs.previous_tag }}"
if [ -z "$PREV_TAG" ]; then
echo "warning=" >> $GITHUB_OUTPUT
exit 0
fi
# Analyze commits between tags
COMMITS=$(git log ${PREV_TAG}..HEAD --format="%s" --no-merges)
BREAKING=$(echo "$COMMITS" | grep -cE "^[^:]+!:|^[^:]+: .+BREAKING CHANGE" || true)
FEAT=$(echo "$COMMITS" | grep -cE "^feat(\(.+\))?:" || true)
FIX=$(echo "$COMMITS" | grep -cE "^fix(\(.+\))?:" || true)
parse_version() {
local version="$1"
if [[ $version =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(b[0-9]+)?$ ]]; then
echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} ${BASH_REMATCH[4]}"
else
echo "Invalid version format: $version" >&2
exit 1
fi
}
# Parse versions (support beta/prerelease suffix like 0.25.0b0)
PREV_VERSION="${PREV_TAG#v}"
read -r PREV_MAJOR PREV_MINOR PREV_PATCH PREV_PRERELEASE <<< "$(parse_version "$PREV_VERSION")"
read -r MAJOR MINOR PATCH PRERELEASE <<< "$(parse_version "$TAG_VERSION")"
WARNING=""
SUGGESTION=""
# Pre-1.0 version rules (0.x.y)
if [ "$MAJOR" -eq 0 ]; then
if [ $BREAKING -gt 0 ] && [ "$MINOR" = "$PREV_MINOR" ]; then
WARNING="⚠️ **Version Warning**: $BREAKING breaking change(s) detected, but only PATCH version bumped."
SUGGESTION="In pre-1.0 (0.x.y), breaking changes should bump MINOR version. Consider **v0.$((MINOR + 1)).0** instead of v$TAG_VERSION"
elif [ $FEAT -gt 0 ] && [ "$MINOR" = "$PREV_MINOR" ] && [ "$PATCH" != "$PREV_PATCH" ]; then
WARNING="⚠️ **Version Warning**: $FEAT new feature(s) detected, but only PATCH version bumped."
SUGGESTION="New features should bump MINOR version. Consider **v0.$((MINOR + 1)).0** instead of v$TAG_VERSION"
fi
else
# Post-1.0 version rules (x.y.z)
if [ $BREAKING -gt 0 ] && [ "$MAJOR" = "$PREV_MAJOR" ]; then
WARNING="⚠️ **Version Warning**: $BREAKING breaking change(s) detected, but MAJOR version not bumped."
SUGGESTION="Breaking changes require MAJOR version bump. Consider **v$((MAJOR + 1)).0.0** instead of v$TAG_VERSION"
elif [ $FEAT -gt 0 ] && [ "$MINOR" = "$PREV_MINOR" ] && [ "$PATCH" != "$PREV_PATCH" ]; then
WARNING="⚠️ **Version Warning**: $FEAT new feature(s) detected, but only PATCH version bumped."
SUGGESTION="New features should bump MINOR version. Consider **v$MAJOR.$((MINOR + 1)).0** instead of v$TAG_VERSION"
fi
fi
# Output warning message
if [ -n "$WARNING" ]; then
echo "$WARNING"
echo "$SUGGESTION"
echo ""
echo "Commits analyzed: Breaking=$BREAKING, Features=$FEAT, Fixes=$FIX"
# Set output for later steps (using heredoc for multi-line)
# Use random delimiter to avoid collision if content contains 'EOF'
WARN_DELIM=$(openssl rand -hex 16)
{
echo "warning<<${WARN_DELIM}"
echo "$WARNING"
echo ""
echo "$SUGGESTION"
echo ""
echo "**Commits analyzed:** Breaking=$BREAKING, Features=$FEAT, Fixes=$FIX"
echo ""
echo "**To fix:**"
echo "1. Run locally: \`./scripts/release/suggest-version\`"
echo "2. Create correct tag: \`./scripts/release/prepare <suggested-version>\`"
echo "3. Push the corrected tag: \`git push origin v<suggested-version>\`"
echo ""
echo "**This tag will be automatically deleted in the next step.**"
echo "${WARN_DELIM}"
} >> $GITHUB_OUTPUT
else
echo "✓ Version bump looks appropriate for the changes"
echo "warning=" >> $GITHUB_OUTPUT
fi
- name: Delete inappropriate version tag
if: steps.version_check.outputs.warning != ''
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
echo "❌ Deleting tag $TAG_NAME (version not appropriate for changes)"
echo ""
echo "${{ steps.version_check.outputs.warning }}"
echo ""
git push origin --delete "$TAG_NAME"
exit 1
- name: Install git-cliff
if: steps.version_check.outputs.warning == ''
run: |
wget https://github.com/orhun/git-cliff/releases/download/v2.4.0/git-cliff-2.4.0-x86_64-unknown-linux-gnu.tar.gz
tar -xzf git-cliff-2.4.0-x86_64-unknown-linux-gnu.tar.gz
sudo mv git-cliff-2.4.0/git-cliff /usr/local/bin/
git-cliff --version
- name: Generate release notes
if: steps.version_check.outputs.warning == ''
id: release_notes
run: |
FROM_TAG="${{ steps.previoustag.outputs.previous_tag }}"
TO_TAG="${GITHUB_REF#refs/tags/}"
echo "Generating release notes from ${FROM_TAG} to ${TO_TAG}"
# Use our script with git-cliff backend (AI disabled in CI)
# git-cliff will handle filtering via cliff.toml
USE_AI=false ./scripts/release/generate-notes "${FROM_TAG}" "${TO_TAG}" > release-notes.md
# Extract title from release notes (first line starting with "# ")
TITLE=$(head -n 1 release-notes.md | sed 's/^# //')
if [ -z "$TITLE" ]; then
TITLE="Release Updates"
fi
echo "title=$TITLE" >> $GITHUB_OUTPUT
# Output for GitHub Actions
# Use random delimiter to avoid collision if release notes contain 'EOF'
NOTES_DELIM=$(openssl rand -hex 16)
{
echo "notes<<${NOTES_DELIM}"
cat release-notes.md
printf '\n' # Ensure content ends with newline (git-cliff trim=true removes it)
echo "${NOTES_DELIM}"
} >> $GITHUB_OUTPUT
- name: Create GitHub Release
if: steps.version_check.outputs.warning == ''
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.release_notes.outputs.title }}
body: ${{ steps.release_notes.outputs.notes }}
draft: false
prerelease: ${{ contains(github.ref, 'b') }}
generate_release_notes: false # We provide our own
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Summary
if: steps.version_check.outputs.warning == ''
run: |
echo "✅ Release notes generated and published!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Tag:** ${GITHUB_REF#refs/tags/}" >> $GITHUB_STEP_SUMMARY
echo "**Previous tag:** ${{ steps.previoustag.outputs.previous_tag }}" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.sync-manifest.outputs.updated }}" == "true" ]]; then
echo "**Manifest sync:** Updated manifest.json to version ${{ needs.sync-manifest.outputs.version }}" >> $GITHUB_STEP_SUMMARY
else
echo "**Manifest sync:** No update needed (version already matches)" >> $GITHUB_STEP_SUMMARY
fi
if [ -n "${{ steps.version_check.outputs.warning }}" ]; then
echo "**Version check:** ⚠️ Warning detected (see release notes)" >> $GITHUB_STEP_SUMMARY
else
echo "**Version check:** ✓ Version appropriate for changes" >> $GITHUB_STEP_SUMMARY
fi