mirror of
https://github.com/apricote/releaser-pleaser.git
synced 2026-01-13 21:21:03 +00:00
refactor: move things to packages (#39)
This commit is contained in:
parent
44184a77f9
commit
a0a064d387
32 changed files with 923 additions and 892 deletions
110
internal/versioning/semver.go
Normal file
110
internal/versioning/semver.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
package versioning
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
|
||||
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
||||
"github.com/apricote/releaser-pleaser/internal/git"
|
||||
)
|
||||
|
||||
var _ Strategy = SemVerNextVersion
|
||||
|
||||
func SemVerNextVersion(r git.Releases, versionBump VersionBump, nextVersionType NextVersionType) (string, error) {
|
||||
latest, err := parseSemverWithDefault(r.Latest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse latest version: %w", err)
|
||||
}
|
||||
|
||||
stable, err := parseSemverWithDefault(r.Stable)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse stable version: %w", err)
|
||||
}
|
||||
|
||||
// If there is a previous stable release, we use that as the version anchor. Falling back to any pre-releases
|
||||
// if they are the only tags in the repo.
|
||||
next := latest
|
||||
if r.Stable != nil {
|
||||
next = stable
|
||||
}
|
||||
|
||||
switch versionBump {
|
||||
case UnknownVersion:
|
||||
return "", fmt.Errorf("invalid latest bump (unknown)")
|
||||
case PatchVersion:
|
||||
err = next.IncrementPatch()
|
||||
case MinorVersion:
|
||||
err = next.IncrementMinor()
|
||||
case MajorVersion:
|
||||
err = next.IncrementMajor()
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch nextVersionType {
|
||||
case NextVersionTypeUndefined, NextVersionTypeNormal:
|
||||
next.Pre = make([]semver.PRVersion, 0)
|
||||
case NextVersionTypeAlpha, NextVersionTypeBeta, NextVersionTypeRC:
|
||||
id := uint64(0)
|
||||
|
||||
if len(latest.Pre) >= 2 && latest.Pre[0].String() == nextVersionType.String() {
|
||||
if latest.Pre[1].String() == "" || !latest.Pre[1].IsNumeric() {
|
||||
return "", fmt.Errorf("invalid format of previous tag")
|
||||
}
|
||||
id = latest.Pre[1].VersionNum + 1
|
||||
}
|
||||
|
||||
setPRVersion(&next, nextVersionType.String(), id)
|
||||
}
|
||||
|
||||
return "v" + next.String(), nil
|
||||
}
|
||||
|
||||
func BumpFromCommits(commits []commitparser.AnalyzedCommit) VersionBump {
|
||||
bump := UnknownVersion
|
||||
|
||||
for _, commit := range commits {
|
||||
entryBump := UnknownVersion
|
||||
switch {
|
||||
case commit.BreakingChange:
|
||||
entryBump = MajorVersion
|
||||
case commit.Type == "feat":
|
||||
entryBump = MinorVersion
|
||||
case commit.Type == "fix":
|
||||
entryBump = PatchVersion
|
||||
}
|
||||
|
||||
if entryBump > bump {
|
||||
bump = entryBump
|
||||
}
|
||||
}
|
||||
|
||||
return bump
|
||||
}
|
||||
|
||||
func setPRVersion(version *semver.Version, prType string, count uint64) {
|
||||
version.Pre = []semver.PRVersion{
|
||||
{VersionStr: prType},
|
||||
{VersionNum: count, IsNum: true},
|
||||
}
|
||||
}
|
||||
|
||||
func parseSemverWithDefault(tag *git.Tag) (semver.Version, error) {
|
||||
version := "v0.0.0"
|
||||
if tag != nil {
|
||||
version = tag.Name
|
||||
}
|
||||
|
||||
// The lib can not handle v prefixes
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
|
||||
parsedVersion, err := semver.Parse(version)
|
||||
if err != nil {
|
||||
return semver.Version{}, fmt.Errorf("failed to parse version %q: %w", version, err)
|
||||
}
|
||||
|
||||
return parsedVersion, nil
|
||||
}
|
||||
390
internal/versioning/semver_test.go
Normal file
390
internal/versioning/semver_test.go
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
package versioning
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/apricote/releaser-pleaser/internal/commitparser"
|
||||
"github.com/apricote/releaser-pleaser/internal/git"
|
||||
)
|
||||
|
||||
func TestReleases_NextVersion(t *testing.T) {
|
||||
type args struct {
|
||||
releases git.Releases
|
||||
versionBump VersionBump
|
||||
nextVersionType NextVersionType
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "simple bump (major)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MajorVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v2.0.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "simple bump (minor)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MinorVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v1.2.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "simple bump (patch)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v1.1.2",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "normal to prerelease (major)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MajorVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v2.0.0-rc.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "normal to prerelease (minor)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MinorVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v1.2.0-rc.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "normal to prerelease (patch)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v1.1.2-rc.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease bump (major)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v2.0.0-rc.0"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MajorVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v2.0.0-rc.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease bump (minor)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.2.0-rc.0"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MinorVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v1.2.0-rc.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease bump (patch)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.2-rc.0"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v1.1.2-rc.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease different bump (major)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.2.0-rc.0"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MajorVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v2.0.0-rc.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease different bump (minor)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.2-rc.0"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
versionBump: MinorVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v1.2.0-rc.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease to prerelease",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-alpha.2"},
|
||||
Stable: &git.Tag{Name: "v1.1.0"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "v1.1.1-rc.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease to normal (explicit)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-alpha.2"},
|
||||
Stable: &git.Tag{Name: "v1.1.0"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeNormal,
|
||||
},
|
||||
want: "v1.1.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "prerelease to normal (implicit)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-alpha.2"},
|
||||
Stable: &git.Tag{Name: "v1.1.0"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v1.1.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "nil tag (major)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: nil,
|
||||
Stable: nil,
|
||||
},
|
||||
versionBump: MajorVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v1.0.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "nil tag (minor)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: nil,
|
||||
Stable: nil,
|
||||
},
|
||||
versionBump: MinorVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v0.1.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "nil tag (patch)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: nil,
|
||||
Stable: nil,
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v0.0.1",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "nil stable release (major)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-rc.0"},
|
||||
Stable: nil,
|
||||
},
|
||||
versionBump: MajorVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v2.0.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "nil stable release (minor)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-rc.0"},
|
||||
Stable: nil,
|
||||
},
|
||||
versionBump: MinorVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "v1.2.0",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "nil stable release (patch)",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-rc.0"},
|
||||
Stable: nil,
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
// TODO: Is this actually correct our should it be v1.1.1?
|
||||
want: "v1.1.2",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "error on invalid tag semver",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "foodazzle"},
|
||||
Stable: &git.Tag{Name: "foodazzle"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "",
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "error on invalid tag prerelease",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1-rc.foo"},
|
||||
Stable: &git.Tag{Name: "v1.1.1-rc.foo"},
|
||||
},
|
||||
versionBump: PatchVersion,
|
||||
nextVersionType: NextVersionTypeRC,
|
||||
},
|
||||
want: "",
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "error on invalid bump",
|
||||
args: args{
|
||||
releases: git.Releases{
|
||||
Latest: &git.Tag{Name: "v1.1.1"},
|
||||
Stable: &git.Tag{Name: "v1.1.1"},
|
||||
},
|
||||
|
||||
versionBump: UnknownVersion,
|
||||
nextVersionType: NextVersionTypeUndefined,
|
||||
},
|
||||
want: "",
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := SemVerNextVersion(tt.args.releases, tt.args.versionBump, tt.args.nextVersionType)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("SemVerNextVersion(Releases(%v, %v), %v, %v)", tt.args.releases.Latest, tt.args.releases.Stable, tt.args.versionBump, tt.args.nextVersionType)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "SemVerNextVersion(Releases(%v, %v), %v, %v)", tt.args.releases.Latest, tt.args.releases.Stable, tt.args.versionBump, tt.args.nextVersionType)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionBumpFromCommits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analyzedCommits []commitparser.AnalyzedCommit
|
||||
want VersionBump
|
||||
}{
|
||||
{
|
||||
name: "no entries (unknown)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{},
|
||||
want: UnknownVersion,
|
||||
},
|
||||
{
|
||||
name: "non-release type (unknown)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{Type: "docs"}},
|
||||
want: UnknownVersion,
|
||||
},
|
||||
{
|
||||
name: "single breaking (major)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{BreakingChange: true}},
|
||||
want: MajorVersion,
|
||||
},
|
||||
{
|
||||
name: "single feat (minor)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{Type: "feat"}},
|
||||
want: MinorVersion,
|
||||
},
|
||||
{
|
||||
name: "single fix (patch)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{Type: "fix"}},
|
||||
want: PatchVersion,
|
||||
},
|
||||
{
|
||||
name: "multiple entries (major)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{Type: "fix"}, {BreakingChange: true}},
|
||||
want: MajorVersion,
|
||||
},
|
||||
{
|
||||
name: "multiple entries (minor)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{Type: "fix"}, {Type: "feat"}},
|
||||
want: MinorVersion,
|
||||
},
|
||||
{
|
||||
name: "multiple entries (patch)",
|
||||
analyzedCommits: []commitparser.AnalyzedCommit{{Type: "docs"}, {Type: "fix"}},
|
||||
want: PatchVersion,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, BumpFromCommits(tt.analyzedCommits), "BumpFromCommits(%v)", tt.analyzedCommits)
|
||||
})
|
||||
}
|
||||
}
|
||||
56
internal/versioning/versioning.go
Normal file
56
internal/versioning/versioning.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package versioning
|
||||
|
||||
import (
|
||||
"github.com/leodido/go-conventionalcommits"
|
||||
|
||||
"github.com/apricote/releaser-pleaser/internal/git"
|
||||
)
|
||||
|
||||
type Strategy = func(git.Releases, VersionBump, NextVersionType) (string, error)
|
||||
|
||||
type VersionBump conventionalcommits.VersionBump
|
||||
|
||||
const (
|
||||
UnknownVersion VersionBump = iota
|
||||
PatchVersion
|
||||
MinorVersion
|
||||
MajorVersion
|
||||
)
|
||||
|
||||
type NextVersionType int
|
||||
|
||||
const (
|
||||
NextVersionTypeUndefined NextVersionType = iota
|
||||
NextVersionTypeNormal
|
||||
NextVersionTypeRC
|
||||
NextVersionTypeBeta
|
||||
NextVersionTypeAlpha
|
||||
)
|
||||
|
||||
func (n NextVersionType) String() string {
|
||||
switch n {
|
||||
case NextVersionTypeUndefined:
|
||||
return "undefined"
|
||||
case NextVersionTypeNormal:
|
||||
return "normal"
|
||||
case NextVersionTypeRC:
|
||||
return "rc"
|
||||
case NextVersionTypeBeta:
|
||||
return "beta"
|
||||
case NextVersionTypeAlpha:
|
||||
return "alpha"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (n NextVersionType) IsPrerelease() bool {
|
||||
switch n {
|
||||
case NextVersionTypeRC, NextVersionTypeBeta, NextVersionTypeAlpha:
|
||||
return true
|
||||
case NextVersionTypeUndefined, NextVersionTypeNormal:
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue