Compare commits

...

4 commits

16 changed files with 330 additions and 189 deletions

27
.golangci.yaml Normal file
View file

@ -0,0 +1,27 @@
linters:
presets:
- bugs
- error
- import
- metalinter
- module
- unused
enable:
- testifylint
disable:
# preset error
# These should probably be cleaned up at some point if we want to publish part of this as a library.
- err113 # Very annoying to define static errors everywhere
- wrapcheck # Very annoying to wrap errors everywhere
# preset import
- depguard
linters-settings:
gci:
sections:
- standard
- default
- localmodule

View file

@ -12,14 +12,19 @@ inputs:
description: 'GitHub token for creating and grooming release PRs, defaults to using secrets.GITHUB_TOKEN' description: 'GitHub token for creating and grooming release PRs, defaults to using secrets.GITHUB_TOKEN'
required: false required: false
default: ${{ github.token }} default: ${{ github.token }}
extra-files:
description: 'List of files that are scanned for version references.'
required: false
default: ""
outputs: {} outputs: {}
runs: runs:
using: 'docker' using: 'docker'
image: ghcr.io/apricote/releaser-pleaser:v0.1.0 image: ghcr.io/apricote/releaser-pleaser:v0.1.0 # x-releaser-pleaser-version
args: args:
- run - run
- --forge=github - --forge=github
- --branch=${{ inputs.branch }} - --branch=${{ inputs.branch }}
- --extra-files="${{ inputs.extra-files }}"
env: env:
GITHUB_TOKEN: ${{ inputs.token }} GITHUB_TOKEN: ${{ inputs.token }}
GITHUB_USER: "oauth2" GITHUB_USER: "oauth2"

View file

