From b886bb27f85e7215e0bebcf8a1fc040688510546 Mon Sep 17 00:00:00 2001 From: Keaton Svoma Date: Sat, 30 May 2026 23:41:04 -0500 Subject: [PATCH] fix(site): parse version from JSON output so badge shows the release tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The site generator invoked `jamf-cli version` and parsed the first line expecting the text banner `jamf-cli `. But the global `-o` default is `json`, and since #195 the version command honors it — so the bare call returns JSON, whose first line is `{`. parseVersion saw a single field, returned "unknown", and the display layer prefixed `v`, rendering "vunknown" on the deployed site. Fix: - Invoke `version -o json` explicitly and read the structured `version` field, falling back to the legacy banner for older binaries that predate `-o` support (the deploy builds the binary from the latest release tag while taking the generator from main). - Strip the git-describe `-dirty` marker so the clean release tag shows. The deploy overlays site files from main onto the tagged checkout, dirtying the tree; without this a clean tag surfaced as "v1.17.0-dirty". All build paths (deploy, release, local `make site`) now resolve to the nearest release tag, e.g. v1.17.0. Co-Authored-By: Claude Opus 4.8 (1M context) --- generator/site/main.go | 36 ++++++++++++++++++++++++++++++------ generator/site/main_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/generator/site/main.go b/generator/site/main.go index 836af901..d4041334 100644 --- a/generator/site/main.go +++ b/generator/site/main.go @@ -53,7 +53,7 @@ func main() { previous := flag.String("previous", "", "path to previous commands.json for new-command detection") flag.Parse() - versionOut, err := exec.Command(*binary, "version").Output() + versionOut, err := exec.Command(*binary, "version", "-o", "json").Output() if err != nil { fmt.Fprintf(os.Stderr, "error running %s version: %v\n", *binary, err) os.Exit(1) @@ -369,13 +369,16 @@ func splitCSV(s string) []string { } func parseVersion(output string) string { - line, _, _ := strings.Cut(output, "\n") - fields := strings.Fields(line) - if len(fields) < 2 { + v := extractRawVersion(output) + if v == "" { return "unknown" } - v := fields[1] - // Strip git-describe suffix (e.g. "v1.2.0-52-gffc0b5a-dirty" → "v1.2.0") + // Drop the git-describe "-dirty" marker so the site shows the clean + // release tag. The deploy workflow builds from the latest release tag but + // overlays site files from main, which dirties the tree — without this a + // clean tag surfaces as "v1.17.0-dirty" instead of "v1.17.0". + v = strings.TrimSuffix(v, "-dirty") + // Strip git-describe suffix (e.g. "v1.2.0-52-gffc0b5a" → "v1.2.0") if parts := strings.SplitN(v, "-", 2); len(parts) == 2 && strings.ContainsAny(parts[1], "0123456789") { if _, err := strconv.Atoi(strings.Split(parts[1], "-")[0]); err == nil { v = parts[0] @@ -385,3 +388,24 @@ func parseVersion(output string) string { v = strings.TrimPrefix(v, "v") return v } + +// extractRawVersion pulls the raw version string out of `jamf-cli version` +// output. The command emits JSON by default (the global -o default is json), +// so we parse that first; older binaries that predate -o support print a +// "jamf-cli " banner, so we fall back to that. Returns "" when +// neither yields a version, letting the caller map it to "unknown". +func extractRawVersion(output string) string { + if strings.HasPrefix(strings.TrimSpace(output), "{") { + var report struct { + Version string `json:"version"` + } + if json.Unmarshal([]byte(output), &report) == nil { + return report.Version + } + } + line, _, _ := strings.Cut(output, "\n") + if fields := strings.Fields(line); len(fields) >= 2 { + return fields[1] + } + return "" +} diff --git a/generator/site/main_test.go b/generator/site/main_test.go index 7ad6531a..fc913757 100644 --- a/generator/site/main_test.go +++ b/generator/site/main_test.go @@ -285,6 +285,31 @@ func TestParseVersion(t *testing.T) { input: "", want: "unknown", }, + { + name: "json default output strips git-describe suffix", + input: "{\n \"version\": \"v1.17.0-25-g74846ff\",\n \"commit\": \"74846ff\",\n \"built\": \"2026-05-31T04:31:46Z\"\n}\n", + want: "1.17.0", + }, + { + name: "json exact tag", + input: `{"version":"v1.2.0","commit":"abc1234"}`, + want: "1.2.0", + }, + { + name: "json empty version falls back to unknown", + input: `{"version":"","commit":"abc1234"}`, + want: "unknown", + }, + { + name: "json exact tag dirty build strips dirty marker", + input: `{"version":"v1.17.0-dirty","commit":"abc1234"}`, + want: "1.17.0", + }, + { + name: "json describe with count and dirty marker", + input: `{"version":"v1.17.0-25-g74846ff-dirty","commit":"74846ff"}`, + want: "1.17.0", + }, } for _, tt := range tests {