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 | |||
| 56 | 56 | 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 | 61 | ### From source | |
| 60 | 62 | ||
| 61 | 63 | ```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@latest | 56 | 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 source | 61 | ### From source | ||
| 60 | 62 | ||||
| 61 | ```bash | 63 | ```bash | ||
M cmd/zot/main.go +21 -1
| @@ -4,6 +4,8 @@ package main | |||
| 4 | 4 | import ( | |
| 5 | 5 | "fmt" | |
| 6 | 6 | "os" | |
| 7 | + | "runtime/debug" | |
| 8 | + | "strings" | |
| 7 | 9 | ||
| 8 | 10 | "github.com/patriceckhart/zot/packages/agent" | |
| 9 | 11 | ) | |
| @@ -22,7 +24,7 @@ var ( | |||
| 22 | 24 | ) | |
| 23 | 25 | ||
| 24 | 26 | func main() { | |
| 25 | - | v := version | |
| 27 | + | v := resolvedVersion(version) | |
| 26 | 28 | if commit != "" { | |
| 27 | 29 | short := commit | |
| 28 | 30 | if len(short) > 7 { | |
| @@ -39,3 +41,21 @@ func main() { | |||
| 39 | 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 | + | } | |
| @@ -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 := version | 27 | + | v := resolvedVersion(version) |
| 26 | if commit != "" { | 28 | if commit != "" { | ||
| 27 | short := commit | 29 | 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: | |||
| 68 | 68 | notes: | |
| 69 | 69 | * The binary must be writable by the current user. On a system-wide | |
| 70 | 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 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. | |
| 74 | 74 | * Honours $GITHUB_TOKEN if set, so private-repo releases work. | |
| 75 | 75 | * After the binary is installed, every extension under | |
| 76 | 76 | $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-wide | 69 | * 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 from | 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 | 72 | + | 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 under | 75 | * After the binary is installed, every extension under | ||
| 76 | $ZOT_HOME/extensions/ that is a git checkout is fast-forward | 76 | $ZOT_HOME/extensions/ that is a git checkout is fast-forward | ||