Report development builds as such, not as the release version

Only a release build (the commit tagged v<VERSION>, unchanged) gives a
version number. Others print 'dev <commit> (3 commits after v0.2.0-rc.1)',
from git describe (the Makefile's DESCRIBE), in jarld version,
jarl --version, jarl status, jarl whoami and the startup log; a build
without git says '(source build)'. The CLI's User-Agent and the footer's
tooltip follow suit, and /status's build gains since and ahead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-09 15:28:13 +00:00
1 parent 88bb89a9ff
commit fb1ee09250
7 files changed
+123 -23

No files matched your search

+65 -9
View File
@@ -4,12 +4,17 @@
//
// The Makefile sets Version from the VERSION file, and Tag only when the
// build is of that release: HEAD is the tag v<VERSION> and no tracked file
// is changed. Other builds are identified by their commit, which Go
// records from git by itself.
// is changed. Only such a build reports a version number. Other builds
// are development builds, identified by their commit (which Go records
// from git by itself) and by where they stand relative to the last tag
// (Describe, from git describe).
package version
import (
"regexp"
"runtime/debug"
"strconv"
"strings"
"sync"
)
@@ -23,6 +28,11 @@ var Tag = ""
// information when not set at build time.
var Commit = ""
// Describe is 'git describe --tags --long' of the build's commit
// ("v0.2.0-rc.1-3-gca0f148b78b6"), set by the Makefile; it says how many
// commits a development build is past the last tag.
var Describe = ""
// Repo is Jarl's source repository.
const Repo = "https://gitea.adbraun.lol/adam/jarl"
@@ -32,6 +42,11 @@ type Build struct {
Tag string `json:"tag,omitempty"`
Commit string `json:"commit,omitempty"`
Modified bool `json:"modified,omitempty"` // built with uncommitted changes
// Since is the last tag before a development build's commit, and
// Ahead how many commits the build is past it (0: the tag's commit,
// with uncommitted changes).
Since string `json:"since,omitempty"`
Ahead int `json:"ahead,omitempty"`
// Label is the short form to show: the tag of a release, otherwise
// the abbreviated commit (or the version when the commit is unknown).
Label string `json:"label"`
@@ -53,9 +68,23 @@ var info = sync.OnceValue(func() Build {
}
}
}
return describe(Version, Tag, commit, modified)
b := describe(Version, Tag, commit, modified)
b.Since, b.Ahead = parseDescribe(Describe, b.Commit)
return b
})
// parseDescribe splits git describe --long output, if it is of commit.
func parseDescribe(d, commit string) (since string, ahead int) {
m := describeRe.FindStringSubmatch(d)
if m == nil || commit == "" || !strings.HasPrefix(commit, m[3]) {
return "", 0
}
n, _ := strconv.Atoi(m[2])
return m[1], n
}
var describeRe = regexp.MustCompile(`^(v.+)-(\d+)-g([0-9a-f]+)$`)
func describe(version, tag, commit string, modified bool) Build {
b := Build{Version: version, Tag: tag, Commit: commit, Modified: modified}
switch {
@@ -76,14 +105,41 @@ func describe(version, tag, commit string, modified bool) Build {
// Info describes this build.
func Info() Build { return info() }
// String is the version for logs and 'version' commands: "0.2.0" for a
// release, otherwise the version and the commit ("0.2.0-dev (a1b2c3d4e5f6)").
func String() string {
b := Info()
if b.Tag != "" || b.Commit == "" {
// String is the version for logs and 'version' commands. Only a release
// build gives a version ("0.2.0"); any other build says it's a
// development build: "dev a1b2c3d4e5f6 (3 commits after v0.2.0-rc.1)".
func String() string { return Info().String() }
func (b Build) String() string {
switch {
case b.Tag != "":
return b.Version
case b.Commit == "":
// No git information (built from a source archive).
return b.Version + " (source build)"
}
return b.Version + " (" + b.Label + ")"
s := "dev " + b.Label
switch {
case b.Since == "":
case b.Ahead == 1:
s += " (1 commit after " + b.Since + ")"
case b.Ahead > 1:
s += " (" + strconv.Itoa(b.Ahead) + " commits after " + b.Since + ")"
case b.Modified:
s += " (" + b.Since + " with uncommitted changes)"
default:
s += " (at " + b.Since + ")"
}
return s
}
// Short is the version for a User-Agent: the version of a release,
// otherwise the label ("a1b2c3d4e5f6").
func Short() string {
if b := Info(); b.Tag == "" && b.Commit != "" {
return b.Label
}
return Version
}
func short(commit string) string {