feat: preserve fixed upstream versions with fork suffixes
Gitbump CI / test (push) Successful in 46s
Gitbump CI / build (push) Successful in 28s
Gitbump CI / notify-failure (push) Skipped

This commit is contained in:
meda committed 2026-09-25 10:02:33 +02:00
1 parent d6cf715a4a
commit 54a7b1e6d8
4 files changed
+201 -23

No files matched your search

+99 -20
View File
@@ -22,10 +22,20 @@ type Config struct {
dryRun bool
forcedBump string
versionPrefix string
suffixLabel string
patterns Patterns
noPush bool
}
type taggedVersion struct {
major int
minor int
patch int
suffixLabel string
suffixRevision int
hasSuffix bool
}
type Patterns struct {
major []string
minor []string
@@ -74,11 +84,11 @@ func main() {
return
}
major, minor, patch, err := parseVersion(tag, config.versionPrefix)
version, err := parseTaggedVersion(tag, config.versionPrefix)
if err != nil {
log.Fatalf("Error parsing version: %v", err)
}
fmt.Println("version:", major, minor, patch)
fmt.Println("version:", version.major, version.minor, version.patch)
bumpType := getBumpType(commits, config)
fmt.Println("bump type:", bumpType)
@@ -87,7 +97,18 @@ func main() {
return
}
newVersion := calculateNewVersion(major, minor, patch, bumpType, config.versionPrefix)
var newVersion string
if bumpType == "suffix" {
newVersion, err = calculateSuffixVersion(version, config.suffixLabel, config.versionPrefix)
} else {
if version.hasSuffix {
log.Fatalf("Cannot apply %s bump to suffixed version %s", bumpType, tag)
}
newVersion = calculateNewVersion(version.major, version.minor, version.patch, bumpType, config.versionPrefix)
}
if err != nil {
log.Fatalf("Error calculating version: %v", err)
}
fmt.Println("new version:", newVersion)
if config.dryRun {
@@ -109,8 +130,9 @@ func parseFlags() Config {
flag.BoolVar(&config.printVersion, "version", false, "Just print version")
flag.BoolVar(&config.dryRun, "dry-run", getEnvBool("DRY_RUN", false), "Show what would be done without making changes")
flag.StringVar(&config.forcedBump, "bump", os.Getenv("BUMP"), "Force specific bump type (major, minor, or patch)")
flag.StringVar(&config.forcedBump, "bump", getEnv("BUMP", ""), "Force specific bump type (major, minor, patch, or suffix)")
flag.StringVar(&config.versionPrefix, "prefix", getEnv("PREFIX", "v"), "Version prefix (default 'v')")
flag.StringVar(&config.suffixLabel, "suffix", getEnv("SUFFIX", ""), "Suffix label used when creating the first suffix")
flag.BoolVar(&config.noPush, "no-push", getEnvBool("NO_PUSH", false), "Create tag but don't push to remote")
majorPatterns := flag.String("major-patterns", getEnv("MAJOR_PATTERNS", "bump major,breaking change"), "Comma-separated list will be joined with '|' for major version bumps")
@@ -127,20 +149,20 @@ func parseFlags() Config {
chore: strings.Split(*chorePatterns, ","),
}
if config.forcedBump != "" && !contains([]string{"major", "minor", "patch"}, config.forcedBump) {
log.Fatalf("Invalid bump type: %s. Must be one of: major, minor, patch", config.forcedBump)
if config.forcedBump != "" && !contains([]string{"major", "minor", "patch", "suffix"}, config.forcedBump) {
log.Fatalf("Invalid bump type: %s. Must be one of: major, minor, patch, suffix", config.forcedBump)
}
return config
}
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
if value, ok := os.LookupEnv(key); ok && value != "" {
return value
}
// Check for Gitea/GitHub Actions input prefix
inputKey := "INPUT_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_")
if value, ok := os.LookupEnv(inputKey); ok {
if value, ok := os.LookupEnv(inputKey); ok && value != "" {
return value
}
return fallback
@@ -214,29 +236,60 @@ func getCommitsSinceTag(tag string) ([]string, error) {
}
func parseVersion(tag, prefix string) (int, int, int, error) {
tag = strings.TrimPrefix(tag, prefix)
version, err := parseTaggedVersion(tag, prefix)
if err != nil {
return 0, 0, 0, err
}
if version.hasSuffix {
return 0, 0, 0, fmt.Errorf("version has a suffix: %s", tag)
}
return version.major, version.minor, version.patch, nil
}
parts := strings.Split(tag, ".")
func parseTaggedVersion(tag, prefix string) (taggedVersion, error) {
rawVersion := strings.TrimPrefix(tag, prefix)
baseVersion := rawVersion
suffix := ""
if separator := strings.IndexByte(rawVersion, '-'); separator >= 0 {
baseVersion = rawVersion[:separator]
suffix = rawVersion[separator+1:]
}
parts := strings.Split(baseVersion, ".")
if len(parts) != 3 {
return 0, 0, 0, fmt.Errorf("invalid version format: %s", tag)
return taggedVersion{}, fmt.Errorf("invalid version format: %s", rawVersion)
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return 0, 0, 0, err
if err != nil || major < 0 {
return taggedVersion{}, fmt.Errorf("invalid major version: %s", parts[0])
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return 0, 0, 0, err
if err != nil || minor < 0 {
return taggedVersion{}, fmt.Errorf("invalid minor version: %s", parts[1])
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return 0, 0, 0, err
if err != nil || patch < 0 {
return taggedVersion{}, fmt.Errorf("invalid patch version: %s", parts[2])
}
return major, minor, patch, nil
version := taggedVersion{major: major, minor: minor, patch: patch}
if suffix == "" {
return version, nil
}
suffixParts := strings.Split(suffix, ".")
if len(suffixParts) != 2 || suffixParts[0] == "" {
return taggedVersion{}, fmt.Errorf("invalid version suffix: %s", suffix)
}
revision, err := strconv.Atoi(suffixParts[1])
if err != nil || revision < 1 {
return taggedVersion{}, fmt.Errorf("invalid version suffix revision: %s", suffix)
}
version.suffixLabel = suffixParts[0]
version.suffixRevision = revision
version.hasSuffix = true
return version, nil
}
func getBumpType(commits []string, config Config) string {
@@ -289,6 +342,32 @@ func calculateNewVersion(major, minor, patch int, bumpType, prefix string) strin
return fmt.Sprintf("%s%d.%d.%d", prefix, major, minor, patch)
}
func calculateSuffixVersion(version taggedVersion, suffixLabel, prefix string) (string, error) {
if version.hasSuffix {
if suffixLabel != "" && suffixLabel != version.suffixLabel {
return "", fmt.Errorf("suffix label %q does not match %q", suffixLabel, version.suffixLabel)
}
version.suffixRevision++
return formatTaggedVersion(version, prefix), nil
}
if suffixLabel == "" {
return "", fmt.Errorf("suffix label is required for a base version")
}
version.suffixLabel = suffixLabel
version.suffixRevision = 1
version.hasSuffix = true
return formatTaggedVersion(version, prefix), nil
}
func formatTaggedVersion(version taggedVersion, prefix string) string {
base := fmt.Sprintf("%s%d.%d.%d", prefix, version.major, version.minor, version.patch)
if version.hasSuffix {
return fmt.Sprintf("%s-%s.%d", base, version.suffixLabel, version.suffixRevision)
}
return base
}
func createAndPushTag(version string, noPush bool) error {
if out, err := git("tag " + version); err != nil {
return fmt.Errorf("error creating tag: %v; stdout: %v", err, out)