From f0eed8cc563655b67382ab5c887affc86729332d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Fri, 23 Aug 2024 22:17:38 +0200 Subject: [PATCH] refactor: move changelog file to updater interface (#15) --- changelog.go | 62 ---------------------------------- changelog_test.go | 84 ---------------------------------------------- releaserpleaser.go | 10 +++--- updater.go | 19 +++++++++++ updater_test.go | 55 ++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 151 deletions(-) diff --git a/changelog.go b/changelog.go index 43568d5..286faf4 100644 --- a/changelog.go +++ b/changelog.go @@ -3,14 +3,8 @@ package rp import ( "bytes" _ "embed" - "fmt" "html/template" - "io" "log" - "os" - "regexp" - - "github.com/go-git/go-git/v5" ) const ( @@ -20,8 +14,6 @@ const ( var ( changelogTemplate *template.Template - - headerRegex = regexp.MustCompile(`^# Changelog\n`) ) //go:embed changelog.md.tpl @@ -35,60 +27,6 @@ func init() { } } -func UpdateChangelogFile(wt *git.Worktree, newEntry string) error { - file, err := wt.Filesystem.OpenFile(ChangelogFile, os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - return err - } - defer file.Close() - - content, err := io.ReadAll(file) - if err != nil { - return err - } - - headerIndex := headerRegex.FindIndex(content) - if headerIndex == nil && len(content) != 0 { - return fmt.Errorf("unexpected format of CHANGELOG.md, header does not match") - } - if headerIndex != nil { - // Remove the header from the content - content = content[headerIndex[1]:] - } - - err = file.Truncate(0) - if err != nil { - return err - } - _, err = file.Seek(0, io.SeekStart) - if err != nil { - return err - } - - _, err = file.Write([]byte(ChangelogHeader + "\n\n" + newEntry)) - if err != nil { - return err - } - - _, err = file.Write(content) - if err != nil { - return err - } - - // Close file to make sure it is written to disk. - err = file.Close() - if err != nil { - return err - } - - _, err = wt.Add(ChangelogFile) - if err != nil { - return err - } - - return nil -} - func NewChangelogEntry(commits []AnalyzedCommit, version, link, prefix, suffix string) (string, error) { features := make([]AnalyzedCommit, 0) fixes := make([]AnalyzedCommit, 0) diff --git a/changelog_test.go b/changelog_test.go index 3d1612b..3fabff8 100644 --- a/changelog_test.go +++ b/changelog_test.go @@ -1,99 +1,15 @@ package rp import ( - "io" "testing" - "github.com/go-git/go-git/v5" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/apricote/releaser-pleaser/internal/testutils" ) func ptr[T any](input T) *T { return &input } -func TestUpdateChangelogFile(t *testing.T) { - tests := []struct { - name string - repoFn testutils.Repo - entry string - expectedContent string - wantErr assert.ErrorAssertionFunc - }{ - { - name: "empty repo", - repoFn: testutils.WithTestRepo(), - entry: "## v1.0.0\n", - expectedContent: "# Changelog\n\n## v1.0.0\n", - wantErr: assert.NoError, - }, - { - name: "repo with well-formatted changelog", - repoFn: testutils.WithTestRepo(testutils.WithCommit("feat: add changelog", testutils.WithFile(ChangelogFile, `# Changelog - -## v0.0.1 - -- Bazzle - -## v0.1.0 - -### Bazuuum -`))), - entry: "## v1.0.0\n\n- Version 1, juhu.\n", - expectedContent: `# Changelog - -## v1.0.0 - -- Version 1, juhu. - -## v0.0.1 - -- Bazzle - -## v0.1.0 - -### Bazuuum -`, - wantErr: assert.NoError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - repo := tt.repoFn(t) - wt, err := repo.Worktree() - require.NoError(t, err, "failed to get worktree") - - err = UpdateChangelogFile(wt, tt.entry) - if !tt.wantErr(t, err) { - return - } - - wtStatus, err := wt.Status() - require.NoError(t, err, "failed to get worktree status") - - assert.Len(t, wtStatus, 1, "worktree status does not have the expected entry number") - - changelogFileStatus := wtStatus.File(ChangelogFile) - - assert.Equal(t, git.Unmodified, changelogFileStatus.Worktree, "unexpected file status in worktree") - assert.Equal(t, git.Added, changelogFileStatus.Staging, "unexpected file status in staging") - - changelogFile, err := wt.Filesystem.Open(ChangelogFile) - require.NoError(t, err) - defer changelogFile.Close() - - changelogFileContent, err := io.ReadAll(changelogFile) - require.NoError(t, err) - - assert.Equal(t, tt.expectedContent, string(changelogFileContent)) - }) - } -} - func Test_NewChangelogEntry(t *testing.T) { type args struct { analyzedCommits []AnalyzedCommit diff --git a/releaserpleaser.go b/releaserpleaser.go index 42fd6fe..5dfcf12 100644 --- a/releaserpleaser.go +++ b/releaserpleaser.go @@ -250,11 +250,6 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error { // Info for updaters info := ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry} - err = UpdateChangelogFile(worktree, changelogEntry) - if err != nil { - return fmt.Errorf("failed to update changelog file: %w", err) - } - updateFile := func(path string, updaters []Updater) error { file, err := worktree.Filesystem.OpenFile(path, os.O_RDWR, 0) if err != nil { @@ -297,6 +292,11 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error { return nil } + err = updateFile(ChangelogFile, []Updater{&ChangelogUpdater{}}) + if err != nil { + return fmt.Errorf("failed to update changelog file: %w", err) + } + for _, path := range rp.extraFiles { _, err = worktree.Filesystem.Stat(path) if err != nil { diff --git a/updater.go b/updater.go index 32bd2ef..3aaedae 100644 --- a/updater.go +++ b/updater.go @@ -1,12 +1,14 @@ package rp import ( + "fmt" "regexp" "strings" ) var ( GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`) + ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`) ) type ReleaseInfo struct { @@ -26,3 +28,20 @@ func (u *GenericUpdater) UpdateContent(content string, info ReleaseInfo) (string return GenericUpdaterSemVerRegex.ReplaceAllString(content, version+"${2}"), nil } + +type ChangelogUpdater struct{} + +func (u *ChangelogUpdater) UpdateContent(content string, info ReleaseInfo) (string, error) { + headerIndex := ChangelogUpdaterHeaderRegex.FindStringIndex(content) + if headerIndex == nil && len(content) != 0 { + return "", fmt.Errorf("unexpected format of CHANGELOG.md, header does not match") + } + if headerIndex != nil { + // Remove the header from the content + content = content[headerIndex[1]:] + } + + content = ChangelogHeader + "\n\n" + info.ChangelogEntry + content + + return content, nil +} diff --git a/updater_test.go b/updater_test.go index f82a2ee..c0e1419 100644 --- a/updater_test.go +++ b/updater_test.go @@ -72,3 +72,58 @@ func TestGenericUpdater_UpdateContent(t *testing.T) { }) } } + +func TestChangelogUpdater_UpdateContent(t *testing.T) { + updater := &ChangelogUpdater{} + + tests := []updaterTestCase{ + { + name: "empty file", + content: "", + info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n"}, + want: "# Changelog\n\n## v1.0.0\n", + wantErr: assert.NoError, + }, + { + name: "well-formatted changelog", + content: `# Changelog + +## v0.0.1 + +- Bazzle + +## v0.1.0 + +### Bazuuum +`, + info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"}, + want: `# Changelog + +## v1.0.0 + +- Version 1, juhu. + +## v0.0.1 + +- Bazzle + +## v0.1.0 + +### Bazuuum +`, + wantErr: assert.NoError, + }, + { + name: "error on invalid header", + content: "What even is this file?", + info: ReleaseInfo{ChangelogEntry: "## v1.0.0\n\n- Version 1, juhu.\n"}, + want: "", + wantErr: assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + runUpdaterTest(t, updater, tt) + }) + } +}