1
0
mirror of https://github.com/therootcompany/go-gitver.git synced 2024-11-16 17:08:59 +00:00

Compare commits

..

23 Commits

Author SHA1 Message Date
1638772379
docs: fix typo 2023-01-18 14:10:35 -07:00
752451d77b
fix typo 2021-04-15 02:15:55 -06:00
3409d5f2e4 update examples 2020-10-12 14:46:35 -06:00
cfbbc73f48 complete transition to v2 2020-10-12 14:42:55 -06:00
1ee983f56d add v2 to go.mod 2020-10-10 16:33:22 -06:00
a745735392 add GoDoc link 2020-10-10 16:16:08 -06:00
b667843a2b v2.0.0-pre1: make compatible with GoReleaser 2020-10-10 16:13:09 -06:00
37c1fd4b56 fix typos in example fmt.Printf 2020-09-12 08:57:44 +00:00
a45a154eaf shorter example string 2020-07-17 02:55:32 +00:00
d5c2342f76 clarify usage 2020-07-17 02:52:49 +00:00
6dace8255b whitespace fix 2020-07-17 02:46:24 +00:00
82ec670e8a made README more clear 2020-07-17 02:46:03 +00:00
d4628bd751 fail softly, as documented 2019-07-08 14:36:22 -06:00
a3af8e6c5b v1.1.2 2019-07-01 02:33:28 -06:00
0921ed1e60 separate ExecAndParse, bugfix source format 2019-07-01 02:32:19 -06:00
039b465b75 add common issue 2019-06-21 00:47:42 -06:00
ef45a8a999 bump version 2019-06-21 00:19:55 -06:00
0b8c2d86df v1.1.1: update docs, template conditionals 2019-06-21 00:18:07 -06:00
639a7e246f update markdown syntax 2019-06-21 00:08:45 -06:00
2da59efd62 update syntax highlighting 2019-06-21 00:07:27 -06:00
84130f846a v1.1.0: better docs, examples, and options 2019-06-21 00:02:09 -06:00
9f05e2304c rename outfile xversion.go to lower init() priority, add outfile option 2019-06-21 00:01:02 -06:00
47b8ec43ae fix docs, add more docs, allow different package name 2019-06-20 23:27:41 -06:00
26 changed files with 592 additions and 192 deletions

7
.gitignore vendored
View File

