bluesky viewer in the terminal
1package main
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "os"
8
9 "github.com/stormlightlabs/skypanel/cli/internal/config"
10 "github.com/stormlightlabs/skypanel/cli/internal/store"
11 "github.com/stormlightlabs/skypanel/cli/internal/ui"
12 "github.com/urfave/cli/v3"
13)
14
15func SetupAction(ctx context.Context, cmd *cli.Command) error {
16
17 ui.Titleln("Setup: Initializing persistence layer")
18 fmt.Println()
19
20 configDir, err := config.GetConfigDir()
21 if err != nil {
22 return fmt.Errorf("failed to get config directory: %w", err)
23 }
24
25 dbPath, err := config.GetCacheDB()
26 if err != nil {
27 return fmt.Errorf("failed to get database path: %w", err)
28 }
29
30 ui.Infoln("Config directory: %s", configDir)
31 ui.Infoln("Database path: %s", dbPath)
32 fmt.Println()
33
34 if _, err := os.Stat(configDir); os.IsNotExist(err) {
35 ui.Infoln("Creating config directory...")
36 if err := os.MkdirAll(configDir, 0700); err != nil {
37 logger.Error("Failed to create config directory", "error", err)
38 return err
39 }
40 ui.Successln("Config directory created")
41 } else {
42 ui.Successln("Config directory exists")
43 }
44
45 dbExists := true
46 if _, err := os.Stat(dbPath); os.IsNotExist(err) {
47 dbExists = false
48 ui.Infoln("Database does not exist, will be created")
49 } else {
50 ui.Successln("Database file exists")
51 }
52
53 fmt.Println()
54
55 db, err := sql.Open("sqlite3", dbPath)
56 if err != nil {
57 logger.Error("Failed to open database", "error", err)
58 return err
59 }
60 defer db.Close()
61
62 statusBefore, err := store.GetMigrationStatus(db)
63 if err != nil && !dbExists {
64 logger.Debug("Migration status check returned error (expected for new database)", "error", err)
65 statusBefore = &store.MigrationStatus{CurrentVersion: 0, LatestVersion: 0, PendingCount: 0}
66 } else if err != nil {
67 logger.Error("Failed to check migration status", "error", err)
68 return err
69 }
70
71 if statusBefore.IsUpToDate && dbExists {
72 ui.Successln("Database is up to date (v%d)", statusBefore.CurrentVersion)
73 return nil
74 }
75
76 ui.Infoln("Running migrations...")
77 if err := store.RunMigrations(db); err != nil {
78 logger.Error("Failed to run migrations", "error", err)
79 return err
80 }
81
82 statusAfter, err := store.GetMigrationStatus(db)
83 if err != nil {
84 logger.Error("Failed to verify migration status", "error", err)
85 return err
86 }
87
88 fmt.Println()
89 ui.Successln("Setup complete!")
90 ui.Infoln("Database version: v%d", statusAfter.CurrentVersion)
91 ui.Infoln("Migrations applied: %d", statusAfter.CurrentVersion-statusBefore.CurrentVersion)
92
93 return nil
94}
95
96func SetupCommand() *cli.Command {
97 return &cli.Command{
98 Name: "setup",
99 Usage: "Initialize the persistence layer (database and config)",
100 Description: `Initialize the skycli persistence layer by creating:
101 - Config directory at ~/.skycli
102 - SQLite database at ~/.skycli/cache.db
103 - Running all database migrations
104
105 This command is idempotent and safe to run multiple times.
106 It will show the current state and only make necessary changes.`,
107 Action: SetupAction,
108 }
109}