From 1a37cf2aed433922c9c0698c813d85a21730640c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 12 Jun 2019 01:45:28 -0600 Subject: [PATCH] add version info to build --- .gitignore | 1 + README.md | 4 +- cmd/watchdog/watchdog.go | 23 ++++++++ tools/version/version.go | 123 +++++++++++++++++++++++++++++++++++++++ watchdog.go | 6 +- 5 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 tools/version/version.go diff --git a/.gitignore b/.gitignore index e59495e..b0690ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ watchdog +generated-version.go diff --git a/README.md b/README.md index 478e185..f8ad0b2 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Git: ```bash git clone https://git.coolaj86.com/coolaj86/watchdog.go.git pushd watchdog.go/ -go build -o bin/watchdog +go generate -mod=vendor ./... +pushd cmd/watchdog +go build -mod=vendor ``` Zip: diff --git a/cmd/watchdog/watchdog.go b/cmd/watchdog/watchdog.go index bbe968e..c1b3818 100644 --- a/cmd/watchdog/watchdog.go +++ b/cmd/watchdog/watchdog.go @@ -1,3 +1,6 @@ +//go:generate go run ../../tools/version/version.go + +// Watchdog Binary package main import ( @@ -6,15 +9,35 @@ import ( "io/ioutil" "log" "os" + "strings" watchdog "git.rootprojects.org/root/watchdog.go" ) +var ( + Version = "v0.0.0" + GitRev = "00000000" + Timestamp = "0000-00-00T00:00:00Z" +) + func usage() { fmt.Println("Usage: watchdog -c config.json") } func main() { + for i := range os.Args { + switch { + case strings.HasSuffix(os.Args[i], "version"): + fmt.Println(Timestamp) + fmt.Println(Version) + fmt.Println(GitRev) + os.Exit(0) + case strings.HasSuffix(os.Args[i], "help"): + usage() + os.Exit(0) + } + } + if 3 != len(os.Args) { usage() os.Exit(1) diff --git a/tools/version/version.go b/tools/version/version.go new file mode 100644 index 0000000..f1a04a4 --- /dev/null +++ b/tools/version/version.go @@ -0,0 +1,123 @@ +// +build tools + +package main + +import ( + "bytes" + "fmt" + "go/format" + "os" + "os/exec" + "regexp" + "strconv" + "strings" + "text/template" + "time" +) + +var exactVer *regexp.Regexp +var gitVer *regexp.Regexp +var verFile = "generated-version.go" + +func init() { + // exactly vX.Y.Z (go-compatible semver) + exactVer = regexp.MustCompile(`^v\d+\.\d+\.\d+$`) + + // vX.Y.Z-n-g0000000 git post-release, semver prerelease + gitVer = regexp.MustCompile(`^(v\d+\.\d+)\.(\d+)-(\d+)-g`) +} + +// Goal: Either use an exact version like v1.0.0 +// or translate the git version like v1.0.0-4-g0000000 +// to a semver like v1.0.1-pre4+g0000000 +// Don't fail when git repo isn't available. +func main() { + desc := gitDesc() + rev := gitRev() + ver := semVer(desc) + ts := time.Now().Format(time.RFC3339) + + v := struct { + Timestamp string + Version string + GitRev string + }{ + Timestamp: ts, + Version: ver, + GitRev: rev, + } + + // Create or overwrite the go file from template + var buf bytes.Buffer + if err := versionTpl.Execute(&buf, v); nil != err { + panic(err) + } + + // Format + src, err := format.Source(buf.Bytes()) + if nil != err { + panic(err) + } + + // Write to disk (in the Current Working Directory) + f, err := os.Create(verFile) + if nil != err { + panic(err) + } + if _, err := f.Write(src); nil != err { + panic(err) + } + if err := f.Close(); nil != err { + panic(err) + } +} + +func gitDesc() string { + args := strings.Split("git describe --tags --dirty --always", " ") + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if nil != err { + // Don't panic, just carry on + out = []byte("v0.0.0-0-g0000000") + } + return strings.TrimSpace(string(out)) +} + +func gitRev() string { + args := strings.Split("git rev-parse HEAD", " ") + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if nil != err { + // Don't panic, just carry on + out = []byte("00000000000000000") + } + return strings.TrimSpace(string(out)) +} + +func semVer(desc string) string { + var ver string + if exactVer.MatchString(desc) { + // v1.0.0 + ver = desc + } else if gitVer.MatchString(desc) { + // ((v1.0).(0)-(1)) + vers := gitVer.FindStringSubmatch(desc) + patch, err := strconv.Atoi(vers[2]) + if nil != err { + panic(err) + } + // v1.0.1-pre1 + ver = fmt.Sprintf("%s.%d-pre%s", vers[1], patch+1, vers[3]) + } + return ver +} + +var versionTpl = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT. +package main + +func init() { + Timestamp = "{{ .Timestamp }}" + Version = "{{ .Version }}" + GitRev = "{{ .GitRev }}" +} +`)) diff --git a/watchdog.go b/watchdog.go index 134d687..86f77c4 100644 --- a/watchdog.go +++ b/watchdog.go @@ -177,14 +177,14 @@ func (d *Dog) notify(hardFail bool) { // because `{{` gets urlencoded //k = strings.Replace(k, "{{ .Name }}", d.Name, -1) v = strings.Replace(v, "{{ .Name }}", d.Name, -1) - d.logger <- fmt.Sprintf("[HEADER] %s: %s", k, v) + d.Logger <- fmt.Sprintf("[HEADER] %s: %s", k, v) form.Set(k, v) } body = strings.NewReader(form.Encode()) } else if 0 != len(h.JSON) { bodyBuf, err := json.Marshal(h.JSON) if nil != err { - d.logger <- fmt.Sprintf("[Notify] JSON Marshal Error for '%s': %s", h.Name, err) + d.Logger <- fmt.Sprintf("[Notify] JSON Marshal Error for '%s': %s", h.Name, err) continue } // `{{` should be left alone @@ -194,7 +194,7 @@ func (d *Dog) notify(hardFail bool) { client := NewHTTPClient() req, err := http.NewRequest(h.Method, h.URL, body) if nil != err { - d.logger <- fmt.Sprintf("[Notify] HTTP Client Network Error for '%s': %s", h.Name, err) + d.Logger <- fmt.Sprintf("[Notify] HTTP Client Network Error for '%s': %s", h.Name, err) continue }