fix(dolt): enable server mode for Gas Town integration (bd-nnjik)

- Skip Bootstrap when ServerMode is true (bootstrap is for embedded cold-start)
- Fix doltExists() to follow symlinks using os.Stat
- Pass database name from metadata.json to DoltStore config

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beads/crew/emma
2026-01-23 22:24:55 -08:00
committed by Steve Yegge
parent a9c8c952f6
commit db0c6fbd3f
4 changed files with 51 additions and 24 deletions
+13
View File
@@ -262,3 +262,16 @@ func (c *Config) GetDoltServerUser() string {
} }
return c.DoltServerUser return c.DoltServerUser
} }
// GetDoltDatabase returns the database name for Dolt server mode.
// This is different from DatabasePath which returns the on-disk path.
// For server mode, Database field contains the database name on the server
// (e.g., "hq", "gastown", "beads"). Defaults to "beads".
func (c *Config) GetDoltDatabase() string {
db := strings.TrimSpace(c.Database)
if db == "" || db == "beads.db" || db == "dolt" {
return "beads"
}
// Strip any path components - just want the database name
return filepath.Base(db)
}
+9 -3
View File
@@ -101,9 +101,15 @@ func doltExists(doltPath string) bool {
} }
for _, entry := range entries { for _, entry := range entries {
if entry.IsDir() { // Use os.Stat to follow symlinks - entry.IsDir() returns false for symlinks
doltDir := filepath.Join(doltPath, entry.Name(), ".dolt") fullPath := filepath.Join(doltPath, entry.Name())
if info, err := os.Stat(doltDir); err == nil && info.IsDir() { info, err := os.Stat(fullPath)
if err != nil {
continue
}
if info.IsDir() {
doltDir := filepath.Join(fullPath, ".dolt")
if doltInfo, err := os.Stat(doltDir); err == nil && doltInfo.IsDir() {
return true return true
} }
} }
+4
View File
@@ -32,6 +32,7 @@ type Options struct {
ServerHost string // Server host (default: 127.0.0.1) ServerHost string // Server host (default: 127.0.0.1)
ServerPort int // Server port (default: 3306) ServerPort int // Server port (default: 3306)
ServerUser string // MySQL user (default: root) ServerUser string // MySQL user (default: root)
Database string // Database name for Dolt server mode (default: beads)
} }
// New creates a storage backend based on the backend type. // New creates a storage backend based on the backend type.
@@ -101,6 +102,9 @@ func NewFromConfigWithOptions(ctx context.Context, beadsDir string, opts Options
if opts.ServerUser == "" { if opts.ServerUser == "" {
opts.ServerUser = cfg.GetDoltServerUser() opts.ServerUser = cfg.GetDoltServerUser()
} }
if opts.Database == "" {
opts.Database = cfg.GetDoltDatabase()
}
} }
return NewWithOptions(ctx, backend, cfg.DatabasePath(beadsDir), opts) return NewWithOptions(ctx, backend, cfg.DatabasePath(beadsDir), opts)
default: default:
+25 -21
View File
@@ -15,34 +15,38 @@ import (
func init() { func init() {
RegisterBackend(configfile.BackendDolt, func(ctx context.Context, path string, opts Options) (storage.Storage, error) { RegisterBackend(configfile.BackendDolt, func(ctx context.Context, path string, opts Options) (storage.Storage, error) {
// Check if bootstrap is needed (JSONL exists but Dolt doesn't) // Only bootstrap in embedded mode - server mode has database on server
// Path is the dolt subdirectory, parent is .beads directory if !opts.ServerMode {
beadsDir := filepath.Dir(path) // Check if bootstrap is needed (JSONL exists but Dolt doesn't)
// Path is the dolt subdirectory, parent is .beads directory
beadsDir := filepath.Dir(path)
bootstrapped, result, err := dolt.Bootstrap(ctx, dolt.BootstrapConfig{ bootstrapped, result, err := dolt.Bootstrap(ctx, dolt.BootstrapConfig{
BeadsDir: beadsDir, BeadsDir: beadsDir,
DoltPath: path, DoltPath: path,
LockTimeout: opts.LockTimeout, LockTimeout: opts.LockTimeout,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("bootstrap failed: %w", err) return nil, fmt.Errorf("bootstrap failed: %w", err)
} }
if bootstrapped && result != nil { if bootstrapped && result != nil {
// Report bootstrap results // Report bootstrap results
fmt.Fprintf(os.Stderr, "Bootstrapping Dolt from JSONL...\n") fmt.Fprintf(os.Stderr, "Bootstrapping Dolt from JSONL...\n")
if len(result.ParseErrors) > 0 { if len(result.ParseErrors) > 0 {
fmt.Fprintf(os.Stderr, " Skipped %d malformed lines (see above for details)\n", len(result.ParseErrors)) fmt.Fprintf(os.Stderr, " Skipped %d malformed lines (see above for details)\n", len(result.ParseErrors))
}
fmt.Fprintf(os.Stderr, " Imported %d issues", result.IssuesImported)
if result.IssuesSkipped > 0 {
fmt.Fprintf(os.Stderr, ", skipped %d duplicates", result.IssuesSkipped)
}
fmt.Fprintf(os.Stderr, "\n Dolt database ready\n")
} }
fmt.Fprintf(os.Stderr, " Imported %d issues", result.IssuesImported)
if result.IssuesSkipped > 0 {
fmt.Fprintf(os.Stderr, ", skipped %d duplicates", result.IssuesSkipped)
}
fmt.Fprintf(os.Stderr, "\n Dolt database ready\n")
} }
return dolt.New(ctx, &dolt.Config{ return dolt.New(ctx, &dolt.Config{
Path: path, Path: path,
Database: opts.Database,
ReadOnly: opts.ReadOnly, ReadOnly: opts.ReadOnly,
ServerMode: opts.ServerMode, ServerMode: opts.ServerMode,
ServerHost: opts.ServerHost, ServerHost: opts.ServerHost,