Skip to content

Commit

Permalink
Merge pull request #19 from bvieira/#17
Browse files Browse the repository at this point in the history
Feature: add next version on changelog
  • Loading branch information
bvieira authored Apr 11, 2021
2 parents 68f1b42 + 82b257b commit 591e00e
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 44 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ Commands like `commit-log` and `commit-notes` has a range option. Supported rang

By default, it's used [--date=short](https://git-scm.com/docs/git-log#Documentation/git-log.txt---dateltformatgt) at `git log`, all dates returned from it will be in `YYYY-MM-DD` format.

Range `tag` will use `git describe` to get the last tag available if `start` is empty, the others types won't use the existing tags. It's recommended to always use a start limit in a old repository with a lot of commits. This behavior was maintained to not break the retrocompatibility.
Range `tag` will use `git for-each-ref refs/tags` to get the last tag available if `start` is empty, the others types won't use the existing tags. It's recommended to always use a start limit in a old repository with a lot of commits. This behavior was maintained to not break the retrocompatibility.

Range `date` use git log `--since` and `--until`. It's possible to use all supported formats from [git log](https://git-scm.com/docs/git-log#Documentation/git-log.txt---sinceltdategt). If `end` is in `YYYY-MM-DD` format, `sv` will add a day on git log command to make the end date inclusive.

