144 lines
4.1 KiB
Go
Raw Normal View History

2025-05-18 18:08:01 -04:00
package main
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
)
type VERSION struct {
Filename string
Major, Minor, Patch, Revision uint64
HasMajor, HasMinor, HasPatch, HasRevision bool
}
func (v *VERSION) Flush() error {
return os.WriteFile(v.Filename, []byte(v.String()), 0664)
}
func (v VERSION) String() string {
var buffer strings.Builder
if v.HasMajor || v.HasMinor || v.HasPatch || v.HasRevision {
buffer.WriteString(fmt.Sprintf("%d", v.Major))
}
if v.HasMinor || v.HasPatch || v.HasRevision {
buffer.WriteString(fmt.Sprintf(".%d", v.Minor))
}
if v.HasPatch || v.HasRevision {
buffer.WriteString(fmt.Sprintf(".%d", v.Patch))
}
if v.HasRevision {
buffer.WriteString(fmt.Sprintf("-%d", v.Revision))
}
return buffer.String()
}
func NewVersion(filename string) (VERSION, error) {
f, err := os.ReadFile(filename)
if err != nil {
return VERSION{}, fmt.Errorf("error reading %s: %w", filename, err)
}
fields := strings.FieldsFunc(string(f), func(r rune) bool { return r == '.' || r == '-' })
if len(fields) > 4 {
return VERSION{}, fmt.Errorf("invalid VERSION file format - %d fields (expected 0-4)", len(fields))
}
results := VERSION{
Filename: filename,
}
if len(fields) > 0 {
results.Major, err = strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return VERSION{}, fmt.Errorf("error parsing major version: %w", err)
}
results.HasMajor = true
}
if len(fields) > 1 {
results.Minor, err = strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return VERSION{}, fmt.Errorf("error parsing minor version: %w", err)
}
results.HasMinor = true
}
if len(fields) > 2 {
results.Patch, err = strconv.ParseUint(fields[2], 10, 64)
if err != nil {
return VERSION{}, fmt.Errorf("error parsing patch version: %w", err)
}
results.HasPatch = true
}
if len(fields) > 3 {
results.Revision, err = strconv.ParseUint(fields[3], 10, 64)
if err != nil {
return VERSION{}, fmt.Errorf("error parsing revision: %w", err)
}
results.HasRevision = true
}
return results, nil
}
func IncrementVersion(version VERSION, major, minor, patch, revision bool) VERSION {
if major {
version.Major++
version.HasMajor = true
}
if minor {
version.Minor++
version.HasMinor = true
}
if patch {
version.Patch++
version.HasPatch = true
}
if revision {
version.Revision++
version.HasRevision = true
}
return version
}
func main() {
flag.Usage = func() {
fmt.Println("usage: increment_version <path to VERSION file>")
fmt.Println(" increment_version <optional flags> <path to VERSION file>")
fmt.Println("increment_version increments the version of a given VERSION file.")
fmt.Println("It accepts the following formats:")
fmt.Println("\t<major>.<minor>.<patch>-<revision>")
fmt.Println("\t<major>.<minor>.<patch>")
fmt.Println("\t<major>.<minor>")
fmt.Println("\t<major>")
fmt.Println("the following flags are used to determine which fields to increment.")
fmt.Println("using a flag without a corresponding value in the VERSION file will result")
fmt.Println("in that field, along with all the necessary fields to the left being")
fmt.Println("created inside that file.")
}
var major, minor, patch, revision, dry bool
flag.BoolVar(&major, "major", false, "increment major version")
flag.BoolVar(&minor, "minor", false, "increment minor version")
flag.BoolVar(&patch, "patch", false, "increment patch version")
flag.BoolVar(&revision, "revision", false, "increment revision")
flag.BoolVar(&dry, "dry", true, "do not change the file (just print the new version)")
flag.Parse()
args := flag.Args()
if len(args) == 0 {
fmt.Println("requires path to VERSION file")
flag.Usage()
os.Exit(1)
}
filename := strings.Join(args, " ")
oldversion, err := NewVersion(filename)
if err != nil {
fmt.Printf("error loading version file %s: %v\n", filename, err)
os.Exit(1)
}
newversion := IncrementVersion(oldversion, major, minor, patch, revision)
fmt.Printf("'%s' => '%s'\n", oldversion, newversion)
if !dry {
err = newversion.Flush()
if err != nil {
fmt.Printf("error writing updated VERSION to %s: %v\n", filename, err)
}
}
}