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
|
// Best-effort tracking - failures are silent
|
||||||
trackBdVersion()
|
trackBdVersion()
|
||||||
|
|
||||||
|
// Auto-migrate database on version bump (bd-jgxi)
|
||||||
|
// Best-effort migration - failures are silent to avoid disrupting commands
|
||||||
|
autoMigrateOnVersionBump()
|
||||||
|
|
||||||
// Initialize daemon status
|
// Initialize daemon status
|
||||||
socketPath := getSocketPath()
|
socketPath := getSocketPath()
|
||||||
daemonStatus = DaemonStatus{
|
daemonStatus = DaemonStatus{
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/beads"
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
"github.com/steveyegge/beads/internal/configfile"
|
"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.
|
// 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("💡 Run 'bd upgrade review' to see what changed")
|
||||||
fmt.Println()
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
"github.com/steveyegge/beads/internal/configfile"
|
"github.com/steveyegge/beads/internal/configfile"
|
||||||
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetVersionsSince(t *testing.T) {
|
func TestGetVersionsSince(t *testing.T) {
|
||||||
@@ -308,3 +311,187 @@ func TestMaybeShowUpgradeNotification(t *testing.T) {
|
|||||||
t.Error("Should not change acknowledged state on subsequent calls")
|
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