mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: Add buildinfo package to embed version (#840)
This also resolves build time and commit hash using the Go 1.18 debug/buildinfo package. An external URL is outputted on running version as well to easily route the caller to a release or commit.
This commit is contained in:
+5
-2
@@ -16,7 +16,7 @@ before:
|
||||
builds:
|
||||
- id: coder-slim
|
||||
dir: cmd/coder
|
||||
ldflags: ["-s -w"]
|
||||
ldflags: ["-s -w -X github.com/coder/coder/cli/buildinfo.tag={{ .Version }}"]
|
||||
env: [CGO_ENABLED=0]
|
||||
goos: [darwin, linux, windows]
|
||||
goarch: [amd64]
|
||||
@@ -28,7 +28,7 @@ builds:
|
||||
- id: coder
|
||||
dir: cmd/coder
|
||||
flags: [-tags=embed]
|
||||
ldflags: ["-s -w"]
|
||||
ldflags: ["-s -w -X github.com/coder/coder/cli/buildinfo.tag={{ .Version }}"]
|
||||
env: [CGO_ENABLED=0]
|
||||
goos: [darwin, linux, windows]
|
||||
goarch: [amd64, arm64]
|
||||
@@ -58,3 +58,6 @@ nfpms:
|
||||
|
||||
release:
|
||||
ids: [coder, packages]
|
||||
|
||||
snapshot:
|
||||
name_template: '{{ .Version }}-devel+{{ .ShortCommit }}'
|
||||
|
||||
Vendored
+1
@@ -5,6 +5,7 @@
|
||||
"coderd",
|
||||
"coderdtest",
|
||||
"codersdk",
|
||||
"devel",
|
||||
"drpc",
|
||||
"drpcconn",
|
||||
"drpcmux",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package buildinfo
|
||||
|
||||
import (
|
||||
"path"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
var (
|
||||
buildInfo *debug.BuildInfo
|
||||
buildInfoValid bool
|
||||
readBuildInfo sync.Once
|
||||
|
||||
// Injected with ldflags at build!
|
||||
tag string
|
||||
)
|
||||
|
||||
// Version returns the semantic version of the build.
|
||||
// Use golang.org/x/mod/semver to compare versions.
|
||||
func Version() string {
|
||||
revision, valid := revision()
|
||||
if valid {
|
||||
revision = "+" + revision[:7]
|
||||
}
|
||||
if tag == "" {
|
||||
return "v0.0.0-devel" + revision
|
||||
}
|
||||
if semver.Build(tag) == "" {
|
||||
tag += revision
|
||||
}
|
||||
return "v" + tag
|
||||
}
|
||||
|
||||
// ExternalURL returns a URL referencing the current Coder version.
|
||||
// For production builds, this will link directly to a release.
|
||||
// For development builds, this will link to a commit.
|
||||
func ExternalURL() string {
|
||||
repo := "https://github.com/coder/coder"
|
||||
revision, valid := revision()
|
||||
if !valid {
|
||||
return repo
|
||||
}
|
||||
return path.Join(repo, "commit", revision)
|
||||
}
|
||||
|
||||
// Time returns when the Git revision was published.
|
||||
func Time() (time.Time, bool) {
|
||||
value, valid := find("vcs.time")
|
||||
if !valid {
|
||||
return time.Time{}, false
|
||||
}
|
||||
parsed, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
panic("couldn't parse time: " + err.Error())
|
||||
}
|
||||
return parsed, true
|
||||
}
|
||||
|
||||
// revision returns the Git hash of the build.
|
||||
func revision() (string, bool) {
|
||||
return find("vcs.revision")
|
||||
}
|
||||
|
||||
// find panics if a setting with the specific key was not
|
||||
// found in the build info.
|
||||
func find(key string) (string, bool) {
|
||||
readBuildInfo.Do(func() {
|
||||
buildInfo, buildInfoValid = debug.ReadBuildInfo()
|
||||
})
|
||||
if !buildInfoValid {
|
||||
panic("couldn't read build info")
|
||||
}
|
||||
for _, setting := range buildInfo.Settings {
|
||||
if setting.Key != key {
|
||||
continue
|
||||
}
|
||||
return setting.Value, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package buildinfo_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"github.com/coder/coder/cli/buildinfo"
|
||||
)
|
||||
|
||||
func TestBuildInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Version", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
version := buildinfo.Version()
|
||||
require.True(t, semver.IsValid(version))
|
||||
prerelease := semver.Prerelease(version)
|
||||
require.Equal(t, "-devel", prerelease)
|
||||
require.Equal(t, "v0", semver.Major(version))
|
||||
})
|
||||
t.Run("ExternalURL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, "https://github.com/coder/coder", buildinfo.ExternalURL())
|
||||
})
|
||||
// Tests don't include Go build info.
|
||||
t.Run("NoTime", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, valid := buildinfo.Time()
|
||||
require.False(t, valid)
|
||||
})
|
||||
}
|
||||
+15
@@ -4,12 +4,14 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/kirsle/configdir"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/coder/coder/cli/buildinfo"
|
||||
"github.com/coder/coder/cli/cliui"
|
||||
"github.com/coder/coder/cli/config"
|
||||
"github.com/coder/coder/codersdk"
|
||||
@@ -28,6 +30,7 @@ const (
|
||||
func Root() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "coder",
|
||||
Version: buildinfo.Version(),
|
||||
SilenceUsage: true,
|
||||
Long: ` ▄█▀ ▀█▄
|
||||
▄▄ ▀▀▀ █▌ ██▀▀█▄ ▐█
|
||||
@@ -55,6 +58,7 @@ func Root() *cobra.Command {
|
||||
`Flags:`, header.Render("Flags:"),
|
||||
`Additional help topics:`, header.Render("Additional help:"),
|
||||
).Replace(cmd.UsageTemplate()))
|
||||
cmd.SetVersionTemplate(versionTemplate())
|
||||
|
||||
cmd.AddCommand(
|
||||
configSSH(),
|
||||
@@ -142,3 +146,14 @@ func isTTY(cmd *cobra.Command) bool {
|
||||
}
|
||||
return isatty.IsTerminal(file.Fd())
|
||||
}
|
||||
|
||||
func versionTemplate() string {
|
||||
template := `Coder {{printf "%s" .Version}}`
|
||||
buildTime, valid := buildinfo.Time()
|
||||
if valid {
|
||||
template += " " + buildTime.Format(time.UnixDate)
|
||||
}
|
||||
template += "\r\n" + buildinfo.ExternalURL()
|
||||
template += "\r\n"
|
||||
return template
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ require (
|
||||
github.com/zclconf/go-cty v1.10.0 // indirect
|
||||
github.com/zeebo/errs v1.2.2 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/mod v0.5.1
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
|
||||
Reference in New Issue
Block a user