diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a45aae0..d80b0d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,13 @@ on: branches: [main] pull_request: +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check: runs-on: ubuntu-latest @@ -15,8 +22,50 @@ jobs: with: go-version-file: go.mod + - name: Gofmt + run: | + fmt_out="$(gofmt -l .)" + if [ -n "$fmt_out" ]; then + echo "The following files are not gofmt'd:" + echo "$fmt_out" + exit 1 + fi + + - name: Build + run: go build ./... + - name: Vet run: go vet ./... + - name: Tidy + run: | + go mod tidy + git diff --exit-code go.mod go.sum + - name: Test run: go test -short -race ./... + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - uses: golangci/golangci-lint-action@v9 + with: + version: v2.13 + + vulncheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: govulncheck + run: go run golang.org/x/vuln/cmd/govulncheck@latest ./... diff --git a/cli/blossom/coverage_gaps_test.go b/cli/blossom/coverage_gaps_test.go index 7c7a38e..d67e303 100644 --- a/cli/blossom/coverage_gaps_test.go +++ b/cli/blossom/coverage_gaps_test.go @@ -198,7 +198,7 @@ func TestBlossomMirror_SucceededTransferButInvalidResponse(t *testing.T) { t.Run(hook, func(t *testing.T) { r := newRunner(t) source := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.Write([]byte("mirror source content")) + _, _ = w.Write([]byte("mirror source content")) })) defer source.Close() @@ -234,7 +234,7 @@ func TestBlossomMirror_MultiServerFanOut(t *testing.T) { } r := newRunner(t) source := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.Write([]byte("fan out mirror content")) + _, _ = w.Write([]byte("fan out mirror content")) })) defer source.Close() diff --git a/cli/blossom/download.go b/cli/blossom/download.go index ecbf5a2..5dbe30e 100644 --- a/cli/blossom/download.go +++ b/cli/blossom/download.go @@ -71,7 +71,7 @@ omitted, or streams to stdout with "-o -" (suppressing the summary line).`, if err != nil { return classifyHTTPError(cmd, hash, err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() outPath, _ := cmd.Flags().GetString("output") toStdout := outPath == "-" @@ -90,7 +90,7 @@ omitted, or streams to stdout with "-o -" (suppressing the summary line).`, if err != nil { return common.RuntimeError(cmd, err) } - defer f.Close() + defer func() { _ = f.Close() }() out = f createdPath = outPath } @@ -100,7 +100,7 @@ omitted, or streams to stdout with "-o -" (suppressing the summary line).`, if createdPath != "" { // Best-effort cleanup: don't leave a truncated, // corrupt-looking file behind after a failed transfer. - os.Remove(createdPath) + _ = os.Remove(createdPath) } return common.NetworkError(cmd, usedServer, err) } diff --git a/cli/blossom/fakeserver_test.go b/cli/blossom/fakeserver_test.go index 2fea17d..ddab818 100644 --- a/cli/blossom/fakeserver_test.go +++ b/cli/blossom/fakeserver_test.go @@ -144,7 +144,7 @@ func newFakeBlossomServer() *fakeBlossomServer { // same goroutine, which the Go memory model does guarantee orders // correctly relative to the spawned goroutine. s.Server = httptest.NewUnstartedServer(mux) - s.Server.Start() + s.Start() return s } @@ -261,7 +261,7 @@ func (s *fakeBlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) nipB7.WriteError(w, http.StatusBadGateway, err.Error()) return } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() data, err := io.ReadAll(resp.Body) if err != nil { nipB7.WriteError(w, http.StatusBadGateway, err.Error()) @@ -277,7 +277,7 @@ func (s *fakeBlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) func (s *fakeBlossomServer) writeDescriptor(w http.ResponseWriter, hash string, size int64, contentType string, hooks fakeServerHooks) { if hooks.MalformedResponseBody { - w.Write([]byte("not json")) + _, _ = w.Write([]byte("not json")) return } d := nipB7.BlobDescriptor{ @@ -290,7 +290,7 @@ func (s *fakeBlossomServer) writeDescriptor(w http.ResponseWriter, hash string, if hooks.InvalidDescriptor { d.URL = "" // fails BlobDescriptor.Validate() despite a 2xx transfer } - json.NewEncoder(w).Encode(d) + _ = json.NewEncoder(w).Encode(d) } func (s *fakeBlossomServer) handleReport(w http.ResponseWriter, r *http.Request) { @@ -354,7 +354,7 @@ func (s *fakeBlossomServer) handleList(w http.ResponseWriter, r *http.Request) { s.mu.Unlock() nipB7.SortDescending(descriptors) - json.NewEncoder(w).Encode(descriptors) + _ = json.NewEncoder(w).Encode(descriptors) } func (s *fakeBlossomServer) handleBlob(w http.ResponseWriter, r *http.Request) { @@ -393,7 +393,7 @@ func (s *fakeBlossomServer) handleBlob(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) return } - w.Write(b.data) + _, _ = w.Write(b.data) case http.MethodDelete: // BUD-11: delete tokens "should be scoped to exactly this hash" -- diff --git a/cli/blossom/list.go b/cli/blossom/list.go index 8ede016..47e95db 100644 --- a/cli/blossom/list.go +++ b/cli/blossom/list.go @@ -104,11 +104,11 @@ resolved pubkey when omitted.`, return nil } w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0) - fmt.Fprintln(w, "HASH\tSIZE\tTYPE\tUPLOADED\tURL") + _, _ = fmt.Fprintln(w, "HASH\tSIZE\tTYPE\tUPLOADED\tURL") for _, d := range descriptors { - fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n", d.Sha256, d.Size, d.Type, time.Unix(d.Uploaded, 0).Format(time.RFC3339), d.URL) + _, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n", d.Sha256, d.Size, d.Type, time.Unix(d.Uploaded, 0).Format(time.RFC3339), d.URL) } - w.Flush() + _ = w.Flush() return nil }, } diff --git a/cli/blossom/report.go b/cli/blossom/report.go index 60639da..c681239 100644 --- a/cli/blossom/report.go +++ b/cli/blossom/report.go @@ -78,9 +78,9 @@ server: --server, or the first configured default.`, } cmd.Flags().String("type", "", `Report type (e.g. "nudity", "malware", "illegal", "spam") (required)`) - cmd.MarkFlagRequired("type") + _ = cmd.MarkFlagRequired("type") cmd.Flags().String("reason", "", "Human-readable reason (required)") - cmd.MarkFlagRequired("reason") + _ = cmd.MarkFlagRequired("reason") return cmd } diff --git a/cli/blossom/servers_publish_test.go b/cli/blossom/servers_publish_test.go index a8cab8d..7039ac7 100644 --- a/cli/blossom/servers_publish_test.go +++ b/cli/blossom/servers_publish_test.go @@ -43,7 +43,7 @@ func mockRelayServer(t *testing.T) (wsURL string) { if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() for { _, msg, err := conn.ReadMessage() diff --git a/cli/blossom/shared_test.go b/cli/blossom/shared_test.go index 59bee05..33cb2da 100644 --- a/cli/blossom/shared_test.go +++ b/cli/blossom/shared_test.go @@ -25,7 +25,7 @@ func captureStdout(t *testing.T, fn func()) string { } os.Stdout = w fn() - w.Close() + _ = w.Close() os.Stdout = orig out, err := io.ReadAll(r) if err != nil { diff --git a/cli/blossom/upload.go b/cli/blossom/upload.go index b6f47ea..f69afef 100644 --- a/cli/blossom/upload.go +++ b/cli/blossom/upload.go @@ -122,7 +122,7 @@ func uploadOne(ctx context.Context, hc *bclient.Client, server, path, contentTyp res.Error = err.Error() return res } - defer f.Close() + defer func() { _ = f.Close() }() info, err := f.Stat() if err != nil { @@ -161,7 +161,7 @@ func detectContentType(path string) (string, error) { if err != nil { return "", err } - defer f.Close() + defer func() { _ = f.Close() }() buf := make([]byte, 512) n, err := f.Read(buf) diff --git a/cli/bunker/board.go b/cli/bunker/board.go index 9efecaa..67ade59 100644 --- a/cli/bunker/board.go +++ b/cli/bunker/board.go @@ -108,7 +108,7 @@ func (t *PendingTable) Init(ctx context.Context) *PendingTable { t.table.SetFixed(1, 0). SetSelectable(true, false) - tui.WireFocusBorder(t.table, t.Flex.Box) + tui.WireFocusBorder(t.table, t.Box) t.table.SetSelectedStyle(tcell.Style{}. Background(tui.ColorPrimary). @@ -268,7 +268,7 @@ func (t *PendingTable) showBunkerURI() { // Pending Requests. Polling for it here instead answers "did that just // work, or do I need a new link?" without leaving the dialog. waiting := tview.NewTextView().SetDynamicColors(true) - fmt.Fprint(waiting, formatWaitingStatus(deadline)) + _, _ = fmt.Fprint(waiting, formatWaitingStatus(deadline)) status := tview.NewTextView().SetDynamicColors(true) @@ -401,7 +401,7 @@ func (t *PendingTable) watchForPairing(ctx context.Context, stop <-chan struct{} status := formatWaitingStatus(deadline) t.app.QueueUpdateDraw(func() { waiting.Clear() - fmt.Fprint(waiting, status) + _, _ = fmt.Fprint(waiting, status) }) if time.Now().After(deadline) { return @@ -776,7 +776,7 @@ func (t *PendingTable) updateActionsBar() { autoColor, autoText := tui.StatusText(auto) t.actions.Clear() - fmt.Fprintf(t.actions, "[%s::b]Auto-Prompt:[%s]%s", tui.ColorPrimary, autoColor, autoText) + _, _ = fmt.Fprintf(t.actions, "[%s::b]Auto-Prompt:[%s]%s", tui.ColorPrimary, autoColor, autoText) } // appLabel resolves pubkey to labelFor(session) against the cache @@ -1042,7 +1042,7 @@ func (t *PendingTable) openSignEventApprovalDialog(p Pending, title, text string SetWordWrap(true). SetScrollable(true) jsonView.SetBorderPadding(0, 0, 0, 0) - fmt.Fprint(jsonView, tui.ColorizeEventJSON(p.Event)) + _, _ = fmt.Fprint(jsonView, tui.ColorizeEventJSON(p.Event)) jsonView.ScrollToBeginning() form := tview.NewForm(). @@ -1073,7 +1073,7 @@ func (t *PendingTable) openSignEventApprovalDialog(p Pending, title, text string wireScrollCapture(form, jsonView) header := tview.NewTextView().SetDynamicColors(true) - fmt.Fprintf(header, "[::b]%s[-:-:-]\n%s", tview.Escape(title), tview.Escape(text)) + _, _ = fmt.Fprintf(header, "[::b]%s[-:-:-]\n%s", tview.Escape(title), tview.Escape(text)) view := tview.NewFlex().SetDirection(tview.FlexRow). AddItem(header, 2, 0, false). @@ -1208,7 +1208,7 @@ func (t *SessionsTable) Init(ctx context.Context) *SessionsTable { SetBorder(true). SetBorderPadding(0, 1, 1, 1) - tui.WireFocusBorder(t.Table, t.Table.Box) + tui.WireFocusBorder(t.Table, t.Box) t.SetSelectedStyle(tcell.Style{}. Background(tui.ColorPrimary). @@ -1381,7 +1381,7 @@ func (t *SessionsTable) openGrantsOverlay(s Session) { } hint := tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignCenter) - fmt.Fprint(hint, strings.Join([]string{ + _, _ = fmt.Fprint(hint, strings.Join([]string{ hintTag("", "Revoke"), hintTag("", "Extend"), hintTag("", "Close"), @@ -1657,7 +1657,7 @@ func (t *HistoryTable) Init(ctx context.Context) *HistoryTable { SetBorder(true). SetBorderPadding(0, 1, 1, 1) - tui.WireFocusBorder(t.Table, t.Table.Box) + tui.WireFocusBorder(t.Table, t.Box) t.SetSelectedStyle(tcell.Style{}. Background(tui.ColorPrimary). @@ -1837,7 +1837,7 @@ func (t *HistoryTable) showEventDetail(h HistoryEntry) { SetWordWrap(true). SetScrollable(true) jsonView.SetBorderPadding(0, 0, 0, 0) - fmt.Fprint(jsonView, tui.ColorizeEventJSON(h.Event)) + _, _ = fmt.Fprint(jsonView, tui.ColorizeEventJSON(h.Event)) jsonView.ScrollToBeginning() form := tview.NewForm(). @@ -2208,8 +2208,8 @@ func (b *IdentityBar) Update() { return } b.Clear() - fmt.Fprintf(b, " [%s::b]Signing as:[-:-:-] %s\n", tui.ColorPrimary, formatIdentity(st)) - fmt.Fprintf(b, " [%s::b]Relays:[-:-:-] %s", tui.ColorPrimary, formatRelayStatuses(st.RelayStatuses)) + _, _ = fmt.Fprintf(b, " [%s::b]Signing as:[-:-:-] %s\n", tui.ColorPrimary, formatIdentity(st)) + _, _ = fmt.Fprintf(b, " [%s::b]Relays:[-:-:-] %s", tui.ColorPrimary, formatRelayStatuses(st.RelayStatuses)) } func (b *IdentityBar) render(ctx context.Context) { @@ -2346,7 +2346,7 @@ func (b *AlertBar) Update() { active := !anyRelayConnected(st.RelayStatuses) && !anyRelayConnecting(st.RelayStatuses) b.Clear() if active { - fmt.Fprintf(b, "[%s::b] ⚠ No relay connected -- signer can't receive requests. Check your network or relay config.[-:-:-]", tui.ColorDanger) + _, _ = fmt.Fprintf(b, "[%s::b] ⚠ No relay connected -- signer can't receive requests. Check your network or relay config.[-:-:-]", tui.ColorDanger) } if b.onAlert != nil { b.onAlert(active) @@ -2653,7 +2653,7 @@ func NewBunkerBoard(app *tui.App, ctx context.Context, client BunkerClient, flow // startup or afterward, no matter how many apps end up trusted over a // long-running board. b.sessions.onCountChange = func(n int) { - b.Flex.ResizeItem(b.sessions, sessionsHeightFor(n), 0) + b.ResizeItem(b.sessions, sessionsHeightFor(n), 0) } b.sessions.Init(ctx) @@ -2668,7 +2668,7 @@ func NewBunkerBoard(app *tui.App, ctx context.Context, client BunkerClient, flow if active { height = alertBarHeight } - b.Flex.ResizeItem(b.alert, height, 0) + b.ResizeItem(b.alert, height, 0) } b.alert.Init(ctx) diff --git a/cli/bunker/board_test.go b/cli/bunker/board_test.go index d90967b..c76b6cb 100644 --- a/cli/bunker/board_test.go +++ b/cli/bunker/board_test.go @@ -563,7 +563,7 @@ func waitForAutoPromptFocus(t *testing.T, app *tui.App) (tview.Primitive, tcell. screen.SetSize(80, 25) app.SetScreen(screen) - go app.Run() + go func() { _ = app.Run() }() t.Cleanup(app.Stop) deadline := time.Now().Add(4 * time.Second) @@ -714,7 +714,7 @@ func TestPendingTableAlwaysButtonOpensDurationDialog(t *testing.T) { board := NewBunkerBoard(app, t.Context(), client, flowLogger, true) app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() deadline := time.Now().Add(4 * time.Second) @@ -1084,7 +1084,7 @@ func TestSessionsTableGrantsOverlayRevokeReturnsToRefreshedOverlay(t *testing.T) screen := tcell.NewSimulationScreen("") screen.SetSize(80, 25) app.SetScreen(screen) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() st := NewSessionsTable(app, client) @@ -1155,7 +1155,7 @@ func TestSessionsTableGrantsOverlayRevokeCancelReturnsUnchanged(t *testing.T) { screen := tcell.NewSimulationScreen("") screen.SetSize(80, 25) app.SetScreen(screen) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() st := NewSessionsTable(app, client) @@ -2028,7 +2028,7 @@ func TestBunkerBoardFooterHintsAreContextual(t *testing.T) { if switchIdx == -1 || backgroundIdx == -1 || connectIdx == -1 || dynamicIdx == -1 { t.Fatalf("FooterHints(%s) = %q, missing one of Switch Panel/Background/Connect/%s entirely", tt.name, hints, tt.firstDynamic) } - if !(switchIdx < backgroundIdx && backgroundIdx < connectIdx && connectIdx < dynamicIdx) { + if switchIdx >= backgroundIdx || backgroundIdx >= connectIdx || connectIdx >= dynamicIdx { t.Errorf("FooterHints(%s) = %q, want Switch Panel then Background then Connect then %s, got indices %d/%d/%d/%d", tt.name, hints, tt.firstDynamic, switchIdx, backgroundIdx, connectIdx, dynamicIdx) } @@ -2181,7 +2181,7 @@ func TestBunkerTUIStartupShowsSplashWhileBoardLoads(t *testing.T) { screen.SetSize(80, 25) app.SetScreen(screen) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() // Mirrors runTUI's own ordering exactly: board construction happens in @@ -2268,7 +2268,7 @@ func TestBunkerBoardOverlayDoesNotBleedThroughToPanelBorders(t *testing.T) { board := NewBunkerBoard(app, t.Context(), client, flowLogger, true) app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() time.Sleep(150 * time.Millisecond) @@ -2355,7 +2355,7 @@ func TestBunkerBoardAutoPromptSurvivesPreLoadRace(t *testing.T) { } app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() deadline := time.Now().Add(4 * time.Second) @@ -2415,7 +2415,7 @@ func TestPendingTableSignEventApprovalOverlayFillsFullScreenNoMargin(t *testing. board := NewBunkerBoard(app, t.Context(), client, flowLogger, true) app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() deadline := time.Now().Add(4 * time.Second) @@ -2474,7 +2474,7 @@ func TestHistoryTableShowEventDetailBorderStaysConsistentUnderConcurrency(t *tes board := NewBunkerBoard(app, t.Context(), client, flowLogger, true) app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() time.Sleep(150 * time.Millisecond) @@ -2556,7 +2556,7 @@ func TestPendingTableShowBunkerURIActsLikeARealModal(t *testing.T) { board := NewBunkerBoard(app, t.Context(), client, flowLogger, true) app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() time.Sleep(150 * time.Millisecond) @@ -2686,7 +2686,7 @@ func TestPendingTableShowConnectingDoesNotClobberRacingApproveDialog(t *testing. board := NewBunkerBoard(app, t.Context(), client, flowLogger, true) app.Load(board) - go app.Run() + go func() { _ = app.Run() }() defer app.Stop() time.Sleep(150 * time.Millisecond) diff --git a/cli/bunker/client_test.go b/cli/bunker/client_test.go index 421c194..3465f80 100644 --- a/cli/bunker/client_test.go +++ b/cli/bunker/client_test.go @@ -62,7 +62,7 @@ func ipcClientFor(t *testing.T, daemon *Daemon) BunkerClient { if err != nil { t.Fatal(err) } - t.Cleanup(func() { client.Close() }) + t.Cleanup(func() { _ = client.Close() }) return client } @@ -646,7 +646,7 @@ func TestConnect_WithGrants_NostrconnectDirection(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() clientConn, err := relayclient.Connect(ctx, relay.url) if err != nil { diff --git a/cli/bunker/clipboard.go b/cli/bunker/clipboard.go index ba0f213..f65af97 100644 --- a/cli/bunker/clipboard.go +++ b/cli/bunker/clipboard.go @@ -55,5 +55,5 @@ func writeOSC52(text string) { seq = "\x1bPtmux;\x1b" + escaped + "\x1b\\" } - os.Stdout.WriteString(seq) + _, _ = os.Stdout.WriteString(seq) } diff --git a/cli/bunker/clipboard_test.go b/cli/bunker/clipboard_test.go index 35b995e..dce5235 100644 --- a/cli/bunker/clipboard_test.go +++ b/cli/bunker/clipboard_test.go @@ -25,7 +25,7 @@ func captureStdout(t *testing.T, fn func()) string { fn() - w.Close() + _ = w.Close() data, err := io.ReadAll(r) if err != nil { t.Fatal(err) diff --git a/cli/bunker/command.go b/cli/bunker/command.go index e411e3c..8fcef7f 100644 --- a/cli/bunker/command.go +++ b/cli/bunker/command.go @@ -106,7 +106,7 @@ itself -- fails if none is running (use "ncli bunker" for that).`, } return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running; start one with `ncli bunker`")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() return runTUI(cmd, bunkerClient, nil) }, @@ -129,7 +129,7 @@ func newStatusCommand() *cobra.Command { fmt.Println("not running") return nil } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() st, err := bunkerClient.Status() if err != nil { @@ -207,7 +207,7 @@ func newStopCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() // Fetched before Stop(), not after: Stop() tears down the very // daemon Status() would otherwise need to answer, so there's no @@ -261,7 +261,7 @@ func newSessionsCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() sessions, err := bunkerClient.ListSessions() if err != nil { @@ -293,7 +293,7 @@ func newSessionsCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() revoked, err := bunkerClient.Revoke(args[0]) if err != nil { @@ -322,7 +322,7 @@ func newSessionsCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() updated, err := bunkerClient.SetName(args[0], args[1]) if err != nil { @@ -351,7 +351,7 @@ func newSessionsCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() sessions, err := bunkerClient.ListSessions() if err != nil { @@ -404,7 +404,7 @@ func newSessionsCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() revoked, err := bunkerClient.RevokeGrant(args[0], method, kind) if err != nil { @@ -422,7 +422,7 @@ func newSessionsCommand() *cobra.Command { }, } revokeGrantCmd.Flags().String("method", "", "NIP-46 method the grant covers (e.g. sign_event, ping, connect)") - revokeGrantCmd.MarkFlagRequired("method") + _ = revokeGrantCmd.MarkFlagRequired("method") revokeGrantCmd.Flags().Int("kind", 0, "Event kind, for a sign_event grant (omit for the any-kind grant)") cmd.AddCommand(revokeGrantCmd) @@ -443,7 +443,7 @@ func newHistoryCommand() *cobra.Command { if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() history, err := bunkerClient.History() if err != nil { @@ -517,7 +517,7 @@ grants " shows what actually landed once paired.`, if err != nil { return common.NotFoundError(cmd, "", errors.New("no bunker daemon is running; start one with `ncli bunker`")) } - defer bunkerClient.Close() + defer func() { _ = bunkerClient.Close() }() uri := "" if len(args) > 0 { @@ -536,7 +536,7 @@ grants " shows what actually landed once paired.`, }, } cmd.Flags().String("grants", "", `Path to a "kind: bunker" YAML spec declaring grants to apply automatically once this pairing completes (see examples/bunker/)`) - cmd.MarkFlagFilename("grants", "yaml", "yml") + _ = cmd.MarkFlagFilename("grants", "yaml", "yml") return cmd } @@ -555,7 +555,7 @@ func newHiddenDaemonCommand() *cobra.Command { }, } cmd.Flags().StringArrayVar(&relayFlags, "relay", nil, "") - cmd.Flags().MarkHidden("relay") + _ = cmd.Flags().MarkHidden("relay") return cmd } @@ -579,11 +579,11 @@ func runDaemonProcess(cmd *cobra.Command, privKeyHex, vaultLabel string, relays if err != nil { return common.RuntimeError(cmd, err) } - defer eventLog.Close() + defer func() { _ = eventLog.Close() }() logPath := filepath.Join(common.AppConfigDir(), "bunker", "daemon.log") logWriter := &lumberjack.Logger{Filename: logPath, MaxSize: 20, MaxBackups: 3, MaxAge: 28, Compress: true} - defer logWriter.Close() + defer func() { _ = logWriter.Close() }() ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM) defer cancel() @@ -598,7 +598,7 @@ func runDaemonProcess(cmd *cobra.Command, privKeyHex, vaultLabel string, relays EventLog: eventLog, InitialHistory: initialHistory, OnLog: func(format string, args ...any) { - fmt.Fprintf(logWriter, "%s "+format+"\n", append([]any{time.Now().Format(time.RFC3339)}, args...)...) + _, _ = fmt.Fprintf(logWriter, "%s "+format+"\n", append([]any{time.Now().Format(time.RFC3339)}, args...)...) }, }) @@ -687,7 +687,11 @@ func runInProcess(pubKeyHex, privKeyHex, vaultLabel string, relays []string) (Bu }) ctx, cancel := context.WithCancel(context.Background()) - go daemon.Run(ctx) + go func() { + if err := daemon.Run(ctx); err != nil { + daemon.log("daemon exited: %v", err) + } + }() return newLocalClient(daemon, time.Now(), cancel), cancel, nil } diff --git a/cli/bunker/daemon_test.go b/cli/bunker/daemon_test.go index a019af1..571badd 100644 --- a/cli/bunker/daemon_test.go +++ b/cli/bunker/daemon_test.go @@ -38,7 +38,7 @@ type connState struct { func (cs *connState) write(conn *websocket.Conn, msg []byte) { cs.mu.Lock() defer cs.mu.Unlock() - conn.WriteMessage(websocket.TextMessage, msg) + _ = conn.WriteMessage(websocket.TextMessage, msg) } // fakeRelay is a minimal single-process Nostr relay used only to exercise @@ -65,7 +65,7 @@ func newFakeRelay(t *testing.T) *fakeRelay { if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() defer fr.forget(conn) for { @@ -109,7 +109,7 @@ func (fr *fakeRelay) handle(conn *websocket.Conn, data []byte) { return } var kind string - json.Unmarshal(raw[0], &kind) + _ = json.Unmarshal(raw[0], &kind) cs := fr.stateFor(conn) @@ -119,7 +119,7 @@ func (fr *fakeRelay) handle(conn *websocket.Conn, data []byte) { return } var subID string - json.Unmarshal(raw[1], &subID) + _ = json.Unmarshal(raw[1], &subID) filters := nip01.NewSubscriptionFilterGroup() for _, fraw := range raw[2:] { @@ -164,7 +164,7 @@ func (fr *fakeRelay) handle(conn *websocket.Conn, data []byte) { return } var subID string - json.Unmarshal(raw[1], &subID) + _ = json.Unmarshal(raw[1], &subID) cs.mu.Lock() delete(cs.subs, subID) cs.mu.Unlock() @@ -372,7 +372,7 @@ func TestDaemon_SignEventRoundTrip(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() // A second connection to the same fake relay plays the "app" side: // subscribe for responses addressed to it, then publish a sign_event @@ -483,7 +483,7 @@ func TestDaemon_NostrconnectFlow(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() // Client side: connect independently and listen for the daemon's own // outgoing "connect" request, addressed to this pubkey. @@ -631,7 +631,7 @@ func TestDaemon_EncryptionScenarios(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() client := newTestNIP46Client(t, ctx, relay.url, testClientPriv) reqID := client.send(signerPub, nip46.MethodPing, []string{}, tt.opts) @@ -704,7 +704,7 @@ func TestDaemon_BunkerPairing_LandsInPendingQueue(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() uri, err := daemon.NewBunkerPairing() if err != nil { @@ -779,7 +779,7 @@ func TestDaemon_BunkerPairing_ApprovedEndToEnd(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() uri, err := daemon.NewBunkerPairing() if err != nil { @@ -858,7 +858,7 @@ func TestDaemon_BunkerPairing_WrongSecretThenRightSecret(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() uri, err := daemon.NewBunkerPairing() if err != nil { @@ -932,7 +932,7 @@ func TestDaemon_BunkerPairing_SecretExpiresForReal(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() uri, err := daemon.NewBunkerPairing() if err != nil { @@ -1002,7 +1002,7 @@ func TestDaemon_Revoke_BlocksSubsequentRequests(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() client := newTestNIP46Client(t, ctx, relay.url, testClientPriv) @@ -1080,7 +1080,7 @@ func TestDaemon_RelayStatuses(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - go daemon.Run(ctx) + go func() { _ = daemon.Run(ctx) }() // runRelay dials asynchronously -- poll briefly for the live relay's // connection to register rather than assuming a fixed sleep is both @@ -1344,13 +1344,13 @@ func TestDaemon_History_RecordsFromQueueResolution(t *testing.T) { Queue: queue, }) - go queue.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "sign_event", Kind: 1}) + go func() { _, _ = queue.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "sign_event", Kind: 1}) }() waitFor(t, func() bool { return len(queue.List()) == 1 }) if err := queue.Resolve("req-1", Allow, true); err != nil { t.Fatal(err) } - go queue.Add(Pending{ID: "req-2", ClientKey: "app2", Method: "ping"}) + go func() { _, _ = queue.Add(Pending{ID: "req-2", ClientKey: "app2", Method: "ping"}) }() waitFor(t, func() bool { return len(queue.List()) == 1 }) if err := queue.Resolve("req-2", Deny, false); err != nil { t.Fatal(err) @@ -1488,7 +1488,7 @@ func TestDaemon_EventLog_PersistsSignedEventAcrossRestart(t *testing.T) { if err != nil { t.Fatal(err) } - defer reloadedLog.Close() + defer func() { _ = reloadedLog.Close() }() if len(reloadedHistory) != 1 { t.Fatalf("reloaded history len = %d, want 1: %+v", len(reloadedHistory), reloadedHistory) @@ -1567,7 +1567,7 @@ func TestDaemon_EventLog_InterruptedPendingSurfacesAsExpiredAfterRestart(t *test if err != nil { t.Fatal(err) } - defer reloadedLog.Close() + defer func() { _ = reloadedLog.Close() }() if len(reloadedHistory) != 1 { t.Fatalf("reloaded history len = %d, want 1: %+v", len(reloadedHistory), reloadedHistory) @@ -1632,7 +1632,7 @@ func TestDaemon_EventLog_RuntimeCompactionBoundsTheFile(t *testing.T) { if err != nil { t.Fatal(err) } - defer eventLog.Close() + defer func() { _ = eventLog.Close() }() daemon := NewDaemon(DaemonConfig{ IdentityPriv: testSignerPriv, @@ -1666,7 +1666,7 @@ func TestDaemon_EventLog_RuntimeCompactionBoundsTheFile(t *testing.T) { if err != nil { t.Fatalf("LoadEventLog after runtime compaction: %v", err) } - defer reloadedLog.Close() + defer func() { _ = reloadedLog.Close() }() if len(reloadedHistory) != maxHistoryTail { t.Errorf("reloaded history len = %d, want maxHistoryTail (%d)", len(reloadedHistory), maxHistoryTail) } @@ -1727,7 +1727,7 @@ func TestDaemon_EventLog_RuntimeCompactionUnderConcurrency(t *testing.T) { if err != nil { t.Fatalf("LoadEventLog after concurrent compaction: %v", err) } - defer reloadedLog.Close() + defer func() { _ = reloadedLog.Close() }() if len(reloadedHistory) != maxHistoryTail { t.Fatalf("reloaded history len = %d, want maxHistoryTail (%d)", len(reloadedHistory), maxHistoryTail) } diff --git a/cli/bunker/eventlog.go b/cli/bunker/eventlog.go index 091130f..2757630 100644 --- a/cli/bunker/eventlog.go +++ b/cli/bunker/eventlog.go @@ -166,7 +166,7 @@ func LoadEventLog(path string) (*EventLog, []HistoryEntry, error) { Expired: true, } if err := l.AppendResolved(h); err != nil { - f.Close() + _ = f.Close() return nil, nil, err } order = append(order, h.ID) @@ -180,7 +180,7 @@ func LoadEventLog(path string) (*EventLog, []HistoryEntry, error) { if len(out) > maxHistoryTail { out = out[len(out)-maxHistoryTail:] if err := l.compact(out); err != nil { - f.Close() + _ = f.Close() return nil, nil, err } } @@ -299,23 +299,23 @@ func (l *EventLog) compact(entries []HistoryEntry) error { for _, h := range entries { data, err := json.Marshal(walEntry{Type: walResolved, History: &h}) if err != nil { - tmp.Close() - os.Remove(tmpPath) + _ = tmp.Close() + _ = os.Remove(tmpPath) return err } if _, err := tmp.Write(append(data, '\n')); err != nil { - tmp.Close() - os.Remove(tmpPath) + _ = tmp.Close() + _ = os.Remove(tmpPath) return err } } if err := tmp.Sync(); err != nil { - tmp.Close() - os.Remove(tmpPath) + _ = tmp.Close() + _ = os.Remove(tmpPath) return err } if err := tmp.Close(); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return err } @@ -324,7 +324,7 @@ func (l *EventLog) compact(entries []HistoryEntry) error { // against the now-compacted file so subsequent appends land in the // right place. if err := l.f.Close(); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return err } if err := os.Rename(tmpPath, l.path); err != nil { diff --git a/cli/bunker/eventlog_test.go b/cli/bunker/eventlog_test.go index 54996e1..a58cfa9 100644 --- a/cli/bunker/eventlog_test.go +++ b/cli/bunker/eventlog_test.go @@ -21,7 +21,7 @@ func TestLoadEventLog_MissingFileIsEmpty(t *testing.T) { if err != nil { t.Fatalf("LoadEventLog() error = %v, want nil for a missing file", err) } - defer log.Close() + defer func() { _ = log.Close() }() if len(history) != 0 { t.Errorf("history = %+v, want empty", history) @@ -74,7 +74,7 @@ func TestEventLog_AppendAndReplayRoundTrip(t *testing.T) { if err != nil { t.Fatal(err) } - defer reloaded.Close() + defer func() { _ = reloaded.Close() }() if len(history) != 2 { t.Fatalf("history len = %d, want 2: %+v", len(history), history) @@ -116,7 +116,7 @@ func TestLoadEventLog_TornLastLineIsDropped(t *testing.T) { if err != nil { t.Fatalf("LoadEventLog() error = %v, want the torn last line silently dropped, not a hard failure", err) } - defer log.Close() + defer func() { _ = log.Close() }() if len(history) != 1 || history[0].ID != "req-1" { t.Errorf("history = %+v, want exactly the one entry before the torn line", history) @@ -192,7 +192,7 @@ func TestLoadEventLog_SelfHealsInterruptedPending(t *testing.T) { if err != nil { t.Fatal(err) } - defer log2.Close() + defer func() { _ = log2.Close() }() if len(history2) != 1 || history2[0].ID != "req-1" { t.Errorf("second load history = %+v, want the same single already-healed entry, not rediscovered again", history2) } @@ -233,7 +233,7 @@ func TestLoadEventLog_CompactsToMaxHistoryTail(t *testing.T) { if err != nil { t.Fatal(err) } - defer log.Close() + defer func() { _ = log.Close() }() if len(history) != maxHistoryTail { t.Fatalf("history len = %d, want maxHistoryTail (%d)", len(history), maxHistoryTail) @@ -256,7 +256,7 @@ func TestLoadEventLog_CompactsToMaxHistoryTail(t *testing.T) { if err != nil { t.Fatal(err) } - defer log2.Close() + defer func() { _ = log2.Close() }() if len(history2) != maxHistoryTail { t.Errorf("second load history len = %d, want maxHistoryTail (%d)", len(history2), maxHistoryTail) } @@ -274,7 +274,7 @@ func TestEventLog_CompactDue(t *testing.T) { if err != nil { t.Fatal(err) } - defer log.Close() + defer func() { _ = log.Close() }() var tail []HistoryEntry for i := 0; i < maxHistoryTail-1; i++ { @@ -311,7 +311,7 @@ func TestEventLog_CompactDue(t *testing.T) { if err != nil { t.Fatal(err) } - defer reloaded.Close() + defer func() { _ = reloaded.Close() }() if len(history) != len(tail) { t.Fatalf("reloaded history len = %d, want %d", len(history), len(tail)) } @@ -337,7 +337,7 @@ func TestEventLog_CompactBeforeSignedEvent_StillPreservesIt(t *testing.T) { if err != nil { t.Fatal(err) } - defer log.Close() + defer func() { _ = log.Close() }() pending := HistoryEntry{ID: "sign-1", Method: "sign_event", Kind: 1, Verdict: Allow} if err := log.AppendResolved(pending); err != nil { @@ -365,7 +365,7 @@ func TestEventLog_CompactBeforeSignedEvent_StillPreservesIt(t *testing.T) { if err != nil { t.Fatal(err) } - defer reloaded.Close() + defer func() { _ = reloaded.Close() }() if len(history) != 1 { t.Fatalf("history len = %d, want 1: %+v", len(history), history) @@ -381,7 +381,7 @@ func countLines(t *testing.T, path string) int { if err != nil { t.Fatal(err) } - defer f.Close() + defer func() { _ = f.Close() }() n := 0 scanner := bufio.NewScanner(f) @@ -406,7 +406,7 @@ func TestEventLog_ConcurrentAppendsAreRaceClean(t *testing.T) { if err != nil { t.Fatal(err) } - defer log.Close() + defer func() { _ = log.Close() }() const n = 50 var wg sync.WaitGroup diff --git a/cli/bunker/ipc_client.go b/cli/bunker/ipc_client.go index 21eea3a..3c90e29 100644 --- a/cli/bunker/ipc_client.go +++ b/cli/bunker/ipc_client.go @@ -47,12 +47,12 @@ func (c *ipcClient) call(req ipcRequest) (json.RawMessage, error) { if err != nil { return nil, err } - c.conn.SetWriteDeadline(time.Now().Add(ipcIdleTimeout)) + _ = c.conn.SetWriteDeadline(time.Now().Add(ipcIdleTimeout)) if _, err := c.conn.Write(append(data, '\n')); err != nil { return nil, err } - c.conn.SetReadDeadline(time.Now().Add(ipcIdleTimeout)) + _ = c.conn.SetReadDeadline(time.Now().Add(ipcIdleTimeout)) if !c.scanner.Scan() { if err := c.scanner.Err(); err != nil { return nil, err diff --git a/cli/bunker/ipc_server.go b/cli/bunker/ipc_server.go index 9de9de8..3b68276 100644 --- a/cli/bunker/ipc_server.go +++ b/cli/bunker/ipc_server.go @@ -104,7 +104,7 @@ func Listen(path string) (net.Listener, error) { return nil, err } if err := os.Chmod(path, 0600); err != nil { - l.Close() + _ = l.Close() return nil, err } return l, nil @@ -117,7 +117,7 @@ func socketIsLive(path string) bool { if err != nil { return false } - conn.Close() + _ = conn.Close() return true } @@ -138,7 +138,7 @@ func NewServer(listener net.Listener, client BunkerClient) *Server { func (s *Server) Serve(ctx context.Context) { go func() { <-ctx.Done() - s.listener.Close() + _ = s.listener.Close() }() for { @@ -151,13 +151,13 @@ func (s *Server) Serve(ctx context.Context) { } func (s *Server) serveConn(conn net.Conn) { - defer conn.Close() + defer func() { _ = conn.Close() }() scanner := bufio.NewScanner(conn) scanner.Buffer(make([]byte, 4096), maxIPCMessageSize) for { - conn.SetReadDeadline(time.Now().Add(ipcIdleTimeout)) + _ = conn.SetReadDeadline(time.Now().Add(ipcIdleTimeout)) if !scanner.Scan() { return } @@ -257,7 +257,7 @@ func writeResponse(conn net.Conn, resp ipcResponse) error { if err != nil { return err } - conn.SetWriteDeadline(time.Now().Add(ipcIdleTimeout)) + _ = conn.SetWriteDeadline(time.Now().Add(ipcIdleTimeout)) _, err = conn.Write(append(data, '\n')) return err } diff --git a/cli/bunker/ipc_test.go b/cli/bunker/ipc_test.go index c62d761..4b63f5b 100644 --- a/cli/bunker/ipc_test.go +++ b/cli/bunker/ipc_test.go @@ -137,7 +137,7 @@ func TestIPC_StatusRoundTrip(t *testing.T) { if err != nil { t.Fatal(err) } - defer client.Close() + defer func() { _ = client.Close() }() st, err := client.Status() if err != nil { @@ -156,7 +156,7 @@ func TestIPC_LogsRoundTrip(t *testing.T) { if err != nil { t.Fatal(err) } - defer client.Close() + defer func() { _ = client.Close() }() snap, err := client.Logs() if err != nil { @@ -182,7 +182,7 @@ func TestIPC_HistoryRoundTrip(t *testing.T) { if err != nil { t.Fatal(err) } - defer client.Close() + defer func() { _ = client.Close() }() got, err := client.History() if err != nil { @@ -201,7 +201,7 @@ func TestIPC_ApproveRejectRevokeConnect(t *testing.T) { if err != nil { t.Fatal(err) } - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Approve("req-1", nil); err != nil { t.Fatal(err) @@ -266,7 +266,7 @@ func TestIPC_Stop(t *testing.T) { if err != nil { t.Fatal(err) } - defer client.Close() + defer func() { _ = client.Close() }() if err := client.Stop(); err != nil { t.Fatal(err) @@ -285,12 +285,12 @@ func TestIPC_MalformedRequest_DoesNotCrashServer(t *testing.T) { if err != nil { t.Fatal(err) } - defer raw.Close() + defer func() { _ = raw.Close() }() - raw.Write([]byte("not json at all\n")) + _, _ = raw.Write([]byte("not json at all\n")) buf := make([]byte, 4096) - raw.SetReadDeadline(time.Now().Add(2 * time.Second)) + _ = raw.SetReadDeadline(time.Now().Add(2 * time.Second)) n, err := raw.Read(buf) if err != nil { t.Fatal(err) @@ -308,7 +308,7 @@ func TestIPC_MalformedRequest_DoesNotCrashServer(t *testing.T) { if err != nil { t.Fatalf("server appears to have crashed after a malformed request: %v", err) } - defer client.Close() + defer func() { _ = client.Close() }() if _, err := client.Status(); err != nil { t.Fatalf("Status() after malformed request error = %v", err) } @@ -326,15 +326,15 @@ func TestIPC_OversizedRequest_DisconnectsWithoutCrashingServer(t *testing.T) { huge[i] = 'a' } huge = append(huge, '\n') - raw.Write(huge) - raw.Close() + _, _ = raw.Write(huge) + _ = raw.Close() // The server must still be alive for a fresh client afterward. client, err := DialIPC(socketPath, time.Second) if err != nil { t.Fatalf("server appears to have crashed after an oversized request: %v", err) } - defer client.Close() + defer func() { _ = client.Close() }() if _, err := client.Status(); err != nil { t.Fatalf("Status() after oversized request error = %v", err) } @@ -353,13 +353,13 @@ func TestIPC_StaleSocketIsReplaced(t *testing.T) { if err != nil { t.Fatal(err) } - l.Close() + _ = l.Close() l2, err := Listen(socketPath) if err != nil { t.Fatalf("Listen() on a stale socket error = %v, want it to detect staleness and rebind", err) } - l2.Close() + _ = l2.Close() } func TestIPC_LiveSocketRefusesSecondListener(t *testing.T) { @@ -384,7 +384,7 @@ func TestIPC_ConcurrentClients(t *testing.T) { t.Error(err) return } - defer client.Close() + defer func() { _ = client.Close() }() if _, err := client.Status(); err != nil { t.Error(err) } diff --git a/cli/bunker/policy.go b/cli/bunker/policy.go index 90351f7..cc42eb8 100644 --- a/cli/bunker/policy.go +++ b/cli/bunker/policy.go @@ -249,7 +249,10 @@ func (s *Store) resolveGrantLocked(sess *Session, idx int) Decision { } else { g.RemainingUses = &remaining } - s.saveLocked() + // Best-effort: the in-memory grant above is already updated and + // authoritative for this process; a disk-sync failure here isn't + // fatal to the request this decision is for. + _ = s.saveLocked() } return verdict @@ -292,7 +295,8 @@ func (s *Store) Prune() { } } if changed { - s.saveLocked() + // Best-effort, same reasoning as resolveGrantLocked above. + _ = s.saveLocked() } } diff --git a/cli/bunker/queue_test.go b/cli/bunker/queue_test.go index 099043e..135977f 100644 --- a/cli/bunker/queue_test.go +++ b/cli/bunker/queue_test.go @@ -52,11 +52,11 @@ func TestQueue_OnAdded_FiresOnceOnAdd(t *testing.T) { got = append(got, p) }) - go q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "ping"}) + go func() { _, _ = q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "ping"}) }() waitFor(t, func() bool { return len(q.List()) == 1 }) // A duplicate Add for the same ID must not fire OnAdded again. - go q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "ping"}) + go func() { _, _ = q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "ping"}) }() time.Sleep(20 * time.Millisecond) mu.Lock() @@ -75,7 +75,7 @@ func TestQueue_OnResolved_FiresFromResolve(t *testing.T) { var got *ResolvedEvent q.OnResolved(func(ev ResolvedEvent) { got = &ev }) - go q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "sign_event", Kind: 1}) + go func() { _, _ = q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "sign_event", Kind: 1}) }() waitFor(t, func() bool { return len(q.List()) == 1 }) if err := q.Resolve("req-1", Deny, true); err != nil { @@ -110,7 +110,7 @@ func TestQueue_OnResolvedCompletesBeforeAddUnblocks(t *testing.T) { sawOnResolvedByAddTime := make(chan bool, 1) go func() { - q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "ping"}) + _, _ = q.Add(Pending{ID: "req-1", ClientKey: "app1", Method: "ping"}) sawOnResolvedByAddTime <- onResolvedCalled.Load() }() waitFor(t, func() bool { return len(q.List()) == 1 }) @@ -141,7 +141,7 @@ func TestQueue_OnResolved_FiresFromExpirySweep(t *testing.T) { var got *ResolvedEvent q.OnResolved(func(ev ResolvedEvent) { got = &ev }) - go q.Add(Pending{ID: "req-exp", ClientKey: "app1", Method: "ping"}) + go func() { _, _ = q.Add(Pending{ID: "req-exp", ClientKey: "app1", Method: "ping"}) }() waitFor(t, func() bool { return len(q.List()) == 1 }) fc.Advance(time.Hour) @@ -198,7 +198,7 @@ func TestQueue_ResolveUnknown(t *testing.T) { func TestQueue_ResolveRace_ExactlyOnce(t *testing.T) { q := NewQueue(0, 0) - go q.Add(Pending{ID: "req-race", ClientKey: "app1", Method: "ping"}) + go func() { _, _ = q.Add(Pending{ID: "req-race", ClientKey: "app1", Method: "ping"}) }() waitFor(t, func() bool { return len(q.List()) == 1 }) var wg sync.WaitGroup @@ -227,8 +227,8 @@ func TestQueue_ResolveRace_ExactlyOnce(t *testing.T) { func TestQueue_Full(t *testing.T) { q := NewQueue(2, 0) - go q.Add(Pending{ID: "r1", ClientKey: "app1", Method: "ping"}) - go q.Add(Pending{ID: "r2", ClientKey: "app1", Method: "ping"}) + go func() { _, _ = q.Add(Pending{ID: "r1", ClientKey: "app1", Method: "ping"}) }() + go func() { _, _ = q.Add(Pending{ID: "r2", ClientKey: "app1", Method: "ping"}) }() waitFor(t, func() bool { return len(q.List()) == 2 }) if _, err := q.Add(Pending{ID: "r3", ClientKey: "app1", Method: "ping"}); err != ErrQueueFull { diff --git a/cli/bunker/spawn_unix.go b/cli/bunker/spawn_unix.go index 372a774..1563af6 100644 --- a/cli/bunker/spawn_unix.go +++ b/cli/bunker/spawn_unix.go @@ -38,7 +38,7 @@ func spawnDaemon(privKeyHex, vaultLabel string, relays []string, logPath, socket if err != nil { return fmt.Errorf("open daemon log %s: %w", logPath, err) } - defer logFile.Close() + defer func() { _ = logFile.Close() }() pr, pw, err := os.Pipe() if err != nil { @@ -58,8 +58,8 @@ func spawnDaemon(privKeyHex, vaultLabel string, relays []string, logPath, socket cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} if err := cmd.Start(); err != nil { - pr.Close() - pw.Close() + _ = pr.Close() + _ = pw.Close() return fmt.Errorf("start daemon process: %w", err) } @@ -67,20 +67,20 @@ func spawnDaemon(privKeyHex, vaultLabel string, relays []string, logPath, socket // ExtraFiles); this process's copy is redundant and must be closed so // the child sees EOF once we finish writing, rather than hanging // waiting for a write end it doesn't know is otherwise unreachable. - pr.Close() + _ = pr.Close() // Detach entirely -- this process doesn't reap the daemon (Wait), it // outlives this invocation by design. if err := cmd.Process.Release(); err != nil { - pw.Close() + _ = pw.Close() return fmt.Errorf("release daemon process: %w", err) } if _, err := io.WriteString(pw, privKeyHex+"\n"+vaultLabel+"\n"); err != nil { - pw.Close() + _ = pw.Close() return fmt.Errorf("write identity key to daemon: %w", err) } - pw.Close() + _ = pw.Close() if err := waitForSocket(socketPath, readyTimeout); err != nil { return fmt.Errorf("%w (check %s for daemon startup errors)", err, logPath) @@ -108,7 +108,7 @@ func ReadIdentityKeyFromFD3() (privKeyHex, vaultLabel string, err error) { if f == nil { return "", "", errors.New("bunker: fd 3 not available -- __daemon must be started via spawnDaemon, not invoked directly") } - defer f.Close() + defer func() { _ = f.Close() }() data, err := io.ReadAll(f) if err != nil { diff --git a/cli/common/args.go b/cli/common/args.go index 0ab4e6b..d42d078 100644 --- a/cli/common/args.go +++ b/cli/common/args.go @@ -26,7 +26,7 @@ func silence(cmd *cobra.Command) { func UsageError(cmd *cobra.Command, err error) error { silence(cmd) if jsonMode, _ := cmd.Flags().GetBool("json"); !jsonMode { - cmd.Help() + _ = cmd.Help() } return wrapCLIError(CodeUsage, "", err) } diff --git a/cli/common/logging.go b/cli/common/logging.go index 09804ba..066861c 100644 --- a/cli/common/logging.go +++ b/cli/common/logging.go @@ -105,13 +105,13 @@ func RedirectStderrToCrashLog(path string) (restore func(), err error) { restoreStderr, err := redirectStderr(crashFile) if err != nil { - crashFile.Close() + _ = crashFile.Close() return nil, err } return func() { restoreStderr() - crashFile.Close() + _ = crashFile.Close() }, nil } diff --git a/cli/common/logging_unix.go b/cli/common/logging_unix.go index b8e83d5..e9b0bd3 100644 --- a/cli/common/logging_unix.go +++ b/cli/common/logging_unix.go @@ -21,12 +21,12 @@ func redirectStderr(f *os.File) (restore func(), err error) { } if err := unix.Dup2(int(f.Fd()), stderrFd); err != nil { - unix.Close(origStderr) + _ = unix.Close(origStderr) return nil, err } return func() { - unix.Dup2(origStderr, stderrFd) - unix.Close(origStderr) + _ = unix.Dup2(origStderr, stderrFd) + _ = unix.Close(origStderr) }, nil } diff --git a/cli/common/meilisearch/meili_client.go b/cli/common/meilisearch/meili_client.go index 98492ae..ee75fcb 100644 --- a/cli/common/meilisearch/meili_client.go +++ b/cli/common/meilisearch/meili_client.go @@ -13,9 +13,9 @@ import ( meilisearch "github.com/meilisearch/meilisearch-go" "github.com/ohstr/nmilat/config" + "github.com/ohstr/nmilat/nip19" "github.com/ohstr/nmilat/search" "github.com/ohstr/nmilat/search/ranking" - "github.com/ohstr/nmilat/nip19" ) type MeiliClient struct { @@ -123,7 +123,7 @@ func (m *MeiliClient) Initialize(ctx context.Context) error { if err != nil { return err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode >= 400 { respBody, _ := io.ReadAll(resp.Body) diff --git a/cli/delegate/command.go b/cli/delegate/command.go index 199c708..983fa21 100644 --- a/cli/delegate/command.go +++ b/cli/delegate/command.go @@ -415,7 +415,7 @@ func (m model) View() string { if _, ok := m.selectedKinds[i]; ok { checked = "x" } - kindList.WriteString(fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice.name)) + fmt.Fprintf(&kindList, "%s [%s] %s\n", cursor, checked, choice.name) } s = kindList.String() case stateCustomKinds: diff --git a/cli/ncli/apply.go b/cli/ncli/apply.go index 64a8855..cb8a3c1 100644 --- a/cli/ncli/apply.go +++ b/cli/ncli/apply.go @@ -59,8 +59,8 @@ func init() { RootCmd.AddCommand(applyCmd) applyCmd.Flags().StringP("file", "f", "", "Path to the workflow config file (required)") - applyCmd.MarkFlagRequired("file") - applyCmd.MarkFlagFilename("file", "yaml", "yml") + _ = applyCmd.MarkFlagRequired("file") + _ = applyCmd.MarkFlagFilename("file", "yaml", "yml") applyCmd.Flags().Bool("strict-pow", false, "Reject stream/sync events whose nonce tag doesn't meet its declared NIP-13 proof-of-work difficulty (default: accept them regardless, since PoW compliance is opt-in per NIP-13, not mandatory). Overrides the spec file's own strictPow field when passed explicitly") } diff --git a/cli/ncli/apply_test.go b/cli/ncli/apply_test.go index df62cd6..dfeef53 100644 --- a/cli/ncli/apply_test.go +++ b/cli/ncli/apply_test.go @@ -39,7 +39,7 @@ func TestApplySIGTERMExitsPromptly(t *testing.T) { if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() for { if _, _, err := conn.ReadMessage(); err != nil { return diff --git a/cli/ncli/dump.go b/cli/ncli/dump.go index d045500..fca7103 100644 --- a/cli/ncli/dump.go +++ b/cli/ncli/dump.go @@ -77,8 +77,8 @@ func init() { registerQueryFlags(dumpCmd, "") dumpCmd.Flags().StringP("out", "o", "", "Output JSON file path") - dumpCmd.MarkFlagRequired("out") - dumpCmd.MarkFlagFilename("out", "json", "jsonp") + _ = dumpCmd.MarkFlagRequired("out") + _ = dumpCmd.MarkFlagFilename("out", "json", "jsonp") dumpCmd.Flags().Duration("timeout", 30*time.Second, "Max time to wait per target before giving up on it (0 = wait forever)") } diff --git a/cli/ncli/find.go b/cli/ncli/find.go index 90b1412..7f479eb 100644 --- a/cli/ncli/find.go +++ b/cli/ncli/find.go @@ -111,7 +111,7 @@ func init() { registerQueryFlags(findCmd, "") findCmd.Flags().StringP("out", "o", "", "Also save the result to this JSON file path") - findCmd.MarkFlagFilename("out", "json", "jsonp") + _ = findCmd.MarkFlagFilename("out", "json", "jsonp") findCmd.Flags().Duration("timeout", 30*time.Second, "Max time to wait per target before giving up on it (0 = wait forever)") } diff --git a/cli/ncli/id.go b/cli/ncli/id.go index 3f2f915..268a067 100644 --- a/cli/ncli/id.go +++ b/cli/ncli/id.go @@ -295,16 +295,16 @@ func runIDList(cmd *cobra.Command) error { } w := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0) if reveal { - fmt.Fprintln(w, "LABEL\tNPUB\tCREATED\tPRIV_HEX\tNSEC") + _, _ = fmt.Fprintln(w, "LABEL\tNPUB\tCREATED\tPRIV_HEX\tNSEC") for _, r := range rows { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", r.Label, r.Npub, r.CreatedAt, r.PrivKeyHex, r.Nsec) + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", r.Label, r.Npub, r.CreatedAt, r.PrivKeyHex, r.Nsec) } } else { - fmt.Fprintln(w, "LABEL\tNPUB\tCREATED") + _, _ = fmt.Fprintln(w, "LABEL\tNPUB\tCREATED") for _, r := range rows { - fmt.Fprintf(w, "%s\t%s\t%s\n", r.Label, r.Npub, r.CreatedAt) + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", r.Label, r.Npub, r.CreatedAt) } } - w.Flush() + _ = w.Flush() return nil } diff --git a/cli/ncli/id_sign.go b/cli/ncli/id_sign.go index 5e2f252..d527b39 100644 --- a/cli/ncli/id_sign.go +++ b/cli/ncli/id_sign.go @@ -116,13 +116,13 @@ func init() { idCmd.AddCommand(idSignCmd) idSignCmd.Flags().String("identity", "", "Vault label/nsec identity to sign with (required; must resolve to a private key -- a pubkey-only npub/hex/nprofile/nip-05 is rejected)") - idSignCmd.MarkFlagRequired("identity") + _ = idSignCmd.MarkFlagRequired("identity") idSignCmd.Flags().StringP("events", "e", "", "Path to a single unsigned event object or an array of them (required)") - idSignCmd.MarkFlagRequired("events") - idSignCmd.MarkFlagFilename("events", "json", "jsonp", "yaml", "yml") + _ = idSignCmd.MarkFlagRequired("events") + _ = idSignCmd.MarkFlagFilename("events", "json", "jsonp", "yaml", "yml") idSignCmd.Flags().StringP("out", "o", "", "Output path for the signed event(s) (required)") - idSignCmd.MarkFlagRequired("out") - idSignCmd.MarkFlagFilename("out", "json", "jsonp", "yaml", "yml") + _ = idSignCmd.MarkFlagRequired("out") + _ = idSignCmd.MarkFlagFilename("out", "json", "jsonp", "yaml", "yml") } diff --git a/cli/ncli/miner.go b/cli/ncli/miner.go index bd8ea79..7c7f0ea 100644 --- a/cli/ncli/miner.go +++ b/cli/ncli/miner.go @@ -386,16 +386,16 @@ func init() { minerCmd.AddCommand(minerCheckCmd) minerMineCmd.Flags().StringP("event", "e", "", "Path to a structured, unsigned event file") - minerMineCmd.MarkFlagFilename("event", "yaml", "yml", "json") + _ = minerMineCmd.MarkFlagFilename("event", "yaml", "yml", "json") minerMineCmd.Flags().String("content", "", "Note content to mine, authored inline (alternative to --event)") minerMineCmd.Flags().String("content-file", "", "Path to a plain .txt file with the note content to mine (alternative to --event/--content)") - minerMineCmd.MarkFlagFilename("content-file", "txt") + _ = minerMineCmd.MarkFlagFilename("content-file", "txt") minerMineCmd.Flags().Int("kind", 1, "Event kind (--content/--content-file mode only)") minerMineCmd.Flags().StringArray("tag", nil, "Tag as key=value, repeatable (--content/--content-file mode only, e.g. --tag t=nostr)") minerMineCmd.Flags().StringP("out", "o", "", "Output path for the mined event (.json/.jsonp/.yaml/.yml)") - minerMineCmd.MarkFlagFilename("out", "yaml", "yml", "json") + _ = minerMineCmd.MarkFlagFilename("out", "yaml", "yml", "json") minerMineCmd.Flags().Bool("in-place", false, "Write the mined result back to --event") minerMineCmd.Flags().IntP("difficulty", "d", 2, "Proof-of-work difficulty, in leading zero bits") @@ -404,7 +404,7 @@ func init() { minerMineCmd.Flags().String("identity", "", "Vault label/npub/hex/nsec/nprofile/nip-05 identity to fill the event's pubkey from -- also signs automatically if a private key is available (nsec, or a vault label)") minerCheckCmd.Flags().StringP("events", "e", "", "File mode: path to a JSON array of already-mined events") - minerCheckCmd.MarkFlagFilename("events", "json", "jsonp") + _ = minerCheckCmd.MarkFlagFilename("events", "json", "jsonp") registerQueryFlags(minerCheckCmd, "Live mode: ") diff --git a/cli/ncli/ping.go b/cli/ncli/ping.go index 53e6ccb..2660581 100644 --- a/cli/ncli/ping.go +++ b/cli/ncli/ping.go @@ -98,7 +98,7 @@ func init() { RootCmd.AddCommand(pingCmd) pingCmd.Flags().StringP("targets", "t", "", "Path to a YAML targets file (only its relays are used)") - pingCmd.MarkFlagFilename("targets", "yaml", "yml") + _ = pingCmd.MarkFlagFilename("targets", "yaml", "yml") pingCmd.Flags().Duration("timeout", 30*time.Second, "Max time to wait per relay before giving up on it (0 = wait forever)") pingCmd.Flags().Bool("tui", false, "Render results as a live interactive board instead of plain log lines (requires a real terminal; ignored otherwise, or with --json/--quiet)") diff --git a/cli/ncli/ping_test.go b/cli/ncli/ping_test.go index a0a5b9d..7882001 100644 --- a/cli/ncli/ping_test.go +++ b/cli/ncli/ping_test.go @@ -74,7 +74,7 @@ func TestPingPositionalRelaysToReport(t *testing.T) { if err != nil { return } - conn.Close() + _ = conn.Close() })) defer server.Close() wsURL := "ws" + server.URL[len("http"):] diff --git a/cli/ncli/publish.go b/cli/ncli/publish.go index 497994f..1d8671b 100644 --- a/cli/ncli/publish.go +++ b/cli/ncli/publish.go @@ -95,8 +95,8 @@ func init() { RootCmd.AddCommand(publishCmd) publishCmd.Flags().StringP("events", "e", "", "Path to a single event object or a JSON array of events (required)") - publishCmd.MarkFlagRequired("events") - publishCmd.MarkFlagFilename("events", "json", "jsonp") + _ = publishCmd.MarkFlagRequired("events") + _ = publishCmd.MarkFlagFilename("events", "json", "jsonp") publishCmd.Flags().StringP("relays", "s", "", "Comma-separated relay URLs to publish to (omit to use the relays configured via \"ncli prefs relays add\")") } diff --git a/cli/ncli/publish_test.go b/cli/ncli/publish_test.go index bd5bbde..b35337b 100644 --- a/cli/ncli/publish_test.go +++ b/cli/ncli/publish_test.go @@ -63,7 +63,7 @@ func mockRelayServer(t *testing.T, accept bool) (wsURL string) { if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() for { _, msg, err := conn.ReadMessage() @@ -89,9 +89,9 @@ func mockRelayServer(t *testing.T, accept bool) (wsURL string) { id := s[idStart:idEnd] if accept { - conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", true, ""]`, id))) + _ = conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", true, ""]`, id))) } else { - conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", false, "rejected"]`, id))) + _ = conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", false, "rejected"]`, id))) } } })) diff --git a/cli/ncli/query.go b/cli/ncli/query.go index 6efcc6b..696c300 100644 --- a/cli/ncli/query.go +++ b/cli/ncli/query.go @@ -23,7 +23,7 @@ func registerQueryFlags(cmd *cobra.Command, prefix string) { } cmd.Flags().StringP("targets", "t", "", targetsDesc) - cmd.MarkFlagFilename("targets", "yaml", "yml") + _ = cmd.MarkFlagFilename("targets", "yaml", "yml") cmd.Flags().StringP("relays", "s", "", relaysDesc) diff --git a/cli/ncli/version.go b/cli/ncli/version.go index 55af480..989600c 100644 --- a/cli/ncli/version.go +++ b/cli/ncli/version.go @@ -29,7 +29,7 @@ scripts or an AI agent.`, // InitConfig's side effects (mkdir, crash log path, lumberjack // setup), which version intentionally skips. A missing/invalid // config file is not this command's concern. - common.LoadViperConfig(cfgFile) + _ = common.LoadViperConfig(cfgFile) logDir := viper.GetString("log_dir") if logDir == "" { logDir = filepath.Join(common.AppConfigDir(), defaultLogDirName) diff --git a/cli/reindex/command.go b/cli/reindex/command.go index cba5b71..8d10d79 100644 --- a/cli/reindex/command.go +++ b/cli/reindex/command.go @@ -11,9 +11,9 @@ import ( "time" "github.com/ohstr/ncli/cli/common/meilisearch" - "github.com/ohstr/nmilat/search" "github.com/ohstr/nmilat/nip01" "github.com/ohstr/nmilat/relay" + "github.com/ohstr/nmilat/search" "github.com/rs/zerolog/log" ) diff --git a/cli/relay/admin.go b/cli/relay/admin.go index b4aa4e6..791c361 100644 --- a/cli/relay/admin.go +++ b/cli/relay/admin.go @@ -255,7 +255,7 @@ func adminRequestBody(method, path string, body interface{}) (map[string]interfa Input: url, } } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode >= 400 { respBody, _ := io.ReadAll(resp.Body) @@ -538,15 +538,15 @@ func runMembersList(cmd *cobra.Command, args []string) error { } tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0) - fmt.Fprintln(tw, "PUBKEY\tROLES\tJOINED") + _, _ = fmt.Fprintln(tw, "PUBKEY\tROLES\tJOINED") for _, raw := range members { m, ok := raw.(map[string]interface{}) if !ok { continue } - fmt.Fprintf(tw, "%s\t%s\t%s\n", truncPubkey(strField(m, "pubkey")), joinOrDash(stringsField(m, "roles")), relativeTime(int64Field(m, "joined_at"))) + _, _ = fmt.Fprintf(tw, "%s\t%s\t%s\n", truncPubkey(strField(m, "pubkey")), joinOrDash(stringsField(m, "roles")), relativeTime(int64Field(m, "joined_at"))) } - tw.Flush() + _ = tw.Flush() return nil } @@ -666,7 +666,7 @@ func runInvitesList(cmd *cobra.Command, args []string) error { } tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0) - fmt.Fprintln(tw, "CODE\tUSES\tEXPIRES\tROLES") + _, _ = fmt.Fprintln(tw, "CODE\tUSES\tEXPIRES\tROLES") for _, raw := range invites { m, ok := raw.(map[string]interface{}) if !ok { @@ -677,9 +677,9 @@ func runInvitesList(cmd *cobra.Command, args []string) error { if maxUses > 0 { usesCol = fmt.Sprintf("%d/%d", int64Field(m, "uses"), maxUses) } - fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", truncCode(strField(m, "code")), usesCol, formatUnix(int64Field(m, "expires_at")), joinOrDash(stringsField(m, "roles"))) + _, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", truncCode(strField(m, "code")), usesCol, formatUnix(int64Field(m, "expires_at")), joinOrDash(stringsField(m, "roles"))) } - tw.Flush() + _ = tw.Flush() return nil } @@ -722,7 +722,7 @@ func runRolesList(cmd *cobra.Command, args []string) error { } tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0) - fmt.Fprintln(tw, "ID\tLABEL\tDESCRIPTION\tCOLOR\tORDER") + _, _ = fmt.Fprintln(tw, "ID\tLABEL\tDESCRIPTION\tCOLOR\tORDER") for _, raw := range roles { m, ok := raw.(map[string]interface{}) if !ok { @@ -735,9 +735,9 @@ func runRolesList(cmd *cobra.Command, args []string) error { if v, ok := m["order"].(float64); ok { order = fmt.Sprintf("%d", int(v)) } - fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n", strField(m, "id"), strField(m, "label"), strField(m, "description"), color, order) + _, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n", strField(m, "id"), strField(m, "label"), strField(m, "description"), color, order) } - tw.Flush() + _ = tw.Flush() return nil } diff --git a/cli/relay/config_test.go b/cli/relay/config_test.go index c0bc4d9..0de878f 100644 --- a/cli/relay/config_test.go +++ b/cli/relay/config_test.go @@ -82,8 +82,8 @@ nip11: tmpDir := t.TempDir() // Change CWD to tmpDir to test local file discovery if no cfgFile passed originalWd, _ := os.Getwd() - defer os.Chdir(originalWd) - os.Chdir(tmpDir) + defer func() { _ = os.Chdir(originalWd) }() + _ = os.Chdir(tmpDir) tmpFile := filepath.Join(tmpDir, "relay.yaml") err := os.WriteFile(tmpFile, []byte(tt.yamlContent), 0644) @@ -150,7 +150,7 @@ func loadRelayConfigFromYAML(t *testing.T, yamlContent string) RelayConfig { tmpDir := t.TempDir() originalWd, err := os.Getwd() require.NoError(t, err) - t.Cleanup(func() { os.Chdir(originalWd) }) + t.Cleanup(func() { _ = os.Chdir(originalWd) }) require.NoError(t, os.Chdir(tmpDir)) tmpFile := filepath.Join(tmpDir, "relay.yaml") diff --git a/cli/relay/context.go b/cli/relay/context.go index a9fd134..9c91969 100644 --- a/cli/relay/context.go +++ b/cli/relay/context.go @@ -106,9 +106,9 @@ func runContextList(cmd *cobra.Command, args []string) error { if name == prefs.CurrentRelayContext { marker = "*" } - fmt.Fprintf(tw, "%s %s\t%s\n", marker, name, prefs.RelayContexts[name]) + _, _ = fmt.Fprintf(tw, "%s %s\t%s\n", marker, name, prefs.RelayContexts[name]) } - tw.Flush() + _ = tw.Flush() return nil } diff --git a/cli/relay/context_run_test.go b/cli/relay/context_run_test.go index 7841877..56ee211 100644 --- a/cli/relay/context_run_test.go +++ b/cli/relay/context_run_test.go @@ -36,7 +36,7 @@ func waitForPort(t *testing.T, addr string, timeout time.Duration) { for time.Now().Before(deadline) { conn, err := net.DialTimeout("tcp", addr, 200*time.Millisecond) if err == nil { - conn.Close() + _ = conn.Close() return } time.Sleep(100 * time.Millisecond) @@ -371,7 +371,7 @@ func waitPortFree(t *testing.T, addr string, timeout time.Duration) { if err != nil { return } - conn.Close() + _ = conn.Close() time.Sleep(100 * time.Millisecond) } t.Fatalf("%s was still accepting connections after %s", addr, timeout) diff --git a/cli/relay/service.go b/cli/relay/service.go index 58faf98..f480b50 100644 --- a/cli/relay/service.go +++ b/cli/relay/service.go @@ -32,9 +32,6 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { MaxConcurrentStoreTasks: config.MaxConcurrentStoreTasks, } - if _, err := time.ParseDuration(config.HandshakeTimeout); err == nil { - // Handshake timeout is usually handled by http server config, but session might use it - } if d, err := time.ParseDuration(config.PingInterval); err == nil { sessionConfig.PingInterval = d } @@ -127,7 +124,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { if status["is_running"].(bool) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) // Or 200, Conflict shows it's busy - json.NewEncoder(w).Encode(status) + _ = json.NewEncoder(w).Encode(status) return } @@ -150,7 +147,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - json.NewEncoder(w).Encode(map[string]string{"status": "started"}) + _ = json.NewEncoder(w).Encode(map[string]string{"status": "started"}) })) mux.HandleFunc("/admin/reindex/zaps", adminAuth(func(w http.ResponseWriter, r *http.Request) { @@ -162,7 +159,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { if status["is_running"].(bool) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) - json.NewEncoder(w).Encode(status) + _ = json.NewEncoder(w).Encode(status) return } @@ -174,7 +171,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - json.NewEncoder(w).Encode(map[string]string{"status": "started"}) + _ = json.NewEncoder(w).Encode(map[string]string{"status": "started"}) })) mux.HandleFunc("/admin/worker/stats", adminAuth(func(w http.ResponseWriter, r *http.Request) { @@ -191,7 +188,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { } w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(stats) + _ = json.NewEncoder(w).Encode(stats) })) mux.HandleFunc("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/admin/search", adminAuth(func(w http.ResponseWriter, r *http.Request) { @@ -213,7 +210,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(map[string]string{"status": "deleted"}) + _ = json.NewEncoder(w).Encode(map[string]string{"status": "deleted"}) })) registerMembershipAdminRoutes(mux, wsHandler, store, adminAuth) @@ -229,7 +226,7 @@ func NewServer(store *relay.EventStore, searchService search.Service) *Service { } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(map[string]string{"status": "deleted"}) + _ = json.NewEncoder(w).Encode(map[string]string{"status": "deleted"}) })) s := &Service{ diff --git a/cli/relay/service_topzapped_test.go b/cli/relay/service_topzapped_test.go index 219a5ad..bac485a 100644 --- a/cli/relay/service_topzapped_test.go +++ b/cli/relay/service_topzapped_test.go @@ -88,7 +88,7 @@ func TestNewServer_TopZappedWindowFromYAML(t *testing.T) { wsURL := "ws" + strings.TrimPrefix(ts.URL, "http") + "/" conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) require.NoError(t, err) - defer conn.Close() + defer func() { _ = conn.Close() }() require.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte(`["REQ","sub1",{"cache":["top-zapped",{}]}]`))) diff --git a/client/client.go b/client/client.go index 025b55a..e8bf3db 100644 --- a/client/client.go +++ b/client/client.go @@ -33,7 +33,7 @@ func loadSpecFromYaml(yamlPath string) (*RootSpec, error) { if err != nil { return nil, err } - defer f.Close() + defer func() { _ = f.Close() }() yamlBytes, err := io.ReadAll(f) if err != nil { @@ -294,7 +294,7 @@ func Process(parent context.Context, specFile string, options *ClientOptions) er if err != nil { return err } - defer watcher.Close() + defer func() { _ = watcher.Close() }() c := NewClient(specFile, options) defer c.stop() @@ -467,7 +467,9 @@ func (c *Client) run(ctx context.Context, completed chan<- interface{}) { c.render(ctx) } }, func() { - c.save() + if err := c.save(); err != nil { + c.app.Error(fmt.Sprintf("Failed to save snapshot: %s", err.Error())) + } }) } @@ -484,7 +486,9 @@ func (c *Client) run(ctx context.Context, completed chan<- interface{}) { <-ctx.Done() c.app.Stop() }() - c.app.Run() + if err := c.app.Run(); err != nil { + log.Error().Err(err).Msg("TUI exited with an error") + } } else { <-ctx.Done() } diff --git a/client/identity.go b/client/identity.go index 223bf97..b8d8af8 100644 --- a/client/identity.go +++ b/client/identity.go @@ -286,7 +286,7 @@ func fetchNip05PubKey(url, name, identifier string) (string, error) { if err != nil { return "", fmt.Errorf("nip-05 lookup for %q failed: %w", identifier, err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("nip-05 lookup for %q: server returned %s", identifier, resp.Status) diff --git a/client/identity_test.go b/client/identity_test.go index c0622ca..38499f4 100644 --- a/client/identity_test.go +++ b/client/identity_test.go @@ -189,7 +189,7 @@ func TestResolveIdentifier_Nip05(t *testing.T) { id, _ := GenerateIdentity() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(map[string]any{ + _ = json.NewEncoder(w).Encode(map[string]any{ "names": map[string]string{"alice": id.PubKeyHex}, }) })) @@ -206,7 +206,7 @@ func TestResolveIdentifier_Nip05(t *testing.T) { func TestFetchNip05PubKey_NameNotFound(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(map[string]any{"names": map[string]string{}}) + _ = json.NewEncoder(w).Encode(map[string]any{"names": map[string]string{}}) })) defer srv.Close() @@ -228,7 +228,7 @@ func TestFetchNip05PubKey_NonOKStatus(t *testing.T) { func TestFetchNip05PubKey_InvalidJSON(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, "not json") + _, _ = fmt.Fprint(w, "not json") })) defer srv.Close() @@ -240,7 +240,7 @@ func TestFetchNip05PubKey_InvalidJSON(t *testing.T) { func TestFetchNip05PubKey_Timeout(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(50 * time.Millisecond) - json.NewEncoder(w).Encode(map[string]any{"names": map[string]string{}}) + _ = json.NewEncoder(w).Encode(map[string]any{"names": map[string]string{}}) })) defer srv.Close() diff --git a/client/inspect_store.go b/client/inspect_store.go index e7c04ee..89668d1 100644 --- a/client/inspect_store.go +++ b/client/inspect_store.go @@ -33,7 +33,7 @@ func NewInspectStore() (*InspectStore, error) { store, err := relay.NewEventStore(filepath.Join(dir, "inspect.db"), &nip11.Limitation{}) if err != nil { - os.RemoveAll(dir) + _ = os.RemoveAll(dir) return nil, fmt.Errorf("failed to open inspect temp store: %w", err) } diff --git a/client/inspect_store_test.go b/client/inspect_store_test.go index 437a423..416859f 100644 --- a/client/inspect_store_test.go +++ b/client/inspect_store_test.go @@ -26,7 +26,7 @@ func TestInspectStoreInsertRoundTrip(t *testing.T) { if err != nil { t.Fatalf("NewInspectStore: %v", err) } - defer store.Close() + defer func() { _ = store.Close() }() ctx := context.Background() event := newInspectStoreTestEvent(1) @@ -53,7 +53,7 @@ func TestInspectStoreInsertToleratesDuplicateEvent(t *testing.T) { if err != nil { t.Fatalf("NewInspectStore: %v", err) } - defer store.Close() + defer func() { _ = store.Close() }() ctx := context.Background() event := newInspectStoreTestEvent(1) @@ -95,7 +95,7 @@ func TestInspectStoreConcurrentInsert(t *testing.T) { if err != nil { t.Fatalf("NewInspectStore: %v", err) } - defer store.Close() + defer func() { _ = store.Close() }() ctx := context.Background() const numEvents = 200 diff --git a/client/integration_test.go b/client/integration_test.go index 1da0c88..ef339da 100644 --- a/client/integration_test.go +++ b/client/integration_test.go @@ -41,7 +41,7 @@ func TestRateLimitRecovery(t *testing.T) { if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() for { _, msg, err := conn.ReadMessage() @@ -84,12 +84,12 @@ func TestRateLimitRecovery(t *testing.T) { if atomic.LoadInt32(&rateLimitActive) == 1 { // REJECT resp := fmt.Sprintf(`["OK", "%s", false, "rate-limited: calm down"]`, id) - conn.WriteMessage(websocket.TextMessage, []byte(resp)) + _ = conn.WriteMessage(websocket.TextMessage, []byte(resp)) } else { // ACCEPT receivedEvents.Store(id, true) resp := fmt.Sprintf(`["OK", "%s", true, ""]`, id) - conn.WriteMessage(websocket.TextMessage, []byte(resp)) + _ = conn.WriteMessage(websocket.TextMessage, []byte(resp)) } } } diff --git a/client/multi_relay_test.go b/client/multi_relay_test.go index c49768b..fc59948 100644 --- a/client/multi_relay_test.go +++ b/client/multi_relay_test.go @@ -58,7 +58,7 @@ func TestMultiRelaySync(t *testing.T) { // Create event store limitation := &nip11.Limitation{ - MaxLimit: 100000, + MaxLimit: 100000, MaxSubscriptions: 100, } // relay.Session/EventStore default their Logger to zerolog.Nop(), which @@ -88,7 +88,7 @@ func TestMultiRelaySync(t *testing.T) { t.Logf("server error: %v", err) } }() - defer server.Close() + defer func() { _ = server.Close() }() // Wait for server to start time.Sleep(100 * time.Millisecond) diff --git a/client/neg_sync.go b/client/neg_sync.go index 93bd3c9..a1850d7 100644 --- a/client/neg_sync.go +++ b/client/neg_sync.go @@ -2,7 +2,6 @@ package client import ( "context" - "encoding/hex" "errors" "fmt" "sort" @@ -14,9 +13,9 @@ import ( "github.com/ohstr/nmilat/nip11" "github.com/ohstr/nmilat/nip13" "github.com/ohstr/nmilat/nip77" - "github.com/ohstr/nmilat/wire" "github.com/ohstr/nmilat/relay" relayclient "github.com/ohstr/nmilat/relay/client" + "github.com/ohstr/nmilat/wire" "github.com/rs/zerolog/log" ) @@ -471,11 +470,6 @@ func (s *SyncModule) Close() { } } -// idBytesToHex converts raw ID bytes to hex. Unused placeholder removed. -func idBytesToHex(idBytes []byte) string { - return hex.EncodeToString(idBytes) -} - var negModeNames = [...]string{"Skip", "Fingerprint", "IdList"} func negModeName(mode int) string { diff --git a/client/neg_sync_test.go b/client/neg_sync_test.go index 75a65c9..f1845db 100644 --- a/client/neg_sync_test.go +++ b/client/neg_sync_test.go @@ -13,9 +13,9 @@ import ( "github.com/ohstr/nmilat/nip01" "github.com/ohstr/nmilat/nip11" "github.com/ohstr/nmilat/nip77" - "github.com/ohstr/nmilat/wire" "github.com/ohstr/nmilat/relay" relayclient "github.com/ohstr/nmilat/relay/client" + "github.com/ohstr/nmilat/wire" ) // TestNegSync_Integration tests NIP-77 negentropy sync against real public @@ -86,8 +86,8 @@ func TestNegSync_Integration(t *testing.T) { t.Fatalf("failed to create temp file: %v", err) } tmpPath := tmpFile.Name() - tmpFile.Close() - defer os.Remove(tmpPath) + _ = tmpFile.Close() + defer func() { _ = os.Remove(tmpPath) }() t.Logf("temp db: %s", tmpPath) diff --git a/client/ping.go b/client/ping.go index 20e044d..047b37c 100644 --- a/client/ping.go +++ b/client/ping.go @@ -191,7 +191,9 @@ func Ping(ctx context.Context, targets *TargetsSpec, opts PingOptions) *PingRepo app.Stop() }() go runChecks() - app.Run() + if err := app.Run(); err != nil { + log.Error().Err(err).Msg("TUI exited with an error") + } return report } diff --git a/client/recovery.go b/client/recovery.go index 9b62fa0..0867127 100644 --- a/client/recovery.go +++ b/client/recovery.go @@ -287,7 +287,7 @@ func (rm *RecoveryManager) collectPending(force bool) []*RetryMeta { var result []*RetryMeta now := time.Now() - rm.metaDB.View(func(tx *bolt.Tx) error { + if err := rm.metaDB.View(func(tx *bolt.Tx) error { b := tx.Bucket(BUCKET_RECOVERY) c := b.Cursor() @@ -302,7 +302,9 @@ func (rm *RecoveryManager) collectPending(force bool) []*RetryMeta { } } return nil - }) + }); err != nil { + log.Error().Err(err).Msg("failed to scan recovery bucket for pending retries") + } return result } @@ -373,7 +375,9 @@ func (rm *RecoveryManager) processBatch(ctx context.Context, destination string, for _, meta := range metas { // If invalid URL, delete if _, urlErr := url.Parse(destination); urlErr != nil { - rm.deleteMeta(meta.EventID, meta.Destination) + if err := rm.deleteMeta(meta.EventID, meta.Destination); err != nil { + log.Error().Err(err).Str("id", meta.EventID).Msg("failed to delete recovery metadata for invalid-URL destination") + } continue } rm.handleRetryFailure(meta, err) @@ -395,7 +399,9 @@ func (rm *RecoveryManager) processBatch(ctx context.Context, destination string, event, err := rm.findEvent(meta.EventID) if err != nil { log.Error().Err(err).Str("id", meta.EventID).Msg("failed to load event for retry, deleting metadata") - rm.deleteMeta(meta.EventID, meta.Destination) + if err := rm.deleteMeta(meta.EventID, meta.Destination); err != nil { + log.Error().Err(err).Str("id", meta.EventID).Msg("failed to delete recovery metadata") + } continue } @@ -430,7 +436,9 @@ func (rm *RecoveryManager) processBatch(ctx context.Context, destination string, } else { // Success log.Info().Str("id", event.ID).Str("dest", destination).Msg("successfully recovered event") - rm.deleteMeta(meta.EventID, meta.Destination) + if err := rm.deleteMeta(meta.EventID, meta.Destination); err != nil { + log.Error().Err(err).Str("id", meta.EventID).Msg("failed to delete recovery metadata for a successfully recovered event") + } } } } @@ -442,11 +450,15 @@ func (rm *RecoveryManager) handleRetryFailure(meta *RetryMeta, err error) { if meta.Attempts >= rm.maxRetries { log.Warn().Str("id", meta.EventID).Msg("max retries reached, dropping event") - rm.deleteMeta(meta.EventID, meta.Destination) + if err := rm.deleteMeta(meta.EventID, meta.Destination); err != nil { + log.Error().Err(err).Str("id", meta.EventID).Msg("failed to delete recovery metadata for a dropped event") + } } else { // Backoff meta.NextRetry = time.Now().Add(rm.retryInterval * time.Duration(meta.Attempts)) - rm.saveMeta(meta) + if err := rm.saveMeta(meta); err != nil { + log.Error().Err(err).Str("id", meta.EventID).Msg("failed to save recovery metadata backoff") + } } } diff --git a/client/recovery_test.go b/client/recovery_test.go index 5ddd2bf..c04b4cd 100644 --- a/client/recovery_test.go +++ b/client/recovery_test.go @@ -40,7 +40,7 @@ func TestRecoveryManager(t *testing.T) { if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() for { _, _, err := conn.ReadMessage() @@ -93,7 +93,7 @@ func TestRecoveryManager(t *testing.T) { // Just send a hardcoded OK for the event ID we are testing id := "4d6d9c5b65123456789012345678901234567890123456789012345678901234" response := fmt.Sprintf(`["OK", "%s", true, "saved"]`, id) - conn.WriteMessage(websocket.TextMessage, []byte(response)) + _ = conn.WriteMessage(websocket.TextMessage, []byte(response)) } })) defer server.Close() @@ -169,7 +169,7 @@ func newMockRelay(t *testing.T, behavior mockRelayBehavior, received *atomic.Int if err != nil { return } - defer conn.Close() + defer func() { _ = conn.Close() }() for { _, msg, err := conn.ReadMessage() @@ -204,9 +204,9 @@ func newMockRelay(t *testing.T, behavior mockRelayBehavior, received *atomic.Int } if behavior == mockRelayAccept { - conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", true, ""]`, id))) + _ = conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", true, ""]`, id))) } else { - conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", false, "rejected"]`, id))) + _ = conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf(`["OK", "%s", false, "rejected"]`, id))) } } })) diff --git a/client/relay_dial_test.go b/client/relay_dial_test.go index 2e5d6c7..24854b1 100644 --- a/client/relay_dial_test.go +++ b/client/relay_dial_test.go @@ -26,7 +26,7 @@ func mockWSServer(t *testing.T) (wsURL *url.URL, hits *int32) { if err != nil { return } - conn.Close() + _ = conn.Close() })) t.Cleanup(server.Close) diff --git a/client/spec.go b/client/spec.go index 67b14a2..a8863dd 100644 --- a/client/spec.go +++ b/client/spec.go @@ -343,8 +343,8 @@ func (fs *FilterSpec) UnmarshalJSON(data []byte) error { } *fs = FilterSpec(subFilter) - fs.SubscriptionFilter.Since = since - fs.SubscriptionFilter.Until = until + fs.Since = since + fs.Until = until return nil } @@ -481,12 +481,13 @@ func (ss *SyncSpec) UnmarshalJSON(data []byte) error { } for _, f := range []*FlowSpec{temp.From, temp.To} { - if f.Type == FlOW_LOCAL { + switch f.Type { + case FlOW_LOCAL: if temp.local != nil { return errors.New("multiple local stores defined (only one allowed for sync)") } temp.local = f - } else if f.Type == FlOW_REMOTE { + case FlOW_REMOTE: if temp.remote != nil { return errors.New("multiple remote relays defined (only one allowed for sync)") } diff --git a/client/spec_loading_test.go b/client/spec_loading_test.go index ee62204..592ca3b 100644 --- a/client/spec_loading_test.go +++ b/client/spec_loading_test.go @@ -42,7 +42,7 @@ func loadCases(t *testing.T, casesPath string) []string { } yamlFilesPath = append(yamlFilesPath, fcase.Name()) - fcase.Close() + _ = fcase.Close() } return yamlFilesPath @@ -314,12 +314,12 @@ func TestSpecFilter(t *testing.T) { t.Fatal("error wanted") } - if obj.SubscriptionFilter.Until < test.approxTimestamp-toleranceMilliseconds || obj.SubscriptionFilter.Until > test.approxTimestamp+toleranceMilliseconds { - t.Errorf("timestamp out of expected range, %d = %d ", obj.SubscriptionFilter.Until, test.approxTimestamp) + if obj.Until < test.approxTimestamp-toleranceMilliseconds || obj.Until > test.approxTimestamp+toleranceMilliseconds { + t.Errorf("timestamp out of expected range, %d = %d ", obj.Until, test.approxTimestamp) } t.Logf("obj.str.until=%v", obj.Until) - t.Logf("obj.timestamp.until=%v", obj.SubscriptionFilter.Until) + t.Logf("obj.timestamp.until=%v", obj.Until) }) } @@ -375,8 +375,8 @@ kinds: [1] if err := yaml.Unmarshal([]byte(specYaml), &obj); err != nil { t.Fatalf("unexpected error: %v", err) } - if got := obj.SubscriptionFilter.Tags["e"]; len(got) != 1 || got[0] != "abc123" { - t.Fatalf("expected Tags[e]=[abc123], got %v", obj.SubscriptionFilter.Tags) + if got := obj.Tags["e"]; len(got) != 1 || got[0] != "abc123" { + t.Fatalf("expected Tags[e]=[abc123], got %v", obj.Tags) } }) @@ -393,8 +393,8 @@ kinds: [1] if err := yaml.Unmarshal([]byte(specYaml), &obj); err != nil { t.Fatalf("unexpected error: %v", err) } - if len(obj.SubscriptionFilter.Tags) != 0 { - t.Fatalf("expected dot-prefixed key to be ignored, got Tags=%v", obj.SubscriptionFilter.Tags) + if len(obj.Tags) != 0 { + t.Fatalf("expected dot-prefixed key to be ignored, got Tags=%v", obj.Tags) } }) @@ -410,8 +410,8 @@ kinds: [1] if err := yaml.Unmarshal([]byte(`since: "1w 2d"`), &obj); err != nil { t.Fatalf("unexpected error: %v", err) } - if obj.SubscriptionFilter.Since >= uint64(now.Unix()) { - t.Fatalf("expected since to be in the past, got=%d now=%d", obj.SubscriptionFilter.Since, now.Unix()) + if obj.Since >= uint64(now.Unix()) { + t.Fatalf("expected since to be in the past, got=%d now=%d", obj.Since, now.Unix()) } }) @@ -421,8 +421,8 @@ kinds: [1] if err := yaml.Unmarshal([]byte(`since: "-1w 2d"`), &obj); err != nil { t.Fatalf("unexpected error: %v", err) } - if obj.SubscriptionFilter.Since <= uint64(now.Unix()) { - t.Fatalf("expected since to be in the future, got=%d now=%d", obj.SubscriptionFilter.Since, now.Unix()) + if obj.Since <= uint64(now.Unix()) { + t.Fatalf("expected since to be in the future, got=%d now=%d", obj.Since, now.Unix()) } }) @@ -436,7 +436,7 @@ ids: ` var obj *FilterSpec if err := yaml.Unmarshal([]byte(specYaml), &obj); err == nil { - t.Fatalf("expected error unmarshalling unquoted numeric id, got none (ids=%v)", obj.SubscriptionFilter.IDs) + t.Fatalf("expected error unmarshalling unquoted numeric id, got none (ids=%v)", obj.IDs) } }) @@ -452,14 +452,14 @@ search: "lightning" if err := yaml.Unmarshal([]byte(specYaml), &obj); err != nil { t.Fatalf("unexpected error: %v", err) } - if obj.SubscriptionFilter.Limit != 50 { - t.Fatalf("unexpected limit: %d", obj.SubscriptionFilter.Limit) + if obj.Limit != 50 { + t.Fatalf("unexpected limit: %d", obj.Limit) } - if obj.SubscriptionFilter.Search != "lightning" { - t.Fatalf("unexpected search: %q", obj.SubscriptionFilter.Search) + if obj.Search != "lightning" { + t.Fatalf("unexpected search: %q", obj.Search) } - if got := obj.SubscriptionFilter.Tags["p"]; len(got) != 1 || got[0] != "deadbeef" { - t.Fatalf("unexpected tags: %v", obj.SubscriptionFilter.Tags) + if got := obj.Tags["p"]; len(got) != 1 || got[0] != "deadbeef" { + t.Fatalf("unexpected tags: %v", obj.Tags) } }) } diff --git a/client/stream.go b/client/stream.go index c7d4356..3ca15d5 100644 --- a/client/stream.go +++ b/client/stream.go @@ -23,9 +23,9 @@ import ( "github.com/ohstr/nmilat/nip01" "github.com/ohstr/nmilat/nip09" "github.com/ohstr/nmilat/nip11" - "github.com/ohstr/nmilat/wire" "github.com/ohstr/nmilat/relay" relayclient "github.com/ohstr/nmilat/relay/client" + "github.com/ohstr/nmilat/wire" "github.com/rs/zerolog/log" "golang.org/x/sync/errgroup" ) @@ -668,7 +668,7 @@ func (s *Stream) addFlow(way *map[int]ClientSubscription, specs []*FlowSpec) err if err != nil { return fmt.Errorf("failed to open flow file %s: %w", spec.Path, err) } - defer file.Close() + defer func() { _ = file.Close() }() scanner := bufio.NewScanner(file) firstLine := true diff --git a/client/stream_regression_test.go b/client/stream_regression_test.go index 4362cde..d6d8574 100644 --- a/client/stream_regression_test.go +++ b/client/stream_regression_test.go @@ -98,10 +98,7 @@ func TestStreamNoFalseUnexpectedUnderBacklog(t *testing.T) { deadline := time.Now().Add(15 * time.Second) for _, fc := range destFCs { - for { - if fc.stat.FlatRow()[1] >= numEvents { - break - } + for fc.stat.FlatRow()[1] < numEvents { if time.Now().After(deadline) { t.Fatalf("timed out waiting for destination %s to process all %d events (got %d)", fc.stat.GetAttributes().Name, numEvents, fc.stat.FlatRow()[1]) diff --git a/client/stream_test.go b/client/stream_test.go index 2ebcac0..c9583d1 100644 --- a/client/stream_test.go +++ b/client/stream_test.go @@ -31,7 +31,7 @@ func TestRelayLoad(t *testing.T) { dbPath := t.TempDir() + "/load_test_relay.db" limitation := &nip11.Limitation{ - MaxLimit: 100000, + MaxLimit: 100000, MaxSubscriptions: 500, // Increase max subs for load test } store, err := relay.NewEventStore(dbPath, limitation) @@ -53,7 +53,7 @@ func TestRelayLoad(t *testing.T) { t.Logf("server error: %v", err) } }() - defer server.Close() + defer func() { _ = server.Close() }() // Wait for server to start time.Sleep(200 * time.Millisecond) diff --git a/client/tui/eventdialog.go b/client/tui/eventdialog.go index cdac9e2..c029306 100644 --- a/client/tui/eventdialog.go +++ b/client/tui/eventdialog.go @@ -61,7 +61,7 @@ func (a *App) ShowEvent(event *nip01.Event, onSave func()) { // margin bug was, and was independently found (in cli/bunker, which // shares this exact dialog pattern) to bleed the exact same way under // a board's real concurrent redraw traffic. - fmt.Fprint(text, ColorizeEventJSON(event)) + _, _ = fmt.Fprint(text, ColorizeEventJSON(event)) text.ScrollToBeginning() form := tview.NewForm(). diff --git a/client/tui/eventdialog_test.go b/client/tui/eventdialog_test.go index 7212057..7085922 100644 --- a/client/tui/eventdialog_test.go +++ b/client/tui/eventdialog_test.go @@ -64,7 +64,7 @@ func TestColorizeEventJSONRendersCleanlyThroughTextView(t *testing.T) { colored := ColorizeEventJSON(event) tv := tview.NewTextView().SetDynamicColors(true) - fmt.Fprint(tv, colored) + _, _ = fmt.Fprint(tv, colored) rendered := tv.GetText(true) for _, want := range []string{ diff --git a/client/tui/eventtable.go b/client/tui/eventtable.go index 5ef62db..3c8ed25 100644 --- a/client/tui/eventtable.go +++ b/client/tui/eventtable.go @@ -70,7 +70,7 @@ func (t *EventTable) Init(ctx context.Context) *EventTable { SetBorder(true). SetBorderPadding(0, 1, 1, 1) - WireFocusBorder(t.Table, t.Table.Box) + WireFocusBorder(t.Table, t.Box) t.SetSelectedStyle(tcell.Style{}. Background(ColorPrimary). diff --git a/client/tui/footer.go b/client/tui/footer.go index 9bf736c..e30fa1d 100644 --- a/client/tui/footer.go +++ b/client/tui/footer.go @@ -9,7 +9,6 @@ import ( type Status struct { *tview.TextView - text string showPanelNav bool } @@ -29,11 +28,11 @@ func NewStatus(initText string, showPanelNav bool) *Status { func (s *Status) Update(text string) { s.Clear() if s.showPanelNav { - fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Next Panel \t", ColorAccent, ColorMuted) - fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Prev Panel \t", ColorAccent, ColorMuted) + _, _ = fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Next Panel \t", ColorAccent, ColorMuted) + _, _ = fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Prev Panel \t", ColorAccent, ColorMuted) } - fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Toggle Wrap \t", ColorAccent, ColorMuted) - fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Toggle AutoScroll", ColorAccent, ColorMuted) + _, _ = fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Toggle Wrap \t", ColorAccent, ColorMuted) + _, _ = fmt.Fprintf(s, "[%s:-:b] [%s:-:-]Toggle AutoScroll", ColorAccent, ColorMuted) } type Footer struct { @@ -74,7 +73,7 @@ func NewFooterWithHints(hints string) *Footer { status.SetDynamicColors(true). SetTextAlign(tview.AlignLeft). SetBorderPadding(0, 0, 1, 1) - fmt.Fprint(status, hints) + _, _ = fmt.Fprint(status, hints) f := &Footer{Flex: tview.NewFlex(), status: status} f.AddItem(f.status, 0, 1, false) @@ -87,5 +86,5 @@ func NewFooterWithHints(hints string) *Footer { // every focus change rather than fixed once at Layout.Init. func (f *Footer) SetHints(text string) { f.status.Clear() - fmt.Fprint(f.status, text) + _, _ = fmt.Fprint(f.status, text) } diff --git a/client/tui/header.go b/client/tui/header.go index 8ccbee0..6d297e3 100644 --- a/client/tui/header.go +++ b/client/tui/header.go @@ -67,10 +67,10 @@ func (h *Header) drawLogo() { h.Logo.SetDynamicColors(true) lines := strings.Split(LOGO, "\n") - fmt.Fprintf(h.Logo, "[%s]", ColorPrimary) + _, _ = fmt.Fprintf(h.Logo, "[%s]", ColorPrimary) for i := 1; i < len(lines)-1; i++ { - fmt.Fprintf(h.Logo, " [%s::b]%s", "", lines[i]) - fmt.Fprintf(h.Logo, "\n") + _, _ = fmt.Fprintf(h.Logo, " [%s::b]%s", "", lines[i]) + _, _ = fmt.Fprintf(h.Logo, "\n") } h.AddItem(h.Logo, 0, 1, false) diff --git a/client/tui/logger.go b/client/tui/logger.go index 1d44285..fb21790 100644 --- a/client/tui/logger.go +++ b/client/tui/logger.go @@ -39,7 +39,7 @@ func (t *Logger) Init(ctx context.Context) *Logger { t.SetBorder(true). SetBorderPadding(0, 0, 1, 1) - WireFocusBorder(t.logs, t.Flex.Box) + WireFocusBorder(t.logs, t.Box) t.actions.SetDynamicColors(true). SetWrap(false). @@ -97,7 +97,7 @@ func (t *Logger) UpdateActions() { autoColor, autoText := StatusText(t.autoscroll) wrapColor, wrapText := StatusText(t.wrap) - fmt.Fprintf(t.actions, "[%s::b]Autoscroll:[%s]%s \t [%s]Wrap:[%s]%s", ColorPrimary, autoColor, autoText, ColorPrimary, wrapColor, wrapText) + _, _ = fmt.Fprintf(t.actions, "[%s::b]Autoscroll:[%s]%s \t [%s]Wrap:[%s]%s", ColorPrimary, autoColor, autoText, ColorPrimary, wrapColor, wrapText) } func (t *Logger) Update() { @@ -108,7 +108,7 @@ func (t *Logger) Update() { logs := t.logger.GetLastLogs() for _, columns := range logs { - fmt.Fprintln(t.logs, strings.Join(columns, " ")) + _, _ = fmt.Fprintln(t.logs, strings.Join(columns, " ")) } if len(logs) > 0 && t.autoscroll { diff --git a/client/tui/logger_test.go b/client/tui/logger_test.go index ed5fcfa..ef52617 100644 --- a/client/tui/logger_test.go +++ b/client/tui/logger_test.go @@ -176,7 +176,7 @@ func TestLoggerWidget(t *testing.T) { app.Stop() }() - app.Run() + _ = app.Run() } // TestLoggerInputCaptureIsScopedToThisWidget guards against a regression diff --git a/client/tui/table.go b/client/tui/table.go index 0be88db..44bd0f5 100644 --- a/client/tui/table.go +++ b/client/tui/table.go @@ -70,7 +70,7 @@ func (t *Table) Init(ctx context.Context) *Table { SetBorder(true). SetBorderPadding(0, 1, 1, 1) - WireFocusBorder(t.Table, t.Table.Box) + WireFocusBorder(t.Table, t.Box) t.SetSelectedStyle(tcell.Style{}. Background(ColorPrimary). diff --git a/client/tui/table_test.go b/client/tui/table_test.go index a2f9251..e27cd73 100644 --- a/client/tui/table_test.go +++ b/client/tui/table_test.go @@ -42,10 +42,16 @@ func TestTable(t *testing.T) { app.Stop() }() - app.Run() + _ = app.Run() } +// _TestTableSort is intentionally not TestTableSort -- it has no app.Stop() +// call anywhere, so running it under `go test` would hang forever. Kept +// around underscore-prefixed as a manual visual check (like TestTable +// above, run it directly and watch the TUI) rather than deleted outright. +// +//nolint:unused func _TestTableSort(t *testing.T) { app := NewApp().Init() @@ -67,6 +73,6 @@ func _TestTableSort(t *testing.T) { app.Load(table) - app.Run() + _ = app.Run() }