Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 61 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ func visibleLength(s string) int {
if runes[i] == '\033' && i+1 < len(runes) && runes[i+1] == '[' {
// Skip CSI sequence: ESC [ ... (terminated by a letter)
i += 2
for i < len(runes) && !((runes[i] >= 'A' && runes[i] <= 'Z') || (runes[i] >= 'a' && runes[i] <= 'z')) {
for i < len(runes) && (runes[i] < 'A' || runes[i] > 'Z') && (runes[i] < 'a' || runes[i] > 'z') {
i++
}
i++ // Skip the terminating letter
Expand Down Expand Up @@ -933,12 +933,16 @@ func InitDB(dbDir, organization string, progress ProgressInterface) (*DB, error)
}

// Check if database file exists and if schema version matches
var needsRecreation bool = true
needsRecreation := true
if _, err := os.Stat(dbPath); err == nil {
// Database exists, check schema version
tempDB, err := sql.Open("sqlite3", dbPath)
if err == nil {
defer tempDB.Close()
defer func() {
if closeErr := tempDB.Close(); closeErr != nil {
slog.Warn("Failed to close temp database", "error", closeErr)
}
}()
schemaMatches, checkErr := checkSchemaVersion(tempDB, progress)
if checkErr != nil {
if progress != nil {
Expand Down Expand Up @@ -1061,7 +1065,11 @@ func (db *DB) PopulateSearchTable(currentUsername string, progress ProgressInter
if err != nil {
slog.Warn("Failed to query user repositories, proceeding with boost=1.0 for all", "error", err)
} else {
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()
for rows.Next() {
var repo string
if err := rows.Scan(&repo); err == nil {
Expand Down Expand Up @@ -1287,7 +1295,11 @@ func (db *DB) GetRepositories() ([]Repository, error) {
if err != nil {
return nil, fmt.Errorf("failed to query repositories: %w", err)
}
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()

var repositories []Repository
for rows.Next() {
Expand Down Expand Up @@ -1443,7 +1455,11 @@ func (db *DB) GetDiscussions(repository string, fromDate time.Time, toDate time.
if err != nil {
return nil, fmt.Errorf("failed to query discussions: %w", err)
}
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()

var discussions []Discussion
for rows.Next() {
Expand Down Expand Up @@ -1501,7 +1517,11 @@ func (db *DB) GetIssues(repository string, createdFromDate time.Time, createdToD
if err != nil {
return nil, fmt.Errorf("failed to query issues: %w", err)
}
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()

var issues []Issue
for rows.Next() {
Expand Down Expand Up @@ -1566,7 +1586,11 @@ func (db *DB) GetPullRequests(repository string, createdFromDate time.Time, crea
if err != nil {
return nil, fmt.Errorf("failed to query pull requests: %w", err)
}
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()

var pullRequests []PullRequest
for rows.Next() {
Expand Down Expand Up @@ -3337,7 +3361,11 @@ func (db *DB) GetDiscussionsByRepository(repositoryName string) ([]Discussion, e
if err != nil {
return nil, fmt.Errorf("failed to query discussions: %w", err)
}
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()

var discussions []Discussion
for rows.Next() {
Expand Down Expand Up @@ -3506,7 +3534,11 @@ func (se *SearchEngine) searchAllTables(tokens []string, limit int) ([]SearchRes
slog.Error("FTS search query failed", "sql", query, "search_table", "search", "fts_query", ftsQuery, "error", err)
return nil, fmt.Errorf("FTS search failed: %w", err)
}
defer rows.Close()
defer func() {
if closeErr := rows.Close(); closeErr != nil {
slog.Warn("Failed to close rows", "error", closeErr)
}
}()

var results []SearchResult
for rows.Next() {
Expand Down Expand Up @@ -3838,7 +3870,7 @@ func ListPullRequestsTool(db *DB) func(context.Context, *mcp.CallToolRequest, Li
}
}
if !found {
return nil, nil, fmt.Errorf("Invalid fields: %s\n\nUse one of the available fields: %s", field, strings.Join(validFields, ", "))
return nil, nil, fmt.Errorf("invalid fields: %s\n\nUse one of the available fields: %s", field, strings.Join(validFields, ", "))
}
}
}
Expand Down Expand Up @@ -4358,7 +4390,11 @@ func main() {
logErrorAndReturn(progress, "Error: Failed to initialize database: %v", err)
return
}
defer db.Close()
defer func() {
if closeErr := db.Close(); closeErr != nil {
slog.Error("Failed to close database", "error", closeErr)
}
}()

// Acquire lock to prevent concurrent pull operations
if err := db.LockPull(); err != nil {
Expand Down Expand Up @@ -4539,7 +4575,11 @@ func main() {
slog.Error("Failed to initialize database", "error", err)
os.Exit(1)
}
defer db.Close()
defer func() {
if closeErr := db.Close(); closeErr != nil {
slog.Error("Failed to close database", "error", closeErr)
}
}()

if err := RunMCPServer(db); err != nil {
slog.Error("MCP server error", "error", err)
Expand Down Expand Up @@ -5498,7 +5538,11 @@ func requestDeviceCode() (*DeviceCodeResponse, error) {
if err != nil {
return nil, err
}
defer resp.Body.Close()
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
slog.Warn("Failed to close response body", "error", closeErr)
}
}()

body, err := io.ReadAll(resp.Body)
if err != nil {
Expand Down Expand Up @@ -5547,7 +5591,9 @@ func pollForAccessToken(deviceCode *DeviceCodeResponse) (accessToken string, err
}

body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if closeErr := resp.Body.Close(); closeErr != nil {
slog.Warn("Failed to close response body", "error", closeErr)
}
if err != nil {
continue
}
Expand Down
20 changes: 20 additions & 0 deletions main.md
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,26 @@ Download the appropriate archive for your platform from [releases](https://githu
curl -L https://github.com/wham/github-brain/releases/download/v1.2.3/github-brain-darwin-arm64.tar.gz | tar xz
```

## Code Quality

### Linting

Use **golangci-lint** with default configuration for code quality checks.

**Running the linter:**
```bash
# Standalone
golangci-lint run --timeout=5m

# Integrated with build (via scripts/run)
./scripts/run [command]
```

**CI Integration:**
- Linting runs automatically on all PRs via `.github/workflows/build.yml`
- Build fails if linter finds issues (blocking)
- In local development (`scripts/run`), linting runs but is non-blocking to allow rapid iteration

### Release Model

Coded in `.github/workflow/release.yml` and `.github/workflow/build.yml`.
Expand Down
10 changes: 9 additions & 1 deletion scripts/run
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@
set -e
cd "$(dirname "$0")/.."

# Lint the code (non-blocking in development)
echo "Running linter..."
if command -v golangci-lint &> /dev/null; then
golangci-lint run --timeout=5m || echo "Warning: Linter found issues (non-blocking in development)"
else
echo "Warning: golangci-lint not found, skipping linting"
fi

# Build with FTS5 support enabled
CGO_CFLAGS="-DSQLITE_ENABLE_FTS5" go build -gcflags="all=-N -l" -o ./build/github-brain .
CGO_ENABLED=1 CGO_CFLAGS="-DSQLITE_ENABLE_FTS5" CGO_LDFLAGS="-lm" go build -gcflags="all=-N -l" -o ./build/github-brain .
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of CGO_ENABLED=1 and CGO_LDFLAGS flags appears unrelated to the linting changes described in the PR. This build configuration change should either be mentioned in the PR description or separated into a different PR for clarity.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CGO flags were added to align scripts/run with the CI build configuration (see .github/workflows/build.yml:29). Without CGO_LDFLAGS="-lm", the local build fails with undefined reference to log after linting. This ensures developers can run the full lint+build workflow locally matching CI behavior.


# Set home directory to checkout directory
CHECKOUT_DIR="$(pwd)"
Expand Down