Skip to content
Snippets Groups Projects
Verified Commit 01616479 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: use semantic struct

parent a0608f85
No related branches found
No related tags found
No related merge requests found
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"time" "time"
"github.com/jessevdk/go-flags" "github.com/jessevdk/go-flags"
"github.com/go-git/go-git/v5"
) )
var ( var (
...@@ -65,12 +67,16 @@ func increaseMajor() (string, error) { ...@@ -65,12 +67,16 @@ func increaseMajor() (string, error) {
return next.String(), nil return next.String(), nil
} }
var gitRepo *git.Repository
func executeCommand() { func executeCommand() {
arguments = new(commandLineOptions) arguments = new(commandLineOptions)
p := flags.NewParser(arguments, flags.Default) p := flags.NewParser(arguments, flags.Default)
_, err := p.Parse() var err error
_, err = p.Parse()
if err != nil { if err != nil {
os.Exit(-1) os.Exit(-1)
} }
...@@ -83,8 +89,17 @@ func executeCommand() { ...@@ -83,8 +89,17 @@ func executeCommand() {
var newVersion string var newVersion string
command := activeCommand.Name command := activeCommand.Name
if arguments.Git || command == "auto" {
gitRepo, err = git.PlainOpen(".")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
if command == "auto" { if command == "auto" {
updateType, err := GetCommitType(".") updateType, err := GetCommitType(gitRepo)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(-1) os.Exit(-1)
...@@ -108,13 +123,13 @@ func executeCommand() { ...@@ -108,13 +123,13 @@ func executeCommand() {
switch command { switch command {
case "print": case "print":
if arguments.Git { if arguments.Git {
version, err := getLatestSemanticTag(".") version, err := getLatestSemanticTag(gitRepo)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(-1) os.Exit(-1)
} }
fmt.Printf("%s", version) fmt.Printf("%s", version.String())
os.Exit(0) os.Exit(0)
} }
case "date": case "date":
......
...@@ -2,17 +2,51 @@ package main ...@@ -2,17 +2,51 @@ package main
import ( import (
"fmt" "fmt"
"strings"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
"strings"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer"
) )
var versionRegex = regexp.MustCompile(`^v?(\d+)\.(\d+)\.(\d+).*$`)
type SemanticVersion struct {
Major int
Minor int
Patch int
Tag string
}
func (v SemanticVersion) String() string {
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
}
func ParseSemanticVersion(tag string) SemanticVersion {
matches := versionRegex.FindStringSubmatch(tag)
major, _ := strconv.Atoi(matches[1])
minor, _ := strconv.Atoi(matches[2])
patch, _ := strconv.Atoi(matches[3])
return SemanticVersion{major, minor, patch, tag}
}
func (v SemanticVersion) IsLessThan(w SemanticVersion) bool {
if v.Major != w.Major {
return v.Major < w.Major
}
if v.Minor != w.Minor {
return v.Minor < w.Minor
}
if v.Patch != w.Patch {
return v.Patch < w.Patch
}
return false
}
type CommitType int type CommitType int
const ( const (
...@@ -22,13 +56,14 @@ const ( ...@@ -22,13 +56,14 @@ const (
FixCommit FixCommit
) )
func GetCommitType(path string) (CommitType, error) { func GetCommitType(r *git.Repository) (CommitType, error) {
r, err := git.PlainOpen(path)
latestTag, err := getLatestSemanticTag(r)
if err != nil { if err != nil {
return OtherCommit, fmt.Errorf("failed to open repository: %v", err) return OtherCommit, err
} }
tagCommit, err := getLatestTagCommit(r) tagCommit, err := getTagCommit(r, latestTag.Tag)
if err != nil { if err != nil {
return OtherCommit, err return OtherCommit, err
} }
...@@ -41,109 +76,62 @@ func GetCommitType(path string) (CommitType, error) { ...@@ -41,109 +76,62 @@ func GetCommitType(path string) (CommitType, error) {
return commitType, nil return commitType, nil
} }
func getLatestSemanticTag(path string) (string, error) { func getLatestSemanticTag(r *git.Repository) (SemanticVersion, error) {
r, err := git.PlainOpen(path)
if err != nil {
return "", fmt.Errorf("failed to open repository: %v", err)
}
tagList, err := getSemanticTags(r) tagList, err := getSemanticTags(r)
if err != nil { if err != nil {
return "", err return SemanticVersion{}, err
} }
if len(tagList) == 0 { if len(tagList) == 0 {
return "", fmt.Errorf("no semantic tags found") return SemanticVersion{}, fmt.Errorf("no semantic tags found")
} }
sort.Slice(tagList, func(i, j int) bool {
return compareSemanticVersions(tagList[i], tagList[j])
})
return tagList[len(tagList)-1], nil return tagList[len(tagList)-1], nil
} }
func getSemanticTags(r *git.Repository) ([]string, error) { func getTagCommit(r *git.Repository, tag string) (*object.Commit, error) {
tags, err := r.Tags() tags, err := r.Tags()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get tags: %v", err) return nil, fmt.Errorf("failed to get tags: %v", err)
} }
var tagList []string var tagCommit *object.Commit
err = tags.ForEach(func(tag *plumbing.Reference) error { err = tags.ForEach(func(t *plumbing.Reference) error {
tagName := tag.Name().Short() if t.Name().Short() == tag {
if isSemanticVersion(tagName) { tagObj, err := r.TagObject(t.Hash())
tagList = append(tagList, tagName) if err != nil {
return err
}
tagCommit, err = tagObj.Commit()
if err != nil {
return err
}
return storer.ErrStop // stop iteration
} }
return nil return nil
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to iterate over tags: %v", err) return nil, fmt.Errorf("failed to iterate over tags: %v", err)
} }
if tagCommit == nil {
return tagList, nil return nil, fmt.Errorf("tag commit not found in commit log")
}
func isSemanticVersion(tagName string) bool {
versionRegex := regexp.MustCompile(`^v?\d+\.\d+\.\d+.*$`)
return versionRegex.MatchString(tagName)
}
func compareSemanticVersions(a, b string) bool {
// Remove leading "v" if present
a = strings.TrimPrefix(a, "v")
b = strings.TrimPrefix(b, "v")
aParts := strings.Split(a, ".")
bParts := strings.Split(b, ".")
for i := 0; i < len(aParts) && i < len(bParts); i++ {
aNum, _ := strconv.Atoi(aParts[i])
bNum, _ := strconv.Atoi(bParts[i])
if aNum != bNum {
return aNum < bNum
}
} }
return len(aParts) < len(bParts) return tagCommit, nil
} }
func getLatestTagCommit(r *git.Repository) (*object.Commit, error) { func getSemanticTags(r *git.Repository) ([]SemanticVersion, error) {
tags, err := r.Tags() tags, err := r.Tags()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get tags: %v", err) return nil, fmt.Errorf("failed to get tags: %v", err)
} }
var mostRecentTag *plumbing.Reference var tagList []SemanticVersion
var mostRecentCommit *object.Commit
err = tags.ForEach(func(tag *plumbing.Reference) error { err = tags.ForEach(func(tag *plumbing.Reference) error {
obj, err := r.TagObject(tag.Hash()) tagName := tag.Name().Short()
if err != nil { if versionRegex.MatchString(tagName) {
if err == plumbing.ErrObjectNotFound { tagList = append(tagList, ParseSemanticVersion(tagName))
// If the tag object is not found, it's likely a lightweight tag pointing directly to a commit
commit, err := r.CommitObject(tag.Hash())
if err != nil {
return err
}
obj = &object.Tag{Tagger: commit.Author, Message: commit.Message}
} else {
return err
}
}
commit, err := obj.Commit()
if err != nil {
return err
}
if mostRecentTag == nil || commit.Committer.When.After(mostRecentCommit.Committer.When) {
mostRecentTag = tag
mostRecentCommit = commit
} }
return nil return nil
}) })
...@@ -151,7 +139,11 @@ func getLatestTagCommit(r *git.Repository) (*object.Commit, error) { ...@@ -151,7 +139,11 @@ func getLatestTagCommit(r *git.Repository) (*object.Commit, error) {
return nil, fmt.Errorf("failed to iterate over tags: %v", err) return nil, fmt.Errorf("failed to iterate over tags: %v", err)
} }
return mostRecentCommit, nil sort.Slice(tagList, func(i, j int) bool {
return tagList[i].IsLessThan(tagList[j])
})
return tagList, nil
} }
func getCommitTypeSinceTag(r *git.Repository, tagCommit *object.Commit) (CommitType, error) { func getCommitTypeSinceTag(r *git.Repository, tagCommit *object.Commit) (CommitType, error) {
...@@ -196,19 +188,14 @@ func getCommitTypeSinceTag(r *git.Repository, tagCommit *object.Commit) (CommitT ...@@ -196,19 +188,14 @@ func getCommitTypeSinceTag(r *git.Repository, tagCommit *object.Commit) (CommitT
} }
func containsBreakingChangeFooter(message string) bool { func containsBreakingChangeFooter(message string) bool {
// Split the commit message by newline to get the footer text
lines := strings.Split(message, "\n") lines := strings.Split(message, "\n")
// Check last lines for the "BREAKING CHANGE" footer
for i := len(lines) - 1; i >= 0; i-- { for i := len(lines) - 1; i >= 0; i-- {
if strings.TrimSpace(lines[i]) == "" { if strings.TrimSpace(lines[i]) == "" {
// Stop checking when an empty line (beginning of the commit body) is reached
break break
} }
if strings.Contains(strings.ToLower(lines[i]), "breaking change:") { if strings.Contains(strings.ToLower(lines[i]), "breaking change:") {
return true return true
} }
} }
return false return false
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment