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.0.1 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.0.1 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 versions PREV_VERSION="${PREV_TAG#v}" IFS='.' read -r PREV_MAJOR PREV_MINOR PREV_PATCH <<< "$PREV_VERSION" IFS='.' read -r MAJOR MINOR PATCH <<< "$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) { echo "warning<\`" echo "3. Push the corrected tag: \`git push origin v\`" echo "" echo "**This tag will be automatically deleted in the next step.**" echo "EOF" } >> $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 { echo 'notes<> $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: false 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