Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions .github/workflows/validate-changelog.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
name: Validate changelog

on:
workflow_call:
pull_request:
types: [opened, synchronize, reopened]
paths:
- 'CHANGELOG.md'
branches:
- 'legacy#release#v*.*.*'
- 'main#release#v*.*.*'
- 'main#release#major'
- 'main#release#minor'
- 'main#release#patch'
- 'master#release#v*.*.*'
- 'master#release#major'
- 'master#release#minor'
- 'master#release#patch'
- 'release#v*.*.*'
- 'release#major'
- 'release#minor'
- 'release#patch'
- 'release-v*.*.x#release#v*.*.*'
# "!" negates previous positive patterns so it has to be at the end.
- '!release-v*.x.x#release#v*.*.*'

permissions:
contents: read
pull-requests: write

jobs:
validate-changelog:
name: Validate changelog structure
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

- name: Extract version from branch name
id: extract_version
env:
BRANCH_NAME: ${{ github.head_ref }}
run: |
branch_name="$BRANCH_NAME"
echo "Branch name: $branch_name"

version=""

# Use case statement for more reliable pattern matching
case "$branch_name" in
*"#release#v"*)
# Extract version from patterns like main#release#v1.2.3
version=$(echo "$branch_name" | sed -E 's/.*#release#v([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Extracted explicit version: $version"
else
echo "::error::Failed to extract version from: $branch_name"
exit 1
fi
;;

*"#release#major"|*"#release#minor"|*"#release#patch")
# Extract bump type and get version from changelog
bump_type=$(echo "$branch_name" | sed -E 's/.*#release#(major|minor|patch).*/\1/')
echo "Detected semantic version branch ($bump_type), extracting version from changelog..."

if [ ! -f "CHANGELOG.md" ]; then
echo "::error::CHANGELOG.md not found"
exit 1
fi

# Get the first version entry (should be the newest one that architect created)
version=$(grep -E "^## \[[0-9]+\.[0-9]+\.[0-9]+\]" CHANGELOG.md | head -1 | sed -E 's/^## \[([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/')

if [ -z "$version" ]; then
echo "::error::Could not find version in CHANGELOG.md"
echo "::error::Expected to find a line like: ## [X.Y.Z] - YYYY-MM-DD"
echo "Available changelog entries:"
grep -E "^## \[" CHANGELOG.md | head -5 || echo "No version entries found"
exit 1
fi

echo "Extracted version from changelog for $bump_type release: $version"
;;

*)
echo "::error::Could not extract version from branch name: $branch_name"
echo "::error::Expected patterns:"
echo " - main#release#v1.2.3"
echo " - main#release#major"
echo " - main#release#minor"
echo " - main#release#patch"
echo " - master#release#v1.2.3"
echo " - master#release#major|minor|patch"
echo " - release#v1.2.3"
echo " - release#major|minor|patch"
exit 1
;;
esac

if [ -z "$version" ]; then
echo "::error::Version extraction failed - version is empty"
exit 1
fi

echo "Final extracted version: $version"
echo "version=$version" >> $GITHUB_OUTPUT

- name: Validate changelog content
run: |
version="${{ steps.extract_version.outputs.version }}"
changelog_file="CHANGELOG.md"

echo "Validating changelog content for version $version..."
echo "Checking file: $changelog_file"

# Check if CHANGELOG.md exists
if [ ! -f "$changelog_file" ]; then
echo "::error::No $changelog_file found in repository"
exit 1
fi

# Extract the content between the new version and the next version
changelog_section=$(awk "
/^## \[$version\]/ { found=1; print; next }
found && /^## \[/ && !/^## \[$version\]/ { found=0 }
found { print }
" "$changelog_file")

if [ -z "$changelog_section" ]; then
echo "::error::No section found for version $version in changelog"
echo "::error::Expected format: ## [$version] - YYYY-MM-DD"
exit 1
fi

echo "Found changelog section for version $version"

# Check if there are any ### headers (Added, Changed, Deprecated, Removed, Fixed, Security)
subsection_count=$(echo "$changelog_section" | grep -c "^### " || true)

if [ "$subsection_count" -eq 0 ]; then
echo "::error title=Empty Changelog::No changelog content found for version $version"
echo "::error::The release appears to have an empty changelog."
echo ""
echo "Please add content under appropriate sections like:"
echo " ### Added - for new features"
echo " ### Changed - for changes in existing functionality"
echo " ### Fixed - for any bug fixes"
echo " ### Removed - for now removed features"
echo " ### Deprecated - for soon-to-be removed features"
echo " ### Security - in case of vulnerabilities"
echo ""
echo "Current changelog section:"
echo "$changelog_section"
exit 1
else
echo "Changelog validation passed: Found $subsection_count section(s)"
echo "Sections found:"
echo "$changelog_section" | grep "^### " | sed 's/^/ /'
fi

- name: Add PR comment if validation fails
if: failure()
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # 7.0.1
with:
script: |
const version = '${{ steps.extract_version.outputs.version }}';
const body = `## 📝 Changelog Validation Failed

The changelog for version **${version}** appears to be empty.

Please add content under appropriate sections like:
- \`### Added\` - for new features
- \`### Changed\` - for changes in existing functionality
- \`### Fixed\` - for any bug fixes
- \`### Removed\` - for now removed features
- \`### Deprecated\` - for soon-to-be removed features
- \`### Security\` - in case of vulnerabilities

**Example:**
\`\`\`markdown
## [${version}] - ${new Date().toISOString().split('T')[0]}

### Changed

- Updated dependency X to version Y
- Improved error handling in module Z
\`\`\`

This check will pass once you add proper changelog content.`;

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});