Skip to content

Commit 7bde5b6

Browse files
feat: lite build mode and go-test filter rewrite
- Add `-tags lite` build variant (5.2MB, 3.1ms startup) that excludes SQLite for environments where tracking is unnecessary - Rewrite go-test filter to produce compact summary ("169 passed, 0 failed") instead of verbose per-package event counts — 97% savings - Defer SQLite initialization with lazy tracker and concurrent WarmUp() to overlap DB init with command execution - Eliminate redundant config.Load() call in filter pipeline - Add build-lite / install-lite Makefile targets
1 parent 76c99ac commit 7bde5b6

File tree

10 files changed

+126
-44
lines changed

10 files changed

+126
-44
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build test test-race lint install clean
1+
.PHONY: build build-lite test test-race lint install install-lite clean
22

33
BINARY=snip
44
BUILD_DIR=cmd/snip
@@ -7,6 +7,9 @@ LDFLAGS=-ldflags="-s -w"
77
build:
88
CGO_ENABLED=0 go build -o $(BINARY) $(LDFLAGS) ./$(BUILD_DIR)
99

10+
build-lite:
11+
CGO_ENABLED=0 go build -tags lite -o $(BINARY) $(LDFLAGS) ./$(BUILD_DIR)
12+
1013
test:
1114
go test -cover ./...
1215

@@ -20,6 +23,9 @@ lint:
2023
install: build
2124
cp $(BINARY) $(GOPATH)/bin/$(BINARY) 2>/dev/null || cp $(BINARY) /usr/local/bin/$(BINARY)
2225

26+
install-lite: build-lite
27+
cp $(BINARY) $(GOPATH)/bin/$(BINARY) 2>/dev/null || cp $(BINARY) /usr/local/bin/$(BINARY)
28+
2329
clean:
2430
rm -f $(BINARY)
2531
go clean -testcache

filters/go-test.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ pipeline:
1515
- action: "keep_lines"
1616
pattern: "\\S"
1717
- action: "keep_lines"
18-
pattern: "\"Action\":\"(pass|fail|skip|output)\""
19-
- action: "remove_lines"
20-
pattern: "\"Output\":\"=== RUN"
21-
- action: "ndjson_stream"
22-
group_by: "Package"
23-
format: "{{.Key}}: {{.Count}} events"
24-
- action: "format_template"
25-
template: "{{.count}} packages:\n{{.lines}}"
18+
pattern: "\"Test\":\""
19+
- action: "keep_lines"
20+
pattern: "\"Action\":\"(pass|fail)\""
21+
- action: "aggregate"
22+
patterns:
23+
passed: '"Action":"pass"'
24+
failed: '"Action":"fail"'
25+
format: "{{if and (eq .passed 0) (eq .failed 0)}}No tests found{{else}}{{.passed}} passed, {{.failed}} failed{{end}}"
2626

2727
on_error: "passthrough"

internal/cli/cli.go

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,21 @@ func Run(args []string) int {
4747
return 0
4848

4949
case "gain":
50-
tracker, err := lazyTracker()
50+
if !tracking.DriverAvailable {
51+
display.PrintError("gain requires full build (this binary was built with -tags lite)")
52+
return 1
53+
}
54+
cfg, cfgErr := config.Load()
55+
if cfgErr != nil {
56+
cfg = config.DefaultConfig()
57+
}
58+
dbPath := tracking.DBPath(cfg.Tracking.DBPath)
59+
tracker, err := tracking.NewTracker(dbPath)
5160
if err != nil {
5261
display.PrintError(err.Error())
5362
return 1
5463
}
55-
if tracker != nil {
56-
defer func() { _ = tracker.Close() }()
57-
}
64+
defer func() { _ = tracker.Close() }()
5865
if err := display.RunGain(tracker, cmdArgs); err != nil {
5966
display.PrintError(err.Error())
6067
return 1
@@ -106,11 +113,11 @@ func runPipeline(command string, args []string, flags Flags) int {
106113

107114
registry := filter.NewRegistry(filters)
108115

109-
tracker, err := lazyTracker()
110-
if err != nil && flags.Verbose > 0 {
111-
fmt.Fprintf(os.Stderr, "snip: tracking disabled: %v\n", err)
112-
}
113-
if tracker != nil {
116+
// Lazy tracker: DB opens on first use (concurrently with command execution)
117+
var tracker *tracking.Tracker
118+
if tracking.DriverAvailable {
119+
dbPath := tracking.DBPath(cfg.Tracking.DBPath)
120+
tracker = tracking.NewLazyTracker(dbPath)
114121
defer func() { _ = tracker.Close() }()
115122
}
116123

@@ -131,15 +138,6 @@ func runPipeline(command string, args []string, flags Flags) int {
131138
return pipeline.Run(command, args)
132139
}
133140

134-
func lazyTracker() (*tracking.Tracker, error) {
135-
cfg, _ := config.Load()
136-
dbPath := tracking.DBPath("")
137-
if cfg != nil {
138-
dbPath = tracking.DBPath(cfg.Tracking.DBPath)
139-
}
140-
return tracking.NewTracker(dbPath)
141-
}
142-
143141
func printUsage() {
144142
usage := `snip v%s — CLI Token Killer
145143

internal/display/gain_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !lite
2+
13
package display
24

35
import (

internal/engine/pipeline.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ func (p *Pipeline) Run(command string, args []string) int {
4545
finalArgs = injected
4646
}
4747

48+
// Start SQLite init concurrently with command execution
49+
if p.Tracker != nil {
50+
p.Tracker.WarmUp()
51+
}
52+
4853
// Start timing
4954
timed := tracking.Start(p.Tracker)
5055

internal/tracking/driver.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build !lite
2+
3+
package tracking
4+
5+
import _ "modernc.org/sqlite"
6+
7+
// DriverAvailable indicates whether the SQLite driver is compiled in.
8+
const DriverAvailable = true

internal/tracking/driver_lite.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build lite
2+
3+
package tracking
4+
5+
// DriverAvailable indicates whether the SQLite driver is compiled in.
6+
// Lite builds exclude SQLite for faster startup and smaller binary.
7+
const DriverAvailable = false

internal/tracking/timed_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !lite
2+
13
package tracking
24

35
import (

internal/tracking/tracker.go

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,68 @@ import (
55
"fmt"
66
"os"
77
"path/filepath"
8-
9-
_ "modernc.org/sqlite"
8+
"sync"
109
)
1110

1211
// Tracker manages token savings tracking in SQLite.
1312
type Tracker struct {
14-
db *sql.DB
13+
db *sql.DB
14+
dbPath string
15+
once sync.Once
16+
initErr error
1517
}
1618

17-
// NewTracker opens or creates a SQLite database for tracking.
19+
// NewTracker opens or creates a SQLite database for tracking (immediate open).
1820
func NewTracker(dbPath string) (*Tracker, error) {
19-
dir := filepath.Dir(dbPath)
20-
if err := os.MkdirAll(dir, 0755); err != nil {
21-
return nil, fmt.Errorf("create db dir: %w", err)
21+
t := &Tracker{dbPath: dbPath}
22+
if err := t.ensureOpen(); err != nil {
23+
return nil, err
2224
}
25+
return t, nil
26+
}
2327

24-
db, err := sql.Open("sqlite", dbPath)
25-
if err != nil {
26-
return nil, fmt.Errorf("open db: %w", err)
27-
}
28+
// NewLazyTracker creates a tracker that defers DB opening until first use.
29+
func NewLazyTracker(dbPath string) *Tracker {
30+
return &Tracker{dbPath: dbPath}
31+
}
2832

29-
if _, err := db.Exec(createTableSQL); err != nil {
30-
_ = db.Close()
31-
return nil, fmt.Errorf("create table: %w", err)
32-
}
33+
// WarmUp starts opening the DB in the background.
34+
// Call this before command execution so SQLite init overlaps with the command.
35+
func (t *Tracker) WarmUp() {
36+
go func() { _ = t.ensureOpen() }()
37+
}
3338

34-
return &Tracker{db: db}, nil
39+
func (t *Tracker) ensureOpen() error {
40+
t.once.Do(func() {
41+
dir := filepath.Dir(t.dbPath)
42+
if err := os.MkdirAll(dir, 0755); err != nil {
43+
t.initErr = fmt.Errorf("create db dir: %w", err)
44+
return
45+
}
46+
47+
db, err := sql.Open("sqlite", t.dbPath)
48+
if err != nil {
49+
t.initErr = fmt.Errorf("open db: %w", err)
50+
return
51+
}
52+
53+
if _, err := db.Exec(createTableSQL); err != nil {
54+
_ = db.Close()
55+
t.initErr = fmt.Errorf("create table: %w", err)
56+
return
57+
}
58+
59+
t.db = db
60+
})
61+
return t.initErr
3562
}
3663

3764
// Track records a filtered command execution.
3865
func (t *Tracker) Track(originalCmd, snipCmd string, inputTokens, outputTokens int, execTimeMs int64) error {
66+
if err := t.ensureOpen(); err != nil {
67+
return fmt.Errorf("track: %w", err)
68+
}
69+
3970
saved := inputTokens - outputTokens
4071
pct := 0.0
4172
if inputTokens > 0 {
@@ -59,6 +90,9 @@ func (t *Tracker) TrackPassthrough(cmd string, tokens int, execTimeMs int64) err
5990

6091
// GetSummary returns aggregate tracking stats.
6192
func (t *Tracker) GetSummary() (*Summary, error) {
93+
if err := t.ensureOpen(); err != nil {
94+
return nil, fmt.Errorf("summary: %w", err)
95+
}
6296
var s Summary
6397
err := t.db.QueryRow(summarySQL).Scan(&s.TotalCommands, &s.TotalSaved, &s.AvgSavings, &s.TotalTimeMs)
6498
if err != nil {
@@ -69,6 +103,9 @@ func (t *Tracker) GetSummary() (*Summary, error) {
69103

70104
// GetDaily returns daily stats for the last N days.
71105
func (t *Tracker) GetDaily(days int) ([]DayStats, error) {
106+
if err := t.ensureOpen(); err != nil {
107+
return nil, fmt.Errorf("daily: %w", err)
108+
}
72109
if days <= 0 {
73110
days = 7
74111
}
@@ -91,6 +128,9 @@ func (t *Tracker) GetDaily(days int) ([]DayStats, error) {
91128

92129
// GetRecent returns the last N tracked commands.
93130
func (t *Tracker) GetRecent(n int) ([]CommandRecord, error) {
131+
if err := t.ensureOpen(); err != nil {
132+
return nil, fmt.Errorf("recent: %w", err)
133+
}
94134
rows, err := t.db.Query(recentSQL, n)
95135
if err != nil {
96136
return nil, fmt.Errorf("recent: %w", err)
@@ -110,6 +150,9 @@ func (t *Tracker) GetRecent(n int) ([]CommandRecord, error) {
110150

111151
// GetByCommand returns top N commands by tokens saved.
112152
func (t *Tracker) GetByCommand(limit int) ([]CommandStats, error) {
153+
if err := t.ensureOpen(); err != nil {
154+
return nil, fmt.Errorf("by command: %w", err)
155+
}
113156
if limit <= 0 {
114157
limit = 10
115158
}
@@ -132,6 +175,9 @@ func (t *Tracker) GetByCommand(limit int) ([]CommandStats, error) {
132175

133176
// GetWeekly returns weekly stats for the last N weeks.
134177
func (t *Tracker) GetWeekly(weeks int) ([]PeriodStats, error) {
178+
if err := t.ensureOpen(); err != nil {
179+
return nil, fmt.Errorf("weekly: %w", err)
180+
}
135181
if weeks <= 0 {
136182
weeks = 4
137183
}
@@ -155,6 +201,9 @@ func (t *Tracker) GetWeekly(weeks int) ([]PeriodStats, error) {
155201

156202
// GetMonthly returns monthly stats for the last N months.
157203
func (t *Tracker) GetMonthly(months int) ([]PeriodStats, error) {
204+
if err := t.ensureOpen(); err != nil {
205+
return nil, fmt.Errorf("monthly: %w", err)
206+
}
158207
if months <= 0 {
159208
months = 6
160209
}
@@ -178,7 +227,10 @@ func (t *Tracker) GetMonthly(months int) ([]PeriodStats, error) {
178227

179228
// Close closes the database connection.
180229
func (t *Tracker) Close() error {
181-
return t.db.Close()
230+
if t.db != nil {
231+
return t.db.Close()
232+
}
233+
return nil
182234
}
183235

184236
// DBPath resolves the tracking database path.

internal/tracking/tracker_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !lite
2+
13
package tracking
24

35
import (

0 commit comments

Comments
 (0)