Include build information and add version command (#194)

Includes relevant fields listed on
https://goreleaser.com/customization/templates/ into build artifacts.

The version command outputs the version by default:
```
$ bricks version
0.0.21-devel
```

Or all build information if `--json` is specified:
```
$ bricks version --json
{
  "ProjectName": "bricks",
  "Version": "0.0.21-devel",
  "Branch": "version-info",
  "Tag": "v0.0.20",
  "ShortCommit": "193b56b",
  "FullCommit": "193b56b0929128c0836d35e913c46fd66fa2a93c",
  "CommitTime": "2023-02-02T22:04:42+01:00",
  "Summary": "v0.0.20-5-g193b56b",
  "Major": 0,
  "Minor": 0,
  "Patch": 20,
  "Prerelease": "",
  "IsSnapshot": true,
  "BuildTime": "2023-02-02T22:07:36+01:00"
}
```
This commit is contained in:
Pieter Noordhuis 2023-02-03 15:38:53 +01:00 committed by GitHub
parent cd789366b9
commit 1c27f081e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 198 additions and 0 deletions

View File

@ -9,6 +9,25 @@ builds:
- -trimpath
ldflags:
- '-s -w'
- -X github.com/databricks/bricks/internal/build.buildProjectName={{ .ProjectName }}
- -X github.com/databricks/bricks/internal/build.buildVersion={{ .Version }}
# Git information
- -X github.com/databricks/bricks/internal/build.buildBranch={{ .Branch }}
- -X github.com/databricks/bricks/internal/build.buildTag={{ .Tag }}
- -X github.com/databricks/bricks/internal/build.buildShortCommit={{ .ShortCommit }}
- -X github.com/databricks/bricks/internal/build.buildFullCommit={{ .FullCommit }}
- -X github.com/databricks/bricks/internal/build.buildCommitTimestamp={{ .CommitTimestamp }}
- -X github.com/databricks/bricks/internal/build.buildSummary={{ .Summary }}
# Version information
- -X github.com/databricks/bricks/internal/build.buildMajor={{ .Major }}
- -X github.com/databricks/bricks/internal/build.buildMinor={{ .Minor }}
- -X github.com/databricks/bricks/internal/build.buildPatch={{ .Patch }}
- -X github.com/databricks/bricks/internal/build.buildPrerelease={{ .Prerelease }}
- -X github.com/databricks/bricks/internal/build.buildIsSnapshot={{ .IsSnapshot }}
- -X github.com/databricks/bricks/internal/build.buildTimestamp={{ .Timestamp }}
goos:
- windows
- linux

34
cmd/version/version.go Normal file
View File

@ -0,0 +1,34 @@
package version
import (
"encoding/json"
"fmt"
"github.com/databricks/bricks/cmd/root"
"github.com/databricks/bricks/internal/build"
"github.com/spf13/cobra"
)
var detail = false
var versionCmd = &cobra.Command{
Use: "version",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
info := build.GetInfo()
if detail {
enc := json.NewEncoder(cmd.OutOrStdout())
enc.SetIndent("", " ")
return enc.Encode(info)
}
fmt.Fprintln(cmd.OutOrStdout(), info.Version)
return nil
},
}
func init() {
versionCmd.Flags().BoolVar(&detail, "detail", false, "output detailed version information as JSON")
root.RootCmd.AddCommand(versionCmd)
}

117
internal/build/info.go Normal file
View File

@ -0,0 +1,117 @@
package build
import (
"fmt"
"runtime/debug"
"strconv"
"sync"
"time"
"golang.org/x/mod/semver"
)
type Info struct {
ProjectName string
Version string
Branch string
Tag string
ShortCommit string
FullCommit string
CommitTime time.Time
Summary string
Major int64
Minor int64
Patch int64
Prerelease string
IsSnapshot bool
BuildTime time.Time
}
var info Info
var once sync.Once
// getDefaultBuildVersion uses build information stored by Go itself
// to synthesize a build version if one wasn't set.
// This is necessary if the binary was not built through goreleaser.
func getDefaultBuildVersion() string {
bi, ok := debug.ReadBuildInfo()
if !ok {
panic("unable to read build info")
}
m := make(map[string]string)
for _, s := range bi.Settings {
m[s.Key] = s.Value
}
out := "0.0.0-dev"
// Append revision as build metadata.
if v, ok := m["vcs.revision"]; ok {
// First 12 characters of the commit SHA is plenty to identify one.
out = fmt.Sprintf("%s+%s", out, v[0:12])
}
return out
}
func initialize() {
// If buildVersion is empty it means the binary was NOT built through goreleaser.
// We try to pull version information from debug.BuildInfo().
if buildVersion == "" {
buildVersion = getDefaultBuildVersion()
}
// Confirm that buildVersion is valid semver.
// Note that the semver package requires a leading 'v'.
if !semver.IsValid("v" + buildVersion) {
panic(fmt.Sprintf(`version is not a valid semver string: "%s"`, buildVersion))
}
info = Info{
ProjectName: buildProjectName,
Version: buildVersion,
Branch: buildBranch,
Tag: buildTag,
ShortCommit: buildShortCommit,
FullCommit: buildFullCommit,
CommitTime: parseTime(buildCommitTimestamp),
Summary: buildSummary,
Major: parseInt(buildMajor),
Minor: parseInt(buildMinor),
Patch: parseInt(buildPatch),
Prerelease: buildPrerelease,
IsSnapshot: parseBool(buildIsSnapshot),
BuildTime: parseTime(buildTimestamp),
}
}
func GetInfo() Info {
once.Do(initialize)
return info
}
func parseInt(s string) int64 {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
panic(err)
}
return i
}
func parseBool(s string) bool {
b, err := strconv.ParseBool(s)
if err != nil {
panic(err)
}
return b
}
func parseTime(s string) time.Time {
return time.Unix(parseInt(s), 0)
}

View File

@ -0,0 +1,9 @@
package build
import (
"testing"
)
func TestGetDetails(t *testing.T) {
GetInfo()
}

View File

@ -0,0 +1,18 @@
package build
var buildProjectName string = "bricks"
var buildVersion string = ""
var buildBranch string = "undefined"
var buildTag string = "undefined"
var buildShortCommit string = "00000000"
var buildFullCommit string = "0000000000000000000000000000000000000000"
var buildCommitTimestamp string = "0"
var buildSummary string = "v0.0.0"
var buildMajor string = "0"
var buildMinor string = "0"
var buildPatch string = "0"
var buildPrerelease string = ""
var buildIsSnapshot string = "false"
var buildTimestamp string = "0"

View File

@ -13,6 +13,7 @@ import (
"github.com/databricks/bricks/cmd/root"
_ "github.com/databricks/bricks/cmd/sync"
_ "github.com/databricks/bricks/cmd/test"
_ "github.com/databricks/bricks/cmd/version"
)
func main() {