From 711508ce58c507b6259dbfff04a0ff47230acfa6 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Fri, 24 Apr 2026 22:43:49 -0700 Subject: [PATCH] feat: added the --port flag --- internal/cli/run.go | 56 ++++++++++++++++++++++++++++---------- internal/cli/run_test.go | 36 +++++++++++++++++++++--- web/src/pages/DocsPage.tsx | 2 +- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/internal/cli/run.go b/internal/cli/run.go index 809581d..344480c 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -14,8 +14,7 @@ import ( "termtap.dev/internal/tui" ) -// This should be configurable at some point, just in case they build on 8080 -const proxy_addr = "127.0.0.1:8080" +const defaultProxyPort = 8080 var fatalExit = log.Fatalln var stdoutWriter io.Writer = stdioRef{isErr: false} @@ -44,13 +43,13 @@ func Run(args []string) { return } - cmd, ok := parseCommand(args) + cmd, proxyAddr, ok := parseCommand(args) if !ok { displayHelp() return } - session, err := startSessionFn(cmd, proxy_addr) + session, err := startSessionFn(cmd, proxyAddr) if err != nil { fatalExit(err) return @@ -67,21 +66,50 @@ func Run(args []string) { } } -func parseCommand(args []string) (model.Command, bool) { +func parseCommand(args []string) (model.Command, string, bool) { if len(args) < 4 { - return model.Command{}, false + return model.Command{}, "", false } - if args[1] != "run" || args[2] != "--" { - return model.Command{}, false + if args[1] != "run" { + return model.Command{}, "", false } - args = args[3:] - if len(args) == 1 { - return model.Command{Name: args[0], Args: []string{}}, true + port := defaultProxyPort + idx := 2 + for idx < len(args) && args[idx] != "--" { + if args[idx] != "--port" { + return model.Command{}, "", false + } + + if idx+1 >= len(args) { + return model.Command{}, "", false + } + + p, err := strconv.Atoi(args[idx+1]) + if err != nil || p <= 0 || p > 65535 { + return model.Command{}, "", false + } + + port = p + idx += 2 } - return model.Command{Name: args[0], Args: args[1:]}, true + if idx >= len(args) || args[idx] != "--" { + return model.Command{}, "", false + } + + if idx+1 >= len(args) { + return model.Command{}, "", false + } + + cmdArgs := args[idx+1:] + cmd := model.Command{Name: cmdArgs[0], Args: cmdArgs[1:]} + return cmd, proxyAddrForPort(port), true +} + +func proxyAddrForPort(port int) string { + return fmt.Sprintf("127.0.0.1:%d", port) } func displayHelp() { @@ -89,7 +117,7 @@ func displayHelp() { usage: tap demo tap cert - tap run -- [args...] + tap run [--port ] -- [args...] ` fmt.Fprintln(stderrWriter, helpText) @@ -137,5 +165,5 @@ func runCert() { fmt.Fprintf(stdoutWriter, " sudo trust anchor %s\n", quotedCertPath) fmt.Fprintln(stdoutWriter) fmt.Fprintln(stdoutWriter, "Quick curl test:") - fmt.Fprintf(stdoutWriter, " curl --proxy http://%s --cacert %s https://example.com\n", proxy_addr, quotedCertPath) + fmt.Fprintf(stdoutWriter, " curl --proxy http://%s --cacert %s https://example.com\n", proxyAddrForPort(defaultProxyPort), quotedCertPath) } diff --git a/internal/cli/run_test.go b/internal/cli/run_test.go index a4a04eb..21e57d2 100644 --- a/internal/cli/run_test.go +++ b/internal/cli/run_test.go @@ -22,18 +22,24 @@ func TestParseCommand(t *testing.T) { ok bool nameWant string argsWant []string + addrWant string }{ {name: "too few args", args: []string{"tap"}, ok: false}, {name: "missing run token", args: []string{"tap", "oops", "--", "echo"}, ok: false}, {name: "missing separator", args: []string{"tap", "run", "echo"}, ok: false}, - {name: "single command", args: []string{"tap", "run", "--", "echo"}, ok: true, nameWant: "echo", argsWant: []string{}}, - {name: "command with args", args: []string{"tap", "run", "--", "curl", "-s", "https://example.com"}, ok: true, nameWant: "curl", argsWant: []string{"-s", "https://example.com"}}, + {name: "single command", args: []string{"tap", "run", "--", "echo"}, ok: true, nameWant: "echo", argsWant: []string{}, addrWant: "127.0.0.1:8080"}, + {name: "command with args", args: []string{"tap", "run", "--", "curl", "-s", "https://example.com"}, ok: true, nameWant: "curl", argsWant: []string{"-s", "https://example.com"}, addrWant: "127.0.0.1:8080"}, + {name: "custom port", args: []string{"tap", "run", "--port", "9090", "--", "echo"}, ok: true, nameWant: "echo", argsWant: []string{}, addrWant: "127.0.0.1:9090"}, + {name: "port missing value", args: []string{"tap", "run", "--port", "--", "echo"}, ok: false}, + {name: "port invalid", args: []string{"tap", "run", "--port", "wat", "--", "echo"}, ok: false}, + {name: "port out of range", args: []string{"tap", "run", "--port", "70000", "--", "echo"}, ok: false}, + {name: "unknown flag", args: []string{"tap", "run", "--wat", "--", "echo"}, ok: false}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - cmd, ok := parseCommand(tt.args) + cmd, addr, ok := parseCommand(tt.args) if ok != tt.ok { t.Fatalf("ok = %v, want %v", ok, tt.ok) } @@ -46,6 +52,9 @@ func TestParseCommand(t *testing.T) { if strings.Join(cmd.Args, "|") != strings.Join(tt.argsWant, "|") { t.Fatalf("cmd.Args = %#v, want %#v", cmd.Args, tt.argsWant) } + if addr != tt.addrWant { + t.Fatalf("addr = %q, want %q", addr, tt.addrWant) + } }) } } @@ -55,7 +64,7 @@ func TestDisplayHelpWritesToStderr(t *testing.T) { displayHelp() }) - if !strings.Contains(stderr, "tap demo") || !strings.Contains(stderr, "tap cert") || !strings.Contains(stderr, "tap run --") { + if !strings.Contains(stderr, "tap demo") || !strings.Contains(stderr, "tap cert") || !strings.Contains(stderr, "tap run [--port ] --") { t.Fatalf("stderr missing usage text: %q", stderr) } } @@ -164,6 +173,25 @@ func TestRun_SuccessPathDoesNotCallFatal(t *testing.T) { } } +func TestRun_CustomPortPassedToSession(t *testing.T) { + restore := installRunSeams(t) + defer restore() + + var gotAddr string + startSessionFn = func(_ model.Command, addr string) (*app.Session, error) { + gotAddr = addr + return &app.Session{Events: make(chan model.Event)}, nil + } + runTUIFn = func(<-chan model.Event, tui.Controls) error { + return nil + } + + Run([]string{"tap", "run", "--port", "9091", "--", "echo"}) + if gotAddr != "127.0.0.1:9091" { + t.Fatalf("session addr = %q, want %q", gotAddr, "127.0.0.1:9091") + } +} + func TestRunCert_EnsureCAFailureCallsFatalExit(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", "") t.Setenv("HOME", "") diff --git a/web/src/pages/DocsPage.tsx b/web/src/pages/DocsPage.tsx index d48cbc4..fb125fb 100644 --- a/web/src/pages/DocsPage.tsx +++ b/web/src/pages/DocsPage.tsx @@ -159,7 +159,7 @@ export function DocsPage() {

--port - Proxy port (default 8888) + Proxy port (default 8080)