From 4c9658fff5c6cce5171edc1dbd8911ef83bf3d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Wed, 25 Jun 2025 09:04:35 +0200 Subject: [PATCH 1/3] add changelog validation workflow for release PRs --- .github/workflows/validate-changelog.yaml | 189 ++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 .github/workflows/validate-changelog.yaml diff --git a/.github/workflows/validate-changelog.yaml b/.github/workflows/validate-changelog.yaml new file mode 100644 index 0000000..6351117 --- /dev/null +++ b/.github/workflows/validate-changelog.yaml @@ -0,0 +1,189 @@ +name: Validate Release Changelog + +on: + workflow_call: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'CHANGELOG.md' + +permissions: + contents: read + pull-requests: write + +jobs: + validate-changelog: + name: Validate Changelog Content + runs-on: ubuntu-24.04 + # Only run this check on release PRs matching your specific patterns + if: | + contains(github.head_ref, '#release#v') || + contains(github.head_ref, '#release#major') || + contains(github.head_ref, '#release#minor') || + contains(github.head_ref, '#release#patch') + + 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 + }); From 0e586000e5ccd72edd6015ecbc842d40200e42b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Wed, 25 Jun 2025 09:15:23 +0200 Subject: [PATCH 2/3] extract branch filtering --- .github/workflows/validate-changelog.yaml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/validate-changelog.yaml b/.github/workflows/validate-changelog.yaml index 6351117..06b09a3 100644 --- a/.github/workflows/validate-changelog.yaml +++ b/.github/workflows/validate-changelog.yaml @@ -6,6 +6,23 @@ on: 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 @@ -15,12 +32,6 @@ jobs: validate-changelog: name: Validate Changelog Content runs-on: ubuntu-24.04 - # Only run this check on release PRs matching your specific patterns - if: | - contains(github.head_ref, '#release#v') || - contains(github.head_ref, '#release#major') || - contains(github.head_ref, '#release#minor') || - contains(github.head_ref, '#release#patch') steps: - name: Checkout code From 9b6837dceedebc90ba74a0f1dae56f1b7bbabaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nick=20J=C3=BCttner?= Date: Thu, 26 Jun 2025 11:11:15 +0200 Subject: [PATCH 3/3] addressing comments --- .github/workflows/validate-changelog.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-changelog.yaml b/.github/workflows/validate-changelog.yaml index 06b09a3..fbe0d9a 100644 --- a/.github/workflows/validate-changelog.yaml +++ b/.github/workflows/validate-changelog.yaml @@ -1,4 +1,4 @@ -name: Validate Release Changelog +name: Validate changelog on: workflow_call: @@ -30,7 +30,7 @@ permissions: jobs: validate-changelog: - name: Validate Changelog Content + name: Validate changelog structure runs-on: ubuntu-24.04 steps: