Enable errcheck linter and fix all production code warnings

- Enabled errcheck linter (previously disabled)
- Set tests: false in .golangci.yml to focus on production code
- Fixed 27 errcheck warnings using Go best practices:
  * Database resources: defer func() { _ = rows.Close() }()
  * Transaction rollbacks: defer func() { _ = tx.Rollback() }()
  * Best-effort closers: _ = store.Close(), _ = client.Close()
  * File writes: proper error checking on Close()
  * Interactive input: handle EOF gracefully
  * File ops: ignore ENOENT on os.Remove()
- All tests pass
- Closes bd-58

Amp-Thread-ID: https://ampcode.com/threads/T-57c9afd3-9adf-40c2-8be7-3e493d200361
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Steve Yegge
2025-10-25 18:44:38 -07:00
parent bb33007036
commit b855c444d4
22 changed files with 100 additions and 78 deletions

View File

@@ -606,7 +606,7 @@ func readIssueIDsFromFile(filename string) ([]string, error) {
if err != nil {
return nil, err
}
defer f.Close()
defer func() { _ = f.Close() }()
var ids []string
scanner := bufio.NewScanner(f)

View File

@@ -174,7 +174,7 @@ var depTreeCmd = &cobra.Command{
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
}
defer store.Close()
defer func() { _ = store.Close() }()
}
showAllPaths, _ := cmd.Flags().GetBool("show-all-paths")
@@ -245,7 +245,7 @@ var depCyclesCmd = &cobra.Command{
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
}
defer store.Close()
defer func() { _ = store.Close() }()
}
ctx := context.Background()

View File

@@ -20,7 +20,11 @@ func countIssuesInJSONL(path string) (int, error) {
if err != nil {
return 0, err
}
defer file.Close()
defer func() {
if err := file.Close(); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to close file: %v\n", err)
}
}()
count := 0
decoder := json.NewDecoder(file)
@@ -100,13 +104,13 @@ Output to stdout by default, or use -o flag for file output.`,
}
store, err = sqlite.New(dbPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
}
defer func() { _ = store.Close() }()
}
defer store.Close()
}
// Build filter
// Build filter
filter := types.IssueFilter{}
if statusFilter != "" {
status := types.Status(statusFilter)
@@ -153,7 +157,7 @@ Output to stdout by default, or use -o flag for file output.`,
fmt.Fprintf(os.Stderr, "Press Ctrl+C to abort, or Enter to continue: ")
// Read a line from stdin to wait for user confirmation
var response string
fmt.Scanln(&response)
_, _ = fmt.Scanln(&response) // ignore EOF on empty input
}
}
}

View File

@@ -178,7 +178,7 @@ if quiet {
// Prompt to install
fmt.Printf("Install git hooks now? [Y/n] ")
var response string
fmt.Scanln(&response)
_, _ = fmt.Scanln(&response) // ignore EOF on empty input
response = strings.ToLower(strings.TrimSpace(response))
if response == "" || response == "y" || response == "yes" {

View File

@@ -587,7 +587,7 @@ func restartDaemonForVersionMismatch() bool {
cmd.Stdin = devNull
cmd.Stdout = devNull
cmd.Stderr = devNull
defer devNull.Close()
defer func() { _ = devNull.Close() }()
}
if err := cmd.Start(); err != nil {
@@ -637,7 +637,11 @@ func tryAutoStartDaemon(socketPath string) bool {
if !acquireStartLock(lockPath, socketPath) {
return false
}
defer os.Remove(lockPath)
defer func() {
if err := os.Remove(lockPath); err != nil && !os.IsNotExist(err) {
debugLog("failed to remove lock file: %v", err)
}
}()
if handleExistingSocket(socketPath) {
return true
@@ -777,7 +781,7 @@ func setupDaemonIO(cmd *exec.Cmd) {
cmd.Stdin = devNull
go func() {
time.Sleep(1 * time.Second)
devNull.Close()
_ = devNull.Close()
}()
}
}

View File

@@ -148,13 +148,13 @@ var blockedCmd = &cobra.Command{
var err error
store, err = sqlite.New(dbPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
}
defer func() { _ = store.Close() }()
}
defer store.Close()
}
ctx := context.Background()
ctx := context.Background()
blocked, err := store.GetBlockedIssues(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

View File

@@ -55,13 +55,13 @@ Risks:
}
store, err = sqlite.New(dbPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
os.Exit(1)
}
defer func() { _ = store.Close() }()
}
defer store.Close()
}
ctx := context.Background()
ctx := context.Background()
// Get prefix from config, or derive from first issue if not set
prefix, err := store.GetConfig(ctx, "issue_prefix")

View File

@@ -158,7 +158,7 @@ func readIssueFromJSONL(jsonlPath, issueID string) (*types.Issue, error) {
if err != nil {
return nil, fmt.Errorf("failed to open JSONL: %w", err)
}
defer file.Close()
defer func() { _ = file.Close() }()
scanner := bufio.NewScanner(file)
// Increase buffer size for large issues

View File

@@ -153,7 +153,7 @@ func getStaleIssues(thresholdSeconds int) ([]*StaleIssueInfo, error) {
if err != nil {
return nil, fmt.Errorf("failed to query stale issues: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var staleIssues []*StaleIssueInfo
for rows.Next() {
@@ -221,7 +221,7 @@ func releaseStaleIssues(staleIssues []*StaleIssueInfo) (int, error) {
if err != nil {
return 0, fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
releaseCount := 0
now := time.Now()

View File

@@ -55,7 +55,7 @@ func showDaemonVersion() {
fmt.Fprintf(os.Stderr, "Hint: start daemon with 'bd daemon'\n")
os.Exit(1)
}
defer client.Close()
defer func() { _ = client.Close() }()
health, err := client.Health()
if err != nil {