@ -1,4 +1,9 @@
generated-version.go xversion.go
zversion.go
/go-gitver
hello
examples/*/*.sum
# ---> Go # ---> Go
# Binaries for programs and plugins # Binaries for programs and plugins

1
.prettierrc Normal file
View File

@ -0,0 +1 @@
{}

168
README.md
View File

@ -1,36 +1,117 @@
# git-version.go # [Go GitVer](https://git.rootprojects.org/root/go-gitver)
Use git tags to add semver to your go package. Use **git tags** to add (GoReleaser-compatible) [**semver**](https://semver.org/)
to your go package in under 150
[lines of code](https://git.rootprojects.org/root/go-gitver/src/branch/master/gitver/gitver.go).
> Goal: Either use an exact version like v1.0.0 ```txt
> or translate the git version like v1.0.0-4-g0000000 Goals:
> to a semver like v1.0.1-pre4+g0000000
> 1. Use an exact `git tag` version, like v1.0.0, when clean
> Fail gracefully when git repo isn't available. 2. Translate the `git describe` version (v1.0.0-4-g0000000)
to semver (1.0.1-pre4+g0000000) in between releases
3. Note when `dirty` (and have build timestamp)
Fail gracefully when git repo isn't available.
```
# GoDoc
See <https://pkg.go.dev/git.rootprojects.org/root/go-gitver/v2>.
# How it works
1. You define the fallback version and version printing in `main.go`:
```go
//go:generate go run git.rootprojects.org/root/go-gitver/v2
package main
import (
"fmt"
"strings"
)
var (
commit = "0000000"
version = "0.0.0-pre0+0000000"
date = "0000-00-00T00:00:00+0000"
)
func main() {
if (len(os.Args) > 1 && "version" == strings.TrimLeft(os.Args[1], "-")) {
fmt.Printf("Foobar v%s (%s) %s\n", version, commit[:7], date)
}
// ...
}
```
2. You `go generate` or `go run git.rootprojects.org/root/go-gitver/v2` to generate `xversion.go`:
```go
package main
func init() {
commit = "0921ed1e"
version = "1.1.2"
date = "2019-07-01T02:32:58-06:00"
}
```
# Demo # Demo
Generate an `xversion.go` file:
```bash ```bash
go run git.rootprojects.org/root/go-gitver go run git.rootprojects.org/root/go-gitver/v2
cat xversion.go
```
```go
// Code generated by go generate; DO NOT EDIT.
package main
func init() {
commit = "6dace8255b52e123297a44629bc32c015add310a"
version = "1.1.4-pre2+g6dace82"
date = "2020-07-16T20:48:15-06:00"
}
```
<small>**Note**: The file is named `xversion.go` by default so that the
generated file's `init()` will come later, and thus take priority, over
most other files.</small>
See `go-gitver`s self-generated version:
```bash
go run git.rootprojects.org/root/go-gitver/v2 version
```
```txt
6dace8255b52e123297a44629bc32c015add310a
v1.1.4-pre2+g6dace82
2020-07-16T20:48:15-06:00
``` ```
# QuickStart # QuickStart
Add this to the top of your main file: Add this to the top of your main file, so that it runs with `go generate`:
```go ```go
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2
``` ```
Add a file that imports go-gitver (for versioning) Add a file that imports go-gitver (for versioning)
```go ```go
// build +tools // +build tools
package example package example
import _ "git.rootprojects.org/root/go-gitver" import _ "git.rootprojects.org/root/go-gitver/v2"
``` ```
Change you build instructions to be something like this: Change you build instructions to be something like this:
@ -41,18 +122,20 @@ go generate -mod=vendor ./...
go build -mod=vendor -o example cmd/example/*.go go build -mod=vendor -o example cmd/example/*.go
``` ```
You don't have to use `mod vendor`, but I highly recommend it. You don't have to use `-mod=vendor`, but I highly recommend it (just `go mod tidy; go mod vendor` to start).
# Options # Options
``` ```txt
version print version and exit version print version and exit
--fail will cause non-zero exit status on failure --fail exit with non-zero status code on failure
--package <name> will set the package name
--outfile <name> will replace `xversion.go` with the given file path
``` ```
ENVs ENVs
``` ```bash
# Alias for --fail # Alias for --fail
GITVER_FAIL=true GITVER_FAIL=true
``` ```
@ -60,12 +143,12 @@ GITVER_FAIL=true
For example: For example:
```go ```go
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 --fail
``` ```
```bash ```bash
go run -mod=vendor git.rootprojects.org/root/go-gitver version go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 version
``` ```
# Usage # Usage
@ -73,43 +156,43 @@ go run -mod=vendor git.rootprojects.org/root/go-gitver version
See `examples/basic` See `examples/basic`
1. Create a `tools` package in your project 1. Create a `tools` package in your project
2. Guard it against regular builds with `// build +tools` 2. Guard it against regular builds with `// +build tools`
3. Include `_ "git.rootprojects.org/root/go-gitver"` in the imports 3. Include `_ "git.rootprojects.org/root/go-gitver/v2"` in the imports
4. Declare `var GitRev, GitVersion, GitTimestamp string` in your `package main` 4. Declare `var commit, version, date string` in your `package main`
5. Include `//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver` as well 5. Include `//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2` as well
`tools/tools.go`: `tools/tools.go`:
```go ```go
// build +tools // +build tools
// This is a dummy package for build tooling // This is a dummy package for build tooling
package tools package tools
import ( import (
_ "git.rootprojects.org/root/go-gitver" _ "git.rootprojects.org/root/go-gitver/v2"
) )
``` ```
`main.go`: `main.go`:
```go ```go
//go:generate go run git.rootprojects.org/root/go-gitver --fail //go:generate go run git.rootprojects.org/root/go-gitver/v2 --fail
package main package main
import "fmt" import "fmt"
var ( var (
GitRev = "0000000" commit = "0000000"
GitVersion = "v0.0.0-pre0+0000000" version = "0.0.0-pre0+0000000"
GitTimestamp = "0000-00-00T00:00:00+0000" date = "0000-00-00T00:00:00+0000"
) )
func main() { func main() {
fmt.Println(GitRev) fmt.Println(commit)
fmt.Println(GitVersion) fmt.Println(version)
fmt.Println(GitTimestamp) fmt.Println(date)
} }
``` ```
@ -117,7 +200,7 @@ If you're using `go mod vendor` (which I highly recommend that you do),
you'd modify the `go:generate` ever so slightly: you'd modify the `go:generate` ever so slightly:
```go ```go
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 --fail
``` ```
The only reason I didn't do that in the example is that I'd be included The only reason I didn't do that in the example is that I'd be included
@ -125,7 +208,7 @@ the repository in itself and that would be... weird.
# Why a tools package? # Why a tools package?
> import "git.rootprojects.org/root/go-gitver" is a program, not an importable package > import "git.rootprojects.org/root/go-gitver/v2" is a program, not an importable package
Having a tools package with a build tag that you don't use is a nice way to add exact Having a tools package with a build tag that you don't use is a nice way to add exact
versions of a command package used for tooling to your `go.mod` with `go mod tidy`, versions of a command package used for tooling to your `go.mod` with `go mod tidy`,
@ -159,3 +242,20 @@ Shows the most recent commit.
git rev-parse HEAD git rev-parse HEAD
# 0000000000000000000000000000000000000000 # 0000000000000000000000000000000000000000
``` ```
# Errors
### cannot find package "."
```txt
package git.rootprojects.org/root/go-gitver/v2: cannot find package "." in:
/Users/me/go-example/vendor/git.rootprojects.org/root/go-gitver/v2
cmd/example/example.go:1: running "go": exit status 1
```
You forgot to update deps and re-vendor:
```bash
go mod tidy
go mod vendor
```

View File

@ -20,10 +20,12 @@ These are the instructions that someone cloning the repo might use.
```bash ```bash
go generate -mod=vendor ./... go generate -mod=vendor ./...
go build -mod=vendor -o hello *.go go build -mod=vendor -o hello *.go
./hello
./hello --version
``` ```
Note: If the source is distributed in a non-git tarball then Note: If the source is distributed in a non-git tarball then
`generated-version.go` will not be output, and whatever `version-generated.go` will not be output, and whatever
version info is in `package main` will remain as-is. version info is in `package main` will remain as-is.
If you would prefer the build process to fail (i.e. in a CI/CD pipeline), If you would prefer the build process to fail (i.e. in a CI/CD pipeline),

View File

@ -1,3 +1,5 @@
module example.com/hello module example.com/hello
go 1.12 go 1.12
require git.rootprojects.org/root/go-gitver/v2 v2.0.2

View File

@ -1,4 +1,4 @@
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 --fail
package main package main
@ -8,9 +8,9 @@ import (
) )
var ( var (
GitRev = "0000000" commit = "0000000"
GitVersion = "v0.0.0-pre0+0000000" version = "0.0.0-pre0+0000000"
GitTimestamp = "0000-00-00T00:00:00+0000" date = "0000-00-00T00:00:00+0000"
) )
func main() { func main() {
@ -18,9 +18,9 @@ func main() {
flag.Parse() flag.Parse()
if *showVersion { if *showVersion {
fmt.Println(GitRev) fmt.Println(commit)
fmt.Println(GitVersion) fmt.Println(version)
fmt.Println(GitTimestamp) fmt.Println(date)
return return
} }

View File

@ -1,8 +1,8 @@
// build +tools // +build tools
// This is a dummy package for build tooling // This is a dummy package for build tooling
package tools package tools
import ( import (
_ "git.rootprojects.org/root/go-gitver" _ "git.rootprojects.org/root/go-gitver/v2"
) )

View File

@ -0,0 +1,25 @@
# Example
Prints the version or a nice message
# Doesn't have a separate tools package
This is just like `examples/basic`,
but it uses a normal file with a build tag
rather than a tools package.
See `examples/basic` for more details.
# Demo
```bash
go mod tidy
go mod vendor
```
```bash
go generate -mod=vendor ./...
go build -mod=vendor -o hello *.go
./hello
./hello --version
```

5
examples/no-tools/go.mod Normal file
View File

@ -0,0 +1,5 @@
module example.com/hello
go 1.12
require git.rootprojects.org/root/go-gitver/v2 v2.0.2

28
examples/no-tools/main.go Normal file
View File

@ -0,0 +1,28 @@
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 --fail
package main
import (
"flag"
"fmt"
)
var (
commit = "0000000"
version = "0.0.0-pre0+0000000"
date = "0000-00-00T00:00:00+0000"
)
func main() {
showVersion := flag.Bool("version", false, "Print version and exit")
flag.Parse()
if *showVersion {
fmt.Println(commit)
fmt.Println(version)
fmt.Println(date)
return
}
fmt.Println("Hello, World!")
}

View File

@ -0,0 +1,6 @@
// +build tools
// This is a dummy file for build tooling
package main
import _ "git.rootprojects.org/root/go-gitver/v2"

View File

@ -0,0 +1,24 @@
# Example
Prints the version or a nice message
# Doesn't have a separate tools package
This is just like `examples/sub-package` except that its `//go:generate` is in `main.go`
and it outputs `./version/zversion.go` instead of `xversion.go`.
See `examples/basic` for more details.
# Demo
```bash
go mod tidy
go mod vendor
```
```bash
go generate -mod=vendor ./...
go build -mod=vendor -o hello *.go
./hello
./hello --version
```

View File

@ -0,0 +1,5 @@
module example.com/hello
go 1.12
require git.rootprojects.org/root/go-gitver/v2 v2.0.2

View File

@ -0,0 +1,24 @@
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 --package version --outfile ./version/zversion.go
package main
import (
"flag"
"fmt"
"example.com/hello/version"
)
func main() {
showVersion := flag.Bool("version", false, "Print version and exit")
flag.Parse()
if *showVersion {
fmt.Println(version.Commit())
fmt.Println(version.Version())
fmt.Println(version.Date())
return
}
fmt.Println("Hello, World!")
}

View File

@ -0,0 +1,8 @@
// +build tools
// This is a dummy package for build tooling
package tools
import (
_ "git.rootprojects.org/root/go-gitver/v2"
)

View File

@ -0,0 +1,22 @@
package version
var (
commit = "0000000"
version = "0.0.0-pre0+0000000"
date = "0000-00-00T00:00:00+0000"
)
// Commit returns the git commit reference
func Commit() string {
return commit
}
// Version returns the git version, without the leading 'v'
func Version() string {
return version
}
// Date returns the ISO-formatted date string
func Date() string {
return date
}

View File

@ -0,0 +1,24 @@
# Example
Prints the version or a nice message
# Put version in its own packge
This is just like `examples/basic`,
but it uses the package name `version` instead of `main`.
See `examples/basic` for more details.
# Demo
```bash
go mod tidy
go mod vendor
```
```bash
go generate -mod=vendor ./...
go build -mod=vendor -o hello *.go
./hello
./hello --version
```

View File

@ -0,0 +1,5 @@
module example.com/hello
go 1.12
require git.rootprojects.org/root/go-gitver/v2 v2.0.2

View File

@ -0,0 +1,22 @@
package main
import (
"flag"
"fmt"
"example.com/hello/version"
)
func main() {
showVersion := flag.Bool("version", false, "Print version and exit")
flag.Parse()
if *showVersion {
fmt.Println(version.Commit())
fmt.Println(version.Version())
fmt.Println(version.Date())
return
}
fmt.Println("Hello, World!")
}

View File

@ -0,0 +1,8 @@
// +build tools
// This is a dummy package for build tooling
package tools
import (
_ "git.rootprojects.org/root/go-gitver/v2"
)

View File

@ -0,0 +1,24 @@
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2 --package version
package version
var (
commit = "0000000"
version = "0.0.0-pre0+0000000"
date = "0000-00-00T00:00:00+0000"
)
// Commit returns the git commit reference
func Commit() string {
return commit
}
// Version returns the git version, without the leading 'v'
func Version() string {
return version
}
// Date returns the ISO-formatted date string
func Date() string {
return date
}

189
gitver.go
View File

@ -1,4 +1,4 @@
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver/v2
package main package main
@ -6,46 +6,35 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/format" "go/format"
"log"
"os" "os"
"os/exec"
"regexp"
"strconv"
"strings"
"text/template" "text/template"
"time" "time"
"git.rootprojects.org/root/go-gitver/v2/gitver"
) )
var commit, version, date string
var exitCode int var exitCode int
var exactVer *regexp.Regexp var verFile = "xversion.go"
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() { func main() {
pkg := "main"
args := os.Args[1:] args := os.Args[1:]
for i := range args { for i := range args {
arg := args[i] arg := args[i]
if "-f" == arg || "--fail" == arg { if "-f" == arg || "--fail" == arg {
exitCode = 1 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 { } else if "-V" == arg || "version" == arg || "-version" == arg || "--version" == arg {
fmt.Println(GitRev) fmt.Println(commit)
fmt.Println(GitVersion) fmt.Println(version)
fmt.Println(GitTimestamp) fmt.Println(date)
os.Exit(0) os.Exit(0)
} }
} }
@ -53,31 +42,30 @@ func main() {
exitCode = 1 exitCode = 1
} }
desc, err := gitDesc() v, err := gitver.ExecAndParse()
if nil != err { if nil != err {
log.Fatalf("Failed to get git version: %s\n", err) fmt.Fprintf(os.Stderr, "Failed to get git version: %s\n", err)
os.Exit(exitCode) if exitCode > 0 {
} os.Exit(exitCode)
rev := gitRev() }
ver := semVer(desc) v = &gitver.Versions{
ts, err := gitTimestamp(desc) Timestamp: time.Now(),
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 // Create or overwrite the go file from template
var buf bytes.Buffer var buf bytes.Buffer
if err := versionTpl.Execute(&buf, v); nil != err { if err := versionTpl.Execute(&buf, struct {
Package string
Timestamp string
Version string
Commit string
}{
Package: pkg,
Timestamp: v.Timestamp.Format(time.RFC3339),
Version: v.Version,
Commit: v.Rev,
}); nil != err {
panic(err) panic(err)
} }
@ -98,110 +86,19 @@ func main() {
if err := f.Close(); nil != err { if err := f.Close(); nil != err {
panic(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. var versionTpl = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
package main package {{ .Package }}
func init() { func init() {
GitRev = "{{ .GitRev }}" {{ if .Commit -}}
if "" != "{{ .Version }}" { commit = "{{ .Commit }}"
GitVersion = "{{ .Version }}" {{ end -}}
} {{ if .Version -}}
GitTimestamp = "{{ .Timestamp }}" version = "{{ .Version }}"
{{ end -}}
date = "{{ .Timestamp }}"
} }
`)) `))

154
gitver/gitver.go Normal file
View File

@ -0,0 +1,154 @@
package gitver
import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
)
var exactVer *regexp.Regexp
var gitVer *regexp.Regexp
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))?`)
}
// Versions describes the various version properties
type Versions struct {
Timestamp time.Time
Version string
Rev string
}
// ExecAndParse will run git and parse the output
func ExecAndParse() (*Versions, error) {
desc, err := gitDesc()
if nil != err {
return nil, err
}
rev, err := gitRev()
if nil != err {
return nil, err
}
ver, err := semVer(desc)
if nil != err {
return nil, err
}
ts, err := gitTimestamp(desc)
if nil != err {
ts = time.Now()
}
return &Versions{
Timestamp: ts,
Version: ver,
Rev: rev,
}, nil
}
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("0.0.0-0-g0000000")
return "", err
}
return strings.TrimSpace(string(out)), nil
}
func gitRev() (string, error) {
args := strings.Split("git rev-parse HEAD", " ")
cmd := exec.Command(args[0], args[1:]...)
out, err := cmd.CombinedOutput()
if nil != err {
return "", fmt.Errorf("\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)
}
return strings.TrimSpace(string(out)), nil
}
func semVer(desc string) (string, error) {
if exactVer.MatchString(desc) {
// v1.0.0
return strings.TrimPrefix(desc, "v"), nil
}
if !gitVer.MatchString(desc) {
return "", nil
}
// (v1.0).(0)(-(1))(-(g0000000))(-(dirty))
vers := gitVer.FindStringSubmatch(desc)
patch, err := strconv.Atoi(vers[2])
if nil != err {
return "", fmt.Errorf("\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)
}
// 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 strings.TrimPrefix(ver, "v"), nil
}
func gitTimestamp(desc string) (time.Time, error) {
// Other options:
//
// Commit Date
// git log -1 --format=%cd --date=format:%Y-%m-%dT%H:%M:%SZ%z
//
// Author Date
// git log -1 --format=%ad --date=format:%Y-%m-%dT%H:%M:%SZ%z
//
// I think I chose this because it would account for dirty-ness better... maybe?
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)))
}

2
go.mod
View File

@ -1,3 +1,3 @@
module git.rootprojects.org/root/go-gitver module git.rootprojects.org/root/go-gitver/v2
go 1.12 go 1.12

0
go.sum Normal file
View File

9
version.go Normal file
View File

@ -0,0 +1,9 @@
package main
// use recently generated version info as a fallback
// for when git isn't present (i.e. go run <url>)
func init() {
commit = "37c1fd4b5694fd62c9f0d6ad1df47d938accbeec"
version = "2.0.0-pre1-dirty"
date = "2020-10-10T16:05:59-06:00"
}