@@ -1,12 +1,21 @@
1package version
1package version
3−import "runtime/debug"
5+ "runtime/debug"
5// Build-time parameters set via -ldflags.
9// Build-time parameters set via -ldflags.
8 Version = "devel"
12 Version = "devel"
9 Commit = "unknown"
13 Commit = "unknown"
14+ // BuildID is a unique identifier for this build. For release builds it
15+ // equals Commit; for development builds (go run / go build without
16+ // ldflags) it is derived from the executable's modification time, which
17+ // changes on every recompilation.
12// A user may install crush using `go install github.com/charmbracelet/crush@latest`.
21// A user may install crush using `go install github.com/charmbracelet/crush@latest`.
@@ -15,11 +24,30 @@ var (
15// is only set for `go install` and not for `go build`).
24// is only set for `go install` and not for `go build`).
16func init() {
25func init() {
17 info, ok := debug.ReadBuildInfo()
26 info, ok := debug.ReadBuildInfo()
28+ mainVersion := info.Main.Version
29+ if mainVersion != "" && mainVersion != "(devel)" {
30+ Version = mainVersion
34+ // Derive BuildID when not set via ldflags.
36+ BuildID = deriveBuildID()
40+// deriveBuildID uses the running executable's modification time as a unique
41+// build fingerprint. This changes on every recompilation (including `go run`),
42+// making it reliable for detecting stale servers during development.
43+func deriveBuildID() string {
44+ exe, err := os.Executable()
19− return
46+ return "unknown"
21− mainVersion := info.Main.Version
48+ fi, err := os.Stat(exe)
22− if mainVersion != "" && mainVersion != "(devel)" {
49+ if err != nil {
23− Version = mainVersion
50+ return "unknown"
52+ return strconv.FormatInt(fi.ModTime().UnixNano(), 36)