0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-11-28 16:40:36 +00:00

feat(changelog): add add-next-version flag

issue: #17
This commit is contained in:
Beatriz Vieira 2021-04-10 22:59:30 -03:00
parent 8ff0147ec9
commit a1eaa78a6c
6 changed files with 63 additions and 44 deletions

View File

@ -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. 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. 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.

View File

@ -41,11 +41,11 @@ func configShowHandler(cfg Config) func(c *cli.Context) error {
func currentVersionHandler(git sv.Git) func(c *cli.Context) error { func currentVersionHandler(git sv.Git) func(c *cli.Context) error {
return 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 { 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()) fmt.Printf("%d.%d.%d\n", currentVer.Major(), currentVer.Minor(), currentVer.Patch())
return nil return nil
@ -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 { func nextVersionHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) func(c *cli.Context) error {
return 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 { 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 { if err != nil {
return fmt.Errorf("error getting git log, message: %v", err) 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()) fmt.Printf("%d.%d.%d\n", nextVer.Major(), nextVer.Minor(), nextVer.Patch())
return nil return nil
} }
@ -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) { func logRange(git sv.Git, rangeFlag, startFlag, endFlag string) (sv.LogRange, error) {
switch rangeFlag { switch rangeFlag {
case string(sv.TagRange): 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): case string(sv.DateRange):
return sv.NewLogRange(sv.DateRange, startFlag, endFlag), nil return sv.NewLogRange(sv.DateRange, startFlag, endFlag), nil
case string(sv.HashRange): case string(sv.HashRange):
@ -164,7 +164,8 @@ func releaseNotesHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor,
if tag := c.String("t"); tag != "" { if tag := c.String("t"); tag != "" {
rnVersion, date, commits, err = getTagVersionInfo(git, semverProcessor, tag) rnVersion, date, commits, err = getTagVersionInfo(git, semverProcessor, tag)
} else { } 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 { if err != nil {
@ -223,37 +224,38 @@ func find(tag string, tags []sv.GitTag) int {
return -1 return -1
} }
func getNextVersionInfo(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) (semver.Version, time.Time, []sv.GitCommitLog, error) { func getNextVersionInfo(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) (semver.Version, bool, time.Time, []sv.GitCommitLog, error) {
describe := git.Describe() lastTag := git.LastTag()
currentVer, err := sv.ToVersion(describe) currentVer, err := sv.ToVersion(lastTag)
if err != nil { 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 { 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 { func tagHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor) func(c *cli.Context) error {
return 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 { 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 { if err != nil {
return fmt.Errorf("error getting git log, message: %v", err) 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()) fmt.Printf("%d.%d.%d\n", nextVer.Major(), nextVer.Minor(), nextVer.Patch())
if err := git.Tag(nextVer); err != nil { if err := git.Tag(nextVer); err != nil {
@ -344,6 +346,17 @@ func changelogHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor, rnP
size := c.Int("size") size := c.Int("size")
all := c.Bool("all") 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 { for i, tag := range tags {
if !all && i >= size { if !all && i >= size {
break break
@ -361,7 +374,7 @@ func changelogHandler(git sv.Git, semverProcessor sv.SemVerCommitsProcessor, rnP
currentVer, err := sv.ToVersion(tag.Name) currentVer, err := sv.ToVersion(tag.Name)
if err != nil { 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)) releaseNotes = append(releaseNotes, rnProcessor.Create(&currentVer, tag.Date, commits))
} }

View File

@ -128,6 +128,7 @@ func main() {
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.IntFlag{Name: "size", Value: 10, Aliases: []string{"n"}, Usage: "get changelog from last 'n' tags"}, &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: "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)"},
}, },
}, },
{ {

View File

@ -20,7 +20,7 @@ const (
// Git commands // Git commands
type Git interface { type Git interface {
Describe() string LastTag() string
Log(lr LogRange) ([]GitCommitLog, error) Log(lr LogRange) ([]GitCommitLog, error)
Commit(header, body, footer string) error Commit(header, body, footer string) error
Tag(version semver.Version) error Tag(version semver.Version) error
@ -78,9 +78,9 @@ func NewGit(messageProcessor MessageProcessor, cfg TagConfig) *GitImpl {
} }
} }
// Describe runs git describe, it no tag found, return empty // LastTag get last tag, if no tag found, return empty
func (GitImpl) Describe() string { func (GitImpl) LastTag() string {
cmd := exec.Command("git", "describe", "--abbrev=0", "--tags") cmd := exec.Command("git", "for-each-ref", "refs/tags", "--sort", "-creatordate", "--format", "%(refname:short)", "--count", "1")
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return "" return ""

View File

@ -26,7 +26,7 @@ func ToVersion(value string) (semver.Version, error) {
// SemVerCommitsProcessor interface // SemVerCommitsProcessor interface
type 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 // SemVerCommitsProcessorImpl process versions using commit log
@ -50,7 +50,7 @@ func NewSemVerCommitsProcessor(vcfg VersioningConfig, mcfg CommitMessageConfig)
} }
// NextVersion calculates next version based on commit log // 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 var versionToUpdate = none
for _, commit := range commits { for _, commit := range commits {
if v := p.versionTypeToUpdate(commit); v > versionToUpdate { if v := p.versionTypeToUpdate(commit); v > versionToUpdate {
@ -60,13 +60,13 @@ func (p SemVerCommitsProcessorImpl) NextVersion(version semver.Version, commits
switch versionToUpdate { switch versionToUpdate {
case major: case major:
return version.IncMajor() return version.IncMajor(), true
case minor: case minor:
return version.IncMinor() return version.IncMinor(), true
case patch: case patch:
return version.IncPatch() return version.IncPatch(), true
default: default:
return version return version, false
} }
} }

View File

@ -14,21 +14,26 @@ func TestSemVerCommitsProcessorImpl_NextVersion(t *testing.T) {
version semver.Version version semver.Version
commits []GitCommitLog commits []GitCommitLog
want semver.Version want semver.Version
wantUpdated bool
}{ }{
{"no update", true, version("0.0.0"), []GitCommitLog{}, version("0.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")}, {"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")}, {"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")}, {"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")}, {"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")}, {"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")}, {"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")}, {"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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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"}}) 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) { got, gotUpdated := p.NextVersion(tt.version, tt.commits)
t.Errorf("SemVerCommitsProcessorImpl.NextVersion() = %v, want %v", got, tt.want) 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)
} }
}) })
} }