From 5a0bcee8c63fc01ece4824100c9e93b629fe7207 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Sun, 21 Jun 2026 18:46:40 +0200 Subject: [PATCH] Fix TUI startup and test script bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TUI: replace per-call bufio.Scanner with a goroutine+channel reader (lineReader) so a single scanner lives for the connection lifetime; previous pattern silently dropped messages due to scanner read-ahead - test-tui.sh / test-network.sh: add `return 0` to wait_port — when the port opens and the while condition becomes false, bash returned the condition's exit code (1) to the caller, tripping set -e immediately - .gitignore: use /tui (root-only) instead of tui to avoid ignoring cmd/tui/ Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 2 +- cmd/tui/main.go | 63 ++++++++++++++++++++++++++++++++----------------- test-network.sh | 3 ++- test-tui.sh | 3 ++- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index ac6dcbb..1389a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ .env .DS_Store *.swp -tui +/tui diff --git a/cmd/tui/main.go b/cmd/tui/main.go index da2d395..1738f3e 100644 --- a/cmd/tui/main.go +++ b/cmd/tui/main.go @@ -42,11 +42,45 @@ const sideW = 22 // total width of each sidebar box (inner = sideW-2) // ── tea messages ────────────────────────────────────────────────────────────── -type connectedMsg struct{ conn net.Conn } +type connectedMsg struct { + conn net.Conn + reader *lineReader +} type ipcLineMsg struct{ line []byte } type connectErrMsg struct{ err error } type readErrMsg struct{ err error } +// lineReader pumps a TCP connection through a channel so a single bufio.Scanner +// is alive for the lifetime of the connection (avoids read-ahead data loss when +// a new scanner is created on each call). +type lineReader struct { + ch chan []byte +} + +func newLineReader(conn net.Conn) *lineReader { + lr := &lineReader{ch: make(chan []byte, 64)} + go func() { + scanner := bufio.NewScanner(conn) + for scanner.Scan() { + b := make([]byte, len(scanner.Bytes())) + copy(b, scanner.Bytes()) + lr.ch <- b + } + close(lr.ch) + }() + return lr +} + +func (lr *lineReader) next() tea.Cmd { + return func() tea.Msg { + b, ok := <-lr.ch + if !ok { + return readErrMsg{fmt.Errorf("daemon closed connection")} + } + return ipcLineMsg{b} + } +} + // ── model ───────────────────────────────────────────────────────────────────── type entry struct { @@ -62,8 +96,9 @@ type model struct { width, height int - conn net.Conn - enc *json.Encoder + conn net.Conn + enc *json.Encoder + reader *lineReader localID proto.PeerID localAlias string @@ -112,22 +147,7 @@ func connectCmd(port int) tea.Cmd { if err != nil { return connectErrMsg{err} } - return connectedMsg{conn} - } -} - -func listenCmd(conn net.Conn) tea.Cmd { - return func() tea.Msg { - scanner := bufio.NewScanner(conn) - if scanner.Scan() { - b := make([]byte, len(scanner.Bytes())) - copy(b, scanner.Bytes()) - return ipcLineMsg{b} - } - if err := scanner.Err(); err != nil { - return readErrMsg{err} - } - return readErrMsg{fmt.Errorf("daemon closed connection")} + return connectedMsg{conn: conn, reader: newLineReader(conn)} } } @@ -156,11 +176,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case connectedMsg: m.conn = msg.conn m.enc = json.NewEncoder(msg.conn) + m.reader = msg.reader m.status = "joining " + m.networkName + "…" cmds = append(cmds, sendIPC(m.enc, proto.IpcMessage{Type: proto.CmdJoinNetwork, NetworkName: m.networkName}), sendIPC(m.enc, proto.IpcMessage{Type: proto.CmdGetState}), - listenCmd(m.conn), + m.reader.next(), ) case ipcLineMsg: @@ -168,7 +189,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if json.Unmarshal(msg.line, &evt) == nil { m = m.applyEvent(evt) } - cmds = append(cmds, listenCmd(m.conn)) + cmds = append(cmds, m.reader.next()) case readErrMsg: m.errMsg = "IPC disconnected: " + msg.err.Error() diff --git a/test-network.sh b/test-network.sh index 52e3113..ce902c2 100755 --- a/test-network.sh +++ b/test-network.sh @@ -23,7 +23,7 @@ CHARLIE_IPC=19343 NETWORK_NAME="test-$(date +%s)" DATA_ROOT="/tmp/waste-test" rm -rf "$DATA_ROOT" -mkdir -p "$DATA_ROOT" +mkdir -p "$DATA_ROOT/bin" # ── cleanup ─────────────────────────────────────────────────────────────────── PIDS=() @@ -157,6 +157,7 @@ wait_port() { exit 1 fi done + return 0 } # ── build ───────────────────────────────────────────────────────────────────── diff --git a/test-tui.sh b/test-tui.sh index ed0e0b0..200537c 100755 --- a/test-tui.sh +++ b/test-tui.sh @@ -17,7 +17,7 @@ RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' CYAN='\033[0;36m'; DIM='\033[2m'; BOLD='\033[1m'; RESET='\033[0m' rm -rf "$DATA_ROOT" -mkdir -p "$DATA_ROOT" +mkdir -p "$DATA_ROOT/bin" PIDS=() cleanup() { @@ -35,6 +35,7 @@ wait_port() { n=$(( n + 1 )) [ "$n" -gt 80 ] && { echo "timeout waiting for :$port" >&2; exit 1; } done + return 0 } ipc() { echo "$2" | nc -q 0 127.0.0.1 "$1" >/dev/null 2>&1 || true; }