mirror of
https://github.com/lukaszraczylo/semver-generator.git
synced 2026-10-12 00:45:45 +00:00
Fuzzy matching compares each keyword with every word of the commit message. A word matches if it contains the keyword's letters in order. Thus "feat" matches "fix(delegation):" and "major" matches "deps(majors):", which gives false minor and major versions. The new top-level config key "matching" accepts "fuzzy" (the default) or "regex". In regex mode every wording keyword is a Go regular expression. The tool compiles all of them when it loads the config and runs them against the full commit message, so "(?m)^" anchors a pattern to the start of a line. An invalid value, an invalid pattern, or any other config parse error in regex mode stops the tool with a non-zero exit code. It never falls back to fuzzy matching. Fuzzy mode does not change. The blacklist check moves into one helper that both modes use. A replay of 15 repositories gives the same versions as before. This change improves version precision for the repositories that opt in. Claude-Session: https://claude.ai/code/session_01Qq1gbqQek1Zjtofo4VDRdW
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package utils
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
// CalculateSemver calculates the semantic version based on commit messages
|
|
func CalculateSemver(
|
|
commits []CommitDetails,
|
|
tags []TagDetails,
|
|
wording Wording,
|
|
matcher KeywordMatcher,
|
|
blacklist []string,
|
|
initialSemver SemVer,
|
|
respectExisting bool,
|
|
strictMode bool,
|
|
tagPrefixes []string,
|
|
) SemVer {
|
|
// Default-preserving: callers that don't select a matcher get today's
|
|
// fuzzy behaviour rather than a nil-pointer panic.
|
|
if matcher == nil {
|
|
matcher = FuzzyMatcher{}
|
|
}
|
|
|
|
semver := initialSemver
|
|
startIndex := 0
|
|
|
|
// If respecting existing tags, find the latest tagged commit and start from there
|
|
if respectExisting && len(tags) > 0 {
|
|
latestTagIndex := -1
|
|
var latestTagName string
|
|
|
|
// Find the latest tagged commit (highest index since commits are oldest-first)
|
|
for i, commit := range commits {
|
|
for _, tag := range tags {
|
|
if commit.Hash == tag.Hash {
|
|
if i > latestTagIndex {
|
|
latestTagIndex = i
|
|
latestTagName = tag.Name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we found a tagged commit, use its version and start processing after it
|
|
if latestTagIndex >= 0 {
|
|
Debug("Found latest existing tag", map[string]interface{}{
|
|
"tag": latestTagName,
|
|
"commit": strings.TrimSuffix(commits[latestTagIndex].Message, "\n"),
|
|
})
|
|
semver = ParseExistingSemver(latestTagName, semver, tagPrefixes)
|
|
startIndex = latestTagIndex + 1
|
|
}
|
|
}
|
|
|
|
for _, commit := range commits[startIndex:] {
|
|
// In non-strict mode, increment patch by default
|
|
if !strictMode {
|
|
semver.Patch++
|
|
Debug("Incrementing patch (DEFAULT)", map[string]interface{}{
|
|
"commit": strings.TrimSuffix(commit.Message, "\n"),
|
|
"semver": FormatSemver(semver),
|
|
})
|
|
}
|
|
|
|
// Check for keyword matches
|
|
matchPatch := matcher.Matches(commit.Message, wording.Patch, blacklist)
|
|
matchMinor := matcher.Matches(commit.Message, wording.Minor, blacklist)
|
|
matchMajor := matcher.Matches(commit.Message, wording.Major, blacklist)
|
|
matchReleaseCandidate := matcher.Matches(commit.Message, wording.Release, blacklist)
|
|
|
|
// Apply version changes based on matches
|
|
if matchMajor {
|
|
semver.Major++
|
|
semver.Minor = 0
|
|
semver.Patch = 1
|
|
semver.EnableReleaseCandidate = false
|
|
semver.Release = 0
|
|
Debug("Incrementing major (WORDING)", map[string]interface{}{
|
|
"commit": strings.TrimSuffix(commit.Message, "\n"),
|
|
"semver": FormatSemver(semver),
|
|
})
|
|
continue
|
|
}
|
|
|
|
if matchMinor {
|
|
semver.Minor++
|
|
semver.Patch = 1
|
|
semver.EnableReleaseCandidate = false
|
|
semver.Release = 0
|
|
Debug("Incrementing minor (WORDING)", map[string]interface{}{
|
|
"commit": strings.TrimSuffix(commit.Message, "\n"),
|
|
"semver": FormatSemver(semver),
|
|
})
|
|
continue
|
|
}
|
|
|
|
if matchReleaseCandidate {
|
|
semver.Release++
|
|
semver.Patch = 1
|
|
semver.EnableReleaseCandidate = true
|
|
Debug("Incrementing release candidate (WORDING)", map[string]interface{}{
|
|
"commit": strings.TrimSuffix(commit.Message, "\n"),
|
|
"semver": FormatSemver(semver),
|
|
})
|
|
continue
|
|
}
|
|
|
|
if matchPatch {
|
|
semver.Patch++
|
|
Debug("Incrementing patch (WORDING)", map[string]interface{}{
|
|
"commit": strings.TrimSuffix(commit.Message, "\n"),
|
|
"semver": FormatSemver(semver),
|
|
})
|
|
continue
|
|
}
|
|
}
|
|
|
|
return semver
|
|
}
|