Files
bumpversion/src/main.go
2025-03-29 20:10:55 +03:00

209 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/go-ini/ini"
)
type BumpConfig struct {
CurrentVersion string
Commit bool
Tag bool
TagName string
Parse string
Serialize string
Message string
FilePaths []string
}
func loadConfig(configPath string) (*ini.File, error) {
return ini.Load(configPath)
}
func getBumpConfig(cfg *ini.File) (*BumpConfig, error) {
sec, err := cfg.GetSection("bumpversion")
if err != nil {
return nil, fmt.Errorf("missing [bumpversion] section: %w", err)
}
bc := &BumpConfig{
CurrentVersion: sec.Key("current_version").String(),
Commit: sec.Key("commit").MustBool(false),
Tag: sec.Key("tag").MustBool(false),
TagName: sec.Key("tag_name").String(),
Parse: sec.Key("parse").String(),
Serialize: strings.TrimSpace(sec.Key("serialize").String()),
Message: sec.Key("message").String(),
}
// Получим список файлов для обновления из секций, начинающихся с "bumpversion:file:"
for _, s := range cfg.Sections() {
if strings.HasPrefix(s.Name(), "bumpversion:file:") {
// Имя файла после последнего двоеточия:
parts := strings.SplitN(s.Name(), ":", 3)
if len(parts) == 3 {
filePath := strings.TrimSpace(parts[2])
bc.FilePaths = append(bc.FilePaths, filePath)
}
}
}
return bc, nil
}
// bumpVersion парсит current_version, увеличивает нужную часть и возвращает новую версию.
func bumpVersion(bc *BumpConfig, part string) (string, error) {
re, err := regexp.Compile(bc.Parse)
if err != nil {
return "", fmt.Errorf("failed to compile parse regex: %w", err)
}
matches := re.FindStringSubmatch(bc.CurrentVersion)
if matches == nil {
return "", fmt.Errorf("current version %s does not match parse pattern", bc.CurrentVersion)
}
// Создадим карту групп по именам
groupNames := re.SubexpNames()
var major, minor, patch int
for i, name := range groupNames {
switch name {
case "major":
major, err = strconv.Atoi(matches[i])
if err != nil {
return "", err
}
case "minor":
minor, err = strconv.Atoi(matches[i])
if err != nil {
return "", err
}
case "patch":
patch, err = strconv.Atoi(matches[i])
if err != nil {
return "", err
}
}
}
switch part {
case "major":
major++
minor = 0
patch = 0
case "minor":
minor++
patch = 0
case "patch":
patch++
default:
return "", fmt.Errorf("unknown bump part: %s", part)
}
newVersion := bc.Serialize
newVersion = strings.ReplaceAll(newVersion, "{major}", fmt.Sprintf("%d", major))
newVersion = strings.ReplaceAll(newVersion, "{minor}", fmt.Sprintf("%d", minor))
newVersion = strings.ReplaceAll(newVersion, "{patch}", fmt.Sprintf("%d", patch))
return newVersion, nil
}
func updateFiles(filePaths []string, oldVersion, newVersion string) {
for _, path := range filePaths {
data, err := os.ReadFile(path)
if err != nil {
log.Printf("Unable to read file %s: %v", path, err)
continue
}
newData := strings.ReplaceAll(string(data), oldVersion, newVersion)
if err := os.WriteFile(path, []byte(newData), 0644); err != nil {
log.Printf("Unable to write file %s: %v", path, err)
} else {
log.Printf("Updated file: %s", path)
}
}
}
func gitCommitAndTag(currentVersion, newVersion string, bc *BumpConfig) error {
commitMsg := strings.ReplaceAll(bc.Message, "{current_version}", currentVersion)
commitMsg = strings.ReplaceAll(commitMsg, "{new_version}", newVersion)
if bc.Commit {
if err := exec.Command("git", "add", ".").Run(); err != nil {
return fmt.Errorf("git add error: %w", err)
}
if err := exec.Command("git", "commit", "-m", commitMsg).Run(); err != nil {
return fmt.Errorf("git commit error: %w", err)
}
log.Printf("Committed with message: %s", commitMsg)
}
if bc.Tag {
tagName := strings.ReplaceAll(bc.TagName, "{new_version}", newVersion)
if err := exec.Command("git", "tag", tagName).Run(); err != nil {
return fmt.Errorf("git tag error: %w", err)
}
log.Printf("Created git tag: %s", tagName)
}
return nil
}
func updateConfigFile(configPath string, newVersion string) error {
cfg, err := ini.Load(configPath)
if err != nil {
return err
}
cfg.Section("bumpversion").Key("current_version").SetValue(newVersion)
return cfg.SaveTo(configPath)
}
// Версия приложения
const (
AppName = "BumpVersion"
AppVersion = "0.1.0"
)
func main() {
// Проверяем аргументы командной строки
args := os.Args[1:]
if len(args) > 0 && strings.ToLower(args[0]) == "--version" {
fmt.Printf("%s version %s\n", AppName, AppVersion)
return
}
// Печатаем название и версию при старте
fmt.Printf("Starting %s version %s\n", AppName, AppVersion)
part := flag.String("part", "patch", "Part to bump: major, minor, patch")
configPath := flag.String("config", ".bumpversion.cfg", "Path to config file")
flag.Parse()
cfgFile, err := loadConfig(*configPath)
if err != nil {
log.Fatalf("Error loading config: %v", err)
}
bc, err := getBumpConfig(cfgFile)
if err != nil {
log.Fatalf("Error reading bumpversion configuration: %v", err)
}
fmt.Printf("Current version: %s\n", bc.CurrentVersion)
newVersion, err := bumpVersion(bc, *part)
if err != nil {
log.Fatalf("Error bumping version: %v", err)
}
fmt.Printf("New version: %s\n", newVersion)
// Обновляем файлы, указанные в конфигурации
updateFiles(bc.FilePaths, bc.CurrentVersion, newVersion)
// Выполняем git commit и tag, если требуется
if err := gitCommitAndTag(bc.CurrentVersion, newVersion, bc); err != nil {
log.Printf("Git commit/tag error: %v", err)
}
// Обновляем конфигурационный файл
if err := updateConfigFile(*configPath, newVersion); err != nil {
log.Printf("Error updating config file: %v", err)
}
}