Files
orca/internal/store/migrate.go
T
Jon Chery 9580f347c6 feat(P02): node management with SQLite-backed registry
Implements Phase 2 of v0.1 Foundation:
- internal/model/node.go: Node struct with state machine (pending/ready/left)
- internal/store/store.go: SQLite open with WAL + foreign_keys pragmas
- internal/store/migrate.go: embedded SQL migration runner
- internal/store/migrations/0001_nodes.sql: nodes table schema
- internal/store/node_repo.go: CRUD operations for nodes
- internal/store/node_repo_test.go: 4 tests covering insert/get/list/update/delete
- internal/engine/registry.go: in-memory wrapper with slog audit logging
- internal/cli/node.go: orca node {join,leave,list} wired to registry

Verified: node join/list/leave work end-to-end, JSON output, slog audit logs,
state persists in SQLite, all tests pass with -race.

---ci---
project: orca
phase: 2
milestone: v0.1
status: execute
req_covered:
  - REQ-002
  - REQ-005
  - REQ-008
  - REQ-012
  - REQ-017
  - REQ-018
---/ci---
2026-06-03 12:38:46 +00:00

54 lines
1.5 KiB
Go

package store
import (
"context"
"database/sql"
"embed"
"fmt"
"sort"
"strings"
)
//go:embed migrations/*.sql
var migrationsFS embed.FS
func migrate(db *sql.DB) error {
entries, err := migrationsFS.ReadDir("migrations")
if err != nil {
return fmt.Errorf("read migrations dir: %w", err)
}
names := make([]string, 0, len(entries))
for _, e := range entries {
if !e.IsDir() && strings.HasSuffix(e.Name(), ".sql") {
names = append(names, e.Name())
}
}
sort.Strings(names)
if _, err := db.ExecContext(context.Background(), `CREATE TABLE IF NOT EXISTS schema_migrations (name TEXT PRIMARY KEY, applied_at DATETIME NOT NULL)`); err != nil {
return fmt.Errorf("create schema_migrations: %w", err)
}
for _, name := range names {
var existing string
err := db.QueryRowContext(context.Background(), `SELECT name FROM schema_migrations WHERE name = ?`, name).Scan(&existing)
if err == nil {
continue
}
if err != sql.ErrNoRows {
return fmt.Errorf("check migration %s: %w", name, err)
}
sqlBytes, err := migrationsFS.ReadFile("migrations/" + name)
if err != nil {
return fmt.Errorf("read migration %s: %w", name, err)
}
if _, err := db.ExecContext(context.Background(), string(sqlBytes)); err != nil {
return fmt.Errorf("apply migration %s: %w", name, err)
}
if _, err := db.ExecContext(context.Background(), `INSERT INTO schema_migrations (name, applied_at) VALUES (?, datetime('now'))`, name); err != nil {
return fmt.Errorf("record migration %s: %w", name, err)
}
}
return nil
}