refactor: remove deprecated global daemon support
Global daemon support has been deprecated for most of the project's lifetime. This change removes the dead code entirely: - Remove --global and --migrate-to-global daemon flags - Remove runGlobalDaemon() and migrateToGlobalDaemon() functions - Remove shouldUseGlobalDaemon() and getGlobalBeadsDir() functions - Simplify functions that had global bool parameters - Remove warning about old global socket in getSocketPath() This reduces code complexity and removes 238 net lines of unused code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -45,22 +45,20 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
status, _ := cmd.Flags().GetBool("status")
|
status, _ := cmd.Flags().GetBool("status")
|
||||||
health, _ := cmd.Flags().GetBool("health")
|
health, _ := cmd.Flags().GetBool("health")
|
||||||
metrics, _ := cmd.Flags().GetBool("metrics")
|
metrics, _ := cmd.Flags().GetBool("metrics")
|
||||||
migrateToGlobal, _ := cmd.Flags().GetBool("migrate-to-global")
|
|
||||||
interval, _ := cmd.Flags().GetDuration("interval")
|
interval, _ := cmd.Flags().GetDuration("interval")
|
||||||
autoCommit, _ := cmd.Flags().GetBool("auto-commit")
|
autoCommit, _ := cmd.Flags().GetBool("auto-commit")
|
||||||
autoPush, _ := cmd.Flags().GetBool("auto-push")
|
autoPush, _ := cmd.Flags().GetBool("auto-push")
|
||||||
logFile, _ := cmd.Flags().GetString("log")
|
logFile, _ := cmd.Flags().GetString("log")
|
||||||
global, _ := cmd.Flags().GetBool("global")
|
|
||||||
|
|
||||||
// If no operation flags provided, show help
|
// If no operation flags provided, show help
|
||||||
if !start && !stop && !status && !health && !metrics && !migrateToGlobal {
|
if !start && !stop && !status && !health && !metrics {
|
||||||
_ = cmd.Help()
|
_ = cmd.Help()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If auto-commit/auto-push flags weren't explicitly provided, read from config
|
// If auto-commit/auto-push flags weren't explicitly provided, read from config
|
||||||
// (skip if --stop, --status, --health, --metrics, or --migrate-to-global)
|
// (skip if --stop, --status, --health, --metrics)
|
||||||
if start && !stop && !status && !health && !metrics && !migrateToGlobal && !global {
|
if start && !stop && !status && !health && !metrics {
|
||||||
if !cmd.Flags().Changed("auto-commit") {
|
if !cmd.Flags().Changed("auto-commit") {
|
||||||
if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -92,29 +90,24 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pidFile, err := getPIDFilePath(global)
|
pidFile, err := getPIDFilePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if status {
|
if status {
|
||||||
showDaemonStatus(pidFile, global)
|
showDaemonStatus(pidFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if health {
|
if health {
|
||||||
showDaemonHealth(global)
|
showDaemonHealth()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if metrics {
|
if metrics {
|
||||||
showDaemonMetrics(global)
|
showDaemonMetrics()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if migrateToGlobal {
|
|
||||||
migrateToGlobalDaemon()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +130,7 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
// Check if daemon is already running
|
// Check if daemon is already running
|
||||||
if isRunning, pid := isDaemonRunning(pidFile); isRunning {
|
if isRunning, pid := isDaemonRunning(pidFile); isRunning {
|
||||||
// Check if running daemon has compatible version
|
// Check if running daemon has compatible version
|
||||||
socketPath := getSocketPathForPID(pidFile, global)
|
socketPath := getSocketPathForPID(pidFile)
|
||||||
if client, err := rpc.TryConnectWithTimeout(socketPath, 1*time.Second); err == nil && client != nil {
|
if client, err := rpc.TryConnectWithTimeout(socketPath, 1*time.Second); err == nil && client != nil {
|
||||||
health, healthErr := client.Health()
|
health, healthErr := client.Health()
|
||||||
_ = client.Close()
|
_ = client.Close()
|
||||||
@@ -145,7 +138,7 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
// If we can check version and it's compatible, exit
|
// If we can check version and it's compatible, exit
|
||||||
if healthErr == nil && health.Compatible {
|
if healthErr == nil && health.Compatible {
|
||||||
fmt.Fprintf(os.Stderr, "Error: daemon already running (PID %d, version %s)\n", pid, health.Version)
|
fmt.Fprintf(os.Stderr, "Error: daemon already running (PID %d, version %s)\n", pid, health.Version)
|
||||||
fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop%s' to stop it first\n", boolToFlag(global, " --global"))
|
fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop' to stop it first\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,22 +152,14 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
} else {
|
} else {
|
||||||
// Can't check version - assume incompatible
|
// Can't check version - assume incompatible
|
||||||
fmt.Fprintf(os.Stderr, "Error: daemon already running (PID %d)\n", pid)
|
fmt.Fprintf(os.Stderr, "Error: daemon already running (PID %d)\n", pid)
|
||||||
fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop%s' to stop it first\n", boolToFlag(global, " --global"))
|
fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop' to stop it first\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global daemon doesn't support auto-commit/auto-push (no sync loop)
|
// Validate we're in a git repo
|
||||||
if global && (autoCommit || autoPush) {
|
if !isGitRepo() {
|
||||||
fmt.Fprintf(os.Stderr, "Error: --auto-commit and --auto-push are not supported with --global\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "Hint: global daemon runs in routing mode and doesn't perform background sync\n")
|
|
||||||
fmt.Fprintf(os.Stderr, " Use local daemon (without --global) for auto-commit/auto-push features\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate we're in a git repo (skip for global daemon)
|
|
||||||
if !global && !isGitRepo() {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: not in a git repository\n")
|
fmt.Fprintf(os.Stderr, "Error: not in a git repository\n")
|
||||||
fmt.Fprintf(os.Stderr, "Hint: run 'git init' to initialize a repository\n")
|
fmt.Fprintf(os.Stderr, "Hint: run 'git init' to initialize a repository\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -188,30 +173,24 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Warn if starting daemon in a git worktree
|
// Warn if starting daemon in a git worktree
|
||||||
if !global {
|
// Ensure dbPath is set for warning
|
||||||
// Ensure dbPath is set for warning
|
if dbPath == "" {
|
||||||
if dbPath == "" {
|
if foundDB := beads.FindDatabasePath(); foundDB != "" {
|
||||||
if foundDB := beads.FindDatabasePath(); foundDB != "" {
|
dbPath = foundDB
|
||||||
dbPath = foundDB
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dbPath != "" {
|
|
||||||
warnWorktreeDaemon(dbPath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dbPath != "" {
|
||||||
|
warnWorktreeDaemon(dbPath)
|
||||||
|
}
|
||||||
|
|
||||||
// Start daemon
|
// Start daemon
|
||||||
scope := "local"
|
fmt.Printf("Starting bd daemon (interval: %v, auto-commit: %v, auto-push: %v)\n",
|
||||||
if global {
|
interval, autoCommit, autoPush)
|
||||||
scope = "global"
|
|
||||||
}
|
|
||||||
fmt.Printf("Starting bd daemon (%s, interval: %v, auto-commit: %v, auto-push: %v)\n",
|
|
||||||
scope, interval, autoCommit, autoPush)
|
|
||||||
if logFile != "" {
|
if logFile != "" {
|
||||||
fmt.Printf("Logging to: %s\n", logFile)
|
fmt.Printf("Logging to: %s\n", logFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
startDaemon(interval, autoCommit, autoPush, logFile, pidFile, global)
|
startDaemon(interval, autoCommit, autoPush, logFile, pidFile)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,9 +203,7 @@ func init() {
|
|||||||
daemonCmd.Flags().Bool("status", false, "Show daemon status")
|
daemonCmd.Flags().Bool("status", false, "Show daemon status")
|
||||||
daemonCmd.Flags().Bool("health", false, "Check daemon health and metrics")
|
daemonCmd.Flags().Bool("health", false, "Check daemon health and metrics")
|
||||||
daemonCmd.Flags().Bool("metrics", false, "Show detailed daemon metrics")
|
daemonCmd.Flags().Bool("metrics", false, "Show detailed daemon metrics")
|
||||||
daemonCmd.Flags().Bool("migrate-to-global", false, "Migrate from local to global daemon")
|
|
||||||
daemonCmd.Flags().String("log", "", "Log file path (default: .beads/daemon.log)")
|
daemonCmd.Flags().String("log", "", "Log file path (default: .beads/daemon.log)")
|
||||||
daemonCmd.Flags().Bool("global", false, "Run as global daemon (socket at ~/.beads/bd.sock)")
|
|
||||||
daemonCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output JSON format")
|
daemonCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output JSON format")
|
||||||
rootCmd.AddCommand(daemonCmd)
|
rootCmd.AddCommand(daemonCmd)
|
||||||
}
|
}
|
||||||
@@ -243,7 +220,7 @@ func computeDaemonParentPID() int {
|
|||||||
}
|
}
|
||||||
return os.Getppid()
|
return os.Getppid()
|
||||||
}
|
}
|
||||||
func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, pidFile string, global bool) {
|
func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, pidFile string) {
|
||||||
logF, log := setupDaemonLogger(logPath)
|
logF, log := setupDaemonLogger(logPath)
|
||||||
defer func() { _ = logF.Close() }()
|
defer func() { _ = logF.Close() }()
|
||||||
|
|
||||||
@@ -264,7 +241,7 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, p
|
|||||||
|
|
||||||
// Write crash report to daemon-error file for user visibility
|
// Write crash report to daemon-error file for user visibility
|
||||||
var beadsDir string
|
var beadsDir string
|
||||||
if !global && dbPath != "" {
|
if dbPath != "" {
|
||||||
beadsDir = filepath.Dir(dbPath)
|
beadsDir = filepath.Dir(dbPath)
|
||||||
} else if foundDB := beads.FindDatabasePath(); foundDB != "" {
|
} else if foundDB := beads.FindDatabasePath(); foundDB != "" {
|
||||||
beadsDir = filepath.Dir(foundDB)
|
beadsDir = filepath.Dir(foundDB)
|
||||||
@@ -288,17 +265,14 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, p
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Determine database path first (needed for lock file metadata)
|
// Determine database path first (needed for lock file metadata)
|
||||||
daemonDBPath := ""
|
daemonDBPath := dbPath
|
||||||
if !global {
|
if daemonDBPath == "" {
|
||||||
daemonDBPath = dbPath
|
if foundDB := beads.FindDatabasePath(); foundDB != "" {
|
||||||
if daemonDBPath == "" {
|
daemonDBPath = foundDB
|
||||||
if foundDB := beads.FindDatabasePath(); foundDB != "" {
|
} else {
|
||||||
daemonDBPath = foundDB
|
log.log("Error: no beads database found")
|
||||||
} else {
|
log.log("Hint: run 'bd init' to create a database or set BEADS_DB environment variable")
|
||||||
log.log("Error: no beads database found")
|
return // Use return instead of os.Exit to allow defers to run
|
||||||
log.log("Hint: run 'bd init' to create a database or set BEADS_DB environment variable")
|
|
||||||
return // Use return instead of os.Exit to allow defers to run
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,11 +285,6 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, p
|
|||||||
|
|
||||||
log.log("Daemon started (interval: %v, auto-commit: %v, auto-push: %v)", interval, autoCommit, autoPush)
|
log.log("Daemon started (interval: %v, auto-commit: %v, auto-push: %v)", interval, autoCommit, autoPush)
|
||||||
|
|
||||||
if global {
|
|
||||||
runGlobalDaemon(ctx, log)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for multiple .db files (ambiguity error)
|
// Check for multiple .db files (ambiguity error)
|
||||||
beadsDir := filepath.Dir(daemonDBPath)
|
beadsDir := filepath.Dir(daemonDBPath)
|
||||||
matches, err := filepath.Glob(filepath.Join(beadsDir, "*.db"))
|
matches, err := filepath.Glob(filepath.Join(beadsDir, "*.db"))
|
||||||
|
|||||||
@@ -33,22 +33,11 @@ func shouldAutoStartDaemon() bool {
|
|||||||
return config.GetBool("auto-start-daemon") // Defaults to true
|
return config.GetBool("auto-start-daemon") // Defaults to true
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldUseGlobalDaemon determines if global daemon should be preferred
|
|
||||||
// based on heuristics (multi-repo detection)
|
|
||||||
// Note: Global daemon is deprecated; this always returns false for now
|
|
||||||
func shouldUseGlobalDaemon() bool {
|
|
||||||
// Global daemon support is deprecated
|
|
||||||
// Always use local daemon (per-project .beads/ socket)
|
|
||||||
// Previously supported BEADS_PREFER_GLOBAL_DAEMON env var, but global
|
|
||||||
// daemon has issues with multi-workspace git workflows
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// restartDaemonForVersionMismatch stops the old daemon and starts a new one
|
// restartDaemonForVersionMismatch stops the old daemon and starts a new one
|
||||||
// Returns true if restart was successful
|
// Returns true if restart was successful
|
||||||
func restartDaemonForVersionMismatch() bool {
|
func restartDaemonForVersionMismatch() bool {
|
||||||
// Use local daemon (global is deprecated)
|
pidFile, err := getPIDFilePath()
|
||||||
pidFile, err := getPIDFilePath(false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Logf("failed to get PID file path: %v", err)
|
debug.Logf("failed to get PID file path: %v", err)
|
||||||
return false
|
return false
|
||||||
@@ -173,8 +162,8 @@ func tryAutoStartDaemon(socketPath string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
socketPath, isGlobal := determineSocketMode(socketPath)
|
socketPath = determineSocketPath(socketPath)
|
||||||
return startDaemonProcess(socketPath, isGlobal)
|
return startDaemonProcess(socketPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugLog(msg string, args ...interface{}) {
|
func debugLog(msg string, args ...interface{}) {
|
||||||
@@ -242,40 +231,22 @@ func handleExistingSocket(socketPath string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func determineSocketMode(socketPath string) (string, bool) {
|
func determineSocketPath(socketPath string) string {
|
||||||
home, err := os.UserHomeDir()
|
return socketPath
|
||||||
if err != nil {
|
|
||||||
return socketPath, false
|
|
||||||
}
|
|
||||||
|
|
||||||
globalSocket := filepath.Join(home, ".beads", "bd.sock")
|
|
||||||
if socketPath == globalSocket {
|
|
||||||
return socketPath, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldUseGlobalDaemon() {
|
|
||||||
debugLog("detected multiple repos, auto-starting global daemon")
|
|
||||||
return globalSocket, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return socketPath, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startDaemonProcess(socketPath string, isGlobal bool) bool {
|
func startDaemonProcess(socketPath string) bool {
|
||||||
binPath, err := os.Executable()
|
binPath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
binPath = os.Args[0]
|
binPath = os.Args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{"daemon", "--start"}
|
args := []string{"daemon", "--start"}
|
||||||
if isGlobal {
|
|
||||||
args = append(args, "--global")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(binPath, args...)
|
cmd := exec.Command(binPath, args...)
|
||||||
setupDaemonIO(cmd)
|
setupDaemonIO(cmd)
|
||||||
|
|
||||||
if !isGlobal && dbPath != "" {
|
if dbPath != "" {
|
||||||
cmd.Dir = filepath.Dir(dbPath)
|
cmd.Dir = filepath.Dir(dbPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,22 +360,9 @@ func recordDaemonStartFailure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getSocketPath returns the daemon socket path based on the database location
|
// getSocketPath returns the daemon socket path based on the database location
|
||||||
// Always returns local socket path (.beads/bd.sock relative to database)
|
// Returns local socket path (.beads/bd.sock relative to database)
|
||||||
func getSocketPath() string {
|
func getSocketPath() string {
|
||||||
// Always use local socket (same directory as database: .beads/bd.sock)
|
return filepath.Join(filepath.Dir(dbPath), "bd.sock")
|
||||||
localSocket := filepath.Join(filepath.Dir(dbPath), "bd.sock")
|
|
||||||
|
|
||||||
// Warn if old global socket exists
|
|
||||||
if home, err := os.UserHomeDir(); err == nil {
|
|
||||||
globalSocket := filepath.Join(home, ".beads", "bd.sock")
|
|
||||||
if _, err := os.Stat(globalSocket); err == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Warning: Found old global daemon socket at %s\n", globalSocket)
|
|
||||||
fmt.Fprintf(os.Stderr, "Global sockets are deprecated. Each project now uses its own local daemon.\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "To migrate: Stop the global daemon and restart with 'bd daemon' in each project.\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return localSocket
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitVerboseWarning prints a one-line warning when falling back to direct mode
|
// emitVerboseWarning prints a one-line warning when falling back to direct mode
|
||||||
|
|||||||
@@ -9,21 +9,6 @@ import (
|
|||||||
"github.com/steveyegge/beads/internal/beads"
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getGlobalBeadsDir returns the global beads directory (~/.beads)
|
|
||||||
func getGlobalBeadsDir() (string, error) {
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("cannot get home directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
beadsDir := filepath.Join(home, ".beads")
|
|
||||||
if err := os.MkdirAll(beadsDir, 0700); err != nil {
|
|
||||||
return "", fmt.Errorf("cannot create global beads directory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return beadsDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensureBeadsDir ensures the local beads directory exists (.beads in the current workspace)
|
// ensureBeadsDir ensures the local beads directory exists (.beads in the current workspace)
|
||||||
func ensureBeadsDir() (string, error) {
|
func ensureBeadsDir() (string, error) {
|
||||||
var beadsDir string
|
var beadsDir string
|
||||||
@@ -74,26 +59,14 @@ func getEnvBool(key string, defaultValue bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getSocketPathForPID determines the socket path for a given PID file
|
// getSocketPathForPID determines the socket path for a given PID file
|
||||||
func getSocketPathForPID(pidFile string, global bool) string {
|
func getSocketPathForPID(pidFile string) string {
|
||||||
if global {
|
// Socket is in same directory as PID file
|
||||||
home, _ := os.UserHomeDir()
|
|
||||||
return filepath.Join(home, ".beads", "bd.sock")
|
|
||||||
}
|
|
||||||
// Local daemon: socket is in same directory as PID file
|
|
||||||
return filepath.Join(filepath.Dir(pidFile), "bd.sock")
|
return filepath.Join(filepath.Dir(pidFile), "bd.sock")
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPIDFilePath returns the path to the daemon PID file
|
// getPIDFilePath returns the path to the daemon PID file
|
||||||
func getPIDFilePath(global bool) (string, error) {
|
func getPIDFilePath() (string, error) {
|
||||||
var beadsDir string
|
beadsDir, err := ensureBeadsDir()
|
||||||
var err error
|
|
||||||
|
|
||||||
if global {
|
|
||||||
beadsDir, err = getGlobalBeadsDir()
|
|
||||||
} else {
|
|
||||||
beadsDir, err = ensureBeadsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -101,20 +74,12 @@ func getPIDFilePath(global bool) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getLogFilePath returns the path to the daemon log file
|
// getLogFilePath returns the path to the daemon log file
|
||||||
func getLogFilePath(userPath string, global bool) (string, error) {
|
func getLogFilePath(userPath string) (string, error) {
|
||||||
if userPath != "" {
|
if userPath != "" {
|
||||||
return userPath, nil
|
return userPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var beadsDir string
|
beadsDir, err := ensureBeadsDir()
|
||||||
var err error
|
|
||||||
|
|
||||||
if global {
|
|
||||||
beadsDir, err = getGlobalBeadsDir()
|
|
||||||
} else {
|
|
||||||
beadsDir, err = ensureBeadsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,20 +40,15 @@ func formatUptime(seconds float64) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// showDaemonStatus displays the current daemon status
|
// showDaemonStatus displays the current daemon status
|
||||||
func showDaemonStatus(pidFile string, global bool) {
|
func showDaemonStatus(pidFile string) {
|
||||||
if isRunning, pid := isDaemonRunning(pidFile); isRunning {
|
if isRunning, pid := isDaemonRunning(pidFile); isRunning {
|
||||||
scope := "local"
|
|
||||||
if global {
|
|
||||||
scope = "global"
|
|
||||||
}
|
|
||||||
|
|
||||||
var started string
|
var started string
|
||||||
if info, err := os.Stat(pidFile); err == nil {
|
if info, err := os.Stat(pidFile); err == nil {
|
||||||
started = info.ModTime().Format("2006-01-02 15:04:05")
|
started = info.ModTime().Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
var logPath string
|
var logPath string
|
||||||
if lp, err := getLogFilePath("", global); err == nil {
|
if lp, err := getLogFilePath(""); err == nil {
|
||||||
if _, err := os.Stat(lp); err == nil {
|
if _, err := os.Stat(lp); err == nil {
|
||||||
logPath = lp
|
logPath = lp
|
||||||
}
|
}
|
||||||
@@ -63,7 +58,6 @@ func showDaemonStatus(pidFile string, global bool) {
|
|||||||
status := map[string]interface{}{
|
status := map[string]interface{}{
|
||||||
"running": true,
|
"running": true,
|
||||||
"pid": pid,
|
"pid": pid,
|
||||||
"scope": scope,
|
|
||||||
}
|
}
|
||||||
if started != "" {
|
if started != "" {
|
||||||
status["started"] = started
|
status["started"] = started
|
||||||
@@ -75,7 +69,7 @@ func showDaemonStatus(pidFile string, global bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Daemon is running (PID %d, %s)\n", pid, scope)
|
fmt.Printf("Daemon is running (PID %d)\n", pid)
|
||||||
if started != "" {
|
if started != "" {
|
||||||
fmt.Printf(" Started: %s\n", started)
|
fmt.Printf(" Started: %s\n", started)
|
||||||
}
|
}
|
||||||
@@ -92,23 +86,13 @@ func showDaemonStatus(pidFile string, global bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// showDaemonHealth displays daemon health information
|
// showDaemonHealth displays daemon health information
|
||||||
func showDaemonHealth(global bool) {
|
func showDaemonHealth() {
|
||||||
var socketPath string
|
beadsDir, err := ensureBeadsDir()
|
||||||
if global {
|
if err != nil {
|
||||||
home, err := os.UserHomeDir()
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
if err != nil {
|
os.Exit(1)
|
||||||
fmt.Fprintf(os.Stderr, "Error: cannot get home directory: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
socketPath = filepath.Join(home, ".beads", "bd.sock")
|
|
||||||
} else {
|
|
||||||
beadsDir, err := ensureBeadsDir()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
socketPath = filepath.Join(beadsDir, "bd.sock")
|
|
||||||
}
|
}
|
||||||
|
socketPath := filepath.Join(beadsDir, "bd.sock")
|
||||||
|
|
||||||
client, err := rpc.TryConnect(socketPath)
|
client, err := rpc.TryConnect(socketPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -159,23 +143,13 @@ func showDaemonHealth(global bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// showDaemonMetrics displays daemon metrics
|
// showDaemonMetrics displays daemon metrics
|
||||||
func showDaemonMetrics(global bool) {
|
func showDaemonMetrics() {
|
||||||
var socketPath string
|
beadsDir, err := ensureBeadsDir()
|
||||||
if global {
|
if err != nil {
|
||||||
home, err := os.UserHomeDir()
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
if err != nil {
|
os.Exit(1)
|
||||||
fmt.Fprintf(os.Stderr, "Error: cannot get home directory: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
socketPath = filepath.Join(home, ".beads", "bd.sock")
|
|
||||||
} else {
|
|
||||||
beadsDir, err := ensureBeadsDir()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
socketPath = filepath.Join(beadsDir, "bd.sock")
|
|
||||||
}
|
}
|
||||||
|
socketPath := filepath.Join(beadsDir, "bd.sock")
|
||||||
|
|
||||||
client, err := rpc.TryConnect(socketPath)
|
client, err := rpc.TryConnect(socketPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -242,71 +216,6 @@ func showDaemonMetrics(global bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrateToGlobalDaemon migrates from local to global daemon
|
|
||||||
func migrateToGlobalDaemon() {
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: cannot get home directory: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
localPIDFile := filepath.Join(".beads", "daemon.pid")
|
|
||||||
globalPIDFile := filepath.Join(home, ".beads", "daemon.pid")
|
|
||||||
|
|
||||||
// Check if local daemon is running
|
|
||||||
localRunning, localPID := isDaemonRunning(localPIDFile)
|
|
||||||
if !localRunning {
|
|
||||||
fmt.Println("No local daemon is running")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Stopping local daemon (PID %d)...\n", localPID)
|
|
||||||
stopDaemon(localPIDFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if global daemon is already running
|
|
||||||
globalRunning, globalPID := isDaemonRunning(globalPIDFile)
|
|
||||||
if globalRunning {
|
|
||||||
fmt.Printf("Global daemon already running (PID %d)\n", globalPID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start global daemon
|
|
||||||
fmt.Println("Starting global daemon...")
|
|
||||||
binPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
binPath = os.Args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(binPath, "daemon", "--global") // #nosec G204 - bd daemon command from trusted binary
|
|
||||||
devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0)
|
|
||||||
if err == nil {
|
|
||||||
cmd.Stdout = devNull
|
|
||||||
cmd.Stderr = devNull
|
|
||||||
cmd.Stdin = devNull
|
|
||||||
defer func() { _ = devNull.Close() }()
|
|
||||||
}
|
|
||||||
|
|
||||||
configureDaemonProcess(cmd)
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: failed to start global daemon: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() { _ = cmd.Wait() }()
|
|
||||||
|
|
||||||
// Wait for daemon to be ready
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
|
|
||||||
if isRunning, pid := isDaemonRunning(globalPIDFile); isRunning {
|
|
||||||
fmt.Printf("Global daemon started successfully (PID %d)\n", pid)
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("Migration complete! The global daemon will now serve all your beads repositories.")
|
|
||||||
fmt.Println("Set BEADS_PREFER_GLOBAL_DAEMON=1 in your shell to make this permanent.")
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error: global daemon failed to start\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopDaemon stops a running daemon
|
// stopDaemon stops a running daemon
|
||||||
func stopDaemon(pidFile string) {
|
func stopDaemon(pidFile string) {
|
||||||
isRunning, pid := isDaemonRunning(pidFile)
|
isRunning, pid := isDaemonRunning(pidFile)
|
||||||
@@ -344,9 +253,7 @@ func stopDaemon(pidFile string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if this is global or local daemon
|
socketPath := getSocketPathForPID(pidFile)
|
||||||
isGlobal := strings.Contains(pidFile, filepath.Join(".beads", "daemon.lock"))
|
|
||||||
socketPath := getSocketPathForPID(pidFile, isGlobal)
|
|
||||||
|
|
||||||
if err := process.Kill(); err != nil {
|
if err := process.Kill(); err != nil {
|
||||||
// Ignore "process already finished" errors
|
// Ignore "process already finished" errors
|
||||||
@@ -369,15 +276,15 @@ func stopDaemon(pidFile string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startDaemon starts the daemon in background
|
// startDaemon starts the daemon in background
|
||||||
func startDaemon(interval time.Duration, autoCommit, autoPush bool, logFile, pidFile string, global bool) {
|
func startDaemon(interval time.Duration, autoCommit, autoPush bool, logFile, pidFile string) {
|
||||||
logPath, err := getLogFilePath(logFile, global)
|
logPath, err := getLogFilePath(logFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DAEMON_FOREGROUND") == "1" {
|
if os.Getenv("BD_DAEMON_FOREGROUND") == "1" {
|
||||||
runDaemonLoop(interval, autoCommit, autoPush, logPath, pidFile, global)
|
runDaemonLoop(interval, autoCommit, autoPush, logPath, pidFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,9 +306,6 @@ func startDaemon(interval time.Duration, autoCommit, autoPush bool, logFile, pid
|
|||||||
if logFile != "" {
|
if logFile != "" {
|
||||||
args = append(args, "--log", logFile)
|
args = append(args, "--log", logFile)
|
||||||
}
|
}
|
||||||
if global {
|
|
||||||
args = append(args, "--global")
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(exe, args...) // #nosec G204 - bd daemon command from trusted binary
|
cmd := exec.Command(exe, args...) // #nosec G204 - bd daemon command from trusted binary
|
||||||
cmd.Env = append(os.Environ(), "BD_DAEMON_FOREGROUND=1")
|
cmd.Env = append(os.Environ(), "BD_DAEMON_FOREGROUND=1")
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/rpc"
|
"github.com/steveyegge/beads/internal/rpc"
|
||||||
@@ -40,39 +39,6 @@ func startRPCServer(ctx context.Context, socketPath string, store storage.Storag
|
|||||||
return server, serverErrChan, nil
|
return server, serverErrChan, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runGlobalDaemon runs the global routing daemon
|
|
||||||
func runGlobalDaemon(ctx context.Context, log daemonLogger) {
|
|
||||||
globalDir, err := getGlobalBeadsDir()
|
|
||||||
if err != nil {
|
|
||||||
log.log("Error: cannot get global beads directory: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
socketPath := filepath.Join(globalDir, "bd.sock")
|
|
||||||
|
|
||||||
serverCtx, cancel := context.WithCancel(ctx)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
server, _, err := startRPCServer(serverCtx, socketPath, nil, globalDir, "", log)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sigChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigChan, daemonSignals...)
|
|
||||||
defer signal.Stop(sigChan)
|
|
||||||
|
|
||||||
sig := <-sigChan
|
|
||||||
log.log("Received signal: %v", sig)
|
|
||||||
log.log("Shutting down global daemon...")
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
if err := server.Stop(); err != nil {
|
|
||||||
log.log("Error stopping server: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.log("Global daemon stopped")
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkParentProcessAlive checks if the parent process is still running.
|
// checkParentProcessAlive checks if the parent process is still running.
|
||||||
// Returns true if parent is alive, false if it died.
|
// Returns true if parent is alive, false if it died.
|
||||||
// Returns true if parent PID is 0 or 1 (not tracked, or adopted by init).
|
// Returns true if parent PID is 0 or 1 (not tracked, or adopted by init).
|
||||||
|
|||||||
Reference in New Issue
Block a user