mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-04-09 09:03:40 +00:00
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.
284 lines
12 KiB
YAML
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
|