Implement PR-based changelog automation and sync roadmap with active development #66
194
.github/workflows/changelog-validation.yml
vendored
Normal file
194
.github/workflows/changelog-validation.yml
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# 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
|
||||
12
CHANGELOG.md
12
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
|
||||
|
||||
Reference in New Issue
Block a user