feat(ci): add manifest sync and version validation to release workflow

Implemented automatic manifest.json synchronization and semantic
versioning validation in the release workflow.

Auto-Sync Manifest (sync-manifest job):
- Extracts version from Git tag (v*.*.*)
- Compares with manifest.json version
- Auto-updates manifest.json if mismatch detected
- Commits changes back to main branch
- Sets outputs (updated, version) for downstream jobs
- Prevents HACS version mismatches and conflicts with auto-tag.yml

Version Check Warning System (version_check step):
- Analyzes commits between tags for breaking changes, features, fixes
- Applies semantic versioning rules (pre-1.0 and post-1.0)
- Detects inappropriate version bumps:
  - Features with only PATCH bump → suggests MINOR
  - Breaking changes with only MINOR bump → suggests MAJOR (post-1.0)
  - Breaking changes with only PATCH bump → suggests MINOR (pre-1.0)
- Shows warnings in GitHub Step Summary (prominent)
- Appends warnings to release notes body
- Provides fix instructions but doesn't fail workflow
- Updates final summary with version check status

Workflow changes:
- release-notes job now depends on sync-manifest
- Checks out main branch to get updated manifest if synced
- Summary shows manifest sync status and version check result

Impact: Prevents manual version tag issues, maintains manifest
consistency, and warns about semantic versioning violations without
blocking releases. Fully transparent workflow with clear guidance.
This commit is contained in:
Julian Pawlowski 2025-11-09 15:33:07 +00:00
parent ddc718aabd
commit d7a145d678

View file

@ -6,17 +6,86 @@ on:
- 'v*.*.*' # Triggers on version tags like v1.0.0, v2.1.3, etc.
permissions:
contents: write # Needed to create/update releases
contents: write # Needed to create/update releases and push commits
jobs:
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@v5
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: |
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 v${{ steps.tag.outputs.version }}"
# Push to main branch
git push origin HEAD:main
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@v5
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
@ -25,6 +94,78 @@ jobs:
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<<EOF"
echo "$WARNING"
echo ""
echo "$SUGGESTION"
echo ""
echo "**Commits analyzed:** Breaking=$BREAKING, Features=$FEAT, Fixes=$FIX"
echo ""
echo "**To fix:**"
echo "1. Delete the tag: \`git tag -d v$TAG_VERSION && git push origin :refs/tags/v$TAG_VERSION\`"
echo "2. Run locally: \`./scripts/suggest-version\`"
echo "3. Create correct tag: \`./scripts/prepare-release X.Y.Z\`"
echo "EOF"
} >> $GITHUB_OUTPUT
else
echo "✓ Version bump looks appropriate for the changes"
echo "warning=" >> $GITHUB_OUTPUT
fi
- name: Install git-cliff
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
@ -44,12 +185,30 @@ jobs:
# git-cliff will handle filtering via cliff.toml
USE_AI=false ./scripts/generate-release-notes "${FROM_TAG}" "${TO_TAG}" > release-notes.md
# Append version warning if present
WARNING="${{ steps.version_check.outputs.warning }}"
if [ -n "$WARNING" ]; then
echo "" >> release-notes.md
echo "---" >> release-notes.md
echo "" >> release-notes.md
echo "$WARNING" >> release-notes.md
fi
# Output for GitHub Actions
{
echo 'notes<<EOF'
cat release-notes.md
echo EOF
} >> $GITHUB_OUTPUT - name: Create GitHub Release
} >> $GITHUB_OUTPUT
- name: Version Check Summary
if: steps.version_check.outputs.warning != ''
run: |
echo "### ⚠️ Version Mismatch Detected" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${{ steps.version_check.outputs.warning }}" >> $GITHUB_STEP_SUMMARY
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body: ${{ steps.release_notes.outputs.notes }}
@ -65,3 +224,13 @@ jobs:
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