2019-06-11 03:59:40 +00:00
# git-version.go
2020-07-17 02:46:03 +00:00
Use **git tags** to add [**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 ).
2019-06-21 04:55:24 +00:00
2019-06-21 05:27:41 +00:00
```txt
2020-07-17 02:46:03 +00:00
Goals:
1. Use an exact `git tag` version, like v1.0.0, when clean
2020-07-17 02:46:24 +00:00
2. Translate the `git describe` version (v1.0.0-4-g0000000)
to semver (v1.0.1-pre4+g0000000) in between releases
2020-07-17 02:46:03 +00:00
3. Note when `dirty` (and have build timestamp)
2019-06-21 05:27:41 +00:00
Fail gracefully when git repo isn't available.
```
2019-06-21 04:55:24 +00:00
2020-07-17 02:46:03 +00:00
# How it works
You define the fallback variables in your `main.go` :
```
var (
GitRev = "0000000"
GitVersion = "v0.0.0-pre0+0000000"
GitTimestamp = "0000-00-00T00:00:00+0000"
)
```
You `go generate` or `go run git.rootprojects.org/root/go-gitver` to generate `xversion.go` :
```
package main
func init() {
GitRev = "0921ed1e"
GitVersion = "v1.1.2"
GitTimestamp = "2019-07-01T02:32:58-06:00"
}
```
2019-06-21 04:55:24 +00:00
# Demo
2019-06-21 06:01:02 +00:00
Generate an `xversion.go` file:
2019-06-21 05:27:41 +00:00
2019-06-21 04:55:24 +00:00
```bash
go run git.rootprojects.org/root/go-gitver
2019-06-21 06:01:02 +00:00
cat xversion.go
2019-06-21 05:27:41 +00:00
```
2019-06-21 06:01:02 +00: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 >
2019-06-21 05:27:41 +00:00
See `go-gitver` s self-generated version:
```bash
go run git.rootprojects.org/root/go-gitver version
2019-06-21 04:55:24 +00:00
```
# QuickStart
2020-07-17 02:46:03 +00:00
Add this to the top of your main file, so that it runs with `go generate` :
2019-06-21 04:55:24 +00:00
```go
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver
```
Add a file that imports go-gitver (for versioning)
```go
2019-06-21 05:27:41 +00:00
// +build tools
2019-06-21 04:55:24 +00:00
package example
import _ "git.rootprojects.org/root/go-gitver"
```
Change you build instructions to be something like this:
```bash
go mod vendor
go generate -mod=vendor ./...
go build -mod=vendor -o example cmd/example/*.go
```
2020-07-17 02:46:03 +00:00
You don't have to use `-mod=vendor` , but I highly recommend it (just `go mod tidy; go mod vendor` to start).
2019-06-21 04:55:24 +00:00
# Options
2019-06-21 06:07:27 +00:00
```txt
2019-06-21 05:27:41 +00:00
version print version and exit
--fail exit with non-zero status code on failure
--package < name > will set the package name
2019-06-21 06:01:02 +00:00
--outfile < name > will replace `xversion.go` with the given file path
2019-06-21 04:55:24 +00:00
```
ENVs
2019-06-21 06:07:27 +00:00
```bash
2019-06-21 04:55:24 +00:00
# Alias for --fail
GITVER_FAIL=true
```
For example:
```go
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail
```
```bash
go run -mod=vendor git.rootprojects.org/root/go-gitver version
```
# Usage
See `examples/basic`
1. Create a `tools` package in your project
2019-06-21 05:27:41 +00:00
2. Guard it against regular builds with `// +build tools`
2019-06-21 04:55:24 +00:00
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
2019-06-21 05:27:41 +00:00
// +build tools
2019-06-21 04:55:24 +00:00
// This is a dummy package for build tooling
package tools
import (
_ "git.rootprojects.org/root/go-gitver"
)
```
`main.go` :
```go
//go:generate go run git.rootprojects.org/root/go-gitver --fail
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)
}
```
If you're using `go mod vendor` (which I highly recommend that you do),
you'd modify the `go:generate` ever so slightly:
```go
//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail
```
The only reason I didn't do that in the example is that I'd be included
the repository in itself and that would be... weird.
# Why a tools package?
2019-06-21 06:08:45 +00:00
> import "git.rootprojects.org/root/go-gitver" is a program, not an importable package
2019-06-21 04:55:24 +00:00
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` ,
without getting the error above.
# git: 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
```
2019-06-21 06:47:42 +00:00
# Errors
### cannot find package "."
```txt
package git.rootprojects.org/root/go-gitver: cannot find package "." in:
/Users/me/go-example/vendor/git.rootprojects.org/root/go-gitver
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
```