feat: added the --port flag

This commit is contained in:
Hayden Hargreaves 2026-04-24 22:43:49 -07:00
parent 15c73d7bfe
commit 711508ce58
3 changed files with 75 additions and 19 deletions

View File

@ -14,8 +14,7 @@ import (
"termtap.dev/internal/tui" "termtap.dev/internal/tui"
) )
// This should be configurable at some point, just in case they build on 8080 const defaultProxyPort = 8080
const proxy_addr = "127.0.0.1:8080"
var fatalExit = log.Fatalln var fatalExit = log.Fatalln
var stdoutWriter io.Writer = stdioRef{isErr: false} var stdoutWriter io.Writer = stdioRef{isErr: false}
@ -44,13 +43,13 @@ func Run(args []string) {
return return
} }
cmd, ok := parseCommand(args) cmd, proxyAddr, ok := parseCommand(args)
if !ok { if !ok {
displayHelp() displayHelp()
return return
} }
session, err := startSessionFn(cmd, proxy_addr) session, err := startSessionFn(cmd, proxyAddr)
if err != nil { if err != nil {
fatalExit(err) fatalExit(err)
return 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 { if len(args) < 4 {
return model.Command{}, false return model.Command{}, "", false
} }
if args[1] != "run" || args[2] != "--" { if args[1] != "run" {
return model.Command{}, false return model.Command{}, "", false
} }
args = args[3:] port := defaultProxyPort
if len(args) == 1 { idx := 2
return model.Command{Name: args[0], Args: []string{}}, true 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() { func displayHelp() {
@ -89,7 +117,7 @@ func displayHelp() {
usage: usage:
tap demo tap demo
tap cert tap cert
tap run -- <command> [args...] tap run [--port <port>] -- <command> [args...]
` `
fmt.Fprintln(stderrWriter, helpText) fmt.Fprintln(stderrWriter, helpText)
@ -137,5 +165,5 @@ func runCert() {
fmt.Fprintf(stdoutWriter, " sudo trust anchor %s\n", quotedCertPath) fmt.Fprintf(stdoutWriter, " sudo trust anchor %s\n", quotedCertPath)
fmt.Fprintln(stdoutWriter) fmt.Fprintln(stdoutWriter)
fmt.Fprintln(stdoutWriter, "Quick curl test:") 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)
} }

View File

@ -22,18 +22,24 @@ func TestParseCommand(t *testing.T) {
ok bool ok bool
nameWant string nameWant string
argsWant []string argsWant []string
addrWant string
}{ }{
{name: "too few args", args: []string{"tap"}, ok: false}, {name: "too few args", args: []string{"tap"}, ok: false},
{name: "missing run token", args: []string{"tap", "oops", "--", "echo"}, ok: false}, {name: "missing run token", args: []string{"tap", "oops", "--", "echo"}, ok: false},
{name: "missing separator", args: []string{"tap", "run", "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: "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"}}, {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 { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
cmd, ok := parseCommand(tt.args) cmd, addr, ok := parseCommand(tt.args)
if ok != tt.ok { if ok != tt.ok {
t.Fatalf("ok = %v, want %v", 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, "|") { if strings.Join(cmd.Args, "|") != strings.Join(tt.argsWant, "|") {
t.Fatalf("cmd.Args = %#v, want %#v", cmd.Args, 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() 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 <port>] --") {
t.Fatalf("stderr missing usage text: %q", stderr) 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) { func TestRunCert_EnsureCAFailureCallsFatalExit(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", "") t.Setenv("XDG_CONFIG_HOME", "")
t.Setenv("HOME", "") t.Setenv("HOME", "")

View File

@ -159,7 +159,7 @@ export function DocsPage() {
<div className="space-y-3 text-sm leading-6"> <div className="space-y-3 text-sm leading-6">
<p className="flex flex-col gap-1 md:flex-row md:gap-8"> <p className="flex flex-col gap-1 md:flex-row md:gap-8">
<span className="w-28 text-emerald-400">--port</span> <span className="w-28 text-emerald-400">--port</span>
<span className="text-slate-300">Proxy port (default 8888)</span> <span className="text-slate-300">Proxy port (default 8080)</span>
</p> </p>
</div> </div>
</DocsSection> </DocsSection>