@ -3,14 +3,8 @@ package rp
import ( import (
"bytes" "bytes"
_ "embed" _ "embed"
"fmt"
"html/template" "html/template"
"io"
"log" "log"
"os"
"regexp"
"github.com/go-git/go-git/v5"
) )
const ( const (
@ -20,8 +14,6 @@ const (
var ( var (
changelogTemplate *template.Template changelogTemplate *template.Template
headerRegex = regexp.MustCompile(`^# Changelog\n`)
) )
//go:embed changelog.md.tpl //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) { func NewChangelogEntry(commits []AnalyzedCommit, version, link, prefix, suffix string) (string, error) {
features := make([]AnalyzedCommit, 0) features := make([]AnalyzedCommit, 0)
fixes := make([]AnalyzedCommit, 0) fixes := make([]AnalyzedCommit, 0)

View file

@ -1,99 +1,15 @@
package rp package rp
import ( import (
"io"
"testing" "testing"
"github.com/go-git/go-git/v5"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/apricote/releaser-pleaser/internal/testutils"
) )
func ptr[T any](input T) *T { func ptr[T any](input T) *T {
return &input 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) { func Test_NewChangelogEntry(t *testing.T) {
type args struct { type args struct {
analyzedCommits []AnalyzedCommit analyzedCommits []AnalyzedCommit

View file

@ -1,6 +1,8 @@
package cmd package cmd
import ( import (
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
rp "github.com/apricote/releaser-pleaser" rp "github.com/apricote/releaser-pleaser"
@ -17,6 +19,7 @@ var (
flagBranch string flagBranch string
flagOwner string flagOwner string
flagRepo string flagRepo string
flagExtraFiles string
) )
func init() { func init() {
@ -28,6 +31,7 @@ func init() {
runCmd.PersistentFlags().StringVar(&flagBranch, "branch", "main", "") runCmd.PersistentFlags().StringVar(&flagBranch, "branch", "main", "")
runCmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "") runCmd.PersistentFlags().StringVar(&flagOwner, "owner", "", "")
runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "") runCmd.PersistentFlags().StringVar(&flagRepo, "repo", "", "")
runCmd.PersistentFlags().StringVar(&flagExtraFiles, "extra-files", "", "")
} }
func run(cmd *cobra.Command, _ []string) error { func run(cmd *cobra.Command, _ []string) error {
@ -47,9 +51,9 @@ func run(cmd *cobra.Command, _ []string) error {
BaseBranch: flagBranch, BaseBranch: flagBranch,
} }
switch flagForge { switch flagForge { // nolint:gocritic // Will become a proper switch once gitlab is added
//case "gitlab": // case "gitlab":
//f = rp.NewGitLab(forgeOptions) // f = rp.NewGitLab(forgeOptions)
case "github": case "github":
logger.DebugContext(ctx, "using forge GitHub") logger.DebugContext(ctx, "using forge GitHub")
forge = rp.NewGitHub(logger, &rp.GitHubOptions{ forge = rp.NewGitHub(logger, &rp.GitHubOptions{
@ -59,7 +63,31 @@ func run(cmd *cobra.Command, _ []string) error {
}) })
} }
releaserPleaser := rp.New(forge, logger, flagBranch, rp.NewConventionalCommitsParser(), rp.SemVerNextVersion) extraFiles := parseExtraFiles(flagExtraFiles)
releaserPleaser := rp.New(
forge,
logger,
flagBranch,
rp.NewConventionalCommitsParser(),
rp.SemVerNextVersion,
extraFiles,
[]rp.Updater{&rp.GenericUpdater{}},
)
return releaserPleaser.Run(ctx) return releaserPleaser.Run(ctx)
} }
func parseExtraFiles(input string) []string {
lines := strings.Split(input, "\n")
extraFiles := make([]string, 0, len(lines))
for _, line := range lines {
line = strings.TrimSpace(line)
if len(line) > 0 {
extraFiles = append(extraFiles, line)
}
}
return extraFiles
}

View file

@ -19,7 +19,7 @@ const (
GitHubPerPageMax = 100 GitHubPerPageMax = 100
GitHubPRStateOpen = "open" GitHubPRStateOpen = "open"
GitHubPRStateClosed = "closed" GitHubPRStateClosed = "closed"
GitHubEnvAPIToken = "GITHUB_TOKEN" GitHubEnvAPIToken = "GITHUB_TOKEN" // nolint:gosec // Not actually a hardcoded credential
GitHubEnvUsername = "GITHUB_USER" GitHubEnvUsername = "GITHUB_USER"
GitHubEnvRepository = "GITHUB_REPOSITORY" GitHubEnvRepository = "GITHUB_REPOSITORY"
GitHubLabelColor = "dedede" GitHubLabelColor = "dedede"

4
go.mod
View file

@ -4,7 +4,6 @@ go 1.23.0
require ( require (
github.com/blang/semver/v4 v4.0.0 github.com/blang/semver/v4 v4.0.0
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0 github.com/go-git/go-git/v5 v5.12.0
github.com/google/go-github/v63 v63.0.0 github.com/google/go-github/v63 v63.0.0
github.com/leodido/go-conventionalcommits v0.12.0 github.com/leodido/go-conventionalcommits v0.12.0
@ -17,11 +16,12 @@ require (
dario.cat/mergo v1.0.1 // indirect dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/cloudflare/circl v1.3.9 // indirect github.com/cloudflare/circl v1.4.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.1 // indirect github.com/cyphar/filepath-securejoin v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect

2
go.sum
View file

@ -15,6 +15,8 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cyphar/filepath-securejoin v0.3.1 h1:1V7cHiaW+C+39wEfpH6XlLBQo3j/PciWFrgfCLS8XrE= github.com/cyphar/filepath-securejoin v0.3.1 h1:1V7cHiaW+C+39wEfpH6XlLBQo3j/PciWFrgfCLS8XrE=
github.com/cyphar/filepath-securejoin v0.3.1/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc= github.com/cyphar/filepath-securejoin v0.3.1/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc=

View file

@ -12,8 +12,10 @@ import (
"github.com/apricote/releaser-pleaser/internal/markdown/extensions/ast" "github.com/apricote/releaser-pleaser/internal/markdown/extensions/ast"
) )
var sectionStartRegex = regexp.MustCompile(`^<!-- section-start (.+) -->`) var (
var sectionEndRegex = regexp.MustCompile(`^<!-- section-end (.+) -->`) sectionStartRegex = regexp.MustCompile(`^<!-- section-start (.+) -->`)
sectionEndRegex = regexp.MustCompile(`^<!-- section-end (.+) -->`)
)
const ( const (
sectionTrigger = "<!--" sectionTrigger = "<!--"
@ -21,8 +23,7 @@ const (
SectionEndFormat = "<!-- section-end %s -->" SectionEndFormat = "<!-- section-end %s -->"
) )
type sectionParser struct { type sectionParser struct{}
}
func (s *sectionParser) Open(_ gast.Node, reader text.Reader, _ parser.Context) (gast.Node, parser.State) { func (s *sectionParser) Open(_ gast.Node, reader text.Reader, _ parser.Context) (gast.Node, parser.State) {
line, _ := reader.PeekLine() line, _ := reader.PeekLine()
@ -75,8 +76,7 @@ func (s *sectionParser) Trigger() []byte {
return []byte(sectionTrigger) return []byte(sectionTrigger)
} }
type section struct { type section struct{}
}
// Section is an extension that allow you to use group content under a shared parent ast node. // Section is an extension that allow you to use group content under a shared parent ast node.
var Section = &section{} var Section = &section{}

View file

@ -331,7 +331,7 @@ func (r *Renderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node a
return ast.WalkStop, fmt.Errorf(": %w", err) return ast.WalkStop, fmt.Errorf(": %w", err)
} }
if err := r.writeByte(w, '\n'); err != nil { if err := r.writeByte(w, '\n'); err != nil {
return ast.WalkStop, nil return ast.WalkStop, fmt.Errorf(": %w", err)
} }
// Write the contents of the fenced code block. // Write the contents of the fenced code block.

View file

@ -11,25 +11,26 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var ( var author = &object.Signature{
author = &object.Signature{
Name: "releaser-pleaser", Name: "releaser-pleaser",
When: time.Date(2020, 01, 01, 01, 01, 01, 01, time.UTC), When: time.Date(2020, 01, 01, 01, 01, 01, 01, time.UTC),
} }
)
type CommitOption func(*commitOptions) type CommitOption func(*commitOptions)
type commitOptions struct { type commitOptions struct {
cleanFiles bool cleanFiles bool
files []commitFile files []commitFile
tags []string tags []string
} }
type commitFile struct { type commitFile struct {
path string path string
content string content string
} }
type Commit func(*testing.T, *git.Repository) error type Commit func(*testing.T, *git.Repository) error
type Repo func(*testing.T) *git.Repository type Repo func(*testing.T) *git.Repository
func WithCommit(message string, options ...CommitOption) Commit { func WithCommit(message string, options ...CommitOption) Commit {
@ -83,7 +84,6 @@ func WithCommit(message string, options ...CommitOption) Commit {
} }
return nil return nil
} }
} }

View file

@ -155,6 +155,9 @@ func (pr *ReleasePullRequest) parseVersioningFlags(overrides ReleaseOverrides) R
overrides.NextVersionType = NextVersionTypeBeta overrides.NextVersionType = NextVersionTypeBeta
case LabelNextVersionTypeAlpha: case LabelNextVersionTypeAlpha:
overrides.NextVersionType = NextVersionTypeAlpha overrides.NextVersionType = NextVersionTypeAlpha
case LabelReleasePending, LabelReleaseTagged:
// These labels have no effect on the versioning.
break
} }
} }

View file

@ -3,7 +3,9 @@ package rp
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"log/slog" "log/slog"
"os"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
@ -20,15 +22,19 @@ type ReleaserPleaser struct {
targetBranch string targetBranch string
commitParser CommitParser commitParser CommitParser
nextVersion VersioningStrategy nextVersion VersioningStrategy
extraFiles []string
updaters []Updater
} }
func New(forge Forge, logger *slog.Logger, targetBranch string, commitParser CommitParser, versioningStrategy VersioningStrategy) *ReleaserPleaser { func New(forge Forge, logger *slog.Logger, targetBranch string, commitParser CommitParser, versioningStrategy VersioningStrategy, extraFiles []string, updaters []Updater) *ReleaserPleaser {
return &ReleaserPleaser{ return &ReleaserPleaser{
forge: forge, forge: forge,
logger: logger, logger: logger,
targetBranch: targetBranch, targetBranch: targetBranch,
commitParser: commitParser, commitParser: commitParser,
nextVersion: versioningStrategy, nextVersion: versioningStrategy,
extraFiles: extraFiles,
updaters: updaters,
} }
} }
@ -236,21 +242,74 @@ func (rp *ReleaserPleaser) runReconcileReleasePR(ctx context.Context) error {
return fmt.Errorf("failed to check out branch: %w", err) return fmt.Errorf("failed to check out branch: %w", err)
} }
err = RunUpdater(ctx, nextVersion, worktree)
if err != nil {
return fmt.Errorf("failed to update files with new version: %w", err)
}
changelogEntry, err := NewChangelogEntry(analyzedCommits, nextVersion, rp.forge.ReleaseURL(nextVersion), releaseOverrides.Prefix, releaseOverrides.Suffix) changelogEntry, err := NewChangelogEntry(analyzedCommits, nextVersion, rp.forge.ReleaseURL(nextVersion), releaseOverrides.Prefix, releaseOverrides.Suffix)
if err != nil { if err != nil {
return fmt.Errorf("failed to build changelog entry: %w", err) return fmt.Errorf("failed to build changelog entry: %w", err)
} }
err = UpdateChangelogFile(worktree, changelogEntry) // Info for updaters
info := ReleaseInfo{Version: nextVersion, ChangelogEntry: changelogEntry}
updateFile := func(path string, updaters []Updater) error {
file, err := worktree.Filesystem.OpenFile(path, os.O_RDWR, 0)
if err != nil {
return err
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
return err
}
updatedContent := string(content)
for _, updater := range updaters {
updatedContent, err = updater.UpdateContent(updatedContent, info)
if err != nil {
return fmt.Errorf("failed to run updater %T on file %s", updater, path)
}
}
err = file.Truncate(0)
if err != nil {
return fmt.Errorf("failed to replace file content: %w", err)
}
_, err = file.Seek(0, 0)
if err != nil {
return fmt.Errorf("failed to replace file content: %w", err)
}
_, err = file.Write([]byte(updatedContent))
if err != nil {
return fmt.Errorf("failed to replace file content: %w", err)
}
_, err = worktree.Add(path)
if err != nil {
return fmt.Errorf("failed to add updated file to git worktree: %w", err)
}
return nil
}
err = updateFile(ChangelogFile, []Updater{&ChangelogUpdater{}})
if err != nil { if err != nil {
return fmt.Errorf("failed to update changelog file: %w", err) return fmt.Errorf("failed to update changelog file: %w", err)
} }
for _, path := range rp.extraFiles {
_, err = worktree.Filesystem.Stat(path)
if err != nil {
// TODO: Check for non existing file or dirs
return fmt.Errorf("failed to run file updater because the file %s does not exist: %w", path, err)
}
err = updateFile(path, rp.updaters)
if err != nil {
return fmt.Errorf("failed to run file updater: %w", err)
}
}
releaseCommitMessage := fmt.Sprintf("chore(%s): release %s", rp.targetBranch, nextVersion) releaseCommitMessage := fmt.Sprintf("chore(%s): release %s", rp.targetBranch, nextVersion)
releaseCommitHash, err := worktree.Commit(releaseCommitMessage, &git.CommitOptions{ releaseCommitHash, err := worktree.Commit(releaseCommitMessage, &git.CommitOptions{
Author: GitSignature(), Author: GitSignature(),

View file

@ -1,12 +1,47 @@
package rp package rp
import ( import (
"context" "fmt"
"regexp"
"github.com/go-git/go-git/v5" "strings"
) )
func RunUpdater(ctx context.Context, version string, worktree *git.Worktree) error { var (
// TODO: Implement updater for Go,Python,ExtraFilesMarkers GenericUpdaterSemVerRegex = regexp.MustCompile(`\d+\.\d+\.\d+(-[\w.]+)?(.*x-releaser-pleaser-version)`)
return nil ChangelogUpdaterHeaderRegex = regexp.MustCompile(`^# Changelog\n`)
)
type ReleaseInfo struct {
Version string
ChangelogEntry string
}
type Updater interface {
UpdateContent(content string, info ReleaseInfo) (string, error)
}
type GenericUpdater struct{}
func (u *GenericUpdater) UpdateContent(content string, info ReleaseInfo) (string, error) {
// We strip the "v" prefix to avoid adding/removing it from the users input.
version := strings.TrimPrefix(info.Version, "v")
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
} }

129
updater_test.go Normal file
View file

@ -0,0 +1,129 @@
package rp
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
type updaterTestCase struct {
name string
content string
info ReleaseInfo
want string
wantErr assert.ErrorAssertionFunc
}
func runUpdaterTest(t *testing.T, updater Updater, tt updaterTestCase) {
t.Helper()
got, err := updater.UpdateContent(tt.content, tt.info)
if !tt.wantErr(t, err, fmt.Sprintf("UpdateContent(%v, %v)", tt.content, tt.info)) {
return
}
assert.Equalf(t, tt.want, got, "UpdateContent(%v, %v)", tt.content, tt.info)
}
func TestGenericUpdater_UpdateContent(t *testing.T) {
updater := &GenericUpdater{}
tests := []updaterTestCase{
{
name: "single line",
content: "v1.0.0 // x-releaser-pleaser-version",
info: ReleaseInfo{
Version: "v1.2.0",
},
want: "v1.2.0 // x-releaser-pleaser-version",
wantErr: assert.NoError,
},
{
name: "multiline line",
content: "Foooo\n\v1.2.0\nv1.0.0 // x-releaser-pleaser-version\n",
info: ReleaseInfo{
Version: "v1.2.0",
},
want: "Foooo\n\v1.2.0\nv1.2.0 // x-releaser-pleaser-version\n",
wantErr: assert.NoError,
},
{
name: "invalid existing version",
content: "1.0 // x-releaser-pleaser-version",
info: ReleaseInfo{
Version: "v1.2.0",
},
want: "1.0 // x-releaser-pleaser-version",
wantErr: assert.NoError,
},
{
name: "complicated line",
content: "version: v1.2.0-alpha.1 => Awesome, isnt it? x-releaser-pleaser-version foobar",
info: ReleaseInfo{
Version: "v1.2.0",
},
want: "version: v1.2.0 => Awesome, isnt it? x-releaser-pleaser-version foobar",
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
runUpdaterTest(t, updater, tt)
})
}
}
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)
})
}
}

View file

@ -45,6 +45,9 @@ func SemVerNextVersion(r Releases, versionBump conventionalcommits.VersionBump,
case conventionalcommits.MajorVersion: case conventionalcommits.MajorVersion:
err = next.IncrementMajor() err = next.IncrementMajor()
} }
if err != nil {
return "", err
}
switch nextVersionType { switch nextVersionType {
case NextVersionTypeUndefined, NextVersionTypeNormal: case NextVersionTypeUndefined, NextVersionTypeNormal:
@ -62,10 +65,6 @@ func SemVerNextVersion(r Releases, versionBump conventionalcommits.VersionBump,
setPRVersion(&next, nextVersionType.String(), id) setPRVersion(&next, nextVersionType.String(), id)
} }
if err != nil {
return "", err
}
return "v" + next.String(), nil return "v" + next.String(), nil
} }