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 (
"time"
"github.com/jessevdk/go-flags"
"github.com/go-git/go-git/v5"
)
var (
......@@ -65,12 +67,16 @@ func increaseMajor() (string, error) {
return next.String(), nil
}
var gitRepo *git.Repository
func executeCommand() {
arguments = new(commandLineOptions)
p := flags.NewParser(arguments, flags.Default)
_, err := p.Parse()
var err error
_, err = p.Parse()
if err != nil {
os.Exit(-1)
}
......@@ -83,8 +89,17 @@ func executeCommand() {
var newVersion string
command := activeCommand.Name
if arguments.Git || command == "auto" {
gitRepo, err = git.PlainOpen(".")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
if command == "auto" {
updateType, err := GetCommitType(".")
updateType, err := GetCommitType(gitRepo)
if err != nil {
fmt.Println(err)
os.Exit(-1)
......@@ -108,13 +123,13 @@ func executeCommand() {
switch command {
case "print":
if arguments.Git {
version, err := getLatestSemanticTag(".")
version, err := getLatestSemanticTag(gitRepo)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
fmt.Printf("%s", version)
fmt.Printf("%s", version.String())
os.Exit(0)
}
case "date":
......
......@@ -2,17 +2,51 @@ package main
import (
"fmt"
"strings"
"regexp"
"sort"
"strconv"
"strings"
"github.com/go-git/go-git/v5"
"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/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
const (
......@@ -22,13 +56,14 @@ const (
FixCommit
)
func GetCommitType(path string) (CommitType, error) {
r, err := git.PlainOpen(path)
func GetCommitType(r *git.Repository) (CommitType, error) {
latestTag, err := getLatestSemanticTag(r)
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 {
return OtherCommit, err
}
......@@ -41,109 +76,62 @@ func GetCommitType(path string) (CommitType, error) {
return commitType, nil
}
func getLatestSemanticTag(path string) (string, error) {
r, err := git.PlainOpen(path)
if err != nil {
return "", fmt.Errorf("failed to open repository: %v", err)
}
func getLatestSemanticTag(r *git.Repository) (SemanticVersion, error) {
tagList, err := getSemanticTags(r)
if err != nil {
return "", err
return SemanticVersion{}, err
}
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
}
func getSemanticTags(r *git.Repository) ([]string, error) {
func getTagCommit(r *git.Repository, tag string) (*object.Commit, error) {
tags, err := r.Tags()
if err != nil {
return nil, fmt.Errorf("failed to get tags: %v", err)
}
var tagList []string
err = tags.ForEach(func(tag *plumbing.Reference) error {
tagName := tag.Name().Short()
if isSemanticVersion(tagName) {
tagList = append(tagList, tagName)
var tagCommit *object.Commit
err = tags.ForEach(func(t *plumbing.Reference) error {
if t.Name().Short() == tag {
tagObj, err := r.TagObject(t.Hash())
if err != nil {
return err
}
tagCommit, err = tagObj.Commit()
if err != nil {
return err
}
return storer.ErrStop // stop iteration
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to iterate over tags: %v", err)
}
return tagList, nil
}
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
}
if tagCommit == nil {
return nil, fmt.Errorf("tag commit not found in commit log")
}
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()
if err != nil {
return nil, fmt.Errorf("failed to get tags: %v", err)
}
var mostRecentTag *plumbing.Reference
var mostRecentCommit *object.Commit
var tagList []SemanticVersion
err = tags.ForEach(func(tag *plumbing.Reference) error {
obj, err := r.TagObject(tag.Hash())
if err != nil {
if err == plumbing.ErrObjectNotFound {
// 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
tagName := tag.Name().Short()
if versionRegex.MatchString(tagName) {
tagList = append(tagList, ParseSemanticVersion(tagName))
}
return nil
})
......@@ -151,7 +139,11 @@ func getLatestTagCommit(r *git.Repository) (*object.Commit, error) {
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) {
......@@ -196,19 +188,14 @@ func getCommitTypeSinceTag(r *git.Repository, tagCommit *object.Commit) (CommitT
}
func containsBreakingChangeFooter(message string) bool {
// Split the commit message by newline to get the footer text
lines := strings.Split(message, "\n")
// Check last lines for the "BREAKING CHANGE" footer
for i := len(lines) - 1; i >= 0; i-- {
if strings.TrimSpace(lines[i]) == "" {
// Stop checking when an empty line (beginning of the commit body) is reached
break
}
if strings.Contains(strings.ToLower(lines[i]), "breaking change:") {
return true
}
}
return false
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment