add version parsing from git describe

This commit is contained in:
Mike Bloy 2019-08-24 23:54:06 -05:00
parent 9d85908628
commit 136d2fde51
6 changed files with 182 additions and 7 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
# ---> Go
# Binaries for programs and plugins # Binaries for programs and plugins
gotest
*.exe *.exe
*.exe~ *.exe~
*.dll *.dll

View File

@ -1,9 +1,14 @@
package main package main
import "fmt" import (
"fmt"
"git.bloy.org/mike/gotest/version"
)
func main() { func main() {
fmt.Println(sayHi("Mike")) fmt.Println(sayHi("Mike"))
fmt.Printf("This is %s\n", version.VersionStr)
} }
func sayHi(name string) string { func sayHi(name string) string {

View File

@ -8,11 +8,14 @@ import (
// gitDescribe : Git's "git describe --always --dirty" output // gitDescribe : Git's "git describe --always --dirty" output
var gitDescribe = "" var gitDescribe = ""
// BuildDate : Date of this build in YYYY-MM-DD format
var BuildDate = ""
// GoVersion : The go version used to compile this binary // GoVersion : The go version used to compile this binary
var GoVersion = runtime.Version() var GoVersion = runtime.Version()
// OsArch : The OS and archetcture used for this binary // OsArch : The OS and archetcture used for this binary
var OsArch = fmt.Sprintf("%s (%s)", runtime.GOOS, runtime.GOARCH) var OsArch = fmt.Sprintf("%s (%s)", runtime.GOOS, runtime.GOARCH)
// VersionData : The Info struct of the version specification
var VersionData = parseGitDescribe(gitDescribe)
// VersionStr : The string representation of the version
var VersionStr = VersionData.String()

View File

@ -1,7 +1,9 @@
package version package version
import "testing" import (
import "runtime" "runtime"
"testing"
)
func TestGoVersion(t *testing.T) { func TestGoVersion(t *testing.T) {
got := GoVersion got := GoVersion

105
version/version.go Normal file
View File

@ -0,0 +1,105 @@
package version
import (
"fmt"
"regexp"
"strconv"
"strings"
)
// Info : data structure for program version information
type Info struct {
Major int
Minor int
Patch int
Prerelease []string
BuildInfo []string
}
// Equal compares two Info structs for equality
func (t Info) Equal(o Info) bool {
if t.Major != o.Major || t.Minor != o.Minor || t.Patch != o.Patch {
return false
}
if len(t.Prerelease) != len(o.Prerelease) || len(t.BuildInfo) != len(o.BuildInfo) {
return false
}
for i := 0; i < len(t.Prerelease); i++ {
if t.Prerelease[i] != o.Prerelease[i] {
return false
}
}
for i := 0; i < len(t.BuildInfo); i++ {
if t.BuildInfo[i] != o.BuildInfo[i] {
return false
}
}
return true
}
// String returns a human friendly version string
func (t Info) String() string {
var b strings.Builder
fmt.Fprintf(&b, "v%d.%d.%d", t.Major, t.Minor, t.Patch)
if len(t.Prerelease) > 0 {
fmt.Fprintf(&b, "-%s", strings.Join(t.Prerelease, "."))
}
if len(t.BuildInfo) > 0 {
fmt.Fprintf(&b, "+%s", strings.Join(t.BuildInfo, "."))
}
return b.String()
}
/*
Match groups:
1. version number (without "v")
2. major version
3. minor version
4. patch version
5. prerelease identifier
6. commit count
7. git short hash
8. "-dirty" if dirty, else ""
*/
const reStr = `^(?:v(([0-9]+)(?:\.([0-9]+)(?:\.([0-9]+))?)?))?(?:-((?:alpha|beta|rc|dev)(?:\.[a-zA-Z0-9]+)*))?(?:-([0-9]+)-g)?([a-f0-9A-F]+)?(?:(-dirty))?$`
var re = regexp.MustCompile(reStr)
func parseGitDescribe(input string) Info {
matches := re.FindStringSubmatch(input)
if matches == nil || matches[0] == "" {
return Info{0, 0, 0, []string{"dev"}, []string{"unknown"}}
}
info := Info{}
if matches[2] != "" {
num, _ := strconv.Atoi(matches[2])
info.Major = num
}
if matches[3] != "" {
num, _ := strconv.Atoi(matches[3])
info.Minor = num
}
if matches[4] != "" {
num, _ := strconv.Atoi(matches[4])
info.Patch = num
}
if matches[5] != "" {
info.Prerelease = strings.Split(matches[5], ".")
}
if matches[6] != "" || matches[7] != "" || matches[8] != "" {
info.BuildInfo = append(info.BuildInfo, "git")
if matches[6] != "" {
info.BuildInfo = append(info.BuildInfo, matches[6])
}
if matches[7] != "" {
info.BuildInfo = append(info.BuildInfo, matches[7])
}
if matches[8] != "" {
info.BuildInfo = append(info.BuildInfo, "dirty")
}
}
return info
}

60
version/version_test.go Normal file
View File

@ -0,0 +1,60 @@
package version
import (
"fmt"
"testing"
)
func TestParseGitDescribe(t *testing.T) {
expected := []Info{
Info{0, 0, 0, []string{"dev"}, []string{"unknown"}},
Info{0, 0, 0, []string{}, []string{"git", "feedbeef"}},
Info{1, 2, 0, []string{"dev", "1"}, []string{"git", "83", "feedbeef"}},
Info{1, 2, 3, []string{"dev", "1"}, []string{"git", "83", "feedbeef", "dirty"}},
Info{1, 2, 3, []string{}, []string{"git", "dirty"}},
Info{1, 2, 3, []string{"beta"}, []string{"git", "dirty"}},
Info{1, 2, 3, []string{"rc"}, []string{}},
}
inputs := []string{
"",
"feedbeef",
"v1.2-dev.1-83-gfeedbeef",
"v1.2.3-dev.1-83-gfeedbeef-dirty",
"v1.2.3-dirty",
"v1.2.3-beta-dirty",
"v1.2.3-rc",
}
for i := 0; i < len(expected); i++ {
got := parseGitDescribe(inputs[i])
if !expected[i].Equal(got) {
t.Errorf("Got: %v, Expected: %v", got, expected[i])
}
}
}
func TestInfoStringer(t *testing.T) {
inputs := []Info{
Info{0, 0, 0, []string{"dev"}, []string{"unknown"}},
Info{0, 0, 0, []string{}, []string{"git", "feedbeef"}},
Info{1, 2, 0, []string{"dev", "1"}, []string{"git", "83", "feedbeef"}},
Info{1, 2, 3, []string{"dev", "1"}, []string{"git", "83", "feedbeef", "dirty"}},
Info{1, 2, 3, []string{}, []string{"git", "dirty"}},
Info{1, 2, 3, []string{"beta"}, []string{"git", "dirty"}},
Info{1, 2, 3, []string{"rc"}, []string{}},
}
expected := []string{
"v0.0.0-dev+unknown",
"v0.0.0+git.feedbeef",
"v1.2.0-dev.1+git.83.feedbeef",
"v1.2.3-dev.1+git.83.feedbeef.dirty",
"v1.2.3+git.dirty",
"v1.2.3-beta+git.dirty",
"v1.2.3-rc",
}
for i := 0; i < len(expected); i++ {
got := fmt.Sprint(inputs[i])
if got != expected[i] {
t.Errorf("Got: %v, Expected: %v", got, expected[i])
}
}
}