# ============================================================================ # BlackRoad OS - Automated Release Notes # Copyright (c) 2025 BlackRoad OS, Inc. / Alexa Louise Amundson # All Rights Reserved. # ============================================================================ # # Generates release notes from merged PRs and closed issues. # ============================================================================ name: Release Notes on: workflow_dispatch: inputs: version: description: 'Version for release notes' required: true default: 'v0.0.0' since: description: 'Since date (YYYY-MM-DD) or tag' required: false permissions: contents: write jobs: generate: name: Generate Release Notes runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Generate release notes uses: actions/github-script@v7 id: notes with: script: | const version = '${{ github.event.inputs.version }}'; const since = '${{ github.event.inputs.since }}' || null; // Get merged PRs const prs = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'closed', sort: 'updated', direction: 'desc', per_page: 100 }); const mergedPRs = prs.data.filter(pr => pr.merged_at); // Categorize PRs const categories = { 'Features': [], 'Bug Fixes': [], 'Documentation': [], 'Infrastructure': [], 'Other': [] }; for (const pr of mergedPRs) { const labels = pr.labels.map(l => l.name); const title = `${pr.title} (#${pr.number}) @${pr.user.login}`; if (labels.includes('enhancement') || pr.title.startsWith('feat')) { categories['Features'].push(title); } else if (labels.includes('bug') || pr.title.startsWith('fix')) { categories['Bug Fixes'].push(title); } else if (labels.includes('documentation') || pr.title.startsWith('docs')) { categories['Documentation'].push(title); } else if (labels.includes('infrastructure') || labels.includes('ci')) { categories['Infrastructure'].push(title); } else { categories['Other'].push(title); } } // Build release notes let notes = `# Release ${version}\n\n`; notes += `Released: ${new Date().toISOString().split('T')[0]}\n\n`; for (const [category, items] of Object.entries(categories)) { if (items.length > 0) { notes += `## ${category}\n\n`; for (const item of items.slice(0, 10)) { notes += `- ${item}\n`; } notes += '\n'; } } notes += `---\n\n`; notes += `*Generated by BlackRoad OS Release Notes workflow*\n`; console.log(notes); core.setOutput('notes', notes); - name: Create release notes file run: | mkdir -p releases cat > releases/RELEASE-${{ github.event.inputs.version }}.md << 'EOF' ${{ steps.notes.outputs.notes }} EOF - name: Upload release notes uses: actions/upload-artifact@v4 with: name: release-notes-${{ github.event.inputs.version }} path: releases/RELEASE-${{ github.event.inputs.version }}.md