refactor: Various cleanup (#11)

* refactor: interface for commit message analyzer

* refactor: interface for versioning strategy

* refactor(releasepr): rebuild pr description

Build PR description from scratch and parsed values instead of copying some of the AST to next description.
This commit is contained in:
Julian Tölle 2024-08-08 19:01:44 +02:00 committed by GitHub
parent 8f106e4028
commit d8daad7623
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 197 additions and 268 deletions

View file

@ -232,7 +232,7 @@ func reconcileReleasePR(ctx context.Context, forge rp.Forge, changesets []rp.Cha
}
versionBump := rp.VersionBumpFromChangesets(changesets)
nextVersion, err := releases.NextVersion(versionBump, releaseOverrides.NextVersionType)
nextVersion, err := rp.SemVerNextVersion(releases, versionBump, releaseOverrides.NextVersionType)
if err != nil {
return err
}
@ -352,7 +352,12 @@ func reconcileReleasePR(ctx context.Context, forge rp.Forge, changesets []rp.Cha
logger.InfoContext(ctx, "opened pull request", "pr.title", pr.Title, "pr.id", pr.ID)
} else {
pr.SetTitle(flagBranch, nextVersion)
err = pr.SetDescription(changelogEntry)
overrides, err := pr.GetOverrides()
if err != nil {
return err
}
err = pr.SetDescription(changelogEntry, overrides)
if err != nil {
return err
}

View file

@ -15,24 +15,36 @@ type AnalyzedCommit struct {
BreakingChange bool
}
func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.VersionBump, error) {
type CommitParser interface {
Analyze(commits []Commit) ([]AnalyzedCommit, error)
}
type ConventionalCommitsParser struct {
machine conventionalcommits.Machine
}
func NewConventionalCommitsParser() *ConventionalCommitsParser {
parserMachine := parser.NewMachine(
parser.WithBestEffort(),
parser.WithTypes(conventionalcommits.TypesConventional),
)
return &ConventionalCommitsParser{
machine: parserMachine,
}
}
func (c *ConventionalCommitsParser) AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, error) {
analyzedCommits := make([]AnalyzedCommit, 0, len(commits))
highestVersionBump := conventionalcommits.UnknownVersion
for _, commit := range commits {
msg, err := parserMachine.Parse([]byte(commit.Message))
msg, err := c.machine.Parse([]byte(commit.Message))
if err != nil {
return nil, conventionalcommits.UnknownVersion, fmt.Errorf("failed to parse message of commit %q: %w", commit.Hash, err)
return nil, fmt.Errorf("failed to parse message of commit %q: %w", commit.Hash, err)
}
conventionalCommit, ok := msg.(*conventionalcommits.ConventionalCommit)
if !ok {
return nil, conventionalcommits.UnknownVersion, fmt.Errorf("unable to get ConventionalCommit from parser result: %T", msg)
return nil, fmt.Errorf("unable to get ConventionalCommit from parser result: %T", msg)
}
commitVersionBump := conventionalCommit.VersionBump(conventionalcommits.DefaultStrategy)
@ -47,11 +59,7 @@ func AnalyzeCommits(commits []Commit) ([]AnalyzedCommit, conventionalcommits.Ver
})
}
if commitVersionBump > highestVersionBump {
// Get max version bump from all releasable commits
highestVersionBump = commitVersionBump
}
}
return analyzedCommits, highestVersionBump, nil
return analyzedCommits, nil
}

View file

@ -3,7 +3,6 @@ package rp
import (
"testing"
"github.com/leodido/go-conventionalcommits"
"github.com/stretchr/testify/assert"
)
@ -12,14 +11,12 @@ func TestAnalyzeCommits(t *testing.T) {
name string
commits []Commit
expectedCommits []AnalyzedCommit
expectedBump conventionalcommits.VersionBump
wantErr assert.ErrorAssertionFunc
}{
{
name: "empty commits",
commits: []Commit{},
expectedCommits: []AnalyzedCommit{},
expectedBump: conventionalcommits.UnknownVersion,
wantErr: assert.NoError,
},
{
@ -30,7 +27,6 @@ func TestAnalyzeCommits(t *testing.T) {
},
},
expectedCommits: nil,
expectedBump: conventionalcommits.UnknownVersion,
wantErr: assert.Error,
},
{
@ -41,7 +37,6 @@ func TestAnalyzeCommits(t *testing.T) {
},
},
expectedCommits: []AnalyzedCommit{},
expectedBump: conventionalcommits.UnknownVersion,
wantErr: assert.NoError,
},
{
@ -61,7 +56,6 @@ func TestAnalyzeCommits(t *testing.T) {
Description: "blabla",
},
},
expectedBump: conventionalcommits.PatchVersion,
wantErr: assert.NoError,
},
{
@ -86,7 +80,6 @@ func TestAnalyzeCommits(t *testing.T) {
Description: "foobar",
},
},
expectedBump: conventionalcommits.MinorVersion,
wantErr: assert.NoError,
},
@ -113,19 +106,17 @@ func TestAnalyzeCommits(t *testing.T) {
BreakingChange: true,
},
},
expectedBump: conventionalcommits.MajorVersion,
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
analyzedCommits, versionBump, err := AnalyzeCommits(tt.commits)
analyzedCommits, err := NewConventionalCommitsParser().AnalyzeCommits(tt.commits)
if !tt.wantErr(t, err) {
return
}
assert.Equal(t, tt.expectedCommits, analyzedCommits)
assert.Equal(t, tt.expectedBump, versionBump)
})
}
}

