mirror of
https://github.com/therootcompany/go-gitver.git
synced 2024-11-16 17:08:59 +00:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
57bd3d54bc | |||
beaf3de730 | |||
925fa0dc67 | |||
1e7f37a886 | |||
117a8d6b2e | |||
5684f0b929 | |||
59eae7f7ce | |||
41361316a9 | |||
60debf802f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
|
generated-version.go
|
||||||
|
|
||||||
# ---> Go
|
# ---> Go
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
|
86
README.md
86
README.md
@ -1,3 +1,89 @@
|
|||||||
# git-version.go
|
# git-version.go
|
||||||
|
|
||||||
Use git tags to add semver to your go package.
|
Use git tags to add semver to your go package.
|
||||||
|
|
||||||
|
> 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
|
||||||
|
>
|
||||||
|
> Fail gracefully when git repo isn't available.
|
||||||
|
|
||||||
|
# Demo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go run git.rootprojects.org/root/go-gitver
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
See `examples/basic`
|
||||||
|
|
||||||
|
1. Create a `tools` package in your project
|
||||||
|
2. Guard it against regular builds with `// build +tools`
|
||||||
|
3. Include `_ "git.rootprojects.org/root/go-gitver"` in the imports
|
||||||
|
4. Declare `var GitRev, GitVersion, GitTimestamp string` in your `package main`
|
||||||
|
5. Include `//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver` as well
|
||||||
|
|
||||||
|
`tools/tools.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// build +tools
|
||||||
|
|
||||||
|
// This is a dummy package for build tooling
|
||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "git.rootprojects.org/root/go-gitver"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
`main.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var (
|
||||||
|
GitRev = "0000000"
|
||||||
|
GitVersion = "v0.0.0-pre0+g0000000"
|
||||||
|
GitTimestamp = "0000-00-00T00:00:00+0000"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(GitRev)
|
||||||
|
fmt.Println(GitVersion)
|
||||||
|
fmt.Println(GitTimestamp)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Behind the curtain
|
||||||
|
|
||||||
|
These are the commands that are used under the hood to produce the versions.
|
||||||
|
|
||||||
|
Shows the git tag + description. Assumes that you're using the semver format `v1.0.0` for your base tags.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git describe --tags --dirty --always
|
||||||
|
# v1.0.0
|
||||||
|
# v1.0.0-1-g0000000
|
||||||
|
# v1.0.0-dirty
|
||||||
|
```
|
||||||
|
|
||||||
|
Show the commit date (when the commit made it into the current tree).
|
||||||
|
Internally we use the current date when the working tree is dirty.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git show v1.0.0-1-g0000000 --format=%cd --date=format:%Y-%m-%dT%H:%M:%SZ%z --no-patch
|
||||||
|
# 2010-01-01T20:30:00Z-0600
|
||||||
|
# fatal: ambiguous argument 'v1.0.0-1-g0000000-dirty': unknown revision or path not in the working tree.
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows the most recent commit.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git rev-parse HEAD
|
||||||
|
# 0000000000000000000000000000000000000000
|
||||||
|
```
|
||||||
|
17
examples/basic/main.go
Normal file
17
examples/basic/main.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var (
|
||||||
|
GitRev = "0000000"
|
||||||
|
GitVersion = "v0.0.0-pre0+0000000"
|
||||||
|
GitTimestamp = "0000-00-00T00:00:00+0000"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(GitRev)
|
||||||
|
fmt.Println(GitVersion)
|
||||||
|
fmt.Println(GitTimestamp)
|
||||||
|
}
|
8
examples/basic/tools/tools.go
Normal file
8
examples/basic/tools/tools.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// build +tools
|
||||||
|
|
||||||
|
// This is a dummy package for build tooling
|
||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "git.rootprojects.org/root/go-gitver"
|
||||||
|
)
|
184
gitver.go
Normal file
184
gitver.go
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
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 = "generated-version.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() {
|
||||||
|
args := os.Args[1:]
|
||||||
|
for i := range args {
|
||||||
|
arg := args[i]
|
||||||
|
if "-f" == arg || "--fail" == arg {
|
||||||
|
exitCode = 1
|
||||||
|
} else if "-V" == arg || "version" == arg || "-version" == arg || "--version" == arg {
|
||||||
|
fmt.Println(GitRev)
|
||||||
|
fmt.Println(GitVersion)
|
||||||
|
fmt.Println(GitTimestamp)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Timestamp string
|
||||||
|
Version string
|
||||||
|
GitRev string
|
||||||
|
}{
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
ver = fmt.Sprintf("%s.%d-pre%s", vers[1], patch+1, vers[4])
|
||||||
|
fmt.Println(desc, vers)
|
||||||
|
if "dirty" == vers[8] {
|
||||||
|
//if strings.Contains(desc, "dirty") {
|
||||||
|
ver += "+dirty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 main
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
GitRev = "{{ .GitRev }}"
|
||||||
|
GitVersion = "{{ .Version }}"
|
||||||
|
GitTimestamp = "{{ .Timestamp }}"
|
||||||
|
}
|
||||||
|
`))
|
Loading…
x
Reference in New Issue
Block a user