Expand Down
59 changes: 36 additions & 23 deletions cmd/git-sv/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ func configShowHandler(cfg Config) func(c *cli.Context) error {

func currentVersionHandler(git sv.Git) func(c *cli.Context) error {
return func(c *cli.Context) error {
describe := git.Describe()
lastTag := git.LastTag()

currentVer, err := sv.ToVersion(describe)
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return err
return fmt.Errorf("error parsing version: %s from git tag, message: %v", lastTag, err)
}
fmt.Printf("%d.%d.%d\n", currentVer.Major(), currentVer.Minor(), currentVer.Patch())
return nil
Expand All @@ -54,19 +54,19 @@ func currentVersionHandler(git sv.Git) func(c *cli.Context) error {

func nextVersionHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error {
describe := git.Describe()
lastTag := git.LastTag()

currentVer, err := sv.ToVersion(describe)
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return fmt.Errorf("error parsing version: %s from describe, message: %v", describe, err)
return fmt.Errorf("error parsing version: %s from git tag, message: %v", lastTag, err)
}

commits, err := git.Log(sv.NewLogRange(sv.TagRange, describe, ""))
commits, err := git.Log(sv.NewLogRange(sv.TagRange, lastTag, ""))
if err != nil {
return fmt.Errorf("error getting git log, message: %v", err)
}

nextVer := semverProcessor.NextVersion(currentVer, commits)
nextVer, _ := semverProcessor.NextVersion(currentVer, commits)
fmt.Printf("%d.%d.%d\n", nextVer.Major(), nextVer.Minor(), nextVer.Patch())
return nil
}
Expand Down Expand Up @@ -119,7 +119,7 @@ func getTagCommits(git sv.Git, tag string) ([]sv.GitCommitLog, error) {
func logRange(git sv.Git, rangeFlag, startFlag, endFlag string) (sv.LogRange, error) {
switch rangeFlag {
case string(sv.TagRange):
return sv.NewLogRange(sv.TagRange, str(startFlag, git.Describe()), endFlag), nil
return sv.NewLogRange(sv.TagRange, str(startFlag, git.LastTag()), endFlag), nil
case string(sv.DateRange):
return sv.NewLogRange(sv.DateRange, startFlag, endFlag), nil
case string(sv.HashRange):
Expand Down Expand Up @@ -164,7 +164,8 @@ func releaseNotesHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor,
if tag := c.String("t"); tag != "" {
rnVersion, date, commits, err = getTagVersionInfo(git, semverProcessor, tag)
} else {
rnVersion, date, commits, err = getNextVersionInfo(git, semverProcessor)
// TODO: should generate release notes if version was not updated?
rnVersion, _, date, commits, err = getNextVersionInfo(git, semverProcessor)
}

if err != nil {
Expand Down Expand Up @@ -223,37 +224,38 @@ func find(tag string, tags []sv.GitTag) int {
return -1
}

func getNextVersionInfo(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) (semver.Version, time.Time, []sv.GitCommitLog, error) {
describe := git.Describe()
func getNextVersionInfo(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) (semver.Version, bool, time.Time, []sv.GitCommitLog, error) {
lastTag := git.LastTag()

currentVer, err := sv.ToVersion(describe)
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return semver.Version{}, time.Time{}, nil, fmt.Errorf("error parsing version: %s from describe, message: %v", describe, err)
return semver.Version{}, false, time.Time{}, nil, fmt.Errorf("error parsing version: %s from git tag, message: %v", lastTag, err)
}

commits, err := git.Log(sv.NewLogRange(sv.TagRange, describe, ""))
commits, err := git.Log(sv.NewLogRange(sv.TagRange, lastTag, ""))
if err != nil {
return semver.Version{}, time.Time{}, nil, fmt.Errorf("error getting git log, message: %v", err)
return semver.Version{}, false, time.Time{}, nil, fmt.Errorf("error getting git log, message: %v", err)
}

return semverProcessor.NextVersion(currentVer, commits), time.Now(), commits, nil
version, updated := semverProcessor.NextVersion(currentVer, commits)
return version, updated, time.Now(), commits, nil
}

func tagHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) func(c *cli.Context) error {
return func(c *cli.Context) error {
describe := git.Describe()
lastTag := git.LastTag()

currentVer, err := sv.ToVersion(describe)
currentVer, err := sv.ToVersion(lastTag)
if err != nil {
return fmt.Errorf("error parsing version: %s from describe, message: %v", describe, err)
return fmt.Errorf("error parsing version: %s from git tag, message: %v", lastTag, err)
}

commits, err := git.Log(sv.NewLogRange(sv.TagRange, describe, ""))
commits, err := git.Log(sv.NewLogRange(sv.TagRange, lastTag, ""))
if err != nil {
return fmt.Errorf("error getting git log, message: %v", err)
}

nextVer := semverProcessor.NextVersion(currentVer, commits)
nextVer, _ := semverProcessor.NextVersion(currentVer, commits)
fmt.Printf("%d.%d.%d\n", nextVer.Major(), nextVer.Minor(), nextVer.Patch())

if err := git.Tag(nextVer); err != nil {
Expand Down Expand Up @@ -344,6 +346,17 @@ func changelogHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor, rnP

size := c.Int("size")
all := c.Bool("all")
addNextVersion := c.Bool("add-next-version")

if addNextVersion {
rnVersion, updated, date, commits, uerr := getNextVersionInfo(git, semverProcessor)
if uerr != nil {
return uerr
}
if updated {
releaseNotes = append(releaseNotes, rnProcessor.Create(&rnVersion, date, commits))
}
}
for i, tag := range tags {
if !all && i >= size {
break
Expand All @@ -361,7 +374,7 @@ func changelogHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor, rnP

currentVer, err := sv.ToVersion(tag.Name)
if err != nil {
return fmt.Errorf("error parsing version: %s from describe, message: %v", tag.Name, err)
return fmt.Errorf("error parsing version: %s from git tag, message: %v", tag.Name, err)
}
releaseNotes = append(releaseNotes, rnProcessor.Create(&currentVer, tag.Date, commits))
}
Expand Down
1 change: 1 addition & 0 deletions cmd/git-sv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func main() {
Flags: []cli.Flag{
&cli.IntFlag{Name: "size", Value: 10, Aliases: []string{"n"}, Usage: "get changelog from last 'n' tags"},
&cli.BoolFlag{Name: "all", Usage: "ignore size parameter, get changelog for every tag"},
&cli.BoolFlag{Name: "add-next-version", Usage: "add next version on change log (commits since last tag, but only if there is a new version to release)"},
},
},
{
Expand Down
8 changes: 4 additions & 4 deletions sv/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (

// Git commands
type Git interface {
Describe() string
LastTag() string
Log(lr LogRange) ([]GitCommitLog, error)
Commit(header, body, footer string) error
Tag(version semver.Version) error
Expand Down Expand Up @@ -78,9 +78,9 @@ func NewGit(messageProcessor MessageProcessor, cfg TagConfig) *GitImpl {
}
}

// Describe runs git describe, it no tag found, return empty
func (GitImpl) Describe() string {
cmd := exec.Command("git", "describe", "--abbrev=0", "--tags")
// LastTag get last tag, if no tag found, return empty
func (GitImpl) LastTag() string {
cmd := exec.Command("git", "for-each-ref", "refs/tags", "--sort", "-creatordate", "--format", "%(refname:short)", "--count", "1")
out, err := cmd.CombinedOutput()
if err != nil {
return ""
Expand Down
12 changes: 6 additions & 6 deletions sv/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func ToVersion(value string) (semver.Version, error) {

// SemVerCommitsProcessor interface
type SemVerCommitsProcessor interface {
NextVersion(version semver.Version, commits []GitCommitLog) semver.Version
NextVersion(version semver.Version, commits []GitCommitLog) (semver.Version, bool)
}

// SemVerCommitsProcessorImpl process versions using commit log
Expand All @@ -50,7 +50,7 @@ func NewSemVerCommitsProcessor(vcfg VersioningConfig, mcfg CommitMessageConfig)
}

// NextVersion calculates next version based on commit log
func (p SemVerCommitsProcessorImpl) NextVersion(version semver.Version, commits []GitCommitLog) semver.Version {
func (p SemVerCommitsProcessorImpl) NextVersion(version semver.Version, commits []GitCommitLog) (semver.Version, bool) {
var versionToUpdate = none
for _, commit := range commits {
if v := p.versionTypeToUpdate(commit); v > versionToUpdate {
Expand All @@ -60,13 +60,13 @@ func (p SemVerCommitsProcessorImpl) NextVersion(version semver.Version, commits

switch versionToUpdate {
case major:
return version.IncMajor()
return version.IncMajor(), true
case minor:
return version.IncMinor()
return version.IncMinor(), true
case patch:
return version.IncPatch()
return version.IncPatch(), true
default:
return version
return version, false
}
}

Expand Down
25 changes: 15 additions & 10 deletions sv/semver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,26 @@ func TestSemVerCommitsProcessorImpl_NextVersion(t *testing.T) {
version semver.Version
commits []GitCommitLog
want semver.Version
wantUpdated bool
}{
{"no update", true, version("0.0.0"), []GitCommitLog{}, version("0.0.0")},
{"no update on unknown type", true, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.0")},
{"no update on unmapped known type", false, version("0.0.0"), []GitCommitLog{commitlog("none", map[string]string{})}, version("0.0.0")},
{"update patch on unknown type", false, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.1")},
{"patch update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{})}, version("0.0.1")},
{"minor update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("minor", map[string]string{})}, version("0.1.0")},
{"major update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("major", map[string]string{})}, version("1.0.0")},
{"breaking change update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("patch", map[string]string{"breaking-change": "break"})}, version("1.0.0")},
{"no update", true, version("0.0.0"), []GitCommitLog{}, version("0.0.0"), false},
{"no update on unknown type", true, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.0"), false},
{"no update on unmapped known type", false, version("0.0.0"), []GitCommitLog{commitlog("none", map[string]string{})}, version("0.0.0"), false},
{"update patch on unknown type", false, version("0.0.0"), []GitCommitLog{commitlog("a", map[string]string{})}, version("0.0.1"), true},
{"patch update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{})}, version("0.0.1"), true},
{"minor update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("minor", map[string]string{})}, version("0.1.0"), true},
{"major update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("major", map[string]string{})}, version("1.0.0"), true},
{"breaking change update", false, version("0.0.0"), []GitCommitLog{commitlog("patch", map[string]string{}), commitlog("patch", map[string]string{"breaking-change": "break"})}, version("1.0.0"), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewSemVerCommitsProcessor(VersioningConfig{UpdateMajor: []string{"major"}, UpdateMinor: []string{"minor"}, UpdatePatch: []string{"patch"}, IgnoreUnknown: tt.ignoreUnknown}, CommitMessageConfig{Types: []string{"major", "minor", "patch", "none"}})
if got := p.NextVersion(tt.version, tt.commits); !reflect.DeepEqual(got, tt.want) {
t.Errorf("SemVerCommitsProcessorImpl.NextVersion() = %v, want %v", got, tt.want)
got, gotUpdated := p.NextVersion(tt.version, tt.commits)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("SemVerCommitsProcessorImpl.NextVersion() Version = %v, want %v", got, tt.want)
}
if tt.wantUpdated != gotUpdated {
t.Errorf("SemVerCommitsProcessorImpl.NextVersion() Updated = %v, want %v", gotUpdated, tt.wantUpdated)
}
})
}
Expand Down

0 comments on commit 591e00e

Please sign in to comment.