diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 65f05aa..7a06a0f 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -30,7 +30,8 @@ jobs: CGO_ENABLED: 1 run: | mkdir -p dist - go build -trimpath -ldflags="-s -w" \ + go build -trimpath \ + -ldflags="-s -w -X main.version=${{ github.ref_name }}" \ -o dist/patterm-${{ github.ref_name }}-linux-amd64 \ ./cmd/patterm diff --git a/CHANGELOG.md b/CHANGELOG.md index bb9ce0b..ddbfdd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ loosely follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `patterm --version` prints the build version, git commit, and build + date (e.g. `patterm v0.0.1 (commit abc1234, built 2026-05-14)`). The + version string is injected by the build (`make patterm` derives it + from `git describe`; the release workflow injects the pushed tag). + Commit and date come from the Go toolchain's embedded VCS info, so + nothing has to be bumped by hand. + +### Changed +- CLI flag parsing switched from Go's stdlib `flag` to `spf13/pflag`. + `--project` (and the internal `--socket` / `--identity` / + `--scenario` / `--patterm-bin` flags) are now the only accepted form + — single-hyphen long flags like `-project` are rejected. Help output + renders the canonical `--flag` form. + ## [0.0.1] - 2026-05-14 ### Fixed diff --git a/Makefile b/Makefile index 799570a..2b62e4d 100644 --- a/Makefile +++ b/Makefile @@ -32,11 +32,13 @@ deps-build: $(INSTALL)/lib/libghostty-vt.a clean-deps: rm -rf $(SOURCE) $(INSTALL) +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) + spike: deps go build -o ./bin/spike ./cmd/spike patterm: deps - go build -o ./bin/patterm ./cmd/patterm + go build -ldflags "-X main.version=$(VERSION)" -o ./bin/patterm ./cmd/patterm test: deps go test ./... diff --git a/cmd/patterm/debug_harness.go b/cmd/patterm/debug_harness.go index dee275b..768cfd2 100644 --- a/cmd/patterm/debug_harness.go +++ b/cmd/patterm/debug_harness.go @@ -2,10 +2,11 @@ package main import ( "encoding/json" - "flag" "fmt" "os" + flag "github.com/spf13/pflag" + "github.com/hjbdev/patterm/internal/harness" ) diff --git a/cmd/patterm/main.go b/cmd/patterm/main.go index e45d141..61c009c 100644 --- a/cmd/patterm/main.go +++ b/cmd/patterm/main.go @@ -4,6 +4,7 @@ // // patterm run in $PWD // patterm --project run in +// patterm --version print version and exit // patterm mcp-stdio --socket S --identity I // internal: stdio MCP proxy spawned for // children, forwards JSON-RPC over S @@ -13,15 +14,22 @@ package main import ( "context" - "flag" "fmt" "os" + "runtime/debug" + "time" + + flag "github.com/spf13/pflag" "github.com/hjbdev/patterm/internal/app" "github.com/hjbdev/patterm/internal/mcp" "github.com/hjbdev/patterm/internal/projectkey" ) +// version is overridden at build time via `-ldflags "-X main.version=..."`. +// Defaults to "dev" so source builds are still meaningful. +var version = "dev" + func main() { // The mcp-stdio subcommand is a separate top-level mode: when an // agent CLI launches `patterm mcp-stdio --socket ...`, the same @@ -38,9 +46,17 @@ func main() { return } - var projectDir = flag.String("project", "", "project directory (default $PWD)") + var ( + projectDir = flag.String("project", "", "project directory (default $PWD)") + showVersion = flag.Bool("version", false, "print version and exit") + ) flag.Parse() + if *showVersion { + fmt.Println(versionString()) + return + } + cwd, err := os.Getwd() if err != nil { die("getwd: %v", err) @@ -80,6 +96,33 @@ func runMCPProxy() { } } +func versionString() string { + commit, date := "unknown", "unknown" + if info, ok := debug.ReadBuildInfo(); ok { + dirty := false + for _, s := range info.Settings { + switch s.Key { + case "vcs.revision": + if len(s.Value) >= 7 { + commit = s.Value[:7] + } else if s.Value != "" { + commit = s.Value + } + case "vcs.time": + if t, err := time.Parse(time.RFC3339, s.Value); err == nil { + date = t.Format("2006-01-02") + } + case "vcs.modified": + dirty = s.Value == "true" + } + } + if dirty && commit != "unknown" { + commit += "-dirty" + } + } + return fmt.Sprintf("patterm %s (commit %s, built %s)", version, commit, date) +} + func die(format string, args ...any) { fmt.Fprintf(os.Stderr, "patterm: "+format+"\n", args...) os.Exit(1) diff --git a/go.mod b/go.mod index 5e0e902..6a63a7f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.26.3 require ( github.com/creack/pty v1.1.24 + github.com/spf13/pflag v1.0.10 golang.org/x/term v0.43.0 ) diff --git a/go.sum b/go.sum index bea4245..9d57516 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=