218 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"go/format"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"text/template"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| var exitCode int
 | |
| var exactVer *regexp.Regexp
 | |
| var gitVer *regexp.Regexp
 | |
| var verFile = "xversion.go"
 | |
| 
 | |
| var (
 | |
| 	GitRev       = "0000000"
 | |
| 	GitVersion   = "v0.0.0-pre0+g0000000"
 | |
| 	GitTimestamp = "0000-00-00T00:00:00+0000"
 | |
| )
 | |
| 
 | |
| 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
 | |
| 	// vX.Y.Z-dirty git post-release, semver prerelease
 | |
| 	gitVer = regexp.MustCompile(`^(v\d+\.\d+)\.(\d+)(-(\d+))?(-(g[0-9a-f]+))?(-(dirty))?`)
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	pkg := "main"
 | |
| 
 | |
| 	args := os.Args[1:]
 | |
| 	for i := range args {
 | |
| 		arg := args[i]
 | |
| 		if "-f" == arg || "--fail" == arg {
 | |
| 			exitCode = 1
 | |
| 		} else if ("--outfile" == arg || "-o" == arg) && len(args) > i+1 {
 | |
| 			verFile = args[i+1]
 | |
| 			args[i+1] = ""
 | |
| 		} else if "--package" == arg && len(args) > i+1 {
 | |
| 			pkg = args[i+1]
 | |
| 			args[i+1] = ""
 | |
| 		} else if "-V" == arg || "version" == arg || "-version" == arg || "--version" == arg {
 | |
| 			fmt.Println(GitRev)
 | |
| 			fmt.Println(GitVersion)
 | |
| 			fmt.Println(GitTimestamp)
 | |
| 			os.Exit(0)
 | |
| 		}
 | |
| 	}
 | |
| 	if "" != os.Getenv("GITVER_FAIL") && "false" != os.Getenv("GITVER_FAIL") {
 | |
| 		exitCode = 1
 | |
| 	}
 | |
| 
 | |
| 	desc, err := gitDesc()
 | |
| 	if nil != err {
 | |
| 		log.Fatalf("Failed to get git version: %s\n", err)
 | |
| 		os.Exit(exitCode)
 | |
| 	}
 | |
| 	rev := gitRev()
 | |
| 	ver := semVer(desc)
 | |
| 	ts, err := gitTimestamp(desc)
 | |
| 	if nil != err {
 | |
| 		ts = time.Now()
 | |
| 	}
 | |
| 
 | |
| 	v := struct {
 | |
| 		Package   string
 | |
| 		Timestamp string
 | |
| 		Version   string
 | |
| 		GitRev    string
 | |
| 	}{
 | |
| 		Package:   pkg,
 | |
| 		Timestamp: ts.Format(time.RFC3339),
 | |
| 		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, error) {
 | |
| 	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 "", err
 | |
| 	}
 | |
| 	return strings.TrimSpace(string(out)), nil
 | |
| }
 | |
| 
 | |
| func gitRev() string {
 | |
| 	args := strings.Split("git rev-parse HEAD", " ")
 | |
| 	cmd := exec.Command(args[0], args[1:]...)
 | |
| 	out, err := cmd.CombinedOutput()
 | |
| 	if nil != err {
 | |
| 		fmt.Fprintf(os.Stderr,
 | |
| 			"\nUnexpected Error\n\n"+
 | |
| 				"Please open an issue at https://git.rootprojects.org/root/go-gitver/issues/new \n"+
 | |
| 				"Please include the following:\n\n"+
 | |
| 				"Command: %s\n"+
 | |
| 				"Output: %s\n"+
 | |
| 				"Error: %s\n"+
 | |
| 				"\nPlease and Thank You.\n\n", strings.Join(args, " "), out, err)
 | |
| 		os.Exit(exitCode)
 | |
| 	}
 | |
| 	return strings.TrimSpace(string(out))
 | |
| }
 | |
| 
 | |
| func semVer(desc string) string {
 | |
| 	if exactVer.MatchString(desc) {
 | |
| 		// v1.0.0
 | |
| 		return desc
 | |
| 	}
 | |
| 
 | |
| 	if !gitVer.MatchString(desc) {
 | |
| 		return ""
 | |
| 	}
 | |
| 
 | |
| 	// (v1.0).(0)(-(1))(-(g0000000))(-(dirty))
 | |
| 	vers := gitVer.FindStringSubmatch(desc)
 | |
| 	patch, err := strconv.Atoi(vers[2])
 | |
| 	if nil != err {
 | |
| 		fmt.Fprintf(os.Stderr,
 | |
| 			"\nUnexpected Error\n\n"+
 | |
| 				"Please open an issue at https://git.rootprojects.org/root/go-gitver/issues/new \n"+
 | |
| 				"Please include the following:\n\n"+
 | |
| 				"git description: %s\n"+
 | |
| 				"RegExp: %#v\n"+
 | |
| 				"Error: %s\n"+
 | |
| 				"\nPlease and Thank You.\n\n", desc, gitVer, err)
 | |
| 		os.Exit(exitCode)
 | |
| 	}
 | |
| 
 | |
| 	// v1.0.1-pre1
 | |
| 	// v1.0.1-pre1+g0000000
 | |
| 	// v1.0.1-pre0+dirty
 | |
| 	// v1.0.1-pre0+g0000000-dirty
 | |
| 	if "" == vers[4] {
 | |
| 		vers[4] = "0"
 | |
| 	}
 | |
| 	ver := fmt.Sprintf("%s.%d-pre%s", vers[1], patch+1, vers[4])
 | |
| 	if "" != vers[6] || "dirty" == vers[8] {
 | |
| 		ver += "+"
 | |
| 		if "" != vers[6] {
 | |
| 			ver += vers[6]
 | |
| 			if "" != vers[8] {
 | |
| 				ver += "-"
 | |
| 			}
 | |
| 		}
 | |
| 		ver += vers[8]
 | |
| 	}
 | |
| 
 | |
| 	return ver
 | |
| }
 | |
| 
 | |
| func gitTimestamp(desc string) (time.Time, error) {
 | |
| 	args := []string{
 | |
| 		"git",
 | |
| 		"show", desc,
 | |
| 		"--format=%cd",
 | |
| 		"--date=format:%Y-%m-%dT%H:%M:%SZ%z",
 | |
| 		"--no-patch",
 | |
| 	}
 | |
| 	cmd := exec.Command(args[0], args[1:]...)
 | |
| 	out, err := cmd.CombinedOutput()
 | |
| 	if nil != err {
 | |
| 		// a dirty desc was probably used
 | |
| 		return time.Time{}, err
 | |
| 	}
 | |
| 	return time.Parse(time.RFC3339, strings.TrimSpace(string(out)))
 | |
| }
 | |
| 
 | |
| var versionTpl = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
 | |
| package {{ .Package }}
 | |
| 
 | |
| func init() {
 | |
| 	GitRev = "{{ .GitRev }}"
 | |
|   {{- if .Version }}
 | |
| 	GitVersion = "{{ .Version }}"
 | |
| 	{{ end -}}
 | |
| 	GitTimestamp = "{{ .Timestamp }}"
 | |
| }
 | |
| `))
 |