0
0
mirror of https://github.com/thegeeklab/git-sv.git synced 2024-11-21 12:00:40 +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.
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.

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 {
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
@ -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
}
@ -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):
@ -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 {
@ -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 {
@ -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
@ -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))
}

View File

@ -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)"},
},
},
{

View File

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

View File

@ -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
@ -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 {
@ -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
}
}

View File

@ -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)
}
})
}