Skip to content

Create Draft Release with Auto-Generated Notes #5

Create Draft Release with Auto-Generated Notes

Create Draft Release with Auto-Generated Notes #5

name: Create Draft Release with Auto-Generated Notes
on:
workflow_dispatch:
inputs:
version_type:
description: "Select the version type to increment (major, minor, patch)"
required: true
type: choice
options:
- patch
- minor
- major
release_title:
description: "Enter the title of the release"
required: true
type: string
acknowledge_draft:
description: "I understand that I must re-edit and finalize the draft release (Y/N)"
required: true
type: choice
options:
- "No"
- "Yes"
jobs:
validate-input:
runs-on: ubuntu-latest
steps:
- name: Validate Acknowledgement
if: ${{ github.event.inputs.acknowledge_draft != 'Yes' }}
run: |
echo "You must select 'Yes' to acknowledge your responsibility for finalizing the draft release."
exit 1
- name: Validate title (no empty)
if: ${{ github.event.inputs.release_title == '' }}
run: |
echo "You must enter a title for the release."
exit 1
create-draft-release:
runs-on: ubuntu-latest
needs: validate-input
steps:
- uses: actions/checkout@v4
- name: Fetch Latest Release
id: get-latest-release
uses: actions/github-script@v7
with:
script: |
const latestRelease = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
}).catch(() => null);
if (latestRelease) {
core.setOutput('latest_tag', latestRelease.data.tag_name);
} else {
core.setOutput('latest_tag', 'v0.0.0'); // Default for first release
}
- name: Calculate New Version
id: calculate-version
uses: actions/github-script@v7
with:
script: |
const latestTag = '${{ steps.get-latest-release.outputs.latest_tag }}';
const versionType = '${{ github.event.inputs.version_type }}';
const [major, minor, patch] = latestTag.replace('v', '').split('.').map(Number);
let newVersion;
if (versionType === 'major') {
newVersion = `v${major + 1}.0.0`;
} else if (versionType === 'minor') {
newVersion = `v${major}.${minor + 1}.0`;
} else {
newVersion = `v${major}.${minor}.${patch + 1}`;
}
core.setOutput('new_version', newVersion);
- name: Generate Release Notes
id: generate-release-notes
uses: actions/github-script@v7
with:
script: |
const { data: releaseNotes } = await github.rest.repos.generateReleaseNotes({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: "${{ steps.calculate-version.outputs.new_version }}"
});
const actor = context.actor;
const noteToAdd = `**@${actor} 👈 TODO: Write detailed release note for this version before release**\n`;
const footer = `---\nThis release is prepared by @${actor}`;
const modifiedBody = releaseNotes.body.replace(
'## What\'s Changed',
`## What's Changed\n\n${noteToAdd}`
)
.concat(`\n\n${footer}`);
console.log(`releaseNotes (modified): ${JSON.stringify(modifiedBody, null, 2)}`);
core.setOutput("release_body", modifiedBody);
- name: Prepare Release Title
id: title
env:
# "vX.Y.Z Release Title"
RAW_TITLE: ${{ steps.calculate-version.outputs.new_version }} ${{ github.event.inputs.release_title }}
run: |
# Print RAW_TITLE safely, then escape double quotes
SANITIZED_TITLE="$(printf '%s' "$RAW_TITLE" | sed 's/"/\\"/g')"
echo "sanitized_title=$SANITIZED_TITLE" >> "$GITHUB_OUTPUT"
- name: Write Release Notes to File
run: |
echo "${{ steps.generate-release-notes.outputs.release_body }}" > release-notes.txt
- name: Create Draft Release
run: |
gh release create "${{ steps.calculate-version.outputs.new_version }}" \
--title "${{ steps.title.outputs.sanitized_title }}" \
--notes-file release-notes.txt \
--draft \
--repo "${{ github.repository }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}