From 3eb706c49a9274d3fd300c16b93f43cfcae56eef Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 16 Aug 2025 20:35:41 -0500 Subject: [PATCH] feat: enhance manual release workflow with automatic release notes - Add automatic release notes generation from commit history - Categorize commits into Features, Bug Fixes, and Maintenance - Include installation instructions and changelog links - Add preview-release-notes script for testing - Update GitHub release creation to use generated notes --- .github/workflows/manual-release.yaml | 72 +++++++++++++++++++++++++-- package.json | 1 + tools/preview-release-notes.js | 66 ++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 4 deletions(-) create mode 100755 tools/preview-release-notes.js diff --git a/.github/workflows/manual-release.yaml b/.github/workflows/manual-release.yaml index d7b41cee..60da1ecc 100644 --- a/.github/workflows/manual-release.yaml +++ b/.github/workflows/manual-release.yaml @@ -51,9 +51,11 @@ jobs: - name: Bump version run: npm run version:${{ github.event.inputs.version_bump }} - - name: Get new version + - name: Get new version and previous tag id: version - run: echo "new_version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + run: | + echo "new_version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + echo "previous_tag=$(git describe --tags --abbrev=0)" >> $GITHUB_OUTPUT - name: Update installer package.json run: | @@ -67,6 +69,64 @@ jobs: git add . git commit -m "release: bump to v${{ steps.version.outputs.new_version }}" + - name: Generate release notes + id: release_notes + run: | + # Get commits since last tag + COMMITS=$(git log ${{ steps.version.outputs.previous_tag }}..HEAD --pretty=format:"- %s" --reverse) + + # Categorize commits + FEATURES=$(echo "$COMMITS" | grep -E "^- (feat|Feature)" || true) + FIXES=$(echo "$COMMITS" | grep -E "^- (fix|Fix)" || true) + CHORES=$(echo "$COMMITS" | grep -E "^- (chore|Chore)" || true) + OTHERS=$(echo "$COMMITS" | grep -v -E "^- (feat|Feature|fix|Fix|chore|Chore|release:|Release:)" || true) + + # Build release notes + cat > release_notes.md << 'EOF' + ## šŸš€ What's New in v${{ steps.version.outputs.new_version }} + + EOF + + if [ ! -z "$FEATURES" ]; then + echo "### ✨ New Features" >> release_notes.md + echo "$FEATURES" >> release_notes.md + echo "" >> release_notes.md + fi + + if [ ! -z "$FIXES" ]; then + echo "### šŸ› Bug Fixes" >> release_notes.md + echo "$FIXES" >> release_notes.md + echo "" >> release_notes.md + fi + + if [ ! -z "$OTHERS" ]; then + echo "### šŸ“¦ Other Changes" >> release_notes.md + echo "$OTHERS" >> release_notes.md + echo "" >> release_notes.md + fi + + if [ ! -z "$CHORES" ]; then + echo "### šŸ”§ Maintenance" >> release_notes.md + echo "$CHORES" >> release_notes.md + echo "" >> release_notes.md + fi + + cat >> release_notes.md << 'EOF' + + ## šŸ“¦ Installation + + ```bash + npx bmad-method install + ``` + + **Full Changelog**: https://github.com/bmadcode/BMAD-METHOD/compare/${{ steps.version.outputs.previous_tag }}...v${{ steps.version.outputs.new_version }} + EOF + + # Output for GitHub Actions + echo "RELEASE_NOTES<> $GITHUB_OUTPUT + cat release_notes.md >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: Create and push tag run: | git tag -a "v${{ steps.version.outputs.new_version }}" -m "Release v${{ steps.version.outputs.new_version }}" @@ -86,7 +146,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: v${{ steps.version.outputs.new_version }} - release_name: Release v${{ steps.version.outputs.new_version }} + release_name: "BMad Method v${{ steps.version.outputs.new_version }}" + body: ${{ steps.release_notes.outputs.RELEASE_NOTES }} draft: false prerelease: false @@ -95,4 +156,7 @@ jobs: echo "šŸŽ‰ Successfully released v${{ steps.version.outputs.new_version }}!" echo "šŸ“¦ Published to NPM with @stable tag" echo "šŸ·ļø Git tag: v${{ steps.version.outputs.new_version }}" - echo "āœ… Users running 'npx bmad-method@stable install' will now get version ${{ steps.version.outputs.new_version }}" + echo "āœ… Users running 'npx bmad-method install' will now get version ${{ steps.version.outputs.new_version }}" + echo "" + echo "šŸ“ Release notes preview:" + cat release_notes.md diff --git a/package.json b/package.json index ccd3563d..b0938ab5 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "lint:fix": "eslint . --ext .js,.cjs,.mjs,.yaml --fix", "list:agents": "node tools/cli.js list:agents", "prepare": "husky", + "preview:release": "node tools/preview-release-notes.js", "validate": "node tools/cli.js validate", "version:all": "node tools/bump-all-versions.js", "version:all:major": "node tools/bump-all-versions.js major", diff --git a/tools/preview-release-notes.js b/tools/preview-release-notes.js new file mode 100755 index 00000000..cedb32b5 --- /dev/null +++ b/tools/preview-release-notes.js @@ -0,0 +1,66 @@ +const { execSync } = require('node:child_process'); +const fs = require('node:fs'); + +// Get the latest stable tag (exclude beta tags) +const allTags = execSync('git tag -l | sort -V', { encoding: 'utf8' }).split('\n').filter(Boolean); +const stableTags = allTags.filter((tag) => !tag.includes('beta')); +const latestTag = stableTags.at(-1) || 'v5.0.0'; + +// Get commits since last tag +const commits = execSync(`git log ${latestTag}..HEAD --pretty=format:"- %s" --reverse`, { + encoding: 'utf8', +}) + .split('\n') + .filter(Boolean); + +// Categorize commits +const features = commits.filter((commit) => /^- (feat|Feature)/.test(commit)); +const fixes = commits.filter((commit) => /^- (fix|Fix)/.test(commit)); +const chores = commits.filter((commit) => /^- (chore|Chore)/.test(commit)); +const others = commits.filter( + (commit) => !/^- (feat|Feature|fix|Fix|chore|Chore|release:|Release:)/.test(commit), +); + +// Get next version (you can modify this logic) +const currentVersion = require('../package.json').version; +const versionParts = currentVersion.split('.').map(Number); +const nextVersion = `${versionParts[0]}.${versionParts[1] + 1}.0`; // Default to minor bump + +console.log(`## šŸš€ What's New in v${nextVersion}\n`); + +if (features.length > 0) { + console.log('### ✨ New Features'); + for (const feature of features) console.log(feature); + console.log(''); +} + +if (fixes.length > 0) { + console.log('### šŸ› Bug Fixes'); + for (const fix of fixes) console.log(fix); + console.log(''); +} + +if (others.length > 0) { + console.log('### šŸ“¦ Other Changes'); + for (const other of others) console.log(other); + console.log(''); +} + +if (chores.length > 0) { + console.log('### šŸ”§ Maintenance'); + for (const chore of chores) console.log(chore); + console.log(''); +} + +console.log('\n## šŸ“¦ Installation\n'); +console.log('```bash'); +console.log('npx bmad-method install'); +console.log('```'); + +console.log( + `\n**Full Changelog**: https://github.com/bmadcode/BMAD-METHOD/compare/${latestTag}...v${nextVersion}`, +); + +console.log(`\n---\nšŸ“Š **Summary**: ${commits.length} commits since ${latestTag}`); +console.log(`šŸ·ļø **Previous tag**: ${latestTag}`); +console.log(`šŸš€ **Next version**: v${nextVersion} (estimated)`);