Merge branch 'main' of https://github.com/steveyegge/beads
This commit is contained in:
1328
.beads/beads.jsonl
1328
.beads/beads.jsonl
File diff suppressed because one or more lines are too long
@@ -302,6 +302,10 @@ var rootCmd = &cobra.Command{
|
||||
// Best-effort tracking - failures are silent
|
||||
trackBdVersion()
|
||||
|
||||
// Auto-migrate database on version bump (bd-jgxi)
|
||||
// Best-effort migration - failures are silent to avoid disrupting commands
|
||||
autoMigrateOnVersionBump()
|
||||
|
||||
// Initialize daemon status
|
||||
socketPath := getSocketPath()
|
||||
daemonStatus = DaemonStatus{
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/steveyegge/beads/internal/beads"
|
||||
"github.com/steveyegge/beads/internal/configfile"
|
||||
"github.com/steveyegge/beads/internal/debug"
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
)
|
||||
|
||||
// trackBdVersion checks if bd version has changed since last run and updates metadata.json.
|
||||
@@ -112,3 +116,82 @@ func maybeShowUpgradeNotification() {
|
||||
fmt.Println("💡 Run 'bd upgrade review' to see what changed")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// autoMigrateOnVersionBump automatically migrates the database when CLI version changes.
|
||||
// This function is best-effort - failures are silent to avoid disrupting commands.
|
||||
// Called from PersistentPreRun after trackBdVersion().
|
||||
//
|
||||
// bd-jgxi: Auto-migrate database on CLI version bump
|
||||
func autoMigrateOnVersionBump() {
|
||||
// Only migrate if version upgrade was detected
|
||||
if !versionUpgradeDetected {
|
||||
return
|
||||
}
|
||||
|
||||
// Find the beads directory
|
||||
beadsDir := beads.FindBeadsDir()
|
||||
if beadsDir == "" {
|
||||
// No .beads directory - nothing to migrate
|
||||
return
|
||||
}
|
||||
|
||||
// Load config to get database path
|
||||
cfg, err := configfile.Load(beadsDir)
|
||||
if err != nil || cfg == nil {
|
||||
// Config load failed or doesn't exist - skip migration
|
||||
debug.Logf("auto-migrate: skipping migration, config load failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get database path
|
||||
dbPath := cfg.DatabasePath(beadsDir)
|
||||
|
||||
// Check if database exists
|
||||
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
|
||||
// No database file - nothing to migrate
|
||||
debug.Logf("auto-migrate: skipping migration, database does not exist: %s", dbPath)
|
||||
return
|
||||
}
|
||||
|
||||
// Open database to check current version
|
||||
ctx := context.Background()
|
||||
store, err := sqlite.New(ctx, dbPath)
|
||||
if err != nil {
|
||||
// Failed to open database - skip migration
|
||||
debug.Logf("auto-migrate: failed to open database: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get current database version
|
||||
dbVersion, err := store.GetMetadata(ctx, "bd_version")
|
||||
if err != nil {
|
||||
// Failed to read version - skip migration
|
||||
debug.Logf("auto-migrate: failed to read database version: %v", err)
|
||||
_ = store.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if migration is needed
|
||||
if dbVersion == Version {
|
||||
// Database is already at current version
|
||||
debug.Logf("auto-migrate: database already at version %s", Version)
|
||||
_ = store.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Perform migration: update database version
|
||||
debug.Logf("auto-migrate: migrating database from %s to %s", dbVersion, Version)
|
||||
if err := store.SetMetadata(ctx, "bd_version", Version); err != nil {
|
||||
// Migration failed - log and continue
|
||||
debug.Logf("auto-migrate: failed to update database version: %v", err)
|
||||
_ = store.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Close database
|
||||
if err := store.Close(); err != nil {
|
||||
debug.Logf("auto-migrate: warning: failed to close database: %v", err)
|
||||
}
|
||||
|
||||
debug.Logf("auto-migrate: successfully migrated database to version %s", Version)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/steveyegge/beads/internal/beads"
|
||||
"github.com/steveyegge/beads/internal/configfile"
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
)
|
||||
|
||||
func TestGetVersionsSince(t *testing.T) {
|
||||
@@ -308,3 +311,187 @@ func TestMaybeShowUpgradeNotification(t *testing.T) {
|
||||
t.Error("Should not change acknowledged state on subsequent calls")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoMigrateOnVersionBump_NoUpgrade(t *testing.T) {
|
||||
// Save original state
|
||||
origUpgradeDetected := versionUpgradeDetected
|
||||
defer func() {
|
||||
versionUpgradeDetected = origUpgradeDetected
|
||||
}()
|
||||
|
||||
// Reset state - no upgrade detected
|
||||
versionUpgradeDetected = false
|
||||
|
||||
// Should return early without doing anything
|
||||
autoMigrateOnVersionBump()
|
||||
|
||||
// Test passes if no panic occurs
|
||||
}
|
||||
|
||||
func TestAutoMigrateOnVersionBump_NoDatabase(t *testing.T) {
|
||||
// Create temp .beads directory without a database
|
||||
tmpDir := t.TempDir()
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0755); err != nil {
|
||||
t.Fatalf("Failed to create .beads: %v", err)
|
||||
}
|
||||
|
||||
// Change to temp directory
|
||||
origWd, _ := os.Getwd()
|
||||
defer os.Chdir(origWd)
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("Failed to change to temp dir: %v", err)
|
||||
}
|
||||
|
||||
// Create metadata.json
|
||||
cfg := configfile.DefaultConfig()
|
||||
cfg.LastBdVersion = "0.22.0"
|
||||
if err := cfg.Save(beadsDir); err != nil {
|
||||
t.Fatalf("Failed to save config: %v", err)
|
||||
}
|
||||
|
||||
// Save original state
|
||||
origUpgradeDetected := versionUpgradeDetected
|
||||
defer func() {
|
||||
versionUpgradeDetected = origUpgradeDetected
|
||||
}()
|
||||
|
||||
// Simulate version upgrade
|
||||
versionUpgradeDetected = true
|
||||
|
||||
// Should handle gracefully when database doesn't exist
|
||||
autoMigrateOnVersionBump()
|
||||
|
||||
// Test passes if no panic occurs
|
||||
}
|
||||
|
||||
func TestAutoMigrateOnVersionBump_MigratesVersion(t *testing.T) {
|
||||
// Create temp .beads directory
|
||||
tmpDir := t.TempDir()
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0755); err != nil {
|
||||
t.Fatalf("Failed to create .beads: %v", err)
|
||||
}
|
||||
|
||||
// Change to temp directory
|
||||
origWd, _ := os.Getwd()
|
||||
defer os.Chdir(origWd)
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("Failed to change to temp dir: %v", err)
|
||||
}
|
||||
|
||||
// Create metadata.json
|
||||
cfg := configfile.DefaultConfig()
|
||||
cfg.LastBdVersion = "0.22.0"
|
||||
if err := cfg.Save(beadsDir); err != nil {
|
||||
t.Fatalf("Failed to save config: %v", err)
|
||||
}
|
||||
|
||||
// Create database with old version
|
||||
dbPath := filepath.Join(beadsDir, beads.CanonicalDatabaseName)
|
||||
ctx := context.Background()
|
||||
store, err := sqlite.New(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
|
||||
// Set old database version
|
||||
oldVersion := "0.22.0"
|
||||
if err := store.SetMetadata(ctx, "bd_version", oldVersion); err != nil {
|
||||
t.Fatalf("Failed to set old version: %v", err)
|
||||
}
|
||||
_ = store.Close()
|
||||
|
||||
// Save original state
|
||||
origUpgradeDetected := versionUpgradeDetected
|
||||
defer func() {
|
||||
versionUpgradeDetected = origUpgradeDetected
|
||||
}()
|
||||
|
||||
// Simulate version upgrade
|
||||
versionUpgradeDetected = true
|
||||
|
||||
// Call auto-migration
|
||||
autoMigrateOnVersionBump()
|
||||
|
||||
// Verify database version was updated
|
||||
store, err = sqlite.New(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
newVersion, err := store.GetMetadata(ctx, "bd_version")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read database version: %v", err)
|
||||
}
|
||||
|
||||
if newVersion != Version {
|
||||
t.Errorf("Database version not updated: got %q, want %q", newVersion, Version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoMigrateOnVersionBump_AlreadyMigrated(t *testing.T) {
|
||||
// Create temp .beads directory
|
||||
tmpDir := t.TempDir()
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0755); err != nil {
|
||||
t.Fatalf("Failed to create .beads: %v", err)
|
||||
}
|
||||
|
||||
// Change to temp directory
|
||||
origWd, _ := os.Getwd()
|
||||
defer os.Chdir(origWd)
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("Failed to change to temp dir: %v", err)
|
||||
}
|
||||
|
||||
// Create metadata.json
|
||||
cfg := configfile.DefaultConfig()
|
||||
cfg.LastBdVersion = Version
|
||||
if err := cfg.Save(beadsDir); err != nil {
|
||||
t.Fatalf("Failed to save config: %v", err)
|
||||
}
|
||||
|
||||
// Create database with current version
|
||||
dbPath := filepath.Join(beadsDir, beads.CanonicalDatabaseName)
|
||||
ctx := context.Background()
|
||||
store, err := sqlite.New(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
|
||||
// Set current database version
|
||||
if err := store.SetMetadata(ctx, "bd_version", Version); err != nil {
|
||||
t.Fatalf("Failed to set version: %v", err)
|
||||
}
|
||||
_ = store.Close()
|
||||
|
||||
// Save original state
|
||||
origUpgradeDetected := versionUpgradeDetected
|
||||
defer func() {
|
||||
versionUpgradeDetected = origUpgradeDetected
|
||||
}()
|
||||
|
||||
// Simulate version upgrade
|
||||
versionUpgradeDetected = true
|
||||
|
||||
// Call auto-migration - should be a no-op
|
||||
autoMigrateOnVersionBump()
|
||||
|
||||
// Verify database version is still current
|
||||
store, err = sqlite.New(ctx, dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
currentVersion, err := store.GetMetadata(ctx, "bd_version")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read database version: %v", err)
|
||||
}
|
||||
|
||||
if currentVersion != Version {
|
||||
t.Errorf("Database version changed unexpectedly: got %q, want %q", currentVersion, Version)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user