diff --git a/README.md b/README.md index 91f2c4413..eb44a4024 100644 --- a/README.md +++ b/README.md @@ -176,9 +176,15 @@ build: # Default is empty flags: -tags dev - # Custom ldflags. - # Default is `-s -w` - ldflags: -s -w + # Custom ldflags template. + # This is parsed with Golang template engine and the following variables + # are available: + # - Version + # - Date + # - Commit + # The default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}` + # Date format is `2006-01-02_15:04:05` + ldflags_template: -s -w -X main.build={{.Version}} # GOOS list to build in. # For more info refer to https://golang.org/doc/install/source#environment diff --git a/beta b/beta new file mode 100755 index 000000000..5d262c2ca Binary files /dev/null and b/beta differ diff --git a/context/context.go b/context/context.go index a5f6e2615..1160cb3ff 100644 --- a/context/context.go +++ b/context/context.go @@ -11,6 +11,7 @@ type GitInfo struct { CurrentTag string PreviousTag string Diff string + Commit string } // Context carries along some data through the pipes diff --git a/main.go b/main.go index 68dba7292..deacf432c 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "log" "os" + "time" "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" @@ -19,12 +20,16 @@ import ( "github.com/urfave/cli" ) -var version = "master" +var ( + version = "master" + commit = "master" + date = time.Now().Format("2006-01-02_15:04:05") +) func main() { var app = cli.NewApp() app.Name = "goreleaser" - app.Version = version + app.Version = version + ", commit: " + commit + ", built at " + date app.Usage = "Deliver Go binaries as fast and easily as possible" app.Flags = []cli.Flag{ cli.StringFlag{ diff --git a/pipeline/build/build.go b/pipeline/build/build.go index d5742a639..23330ad2a 100644 --- a/pipeline/build/build.go +++ b/pipeline/build/build.go @@ -61,14 +61,18 @@ func (Pipe) Run(ctx *context.Context) error { } func build(name, goos, goarch string, ctx *context.Context) error { - ldflags := ctx.Config.Build.Ldflags + " -X main.version=" + ctx.Version output := "dist/" + name + "/" + ctx.Config.Build.Binary + extFor(goos) log.Println("Building", output) cmd := []string{"go", "build"} if ctx.Config.Build.Flags != "" { cmd = append(cmd, strings.Fields(ctx.Config.Build.Flags)...) } - cmd = append(cmd, "-ldflags="+ldflags, "-o", output, ctx.Config.Build.Main) + flags, err := ldflags(ctx) + if err != nil { + return err + } + log.Println(flags) + cmd = append(cmd, "-ldflags="+flags, "-o", output, ctx.Config.Build.Main) if err := run(goos, goarch, cmd); err != nil { return err } @@ -84,47 +88,3 @@ func run(goos, goarch string, command []string) error { } return nil } - -// list from https://golang.org/doc/install/source#environment -var valids = []string{ - "androidarm", - "darwin386", - "darwinamd64", - "darwinarm", - "darwinarm64", - "dragonflyamd64", - "freebsd386", - "freebsdamd64", - "freebsdarm", - "linux386", - "linuxamd64", - "linuxarm", - "linuxarm64", - "linuxppc64", - "linuxppc64le", - "linuxmips", - "linuxmipsle", - "linuxmips64", - "linuxmips64le", - "netbsd386", - "netbsdamd64", - "netbsdarm", - "openbsd386", - "openbsdamd64", - "openbsdarm", - "plan9386", - "plan9amd64", - "solarisamd64", - "windows386", - "windowsamd64", -} - -func valid(goos, goarch string) bool { - var s = goos + goarch - for _, a := range valids { - if a == s { - return true - } - } - return false -} diff --git a/pipeline/build/build_test.go b/pipeline/build/build_test.go index 52da0296b..f3c6e03f1 100644 --- a/pipeline/build/build_test.go +++ b/pipeline/build/build_test.go @@ -1,13 +1,32 @@ package build import ( + "runtime" "testing" + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" "github.com/stretchr/testify/assert" ) -func TestValid(t *testing.T) { - assert.True(t, valid("windows", "386")) - assert.True(t, valid("linux", "386")) - assert.False(t, valid("windows", "arm")) +func TestRun(t *testing.T) { + assert.NoError(t, run(runtime.GOOS, runtime.GOARCH, []string{"go", "list", "./..."})) +} + +func TestRunInvalidCommand(t *testing.T) { + assert.Error(t, run(runtime.GOOS, runtime.GOARCH, []string{"gggggo", "nope"})) +} + +func TestBuild(t *testing.T) { + assert := assert.New(t) + var config = config.Project{ + Build: config.Build{ + Binary: "testing", + Flags: "-n", + }, + } + var ctx = &context.Context{ + Config: config, + } + assert.NoError(build("build_test", runtime.GOOS, runtime.GOARCH, ctx)) } diff --git a/pipeline/build/ldflags.go b/pipeline/build/ldflags.go new file mode 100644 index 000000000..ccae0e313 --- /dev/null +++ b/pipeline/build/ldflags.go @@ -0,0 +1,32 @@ +package build + +import ( + "bytes" + "log" + "text/template" + "time" + + "github.com/goreleaser/goreleaser/context" +) + +type ldflagsData struct { + Date string + Commit string + Version string +} + +func ldflags(ctx *context.Context) (string, error) { + var data = ldflagsData{ + Commit: ctx.Git.Commit, + Version: ctx.Git.CurrentTag, + Date: time.Now().Format("2006-01-02_15:04:05"), + } + log.Println(ctx.Git) + var out bytes.Buffer + t, err := template.New("ldflags").Parse(ctx.Config.Build.Ldflags) + if err != nil { + return "", err + } + err = t.Execute(&out, data) + return out.String(), err +} diff --git a/pipeline/build/ldflags_test.go b/pipeline/build/ldflags_test.go new file mode 100644 index 000000000..cbd2541be --- /dev/null +++ b/pipeline/build/ldflags_test.go @@ -0,0 +1,46 @@ +package build + +import ( + "testing" + + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" + "github.com/stretchr/testify/assert" +) + +func TestLdFlagsFullTemplate(t *testing.T) { + assert := assert.New(t) + var config = config.Project{ + Build: config.Build{ + Ldflags: "-s -w -X main.version={{.Version}} -X main.date={{.Date}} -X main.commit={{.Commit}}", + }, + } + var ctx = &context.Context{ + Git: context.GitInfo{ + CurrentTag: "v1.2.3", + Commit: "123", + }, + Config: config, + } + flags, err := ldflags(ctx) + assert.NoError(err) + assert.Contains(flags, "-s -w") + assert.Contains(flags, "-X main.version=v1.2.3") + assert.Contains(flags, "-X main.commit=123") + // TODO assert main.date +} + +func TestInvalidTemplate(t *testing.T) { + assert := assert.New(t) + var config = config.Project{ + Build: config.Build{ + Ldflags: "{invalid{.Template}}}{{}}}", + }, + } + var ctx = &context.Context{ + Config: config, + } + flags, err := ldflags(ctx) + assert.Error(err) + assert.Equal(flags, "") +} diff --git a/pipeline/build/valid_os.go b/pipeline/build/valid_os.go new file mode 100644 index 000000000..b3798d0ea --- /dev/null +++ b/pipeline/build/valid_os.go @@ -0,0 +1,45 @@ +package build + +// list from https://golang.org/doc/install/source#environment +var valids = []string{ + "androidarm", + "darwin386", + "darwinamd64", + "darwinarm", + "darwinarm64", + "dragonflyamd64", + "freebsd386", + "freebsdamd64", + "freebsdarm", + "linux386", + "linuxamd64", + "linuxarm", + "linuxarm64", + "linuxppc64", + "linuxppc64le", + "linuxmips", + "linuxmipsle", + "linuxmips64", + "linuxmips64le", + "netbsd386", + "netbsdamd64", + "netbsdarm", + "openbsd386", + "openbsdamd64", + "openbsdarm", + "plan9386", + "plan9amd64", + "solarisamd64", + "windows386", + "windowsamd64", +} + +func valid(goos, goarch string) bool { + var s = goos + goarch + for _, a := range valids { + if a == s { + return true + } + } + return false +} diff --git a/pipeline/build/valid_os_test.go b/pipeline/build/valid_os_test.go new file mode 100644 index 000000000..52da0296b --- /dev/null +++ b/pipeline/build/valid_os_test.go @@ -0,0 +1,13 @@ +package build + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValid(t *testing.T) { + assert.True(t, valid("windows", "386")) + assert.True(t, valid("linux", "386")) + assert.False(t, valid("windows", "arm")) +} diff --git a/pipeline/defaults/defaults.go b/pipeline/defaults/defaults.go index fab1915ee..3f7ad60ba 100644 --- a/pipeline/defaults/defaults.go +++ b/pipeline/defaults/defaults.go @@ -51,7 +51,7 @@ func (Pipe) Run(ctx *context.Context) error { ctx.Config.Build.Goarch = []string{"amd64", "386"} } if ctx.Config.Build.Ldflags == "" { - ctx.Config.Build.Ldflags = "-s -w" + ctx.Config.Build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}" } if ctx.Config.Archive.NameTemplate == "" { diff --git a/pipeline/git/commit.go b/pipeline/git/commit.go new file mode 100644 index 000000000..f0172bc40 --- /dev/null +++ b/pipeline/git/commit.go @@ -0,0 +1,21 @@ +package git + +import ( + "errors" + "os/exec" + "strings" +) + +func commitHash() (string, error) { + cmd := exec.Command( + "git", + "show", + "--format='%H'", + "HEAD", + ) + bts, err := cmd.CombinedOutput() + if err != nil { + return "", errors.New(err.Error() + ": " + string(bts)) + } + return strings.Replace(strings.Split(string(bts), "\n")[0], "'", "", -1), err +} diff --git a/pipeline/git/commit_test.go b/pipeline/git/commit_test.go new file mode 100644 index 000000000..b46ef3542 --- /dev/null +++ b/pipeline/git/commit_test.go @@ -0,0 +1,15 @@ +package git + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCommit(t *testing.T) { + assert := assert.New(t) + commit, err := commitHash() + assert.NoError(err) + assert.NotEmpty(commit) + assert.NotContains(commit, "'") +} diff --git a/pipeline/git/git.go b/pipeline/git/git.go index cdd62e67a..f1a7db438 100644 --- a/pipeline/git/git.go +++ b/pipeline/git/git.go @@ -49,5 +49,10 @@ func (Pipe) Run(ctx *context.Context) (err error) { if matches, err := regexp.MatchString("^[0-9.]+", ctx.Version); !matches || err != nil { return ErrInvalidVersionFormat{ctx.Version} } + commit, err := commitHash() + if err != nil { + return + } + ctx.Git.Commit = commit return }