From a36552d11efb6fb68d29a628cc904b5e95d59955 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 05:14:28 +0000 Subject: [PATCH] Add changelog validation workflow and update CHANGELOG with current changes Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com> --- .github/workflows/changelog-validation.yml | 194 +++++++++++++++++++++ CHANGELOG.md | 12 ++ 2 files changed, 206 insertions(+) create mode 100644 .github/workflows/changelog-validation.yml diff --git a/.github/workflows/changelog-validation.yml b/.github/workflows/changelog-validation.yml new file mode 100644 index 0000000..e47b4f6 --- /dev/null +++ b/.github/workflows/changelog-validation.yml @@ -0,0 +1,194 @@ +# Copyright (C) 2026 Moko Consulting +# +# This file is part of a Moko Consulting project. +# +# SPDX-License-Identifier: GPL-3.0-or-later + +name: Changelog Validation + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + branches: + - main + - 'dev/**' + - 'rc/**' + - 'version/**' + +permissions: + contents: read + pull-requests: write + +jobs: + validate-changelog-entry: + name: Validate Changelog Entry + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check for changelog entry in PR description + id: check_changelog + uses: actions/github-script@v7 + with: + script: | + const prBody = context.payload.pull_request.body || ''; + const prNumber = context.payload.pull_request.number; + const prAuthor = context.payload.pull_request.user.login; + + // Skip for automated PRs (e.g., Dependabot) + const automatedAuthors = ['dependabot[bot]', 'github-actions[bot]']; + if (automatedAuthors.includes(prAuthor)) { + console.log('Skipping changelog check for automated PR'); + core.setOutput('has_entry', 'true'); + core.setOutput('skip', 'true'); + return; + } + + // Check if PR body contains changelog entry section + const changelogSection = prBody.match(/## Changelog Entry[\s\S]*?```markdown([\s\S]*?)```/); + + if (!changelogSection) { + console.log('No changelog entry section found in PR template'); + core.setOutput('has_entry', 'false'); + core.setOutput('skip', 'false'); + return; + } + + const changelogContent = changelogSection[1].trim(); + + // Check if changelog entry is not just the template placeholder + const isPlaceholder = changelogContent.includes('[Category]') || + changelogContent.includes('Your changelog entry here') || + changelogContent.length < 20; + + if (isPlaceholder) { + console.log('Changelog entry appears to be placeholder text'); + core.setOutput('has_entry', 'false'); + core.setOutput('skip', 'false'); + return; + } + + // Validate that it contains a category + const validCategories = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']; + const hasValidCategory = validCategories.some(cat => changelogContent.includes(`### ${cat}`)); + + if (!hasValidCategory) { + console.log('Changelog entry does not contain a valid category'); + core.setOutput('has_entry', 'false'); + core.setOutput('skip', 'false'); + core.setOutput('reason', 'missing_category'); + return; + } + + console.log('Valid changelog entry found'); + core.setOutput('has_entry', 'true'); + core.setOutput('skip', 'false'); + core.setOutput('entry', changelogContent); + + - name: Comment on PR if changelog entry is missing + if: steps.check_changelog.outputs.has_entry == 'false' && steps.check_changelog.outputs.skip == 'false' + uses: actions/github-script@v7 + with: + script: | + const reason = '${{ steps.check_changelog.outputs.reason }}'; + let message = '## ⚠️ Changelog Entry Required\n\n'; + + if (reason === 'missing_category') { + message += 'Your PR includes a changelog entry, but it does not contain a valid category.\n\n'; + } else { + message += 'This PR is missing a changelog entry or contains only placeholder text.\n\n'; + } + + message += 'Please add a changelog entry in the "Changelog Entry" section of the PR description.\n\n'; + message += '### Valid Categories\n'; + message += '- **Added** - New features or files\n'; + message += '- **Changed** - Modifications to existing functionality\n'; + message += '- **Deprecated** - Features marked for future removal\n'; + message += '- **Removed** - Deleted features or files\n'; + message += '- **Fixed** - Bug fixes\n'; + message += '- **Security** - Security-related changes\n\n'; + message += '### Example\n'; + message += '```markdown\n'; + message += '### Added\n'; + message += '- New feature description (#' + context.payload.pull_request.number + ')\n\n'; + message += '### Changed\n'; + message += '- Modified behavior description (#' + context.payload.pull_request.number + ')\n'; + message += '```\n\n'; + message += 'See [CHANGELOG_PROCESS.md](https://github.com/mokoconsulting-tech/moko-cassiopeia/blob/main/docs/CHANGELOG_PROCESS.md) for detailed guidelines.'; + + // Check if we already commented + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number + }); + + const botComment = comments.data.find(comment => + comment.user.login === 'github-actions[bot]' && + comment.body.includes('Changelog Entry Required') + ); + + if (botComment) { + // Update existing comment + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: message + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: message + }); + } + + - name: Add label if changelog is missing + if: steps.check_changelog.outputs.has_entry == 'false' && steps.check_changelog.outputs.skip == 'false' + uses: actions/github-script@v7 + continue-on-error: true + with: + script: | + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['needs-changelog'] + }); + + - name: Remove label if changelog is present + if: steps.check_changelog.outputs.has_entry == 'true' && steps.check_changelog.outputs.skip == 'false' + uses: actions/github-script@v7 + continue-on-error: true + with: + script: | + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + name: 'needs-changelog' + }); + } catch (error) { + // Label might not exist, that's okay + console.log('Label does not exist or already removed'); + } + + - name: Set status + if: steps.check_changelog.outputs.skip == 'false' + run: | + if [ "${{ steps.check_changelog.outputs.has_entry }}" == "true" ]; then + echo "✅ Changelog entry found and validated" + exit 0 + else + echo "❌ Changelog entry is missing or invalid" + echo "Please add a changelog entry to your PR description" + exit 1 + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 49e9063..52b2add 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,18 @@ # Changelog — Moko-Cassiopeia (VERSION: 03.06.00) +## [Unreleased] +### Added +- PR-based changelog process with comprehensive documentation (#66) + - Created CHANGELOG_PROCESS.md guide with detailed workflow + - Added changelog entry section to PR template + - Integrated changelog guidance into CONTRIBUTING.md and WORKFLOW_GUIDE.md + +### Changed +- Updated roadmap documentation based on current open pull requests (#66) +- Added document generation system as planned feature (#66) +- Synchronized roadmap version timeline with active development branches (#66) + ## [03.06.00] 2026-01-28 ### Changed - Updated version to 03.06.00 across all files