@@ -50,18 +50,50 @@ if (fs.existsSync(readme)) {
5050 console . log ( " ✓ README.md" )
5151}
5252
53- // CHANGELOG.md — prepend new version section
53+ // CHANGELOG.md — prepend new version section with commit messages
5454const changelog = path . join ( root , "CHANGELOG.md" )
5555if ( fs . existsSync ( changelog ) ) {
5656 let text = await Bun . file ( changelog ) . text ( )
57- const entry = [
58- `## [${ version } ] - ${ date } ` ,
59- "" ,
60- "### Added" ,
61- "- See release notes for details" ,
62- "" ,
63- ] . join ( "\n" )
64- text = text . replace ( "## [Unreleased]" , `## [Unreleased]\n\n${ entry } ` )
57+
58+ // Collect commit messages since last tag for changelog content
59+ let changes : string [ ] = [ ]
60+ try {
61+ const lastTag = ( await $ `git describe --tags --abbrev=0` . cwd ( root ) . text ( ) ) . trim ( )
62+ const log = ( await $ `git log ${ lastTag } ..HEAD --oneline --no-decorate` . cwd ( root ) . text ( ) ) . trim ( )
63+ changes = log . split ( "\n" ) . filter ( Boolean ) . map ( ( line ) => line . replace ( / ^ [ a - f 0 - 9 ] + / , "" ) )
64+ } catch {
65+ changes = [ ]
66+ }
67+
68+ // Categorize commits by conventional commit prefix
69+ const added : string [ ] = [ ]
70+ const fixed : string [ ] = [ ]
71+ const changed : string [ ] = [ ]
72+
73+ for ( const msg of changes ) {
74+ if ( / ^ f e a t [: ( ] / i. test ( msg ) ) added . push ( msg . replace ( / ^ f e a t [: ( ] \s * [ ^ ) ] * \) ? : ? \s * / i, "" ) )
75+ else if ( / ^ f i x [: ( ] / i. test ( msg ) ) fixed . push ( msg . replace ( / ^ f i x [: ( ] \s * [ ^ ) ] * \) ? : ? \s * / i, "" ) )
76+ else if ( / ^ ( c h o r e | r e f a c t o r | p e r f | d o c s | s t y l e | b u i l d | c i ) [: ( ] / i. test ( msg ) )
77+ changed . push ( msg . replace ( / ^ ( c h o r e | r e f a c t o r | p e r f | d o c s | s t y l e | b u i l d | c i ) [: ( ] \s * [ ^ ) ] * \) ? : ? \s * / i, "" ) )
78+ else if ( ! / ^ r e l e a s e / i. test ( msg ) ) changed . push ( msg )
79+ }
80+
81+ const sections : string [ ] = [ `## [${ version } ] - ${ date } ` ]
82+ if ( added . length > 0 ) {
83+ sections . push ( "" , "### Added" , ...added . map ( ( m ) => `- ${ m } ` ) )
84+ }
85+ if ( fixed . length > 0 ) {
86+ sections . push ( "" , "### Fixed" , ...fixed . map ( ( m ) => `- ${ m } ` ) )
87+ }
88+ if ( changed . length > 0 ) {
89+ sections . push ( "" , "### Changed" , ...changed . map ( ( m ) => `- ${ m } ` ) )
90+ }
91+ if ( added . length === 0 && fixed . length === 0 && changed . length === 0 ) {
92+ sections . push ( "" , "### Changed" , "- Internal improvements and dependency updates" )
93+ }
94+
95+ const entry = sections . join ( "\n" )
96+ text = text . replace ( "## [Unreleased]" , `## [Unreleased]\n\n${ entry } \n` )
6597 await Bun . file ( changelog ) . write ( text )
6698 console . log ( " ✓ CHANGELOG.md" )
6799}
@@ -112,9 +144,25 @@ console.log(` ✓ ${tag} pushed`)
112144
113145// ─── Step 6: GitHub Release ──────────────────────
114146console . log ( "\n6. Creating GitHub Release..." )
147+
148+ // Collect commit messages since previous tag for release notes
149+ let releaseChanges : string [ ] = [ ]
150+ try {
151+ const prevTag = ( await $ `git describe --tags --abbrev=0 ${ tag } ^` . cwd ( root ) . text ( ) ) . trim ( )
152+ const log = ( await $ `git log ${ prevTag } ..${ tag } ^ --oneline --no-decorate` . cwd ( root ) . text ( ) ) . trim ( )
153+ releaseChanges = log . split ( "\n" ) . filter ( Boolean ) . map ( ( line ) =>
154+ `- ${ line . replace ( / ^ [ a - f 0 - 9 ] + / , "" ) } `
155+ )
156+ } catch {
157+ releaseChanges = [ "- See CHANGELOG.md for details" ]
158+ }
159+
115160const notes = [
116161 `## codeblog-app ${ tag } ` ,
117162 "" ,
163+ "### Changes" ,
164+ ...releaseChanges ,
165+ "" ,
118166 "### Install" ,
119167 "```bash" ,
120168 "curl -fsSL https://codeblog.ai/install.sh | bash" ,
0 commit comments