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

@@ -93,7 +93,7 @@ func (s *SQLiteStorage) GetTier1Candidates(ctx context.Context) ([]*CompactionCa
if err != nil {
return nil, fmt.Errorf("failed to query tier1 candidates: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var candidates []*CompactionCandidate
for rows.Next() {
@@ -170,7 +170,7 @@ func (s *SQLiteStorage) GetTier2Candidates(ctx context.Context) ([]*CompactionCa
if err != nil {
return nil, fmt.Errorf("failed to query tier2 candidates: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var candidates []*CompactionCandidate
for rows.Next() {
@@ -271,7 +271,7 @@ func (s *SQLiteStorage) ApplyCompaction(ctx context.Context, issueID string, lev
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
var commitHashPtr *string
if commitHash != "" {

View File

@@ -72,7 +72,7 @@ func (s *SQLiteStorage) AddDependency(ctx context.Context, dep *types.Dependency
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
// Cycle Detection and Prevention
//
@@ -206,7 +206,7 @@ func (s *SQLiteStorage) addDependencyUnchecked(ctx context.Context, dep *types.D
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
// Cycle detection (same as AddDependency)
var cycleExists bool
@@ -277,7 +277,7 @@ func (s *SQLiteStorage) RemoveDependency(ctx context.Context, issueID, dependsOn
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
result, err := tx.ExecContext(ctx, `
DELETE FROM dependencies WHERE issue_id = ? AND depends_on_id = ?
@@ -319,7 +319,7 @@ func (s *SQLiteStorage) removeDependencyIfExists(ctx context.Context, issueID, d
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
result, err := tx.ExecContext(ctx, `
DELETE FROM dependencies WHERE issue_id = ? AND depends_on_id = ?
@@ -369,7 +369,7 @@ func (s *SQLiteStorage) GetDependencies(ctx context.Context, issueID string) ([]
if err != nil {
return nil, fmt.Errorf("failed to get dependencies: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
return s.scanIssues(ctx, rows)
}
@@ -388,7 +388,7 @@ func (s *SQLiteStorage) GetDependents(ctx context.Context, issueID string) ([]*t
if err != nil {
return nil, fmt.Errorf("failed to get dependents: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
return s.scanIssues(ctx, rows)
}
@@ -404,7 +404,7 @@ func (s *SQLiteStorage) GetDependencyRecords(ctx context.Context, issueID string
if err != nil {
return nil, fmt.Errorf("failed to get dependency records: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var deps []*types.Dependency
for rows.Next() {
@@ -436,7 +436,7 @@ func (s *SQLiteStorage) GetAllDependencyRecords(ctx context.Context) (map[string
if err != nil {
return nil, fmt.Errorf("failed to get all dependency records: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
// Group dependencies by issue ID
depsMap := make(map[string][]*types.Dependency)
@@ -508,7 +508,7 @@ func (s *SQLiteStorage) GetDependencyTree(ctx context.Context, issueID string, m
if err != nil {
return nil, fmt.Errorf("failed to get dependency tree: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
// Use a map to track nodes we've seen and deduplicate
// Key: issue ID, Value: minimum depth where we saw it
@@ -606,7 +606,7 @@ func (s *SQLiteStorage) DetectCycles(ctx context.Context) ([][]*types.Issue, err
if err != nil {
return nil, fmt.Errorf("failed to detect cycles: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var cycles [][]*types.Issue
seen := make(map[string]bool)

View File

@@ -30,7 +30,7 @@ func (s *SQLiteStorage) MarkIssuesDirty(ctx context.Context, issueIDs []string)
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
now := time.Now()
stmt, err := tx.PrepareContext(ctx, `
@@ -41,7 +41,7 @@ func (s *SQLiteStorage) MarkIssuesDirty(ctx context.Context, issueIDs []string)
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Close()
defer func() { _ = stmt.Close() }()
for _, issueID := range issueIDs {
if _, err := stmt.ExecContext(ctx, issueID, now); err != nil {
@@ -61,7 +61,7 @@ func (s *SQLiteStorage) GetDirtyIssues(ctx context.Context) ([]string, error) {
if err != nil {
return nil, fmt.Errorf("failed to get dirty issues: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var issueIDs []string
for rows.Next() {
@@ -99,13 +99,13 @@ func (s *SQLiteStorage) ClearDirtyIssuesByID(ctx context.Context, issueIDs []str
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
stmt, err := tx.PrepareContext(ctx, `DELETE FROM dirty_issues WHERE issue_id = ?`)
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Close()
defer func() { _ = stmt.Close() }()
for _, issueID := range issueIDs {
if _, err := stmt.ExecContext(ctx, issueID); err != nil {
@@ -142,7 +142,7 @@ func markIssuesDirtyTx(ctx context.Context, tx *sql.Tx, issueIDs []string) error
if err != nil {
return fmt.Errorf("failed to prepare dirty statement: %w", err)
}
defer stmt.Close()
defer func() { _ = stmt.Close() }()
for _, issueID := range issueIDs {
if _, err := stmt.ExecContext(ctx, issueID, now); err != nil {

View File

@@ -44,7 +44,7 @@ func (s *SQLiteStorage) GetEpicsEligibleForClosure(ctx context.Context) ([]*type
if err != nil {
return nil, err
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var results []*types.EpicStatus
for rows.Next() {

View File

@@ -17,7 +17,7 @@ func (s *SQLiteStorage) AddComment(ctx context.Context, issueID, actor, comment
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
_, err = tx.ExecContext(ctx, `
INSERT INTO events (issue_id, event_type, actor, comment)
@@ -70,7 +70,7 @@ func (s *SQLiteStorage) GetEvents(ctx context.Context, issueID string, limit int
if err != nil {
return nil, fmt.Errorf("failed to get events: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var events []*types.Event
for rows.Next() {

View File

@@ -22,7 +22,7 @@ func (s *SQLiteStorage) executeLabelOperation(
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
_, err = tx.ExecContext(ctx, labelSQL, labelSQLArgs...)
if err != nil {
@@ -82,7 +82,7 @@ func (s *SQLiteStorage) GetLabels(ctx context.Context, issueID string) ([]string
if err != nil {
return nil, fmt.Errorf("failed to get labels: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var labels []string
for rows.Next() {
@@ -110,7 +110,7 @@ func (s *SQLiteStorage) GetIssuesByLabel(ctx context.Context, label string) ([]*
if err != nil {
return nil, fmt.Errorf("failed to get issues by label: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
return s.scanIssues(ctx, rows)
}

View File

@@ -107,7 +107,7 @@ func (s *SQLiteStorage) GetReadyWork(ctx context.Context, filter types.WorkFilte
if err != nil {
return nil, fmt.Errorf("failed to get ready work: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
return s.scanIssues(ctx, rows)
}
@@ -134,7 +134,7 @@ func (s *SQLiteStorage) GetBlockedIssues(ctx context.Context) ([]*types.BlockedI
if err != nil {
return nil, fmt.Errorf("failed to get blocked issues: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var blocked []*types.BlockedIssue
for rows.Next() {

View File

@@ -232,7 +232,7 @@ func migrateExternalRefColumn(db *sql.DB) error {
if err != nil {
return fmt.Errorf("failed to check schema: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
for rows.Next() {
var cid int
@@ -554,7 +554,7 @@ func (s *SQLiteStorage) CreateIssue(ctx context.Context, issue *types.Issue, act
if err != nil {
return fmt.Errorf("failed to acquire connection: %w", err)
}
defer conn.Close()
defer func() { _ = conn.Close() }()
// Start IMMEDIATE transaction to acquire write lock early and prevent race conditions.
// IMMEDIATE acquires a RESERVED lock immediately, preventing other IMMEDIATE or EXCLUSIVE
@@ -767,7 +767,7 @@ func bulkInsertIssues(ctx context.Context, conn *sql.Conn, issues []*types.Issue
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Close()
defer func() { _ = stmt.Close() }()
for _, issue := range issues {
_, err = stmt.ExecContext(ctx,
@@ -793,7 +793,7 @@ func bulkRecordEvents(ctx context.Context, conn *sql.Conn, issues []*types.Issue
if err != nil {
return fmt.Errorf("failed to prepare event statement: %w", err)
}
defer stmt.Close()
defer func() { _ = stmt.Close() }()
for _, issue := range issues {
eventData, err := json.Marshal(issue)
@@ -820,7 +820,7 @@ func bulkMarkDirty(ctx context.Context, conn *sql.Conn, issues []*types.Issue) e
if err != nil {
return fmt.Errorf("failed to prepare dirty statement: %w", err)
}
defer stmt.Close()
defer func() { _ = stmt.Close() }()
dirtyTime := time.Now()
for _, issue := range issues {
@@ -897,7 +897,7 @@ func (s *SQLiteStorage) CreateIssues(ctx context.Context, issues []*types.Issue,
if err != nil {
return fmt.Errorf("failed to acquire connection: %w", err)
}
defer conn.Close()
defer func() { _ = conn.Close() }()
if _, err := conn.ExecContext(ctx, "BEGIN IMMEDIATE"); err != nil {
return fmt.Errorf("failed to begin immediate transaction: %w", err)
@@ -1177,7 +1177,7 @@ func (s *SQLiteStorage) UpdateIssue(ctx context.Context, id string, updates map[
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
// Update issue
query := fmt.Sprintf("UPDATE issues SET %s WHERE id = ?", strings.Join(setClauses, ", "))
@@ -1230,7 +1230,7 @@ func (s *SQLiteStorage) UpdateIssueID(ctx context.Context, oldID, newID string,
if err != nil {
return fmt.Errorf("failed to get connection: %w", err)
}
defer conn.Close()
defer func() { _ = conn.Close() }()
// Disable foreign keys on this specific connection
_, err = conn.ExecContext(ctx, `PRAGMA foreign_keys = OFF`)
@@ -1242,7 +1242,7 @@ func (s *SQLiteStorage) UpdateIssueID(ctx context.Context, oldID, newID string,
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
_, err = tx.ExecContext(ctx, `
UPDATE issues
@@ -1326,7 +1326,7 @@ func (s *SQLiteStorage) RenameCounterPrefix(ctx context.Context, oldPrefix, newP
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
var lastID int
err = tx.QueryRowContext(ctx, `SELECT last_id FROM issue_counters WHERE prefix = ?`, oldPrefix).Scan(&lastID)
@@ -1370,7 +1370,7 @@ func (s *SQLiteStorage) CloseIssue(ctx context.Context, id string, reason string
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
_, err = tx.ExecContext(ctx, `
UPDATE issues SET status = ?, closed_at = ?, updated_at = ?
@@ -1407,7 +1407,7 @@ func (s *SQLiteStorage) DeleteIssue(ctx context.Context, id string) error {
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
defer func() { _ = tx.Rollback() }()
// Delete dependencies (both directions)
_, err = tx.ExecContext(ctx, `DELETE FROM dependencies WHERE issue_id = ? OR depends_on_id = ?`, id, id)
@@ -1561,7 +1561,7 @@ func (s *SQLiteStorage) checkSingleIssueValidation(ctx context.Context, tx *sql.
if err != nil {
return fmt.Errorf("failed to get dependents for %s: %w", id, err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
hasExternal := false
for rows.Next() {
@@ -1604,7 +1604,7 @@ func (s *SQLiteStorage) collectOrphansForID(ctx context.Context, tx *sql.Tx, id
if err != nil {
return fmt.Errorf("failed to get dependents for %s: %w", id, err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
for rows.Next() {
var depID string
@@ -1811,7 +1811,7 @@ func (s *SQLiteStorage) SearchIssues(ctx context.Context, query string, filter t
if err != nil {
return nil, fmt.Errorf("failed to search issues: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
return s.scanIssues(ctx, rows)
}
@@ -1841,7 +1841,7 @@ func (s *SQLiteStorage) GetAllConfig(ctx context.Context) (map[string]string, er
if err != nil {
return nil, err
}
defer rows.Close()
defer func() { _ = rows.Close() }()
config := make(map[string]string)
for rows.Next() {
@@ -1935,7 +1935,7 @@ func (s *SQLiteStorage) GetIssueComments(ctx context.Context, issueID string) ([
if err != nil {
return nil, fmt.Errorf("failed to query comments: %w", err)
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var comments []*types.Comment
for rows.Next() {