@@ -362,7 +362,10 @@ func (s *ReleaseService) UpdateLocalChangelog(release *models.Release, notes *mo
362362 log .Warn ("failed to move Unreleased section" , "error" , err )
363363 } else {
364364 log .Info ("moved Unreleased section to new version" , "version" , release .Version )
365- return nil
365+ if s .hasUnreleasedContent (content ) {
366+ return nil
367+ }
368+ log .Debug ("unreleased section was empty, will add new version entry" )
366369 }
367370 }
368371 }
@@ -464,11 +467,12 @@ func (s *ReleaseService) prependToChangelog(filename, newContent string) error {
464467func (s * ReleaseService ) prependToChangelogLegacy (filename , current , newContent string ) error {
465468 var sb strings.Builder
466469
467- idx := strings .Index (current , "\n ## " )
470+ versionPattern := regexp .MustCompile (`\n## \[[^]]+]` )
471+ loc := versionPattern .FindStringIndex (current )
468472
469- if idx != - 1 {
470- pre := current [:idx ]
471- post := current [idx :]
473+ if loc != nil {
474+ pre := current [:loc [ 0 ] ]
475+ post := current [loc [ 0 ] :]
472476
473477 sb .WriteString (strings .TrimSpace (pre ))
474478 sb .WriteString ("\n \n " )
@@ -490,11 +494,20 @@ func (s *ReleaseService) prependToChangelogLegacy(filename, current, newContent
490494
491495 result := sb .String ()
492496
497+ // Remove empty Unreleased sections that might be between versions
498+ result = s .removeEmptyUnreleasedSections (result )
499+
493500 result = s .consolidateLinkDefinitions (result )
494501
495502 return os .WriteFile (filename , []byte (result ), 0644 )
496503}
497504
505+ // removeEmptyUnreleasedSections removes empty ## [Unreleased] sections that appear between versions
506+ func (s * ReleaseService ) removeEmptyUnreleasedSections (content string ) string {
507+ emptyUnreleasedPattern := regexp .MustCompile (`(?s)## \[Unreleased]\s*\n(?=## \[)` )
508+ return emptyUnreleasedPattern .ReplaceAllString (content , "" )
509+ }
510+
498511// consolidateLinkDefinitions removes duplicate link reference definitions
499512func (s * ReleaseService ) consolidateLinkDefinitions (content string ) string {
500513 linkDefPattern := regexp .MustCompile (`(?m)^\[([^]]+)]:\s*(.+)$` )
@@ -560,12 +573,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
560573 return nil
561574 }
562575
563- idx := strings .Index (current , "\n ## " )
564- if idx == - 1 {
576+ versionPattern := regexp .MustCompile (`\n## \[[^]]+]` )
577+ loc := versionPattern .FindStringIndex (current )
578+
579+ if loc == nil {
565580 current = strings .TrimSpace (current ) + "\n \n ## [Unreleased]\n \n "
566581 } else {
567- pre := current [:idx ]
568- post := current [idx :]
582+ pre := current [:loc [ 0 ] ]
583+ post := current [loc [ 0 ] :]
569584 current = strings .TrimSpace (pre ) + "\n \n ## [Unreleased]\n \n " + post
570585 }
571586
@@ -584,6 +599,11 @@ func (s *ReleaseService) parseUnreleasedSection(content string) string {
584599 return strings .TrimSpace (matches [1 ])
585600}
586601
602+ // hasUnreleasedContent checks if the Unreleased section has actual content
603+ func (s * ReleaseService ) hasUnreleasedContent (content []byte ) bool {
604+ return s .parseUnreleasedSection (string (content )) != ""
605+ }
606+
587607// MoveUnreleasedToVersion moves Unreleased section content to a new version
588608func (s * ReleaseService ) MoveUnreleasedToVersion (filename string , release * models.Release , notes * models.ReleaseNotes ) error {
589609 content , err := os .ReadFile (filename )
@@ -601,11 +621,17 @@ func (s *ReleaseService) MoveUnreleasedToVersion(filename string, release *model
601621
602622 unreleasedContent := s .parseUnreleasedSection (current )
603623
624+ log := logger .FromContext (context .Background ())
625+ log .Debug ("parsed unreleased section" ,
626+ "unreleased_content" , unreleasedContent ,
627+ "unreleased_content_length" , len (unreleasedContent ),
628+ "is_empty" , unreleasedContent == "" )
629+
604630 if unreleasedContent == "" {
631+ log .Info ("unreleased section is empty, skipping migration" )
605632 return nil
606633 }
607634
608- log := logger .FromContext (context .Background ())
609635 log .Info ("moving Unreleased section to new version" ,
610636 "version" , release .Version ,
611637 "unreleased_content_length" , len (unreleasedContent ))
@@ -617,6 +643,10 @@ func (s *ReleaseService) MoveUnreleasedToVersion(filename string, release *model
617643 unreleasedPattern := regexp .MustCompile (`(?s)## \[Unreleased]\s*\n.*?(\n## \[|$)` )
618644 current = unreleasedPattern .ReplaceAllString (current , "$1" )
619645
646+ log .Debug ("writing modified changelog without unreleased section" ,
647+ "filename" , filename ,
648+ "content_length" , len (current ))
649+
620650 if err := os .WriteFile (filename , []byte (current ), 0644 ); err != nil {
621651 return domainErrors .NewAppError (
622652 domainErrors .TypeInternal ,
@@ -628,6 +658,8 @@ func (s *ReleaseService) MoveUnreleasedToVersion(filename string, release *model
628658 )
629659 }
630660
661+ log .Debug ("changelog written successfully, prepending new version" )
662+
631663 if err := s .prependToChangelog (filename , versionEntry ); err != nil {
632664 return domainErrors .NewAppError (
633665 domainErrors .TypeInternal ,
@@ -974,9 +1006,14 @@ func (s *ReleaseService) formatReleaseItem(item models.ReleaseItem) string {
9741006}
9751007
9761008func (s * ReleaseService ) CommitChangelog (ctx context.Context , version string ) error {
1009+ log := logger .FromContext (ctx )
1010+ log .Info ("starting changelog commit process" , "version" , version )
1011+
9771012 if err := s .git .AddFileToStaging (ctx , "CHANGELOG.md" ); err != nil {
1013+ log .Error ("failed to add CHANGELOG.md to staging" , "error" , err )
9781014 return domainErrors .NewAppError (domainErrors .TypeGit , "failed to add CHANGELOG.md to staging" , err )
9791015 }
1016+ log .Debug ("CHANGELOG.md added to staging" )
9801017
9811018 versionFile , _ , err := s .FindVersionFile (ctx )
9821019 if err != nil {
@@ -988,19 +1025,30 @@ func (s *ReleaseService) CommitChangelog(ctx context.Context, version string) er
9881025 }
9891026
9901027 if _ , err := os .Stat (versionFile ); err == nil {
1028+ log .Debug ("adding version file to staging" , "file" , versionFile )
9911029 if err := s .git .AddFileToStaging (ctx , versionFile ); err != nil {
1030+ log .Error ("failed to add version file to staging" , "file" , versionFile , "error" , err )
9921031 return domainErrors .NewAppError (domainErrors .TypeGit , fmt .Sprintf ("failed to add version file to staging: %s" , versionFile ), err )
9931032 }
1033+ log .Debug ("version file added to staging" , "file" , versionFile )
1034+ } else {
1035+ log .Debug ("version file not found, skipping" , "file" , versionFile )
9941036 }
9951037
1038+ log .Debug ("checking for staged changes" )
9961039 if ! s .git .HasStagedChanges (ctx ) {
1040+ log .Error ("no staged changes detected after adding files" )
9971041 return domainErrors .ErrNoChanges
9981042 }
1043+ log .Debug ("staged changes detected, proceeding with commit" )
9991044
10001045 message := fmt .Sprintf ("chore: update changelog and bump version to %s" , version )
10011046 if err := s .git .CreateCommit (ctx , message ); err != nil {
1047+ log .Error ("failed to create commit" , "error" , err )
10021048 return domainErrors .NewAppError (domainErrors .TypeGit , "failed to commit changelog and version bump" , err )
10031049 }
1050+
1051+ log .Info ("changelog commit process completed successfully" )
10041052 return nil
10051053
10061054}
0 commit comments