Skip to content

Commit c026db5

Browse files
committed
Add workflow to create draft release with auto-generated notes
1 parent a5b4ab8 commit c026db5

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
name: Create Draft Release with Auto-Generated Notes
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version_type:
7+
description: "Select the version type to increment (major, minor, patch)"
8+
required: true
9+
type: choice
10+
options:
11+
- patch
12+
- minor
13+
- major
14+
release_title:
15+
description: "Enter the title of the release"
16+
required: true
17+
type: string
18+
acknowledge_draft:
19+
description: "I understand that I must re-edit and finalize the draft release (Y/N)"
20+
required: true
21+
type: choice
22+
options:
23+
- "No"
24+
- "Yes"
25+
26+
jobs:
27+
validate-input:
28+
runs-on: ubuntu-latest
29+
permissions: {}
30+
steps:
31+
- name: Validate Acknowledgement
32+
if: ${{ github.event.inputs.acknowledge_draft != 'Yes' }}
33+
run: |
34+
echo "You must select 'Yes' to acknowledge your responsibility for finalizing the draft release."
35+
exit 1
36+
- name: Validate title (no empty)
37+
if: ${{ github.event.inputs.release_title == '' }}
38+
run: |
39+
echo "You must enter a title for the release."
40+
exit 1
41+
42+
create-draft-release:
43+
runs-on: ubuntu-latest
44+
needs: validate-input
45+
permissions:
46+
contents: write
47+
steps:
48+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
49+
- name: Fetch Latest Release
50+
id: get-latest-release
51+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
52+
with:
53+
script: |
54+
const latestRelease = await github.rest.repos.getLatestRelease({
55+
owner: context.repo.owner,
56+
repo: context.repo.repo,
57+
}).catch(() => null);
58+
59+
if (latestRelease) {
60+
core.setOutput('latest_tag', latestRelease.data.tag_name);
61+
} else {
62+
core.setOutput('latest_tag', 'v0.0.0'); // Default for first release
63+
}
64+
65+
- name: Calculate New Version
66+
id: calculate-version
67+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
68+
with:
69+
script: |
70+
const latestTag = '${{ steps.get-latest-release.outputs.latest_tag }}';
71+
const versionType = '${{ github.event.inputs.version_type }}';
72+
73+
const [major, minor, patch] = latestTag.replace('v', '').split('.').map(Number);
74+
75+
let newVersion;
76+
if (versionType === 'major') {
77+
newVersion = `v${major + 1}.0.0`;
78+
} else if (versionType === 'minor') {
79+
newVersion = `v${major}.${minor + 1}.0`;
80+
} else {
81+
newVersion = `v${major}.${minor}.${patch + 1}`;
82+
}
83+
84+
core.setOutput('new_version', newVersion);
85+
86+
- name: Generate Release Notes
87+
id: generate-release-notes
88+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
89+
with:
90+
script: |
91+
const { data: releaseNotes } = await github.rest.repos.generateReleaseNotes({
92+
owner: context.repo.owner,
93+
repo: context.repo.repo,
94+
tag_name: "${{ steps.calculate-version.outputs.new_version }}"
95+
});
96+
97+
const actor = context.actor;
98+
const noteToAdd = `**@${actor} 👈 TODO: Write detailed release note for this version before release**\n`;
99+
100+
const footer = `---\nThis release is prepared by @${actor}`;
101+
102+
const modifiedBody = releaseNotes.body.replace(
103+
'## What\'s Changed',
104+
`## What's Changed\n\n${noteToAdd}`
105+
)
106+
.concat(`\n\n${footer}`);
107+
108+
console.log(`releaseNotes (modified): ${JSON.stringify(modifiedBody, null, 2)}`);
109+
core.setOutput("release_body", modifiedBody);
110+
111+
- name: Prepare Release Title
112+
id: title
113+
env:
114+
# "vX.Y.Z Release Title"
115+
RAW_TITLE: ${{ steps.calculate-version.outputs.new_version }} ${{ github.event.inputs.release_title }}
116+
run: |
117+
# Print RAW_TITLE safely, then escape double quotes
118+
SANITIZED_TITLE="$(printf '%s' "$RAW_TITLE" | sed 's/"/\\"/g')"
119+
echo "sanitized_title=$SANITIZED_TITLE" >> "$GITHUB_OUTPUT"
120+
121+
- name: Write Release Notes to File
122+
run: |
123+
echo "${{ steps.generate-release-notes.outputs.release_body }}" > release-notes.txt
124+
125+
- name: Create Draft Release
126+
run: |
127+
gh release create "${{ steps.calculate-version.outputs.new_version }}" \
128+
--title "${{ steps.title.outputs.sanitized_title }}" \
129+
--notes-file release-notes.txt \
130+
--draft \
131+
--repo "${{ github.repository }}"
132+
env:
133+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)