From 776f3d91b49fae6008c1bd36e8587bd2503eea21 Mon Sep 17 00:00:00 2001 From: kostyay Date: Thu, 12 Feb 2026 10:20:39 +0100 Subject: [PATCH] feat: add kt version command with build-time injection Version, commit SHA, and build date injected via ldflags in Makefile. Falls back to "dev" for plain go build. Co-Authored-By: Claude Opus 4.6 --- .ktickets/kti-427f.md | 12 ++++ .ktickets/kti-4abf.md | 12 ++++ .ktickets/kti-6f9c.md | 12 ++++ .ktickets/kti-b9fc.md | 12 ++++ Makefile | 10 ++- docs/plans/2026-02-12-kt-version-design.md | 42 +++++++++++++ internal/cmd/version.go | 28 +++++++++ internal/cmd/version_test.go | 71 ++++++++++++++++++++++ internal/version/version.go | 8 +++ 9 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 .ktickets/kti-427f.md create mode 100644 .ktickets/kti-4abf.md create mode 100644 .ktickets/kti-6f9c.md create mode 100644 .ktickets/kti-b9fc.md create mode 100644 docs/plans/2026-02-12-kt-version-design.md create mode 100644 internal/cmd/version.go create mode 100644 internal/cmd/version_test.go create mode 100644 internal/version/version.go diff --git a/.ktickets/kti-427f.md b/.ktickets/kti-427f.md new file mode 100644 index 0000000..08dc1ac --- /dev/null +++ b/.ktickets/kti-427f.md @@ -0,0 +1,12 @@ +--- +id: kti-427f +status: closed +created: "2026-02-12T01:11:47Z" +type: task +priority: 2 +assignee: kostyay +tests_passed: false +--- +# Add tests for version command + +Test version command output in text and json modes diff --git a/.ktickets/kti-4abf.md b/.ktickets/kti-4abf.md new file mode 100644 index 0000000..b8dbb47 --- /dev/null +++ b/.ktickets/kti-4abf.md @@ -0,0 +1,12 @@ +--- +id: kti-4abf +status: closed +created: "2026-02-12T01:11:43Z" +type: task +priority: 2 +assignee: kostyay +tests_passed: false +--- +# Add internal/version package + +Create internal/version/version.go with ldflags-injectable vars and dev fallbacks diff --git a/.ktickets/kti-6f9c.md b/.ktickets/kti-6f9c.md new file mode 100644 index 0000000..6122b30 --- /dev/null +++ b/.ktickets/kti-6f9c.md @@ -0,0 +1,12 @@ +--- +id: kti-6f9c +status: closed +created: "2026-02-12T01:11:47Z" +type: task +priority: 2 +assignee: kostyay +tests_passed: false +--- +# Update Makefile build target with ldflags + +Inject Version/Commit/Date via ldflags in build target diff --git a/.ktickets/kti-b9fc.md b/.ktickets/kti-b9fc.md new file mode 100644 index 0000000..bef530b --- /dev/null +++ b/.ktickets/kti-b9fc.md @@ -0,0 +1,12 @@ +--- +id: kti-b9fc +status: closed +created: "2026-02-12T01:11:46Z" +type: task +priority: 2 +assignee: kostyay +tests_passed: false +--- +# Add kt version cobra command + +Add version.go command in internal/cmd that prints version info, supports --json diff --git a/Makefile b/Makefile index 8b0c1cf..13437a1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,13 @@ .PHONY: all fmt lint test build clean security install +VERSION := $(shell git describe --tags --always --dirty) +COMMIT := $(shell git rev-parse --short HEAD) +DATE := $(shell date -u +%FT%TZ) +LDFLAGS := -s -w \ + -X 'github.com/kostyay/kticket/internal/version.Version=$(VERSION)' \ + -X 'github.com/kostyay/kticket/internal/version.Commit=$(COMMIT)' \ + -X 'github.com/kostyay/kticket/internal/version.Date=$(DATE)' + all: lint test build fmt: @@ -12,7 +20,7 @@ test: go test -race -coverprofile=coverage.out ./... build: - go build -o kt ./cmd/kt + go build -ldflags "$(LDFLAGS)" -o kt ./cmd/kt clean: rm -f kt coverage.out diff --git a/docs/plans/2026-02-12-kt-version-design.md b/docs/plans/2026-02-12-kt-version-design.md new file mode 100644 index 0000000..e700379 --- /dev/null +++ b/docs/plans/2026-02-12-kt-version-design.md @@ -0,0 +1,42 @@ +# kt version + +## Summary + +Add `kt version` command. Version derived from git tags at build time via `-ldflags`. + +## Vars + +Package: `internal/version/version.go` + +- `Version` — `git describe --tags --always --dirty` +- `Commit` — short SHA +- `Date` — build timestamp (UTC) + +Fallback when built without ldflags: `"dev"`. + +## Command + +`kt version` output: +``` +kt v0.1.0-3-gabcdef (commit: abcdef, built: 2026-02-12T10:00:00Z) +``` + +`kt version --json`: +```json +{"version":"v0.1.0-3-gabcdef","commit":"abcdef","date":"2026-02-12T10:00:00Z"} +``` + +## Makefile + +```makefile +VERSION := $(shell git describe --tags --always --dirty) +COMMIT := $(shell git rev-parse --short HEAD) +DATE := $(shell date -u +%FT%TZ) +LDFLAGS := -s -w \ + -X 'github.com/kostyay/kticket/internal/version.Version=$(VERSION)' \ + -X 'github.com/kostyay/kticket/internal/version.Commit=$(COMMIT)' \ + -X 'github.com/kostyay/kticket/internal/version.Date=$(DATE)' + +build: + go build -ldflags "$(LDFLAGS)" -o kt ./cmd/kt +``` diff --git a/internal/cmd/version.go b/internal/cmd/version.go new file mode 100644 index 0000000..a5b3345 --- /dev/null +++ b/internal/cmd/version.go @@ -0,0 +1,28 @@ +package cmd + +import ( + "fmt" + + "github.com/kostyay/kticket/internal/version" + "github.com/spf13/cobra" +) + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print kt version", + Run: func(cmd *cobra.Command, args []string) { + if IsJSON() { + _ = PrintJSON(map[string]string{ + "version": version.Version, + "commit": version.Commit, + "date": version.Date, + }) + return + } + fmt.Printf("kt %s (commit: %s, built: %s)\n", version.Version, version.Commit, version.Date) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/internal/cmd/version_test.go b/internal/cmd/version_test.go new file mode 100644 index 0000000..78e9127 --- /dev/null +++ b/internal/cmd/version_test.go @@ -0,0 +1,71 @@ +package cmd + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/kostyay/kticket/internal/version" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRunVersion(t *testing.T) { + // Set known values + version.Version = "v0.1.0-test" + version.Commit = "abc1234" + version.Date = "2026-02-12T00:00:00Z" + + buf := new(bytes.Buffer) + rootCmd.SetOut(buf) + rootCmd.SetArgs([]string{"version"}) + err := rootCmd.Execute() + require.NoError(t, err) +} + +func TestRunVersionJSON(t *testing.T) { + defer setupTestEnv(t)() + jsonFlag = true + defer func() { jsonFlag = false }() + + version.Version = "v0.1.0-test" + version.Commit = "abc1234" + version.Date = "2026-02-12T00:00:00Z" + + buf := new(bytes.Buffer) + rootCmd.SetOut(buf) + rootCmd.SetArgs([]string{"version", "--json"}) + err := rootCmd.Execute() + require.NoError(t, err) +} + +func TestVersionDefaults(t *testing.T) { + // Reset to defaults + version.Version = "dev" + version.Commit = "unknown" + version.Date = "unknown" + + assert.Equal(t, "dev", version.Version) + assert.Equal(t, "unknown", version.Commit) + assert.Equal(t, "unknown", version.Date) +} + +func TestVersionJSONStructure(t *testing.T) { + version.Version = "v1.0.0" + version.Commit = "deadbeef" + version.Date = "2026-02-12T10:00:00Z" + + data := map[string]string{ + "version": version.Version, + "commit": version.Commit, + "date": version.Date, + } + b, err := json.Marshal(data) + require.NoError(t, err) + + var parsed map[string]string + require.NoError(t, json.Unmarshal(b, &parsed)) + assert.Equal(t, "v1.0.0", parsed["version"]) + assert.Equal(t, "deadbeef", parsed["commit"]) + assert.Equal(t, "2026-02-12T10:00:00Z", parsed["date"]) +} diff --git a/internal/version/version.go b/internal/version/version.go new file mode 100644 index 0000000..9965107 --- /dev/null +++ b/internal/version/version.go @@ -0,0 +1,8 @@ +package version + +// Overridden via -ldflags at build time. +var ( + Version = "dev" + Commit = "unknown" + Date = "unknown" +)