Add --version flag and enforce --long flags via pflag
Switches CLI flag parsing from Go's stdlib `flag` to spf13/pflag so `--project` (and the internal `--socket` / `--identity` / `--scenario` flags) are the only accepted form; single-hyphen long flags like `-project` are now rejected. Help output renders the canonical `--` form. Adds `patterm --version`, which prints the build version, short commit, and build date (e.g. `patterm v0.0.1 (commit abc1234, built 2026-05-14)`). The version string is injected at build time — `make patterm` derives it from `git describe --tags --always --dirty`, and the release workflow injects the pushed tag. Commit/date come from the Go toolchain's embedded VCS info via `runtime/debug.ReadBuildInfo`, so no manual bumping is required.
This commit is contained in:
@@ -30,7 +30,8 @@ jobs:
|
|||||||
CGO_ENABLED: 1
|
CGO_ENABLED: 1
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
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 \
|
-o dist/patterm-${{ github.ref_name }}-linux-amd64 \
|
||||||
./cmd/patterm
|
./cmd/patterm
|
||||||
|
|
||||||
|
|||||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -6,6 +6,21 @@ loosely follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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
|
## [0.0.1] - 2026-05-14
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -32,11 +32,13 @@ deps-build: $(INSTALL)/lib/libghostty-vt.a
|
|||||||
clean-deps:
|
clean-deps:
|
||||||
rm -rf $(SOURCE) $(INSTALL)
|
rm -rf $(SOURCE) $(INSTALL)
|
||||||
|
|
||||||
|
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
||||||
|
|
||||||
spike: deps
|
spike: deps
|
||||||
go build -o ./bin/spike ./cmd/spike
|
go build -o ./bin/spike ./cmd/spike
|
||||||
|
|
||||||
patterm: deps
|
patterm: deps
|
||||||
go build -o ./bin/patterm ./cmd/patterm
|
go build -ldflags "-X main.version=$(VERSION)" -o ./bin/patterm ./cmd/patterm
|
||||||
|
|
||||||
test: deps
|
test: deps
|
||||||
go test ./...
|
go test ./...
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/hjbdev/patterm/internal/harness"
|
"github.com/hjbdev/patterm/internal/harness"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
//
|
//
|
||||||
// patterm run in $PWD
|
// patterm run in $PWD
|
||||||
// patterm --project <dir> run in <dir>
|
// patterm --project <dir> run in <dir>
|
||||||
|
// patterm --version print version and exit
|
||||||
// patterm mcp-stdio --socket S --identity I
|
// patterm mcp-stdio --socket S --identity I
|
||||||
// internal: stdio MCP proxy spawned for
|
// internal: stdio MCP proxy spawned for
|
||||||
// children, forwards JSON-RPC over S
|
// children, forwards JSON-RPC over S
|
||||||
@@ -13,15 +14,22 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/hjbdev/patterm/internal/app"
|
"github.com/hjbdev/patterm/internal/app"
|
||||||
"github.com/hjbdev/patterm/internal/mcp"
|
"github.com/hjbdev/patterm/internal/mcp"
|
||||||
"github.com/hjbdev/patterm/internal/projectkey"
|
"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() {
|
func main() {
|
||||||
// The mcp-stdio subcommand is a separate top-level mode: when an
|
// The mcp-stdio subcommand is a separate top-level mode: when an
|
||||||
// agent CLI launches `patterm mcp-stdio --socket ...`, the same
|
// agent CLI launches `patterm mcp-stdio --socket ...`, the same
|
||||||
@@ -38,9 +46,17 @@ func main() {
|
|||||||
return
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
|
if *showVersion {
|
||||||
|
fmt.Println(versionString())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die("getwd: %v", err)
|
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) {
|
func die(format string, args ...any) {
|
||||||
fmt.Fprintf(os.Stderr, "patterm: "+format+"\n", args...)
|
fmt.Fprintf(os.Stderr, "patterm: "+format+"\n", args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -4,6 +4,7 @@ go 1.26.3
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/creack/pty v1.1.24
|
github.com/creack/pty v1.1.24
|
||||||
|
github.com/spf13/pflag v1.0.10
|
||||||
golang.org/x/term v0.43.0
|
golang.org/x/term v0.43.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,5 +1,7 @@
|
|||||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
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 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
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=
|
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||||
|
|||||||
Reference in New Issue
Block a user