View file

@ -308,7 +308,7 @@ func (g *GitHub) Changesets(ctx context.Context, commits []Commit) ([]Changeset,
log = log.With("pullrequest.id", pullrequest.GetID())
// TODO: Parse PR description for overrides
changelogEntries, _, err := AnalyzeCommits([]Commit{commit})
changelogEntries, err := NewConventionalCommitsParser().AnalyzeCommits([]Commit{commit})
if err != nil {
log.Warn("unable to parse changelog entries", "error", err)
continue

View file

@ -6,6 +6,7 @@ import (
"fmt"
"log"
"regexp"
"strings"
"text/template"
"github.com/yuin/goldmark/ast"
@ -47,7 +48,7 @@ func NewReleasePullRequest(head, branch, version, changelogEntry string) (*Relea
}
rp.SetTitle(branch, version)
if err := rp.SetDescription(changelogEntry); err != nil {
if err := rp.SetDescription(changelogEntry, ReleaseOverrides{}); err != nil {
return nil, err
}
@ -115,7 +116,6 @@ const (
)
const (
MarkdownSectionOverrides = "overrides"
MarkdownSectionChangelog = "changelog"
)
@ -190,51 +190,6 @@ func (pr *ReleasePullRequest) parseDescription(overrides ReleaseOverrides) (Rele
return overrides, nil
}
func (pr *ReleasePullRequest) overridesText() (string, error) {
source := []byte(pr.Description)
gm := markdown.New()
descriptionAST := gm.Parser().Parse(text.NewReader(source))
var section *east.Section
err := ast.Walk(descriptionAST, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
if n.Type() != ast.TypeBlock || n.Kind() != east.KindSection {
return ast.WalkContinue, nil
}
anySection, ok := n.(*east.Section)
if !ok {
return ast.WalkStop, fmt.Errorf("node has unexpected type: %T", n)
}
if anySection.Name != MarkdownSectionOverrides {
return ast.WalkContinue, nil
}
section = anySection
return ast.WalkStop, nil
})
if err != nil {
return "", err
}
if section == nil {
return "", nil
}
outputBuffer := new(bytes.Buffer)
err = gm.Renderer().Render(outputBuffer, source, section)
if err != nil {
return "", err
}
return outputBuffer.String(), nil
}
func (pr *ReleasePullRequest) ChangelogText() (string, error) {
source := []byte(pr.Description)
gm := markdown.New()
@ -289,11 +244,11 @@ func textFromLines(source []byte, n ast.Node) string {
content = append(content, line.Value(source)...)
}
return string(content)
return strings.TrimSpace(string(content))
}
func (pr *ReleasePullRequest) SetTitle(branch, version string) {
pr.Title = fmt.Sprintf("chore(%s): release %s", branch, version)
pr.Title = fmt.Sprintf(TitleFormat, branch, version)
}
func (pr *ReleasePullRequest) Version() (string, error) {
@ -305,14 +260,9 @@ func (pr *ReleasePullRequest) Version() (string, error) {
return matches[2], nil
}
func (pr *ReleasePullRequest) SetDescription(changelogEntry string) error {
overrides, err := pr.overridesText()
if err != nil {
return err
}
func (pr *ReleasePullRequest) SetDescription(changelogEntry string, overrides ReleaseOverrides) error {
var description bytes.Buffer
err = releasePRTemplate.Execute(&description, map[string]any{
err := releasePRTemplate.Execute(&description, map[string]any{
"Changelog": changelogEntry,
"Overrides": overrides,
})

View file

@ -1,29 +1,32 @@
---
<!-- section-start changelog -->
{{ .Changelog }}
<!-- section-end changelog -->
---
## releaser-pleaser Instructions
{{ if .Overrides }}
{{- .Overrides -}}
{{- else }}
<!-- section-start overrides -->
> If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
<details>
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
### Prefix
If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
## Release Notes
### Prefix / Start
This will be added to the start of the release notes.
```rp-prefix
{{- if .Overrides.Prefix }}
{{ .Overrides.Prefix }}{{ end }}
```
### Suffix
### Suffix / End
This will be added to the end of the release notes.
```rp-suffix
{{- if .Overrides.Suffix }}
{{ .Overrides.Suffix }}{{ end }}
```
<!-- section-end overrides -->
{{ end }}
#### PR by [releaser-pleaser](https://github.com/apricote/releaser-pleaser)
</details>

View file

@ -49,121 +49,96 @@ func TestReleasePullRequest_SetDescription(t *testing.T) {
tests := []struct {
name string
pr *ReleasePullRequest
changelogEntry string
overrides ReleaseOverrides
want string
wantErr assert.ErrorAssertionFunc
}{
{
name: "empty description",
pr: &ReleasePullRequest{},
name: "no overrides",
changelogEntry: `## v1.0.0`,
want: `---
<!-- section-start changelog -->
overrides: ReleaseOverrides{},
want: `<!-- section-start changelog -->
## v1.0.0
<!-- section-end changelog -->
---
## releaser-pleaser Instructions
<details>
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
<!-- section-start overrides -->
> If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
### Prefix
## Release Notes
### Prefix / Start
This will be added to the start of the release notes.
` + "```" + `rp-prefix
` + "```" + `
### Suffix
### Suffix / End
This will be added to the end of the release notes.
` + "```" + `rp-suffix
` + "```" + `
<!-- section-end overrides -->
#### PR by [releaser-pleaser](https://github.com/apricote/releaser-pleaser)
</details>
`,
wantErr: assert.NoError,
},
{
name: "existing overrides",
pr: &ReleasePullRequest{
Description: `---
<!-- section-start changelog -->
## v0.1.0
### Features
- bedazzle
<!-- section-end changelog -->
---
## releaser-pleaser Instructions
<!-- section-start overrides -->
> If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
### Prefix
` + "```" + `rp-prefix
This release is awesome!
` + "```" + `
### Suffix
` + "```" + `rp-suffix
` + "```" + `
<!-- section-end overrides -->
#### PR by [releaser-pleaser](https://github.com/apricote/releaser-pleaser)
`,
},
changelogEntry: `## v1.0.0`,
want: `---
<!-- section-start changelog -->
overrides: ReleaseOverrides{
Prefix: "This release is awesome!",
Suffix: "Fooo",
},
want: `<!-- section-start changelog -->
## v1.0.0
<!-- section-end changelog -->
---
## releaser-pleaser Instructions
<details>
<summary><h4>PR by <a href="https://github.com/apricote/releaser-pleaser">releaser-pleaser</a> 🤖</h4></summary>
<!-- section-start overrides -->
> If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
If you want to modify the proposed release, add you overrides here. You can learn more about the options in the docs.
### Prefix
## Release Notes
### Prefix / Start
This will be added to the start of the release notes.
` + "```" + `rp-prefix
This release is awesome!
` + "```" + `
### Suffix
### Suffix / End
This will be added to the end of the release notes.
` + "```" + `rp-suffix
Fooo
` + "```" + `
<!-- section-end overrides -->
#### PR by [releaser-pleaser](https://github.com/apricote/releaser-pleaser)
</details>
`,
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.pr.SetDescription(tt.changelogEntry)
pr := &ReleasePullRequest{}
err := pr.SetDescription(tt.changelogEntry, tt.overrides)
if !tt.wantErr(t, err) {
return
}
assert.Equal(t, tt.want, tt.pr.Description)
assert.Equal(t, tt.want, pr.Description)
})
}
}

View file

@ -13,7 +13,11 @@ type Releases struct {
Stable *Tag
}
func (r Releases) NextVersion(versionBump conventionalcommits.VersionBump, nextVersionType NextVersionType) (string, error) {
type VersioningStrategy = func(Releases, conventionalcommits.VersionBump, NextVersionType) (string, error)
var _ VersioningStrategy = SemVerNextVersion
func SemVerNextVersion(r Releases, versionBump conventionalcommits.VersionBump, nextVersionType NextVersionType) (string, error) {
latest, err := parseSemverWithDefault(r.Latest)
if err != nil {
return "", fmt.Errorf("failed to parse latest version: %w", err)

View file

@ -10,23 +10,23 @@ import (
func TestReleases_NextVersion(t *testing.T) {
type args struct {
releases Releases
versionBump conventionalcommits.VersionBump
nextVersionType NextVersionType
}
tests := []struct {
name string
releases Releases
args args
want string
wantErr assert.ErrorAssertionFunc
}{
{
name: "simple bump (major)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MajorVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -35,12 +35,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "simple bump (minor)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MinorVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -49,12 +48,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "simple bump (patch)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -63,12 +61,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "normal to prerelease (major)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MajorVersion,
nextVersionType: NextVersionTypeRC,
},
@ -77,12 +74,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "normal to prerelease (minor)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MinorVersion,
nextVersionType: NextVersionTypeRC,
},
@ -91,12 +87,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "normal to prerelease (patch)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeRC,
},
@ -105,11 +100,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease bump (major)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v2.0.0-rc.0"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MajorVersion,
nextVersionType: NextVersionTypeRC,
},
@ -118,11 +113,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease bump (minor)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.2.0-rc.0"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MinorVersion,
nextVersionType: NextVersionTypeRC,
},
@ -131,11 +126,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease bump (patch)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.2-rc.0"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeRC,
},
@ -144,11 +139,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease different bump (major)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.2.0-rc.0"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MajorVersion,
nextVersionType: NextVersionTypeRC,
},
@ -157,11 +152,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease different bump (minor)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.2-rc.0"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.MinorVersion,
nextVersionType: NextVersionTypeRC,
},
@ -170,11 +165,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease to prerelease",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-alpha.2"},
Stable: &Tag{Name: "v1.1.0"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeRC,
},
@ -183,11 +178,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease to normal (explicit)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-alpha.2"},
Stable: &Tag{Name: "v1.1.0"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeNormal,
},
@ -196,11 +191,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "prerelease to normal (implicit)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-alpha.2"},
Stable: &Tag{Name: "v1.1.0"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -209,11 +204,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "nil tag (major)",
args: args{
releases: Releases{
Latest: nil,
Stable: nil,
},
args: args{
versionBump: conventionalcommits.MajorVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -222,11 +217,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "nil tag (minor)",
args: args{
releases: Releases{
Latest: nil,
Stable: nil,
},
args: args{
versionBump: conventionalcommits.MinorVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -235,11 +230,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "nil tag (patch)",
args: args{
releases: Releases{
Latest: nil,
Stable: nil,
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -248,11 +243,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "nil stable release (major)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-rc.0"},
Stable: nil,
},
args: args{
versionBump: conventionalcommits.MajorVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -261,12 +256,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "nil stable release (minor)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-rc.0"},
Stable: nil,
},
args: args{
versionBump: conventionalcommits.MinorVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -275,12 +269,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "nil stable release (patch)",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-rc.0"},
Stable: nil,
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeUndefined,
},
@ -290,11 +283,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "error on invalid tag semver",
args: args{
releases: Releases{
Latest: &Tag{Name: "foodazzle"},
Stable: &Tag{Name: "foodazzle"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeRC,
},
@ -303,11 +296,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "error on invalid tag prerelease",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1-rc.foo"},
Stable: &Tag{Name: "v1.1.1-rc.foo"},
},
args: args{
versionBump: conventionalcommits.PatchVersion,
nextVersionType: NextVersionTypeRC,
},
@ -316,11 +309,11 @@ func TestReleases_NextVersion(t *testing.T) {
},
{
name: "error on invalid bump",
args: args{
releases: Releases{
Latest: &Tag{Name: "v1.1.1"},
Stable: &Tag{Name: "v1.1.1"},
},
args: args{
versionBump: conventionalcommits.UnknownVersion,
nextVersionType: NextVersionTypeUndefined,
@ -331,11 +324,11 @@ func TestReleases_NextVersion(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.releases.NextVersion(tt.args.versionBump, tt.args.nextVersionType)
if !tt.wantErr(t, err, fmt.Sprintf("Releases(%v, %v).NextVersion(%v, %v)", tt.releases.Latest, tt.releases.Stable, tt.args.versionBump, tt.args.nextVersionType)) {
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, "Releases(%v, %v).NextVersion(%v, %v)", tt.releases.Latest, tt.releases.Stable, tt.args.versionBump, tt.args.nextVersionType)
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)
})
}
}