Fix TUI startup and test script bugs
- 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 <noreply@anthropic.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,4 +10,4 @@
|
|||||||
.env
|
.env
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
tui
|
/tui
|
||||||
|
|||||||
@@ -42,11 +42,45 @@ const sideW = 22 // total width of each sidebar box (inner = sideW-2)
|
|||||||
|
|
||||||
// ── tea messages ──────────────────────────────────────────────────────────────
|
// ── tea messages ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
type connectedMsg struct{ conn net.Conn }
|
type connectedMsg struct {
|
||||||
|
conn net.Conn
|
||||||
|
reader *lineReader
|
||||||
|
}
|
||||||
type ipcLineMsg struct{ line []byte }
|
type ipcLineMsg struct{ line []byte }
|
||||||
type connectErrMsg struct{ err error }
|
type connectErrMsg struct{ err error }
|
||||||
type readErrMsg 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 ─────────────────────────────────────────────────────────────────────
|
// ── model ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
type entry struct {
|
type entry struct {
|
||||||
@@ -62,8 +96,9 @@ type model struct {
|
|||||||
|
|
||||||
width, height int
|
width, height int
|
||||||
|
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
enc *json.Encoder
|
enc *json.Encoder
|
||||||
|
reader *lineReader
|
||||||
|
|
||||||
localID proto.PeerID
|
localID proto.PeerID
|
||||||
localAlias string
|
localAlias string
|
||||||
@@ -112,22 +147,7 @@ func connectCmd(port int) tea.Cmd {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return connectErrMsg{err}
|
return connectErrMsg{err}
|
||||||
}
|
}
|
||||||
return connectedMsg{conn}
|
return connectedMsg{conn: conn, reader: newLineReader(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")}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +176,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
case connectedMsg:
|
case connectedMsg:
|
||||||
m.conn = msg.conn
|
m.conn = msg.conn
|
||||||
m.enc = json.NewEncoder(msg.conn)
|
m.enc = json.NewEncoder(msg.conn)
|
||||||
|
m.reader = msg.reader
|
||||||
m.status = "joining " + m.networkName + "…"
|
m.status = "joining " + m.networkName + "…"
|
||||||
cmds = append(cmds,
|
cmds = append(cmds,
|
||||||
sendIPC(m.enc, proto.IpcMessage{Type: proto.CmdJoinNetwork, NetworkName: m.networkName}),
|
sendIPC(m.enc, proto.IpcMessage{Type: proto.CmdJoinNetwork, NetworkName: m.networkName}),
|
||||||
sendIPC(m.enc, proto.IpcMessage{Type: proto.CmdGetState}),
|
sendIPC(m.enc, proto.IpcMessage{Type: proto.CmdGetState}),
|
||||||
listenCmd(m.conn),
|
m.reader.next(),
|
||||||
)
|
)
|
||||||
|
|
||||||
case ipcLineMsg:
|
case ipcLineMsg:
|
||||||
@@ -168,7 +189,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if json.Unmarshal(msg.line, &evt) == nil {
|
if json.Unmarshal(msg.line, &evt) == nil {
|
||||||
m = m.applyEvent(evt)
|
m = m.applyEvent(evt)
|
||||||
}
|
}
|
||||||
cmds = append(cmds, listenCmd(m.conn))
|
cmds = append(cmds, m.reader.next())
|
||||||
|
|
||||||
case readErrMsg:
|
case readErrMsg:
|
||||||
m.errMsg = "IPC disconnected: " + msg.err.Error()
|
m.errMsg = "IPC disconnected: " + msg.err.Error()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ CHARLIE_IPC=19343
|
|||||||
NETWORK_NAME="test-$(date +%s)"
|
NETWORK_NAME="test-$(date +%s)"
|
||||||
DATA_ROOT="/tmp/waste-test"
|
DATA_ROOT="/tmp/waste-test"
|
||||||
rm -rf "$DATA_ROOT"
|
rm -rf "$DATA_ROOT"
|
||||||
mkdir -p "$DATA_ROOT"
|
mkdir -p "$DATA_ROOT/bin"
|
||||||
|
|
||||||
# ── cleanup ───────────────────────────────────────────────────────────────────
|
# ── cleanup ───────────────────────────────────────────────────────────────────
|
||||||
PIDS=()
|
PIDS=()
|
||||||
@@ -157,6 +157,7 @@ wait_port() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── build ─────────────────────────────────────────────────────────────────────
|
# ── build ─────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -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'
|
CYAN='\033[0;36m'; DIM='\033[2m'; BOLD='\033[1m'; RESET='\033[0m'
|
||||||
|
|
||||||
rm -rf "$DATA_ROOT"
|
rm -rf "$DATA_ROOT"
|
||||||
mkdir -p "$DATA_ROOT"
|
mkdir -p "$DATA_ROOT/bin"
|
||||||
|
|
||||||
PIDS=()
|
PIDS=()
|
||||||
cleanup() {
|
cleanup() {
|
||||||
@@ -35,6 +35,7 @@ wait_port() {
|
|||||||
n=$(( n + 1 ))
|
n=$(( n + 1 ))
|
||||||
[ "$n" -gt 80 ] && { echo "timeout waiting for :$port" >&2; exit 1; }
|
[ "$n" -gt 80 ] && { echo "timeout waiting for :$port" >&2; exit 1; }
|
||||||
done
|
done
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc() { echo "$2" | nc -q 0 127.0.0.1 "$1" >/dev/null 2>&1 || true; }
|
ipc() { echo "$2" | nc -q 0 127.0.0.1 "$1" >/dev/null 2>&1 || true; }
|
||||||
|
|||||||
Reference in New Issue
Block a user