detached d25ea2b

Fix version detection for Go installs

commit
d25ea2b57361c344326cfed44ed6c815fb7082e7
author
patriceckhart · · 2026-07-21 16:46 UTC
parent
a11290d
signature
unsigned
refs
tag: v0.2.97
4 files · +77 -4
M README.md +2 -0
@@ -56,6 +56,8 @@ Drops `zot.exe` into `$HOME\bin` and adds it to the user PATH if missing. Open a
5656 go install github.com/patriceckhart/zot/cmd/zot@latest
5757 ```
5858
59+The installed binary reports the tagged module version and supports `zot update`.
60+
5961 ### From source
6062
6163 ```bash
@@ -56,6 +56,8 @@ Drops `zot.exe` into `$HOME\bin` and adds it to the user PATH if missing. Open a
56 go install github.com/patriceckhart/zot/cmd/zot@latest56 go install github.com/patriceckhart/zot/cmd/zot@latest
57 ```57 ```
58 58
59+The installed binary reports the tagged module version and supports `zot update`.
60+
59 ### From source61 ### From source
60 62
61 ```bash63 ```bash
M cmd/zot/main.go +21 -1
@@ -4,6 +4,8 @@ package main
44 import (
55 "fmt"
66 "os"
7+ "runtime/debug"
8+ "strings"
79
810 "github.com/patriceckhart/zot/packages/agent"
911 )
@@ -22,7 +24,7 @@ var (
2224 )
2325
2426 func main() {
25- v := version
27+ v := resolvedVersion(version)
2628 if commit != "" {
2729 short := commit
2830 if len(short) > 7 {
@@ -39,3 +41,21 @@ func main() {
3941 os.Exit(1)
4042 }
4143 }
44+
45+// resolvedVersion falls back to the module version embedded by Go when zot is
46+// installed with "go install ...@version". Release archives still use the
47+// version injected by GoReleaser, and local source builds remain 0.0.0.
48+func resolvedVersion(linkedVersion string) string {
49+ info, _ := debug.ReadBuildInfo()
50+ return resolvedVersionFromBuildInfo(linkedVersion, info)
51+}
52+
53+func resolvedVersionFromBuildInfo(linkedVersion string, info *debug.BuildInfo) string {
54+ if linkedVersion != "" && linkedVersion != "0.0.0" {
55+ return linkedVersion
56+ }
57+ if info == nil || info.Main.Version == "" || info.Main.Version == "(devel)" {
58+ return linkedVersion
59+ }
60+ return strings.TrimPrefix(info.Main.Version, "v")
61+}
@@ -4,6 +4,8 @@ package main
4 import (4 import (
5 "fmt"5 "fmt"
6 "os"6 "os"
7+ "runtime/debug"
8+ "strings"
7 9
8 "github.com/patriceckhart/zot/packages/agent"10 "github.com/patriceckhart/zot/packages/agent"
9 )11 )
@@ -22,7 +24,7 @@ var (
22 )24 )
23 25
24 func main() {26 func main() {
25- v := version27+ v := resolvedVersion(version)
26 if commit != "" {28 if commit != "" {
27 short := commit29 short := commit
28 if len(short) > 7 {30 if len(short) > 7 {
@@ -39,3 +41,21 @@ func main() {
39 os.Exit(1)41 os.Exit(1)
40 }42 }
41 }43 }
44+
45+// resolvedVersion falls back to the module version embedded by Go when zot is
46+// installed with "go install ...@version". Release archives still use the
47+// version injected by GoReleaser, and local source builds remain 0.0.0.
48+func resolvedVersion(linkedVersion string) string {
49+ info, _ := debug.ReadBuildInfo()
50+ return resolvedVersionFromBuildInfo(linkedVersion, info)
51+}
52+
53+func resolvedVersionFromBuildInfo(linkedVersion string, info *debug.BuildInfo) string {
54+ if linkedVersion != "" && linkedVersion != "0.0.0" {
55+ return linkedVersion
56+ }
57+ if info == nil || info.Main.Version == "" || info.Main.Version == "(devel)" {
58+ return linkedVersion
59+ }
60+ return strings.TrimPrefix(info.Main.Version, "v")
61+}
A cmd/zot/main_test.go +51 -0
@@ -0,0 +1,51 @@
1+package main
2+
3+import (
4+ "runtime/debug"
5+ "testing"
6+)
7+
8+func TestResolvedVersionFromBuildInfo(t *testing.T) {
9+ tests := []struct {
10+ name string
11+ linkedVersion string
12+ moduleVersion string
13+ want string
14+ }{
15+ {
16+ name: "go install release",
17+ linkedVersion: "0.0.0",
18+ moduleVersion: "v0.2.94",
19+ want: "0.2.94",
20+ },
21+ {
22+ name: "release linker version wins",
23+ linkedVersion: "0.2.95",
24+ moduleVersion: "v0.2.94",
25+ want: "0.2.95",
26+ },
27+ {
28+ name: "local build",
29+ linkedVersion: "0.0.0",
30+ moduleVersion: "(devel)",
31+ want: "0.0.0",
32+ },
33+ {
34+ name: "missing build info",
35+ linkedVersion: "0.0.0",
36+ want: "0.0.0",
37+ },
38+ }
39+
40+ for _, tt := range tests {
41+ t.Run(tt.name, func(t *testing.T) {
42+ var info *debug.BuildInfo
43+ if tt.moduleVersion != "" {
44+ info = &debug.BuildInfo{Main: debug.Module{Version: tt.moduleVersion}}
45+ }
46+ if got := resolvedVersionFromBuildInfo(tt.linkedVersion, info); got != tt.want {
47+ t.Fatalf("resolvedVersionFromBuildInfo(%q, module %q) = %q, want %q", tt.linkedVersion, tt.moduleVersion, got, tt.want)
48+ }
49+ })
50+ }
51+}
@@ -0,0 +1,51 @@
1+package main
2+
3+import (
4+ "runtime/debug"
5+ "testing"
6+)
7+
8+func TestResolvedVersionFromBuildInfo(t *testing.T) {
9+ tests := []struct {
10+ name string
11+ linkedVersion string
12+ moduleVersion string
13+ want string
14+ }{
15+ {
16+ name: "go install release",
17+ linkedVersion: "0.0.0",
18+ moduleVersion: "v0.2.94",
19+ want: "0.2.94",
20+ },
21+ {
22+ name: "release linker version wins",
23+ linkedVersion: "0.2.95",
24+ moduleVersion: "v0.2.94",
25+ want: "0.2.95",
26+ },
27+ {
28+ name: "local build",
29+ linkedVersion: "0.0.0",
30+ moduleVersion: "(devel)",
31+ want: "0.0.0",
32+ },
33+ {
34+ name: "missing build info",
35+ linkedVersion: "0.0.0",
36+ want: "0.0.0",
37+ },
38+ }
39+
40+ for _, tt := range tests {
41+ t.Run(tt.name, func(t *testing.T) {
42+ var info *debug.BuildInfo
43+ if tt.moduleVersion != "" {
44+ info = &debug.BuildInfo{Main: debug.Module{Version: tt.moduleVersion}}
45+ }
46+ if got := resolvedVersionFromBuildInfo(tt.linkedVersion, info); got != tt.want {
47+ t.Fatalf("resolvedVersionFromBuildInfo(%q, module %q) = %q, want %q", tt.linkedVersion, tt.moduleVersion, got, tt.want)
48+ }
49+ })
50+ }
51+}
M packages/agent/updatecmd.go +3 -3
@@ -68,9 +68,9 @@ usage:
6868 notes:
6969 * The binary must be writable by the current user. On a system-wide
7070 install (e.g. /usr/local/bin/zot owned by root) re-run with sudo.
71- * Dev builds (version 0.0.0) are refused — they typically come from
72- 'go install' or a local 'make build' and shouldn't be silently
73- replaced with a release binary.
71+ * Dev builds (version 0.0.0) are refused. They typically come from
72+ a local source build and shouldn't be silently replaced with a
73+ release binary.
7474 * Honours $GITHUB_TOKEN if set, so private-repo releases work.
7575 * After the binary is installed, every extension under
7676 $ZOT_HOME/extensions/ that is a git checkout is fast-forward
@@ -68,9 +68,9 @@ usage:
68 notes:68 notes:
69 * The binary must be writable by the current user. On a system-wide69 * The binary must be writable by the current user. On a system-wide
70 install (e.g. /usr/local/bin/zot owned by root) re-run with sudo.70 install (e.g. /usr/local/bin/zot owned by root) re-run with sudo.
71- * Dev builds (version 0.0.0) are refused — they typically come from71+ * Dev builds (version 0.0.0) are refused. They typically come from
72- 'go install' or a local 'make build' and shouldn't be silently72+ a local source build and shouldn't be silently replaced with a
73- replaced with a release binary.73+ release binary.
74 * Honours $GITHUB_TOKEN if set, so private-repo releases work.74 * Honours $GITHUB_TOKEN if set, so private-repo releases work.
75 * After the binary is installed, every extension under75 * After the binary is installed, every extension under
76 $ZOT_HOME/extensions/ that is a git checkout is fast-forward76 $ZOT_HOME/extensions/ that is a git checkout is fast-forward

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close