feat: add bd daemon --stop-all to kill all daemon processes (bd-47tn)
Add --stop-all flag to bd daemon command that: - Discovers all running bd daemon processes using the registry - Gracefully stops them all (with force kill fallback) - Reports how many were stopped Useful for: - Cleaning up multiple daemons causing race conditions - Getting a clean slate before running bd sync - Debugging daemon-related issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,7 @@ Common operations:
|
|||||||
bd daemon --start Start the daemon (background)
|
bd daemon --start Start the daemon (background)
|
||||||
bd daemon --start --foreground Start in foreground (for systemd/supervisord)
|
bd daemon --start --foreground Start in foreground (for systemd/supervisord)
|
||||||
bd daemon --stop Stop a running daemon
|
bd daemon --stop Stop a running daemon
|
||||||
|
bd daemon --stop-all Stop ALL running bd daemons
|
||||||
bd daemon --status Check if daemon is running
|
bd daemon --status Check if daemon is running
|
||||||
bd daemon --health Check daemon health and metrics
|
bd daemon --health Check daemon health and metrics
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
start, _ := cmd.Flags().GetBool("start")
|
start, _ := cmd.Flags().GetBool("start")
|
||||||
stop, _ := cmd.Flags().GetBool("stop")
|
stop, _ := cmd.Flags().GetBool("stop")
|
||||||
|
stopAll, _ := cmd.Flags().GetBool("stop-all")
|
||||||
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")
|
||||||
@@ -54,7 +56,7 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
foreground, _ := cmd.Flags().GetBool("foreground")
|
foreground, _ := cmd.Flags().GetBool("foreground")
|
||||||
|
|
||||||
// If no operation flags provided, show help
|
// If no operation flags provided, show help
|
||||||
if !start && !stop && !status && !health && !metrics {
|
if !start && !stop && !stopAll && !status && !health && !metrics {
|
||||||
_ = cmd.Help()
|
_ = cmd.Help()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -119,6 +121,11 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stopAll {
|
||||||
|
stopAllDaemons()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If we get here and --start wasn't provided, something is wrong
|
// If we get here and --start wasn't provided, something is wrong
|
||||||
// (should have been caught by help check above)
|
// (should have been caught by help check above)
|
||||||
if !start {
|
if !start {
|
||||||
@@ -222,6 +229,7 @@ func init() {
|
|||||||
daemonCmd.Flags().Bool("auto-push", false, "Automatically push commits")
|
daemonCmd.Flags().Bool("auto-push", false, "Automatically push commits")
|
||||||
daemonCmd.Flags().Bool("local", false, "Run in local-only mode (no git required, no sync)")
|
daemonCmd.Flags().Bool("local", false, "Run in local-only mode (no git required, no sync)")
|
||||||
daemonCmd.Flags().Bool("stop", false, "Stop running daemon")
|
daemonCmd.Flags().Bool("stop", false, "Stop running daemon")
|
||||||
|
daemonCmd.Flags().Bool("stop-all", false, "Stop all running bd daemons")
|
||||||
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")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/beads/internal/daemon"
|
||||||
"github.com/steveyegge/beads/internal/rpc"
|
"github.com/steveyegge/beads/internal/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -275,6 +276,59 @@ func stopDaemon(pidFile string) {
|
|||||||
fmt.Println("Daemon killed")
|
fmt.Println("Daemon killed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stopAllDaemons stops all running bd daemons (bd-47tn)
|
||||||
|
func stopAllDaemons() {
|
||||||
|
// Discover all running daemons using the registry
|
||||||
|
daemons, err := daemon.DiscoverDaemons(nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error discovering daemons: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter to only alive daemons
|
||||||
|
var alive []daemon.DaemonInfo
|
||||||
|
for _, d := range daemons {
|
||||||
|
if d.Alive {
|
||||||
|
alive = append(alive, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(alive) == 0 {
|
||||||
|
if jsonOutput {
|
||||||
|
fmt.Println(`{"stopped": 0, "message": "No running daemons found"}`)
|
||||||
|
} else {
|
||||||
|
fmt.Println("No running daemons found")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !jsonOutput {
|
||||||
|
fmt.Printf("Found %d running daemon(s), stopping...\n", len(alive))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop all daemons (with force=true for stubborn processes)
|
||||||
|
results := daemon.KillAllDaemons(alive, true)
|
||||||
|
|
||||||
|
if jsonOutput {
|
||||||
|
output, _ := json.MarshalIndent(results, "", " ")
|
||||||
|
fmt.Println(string(output))
|
||||||
|
} else {
|
||||||
|
if results.Stopped > 0 {
|
||||||
|
fmt.Printf("✓ Stopped %d daemon(s)\n", results.Stopped)
|
||||||
|
}
|
||||||
|
if results.Failed > 0 {
|
||||||
|
fmt.Printf("✗ Failed to stop %d daemon(s):\n", results.Failed)
|
||||||
|
for _, f := range results.Failures {
|
||||||
|
fmt.Printf(" - PID %d (%s): %s\n", f.PID, f.Workspace, f.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if results.Failed > 0 {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// startDaemon starts the daemon (in foreground if requested, otherwise background)
|
// startDaemon starts the daemon (in foreground if requested, otherwise background)
|
||||||
func startDaemon(interval time.Duration, autoCommit, autoPush, localMode, foreground bool, logFile, pidFile string) {
|
func startDaemon(interval time.Duration, autoCommit, autoPush, localMode, foreground bool, logFile, pidFile string) {
|
||||||
logPath, err := getLogFilePath(logFile)
|
logPath, err := getLogFilePath(logFile)
|
||||||
|
|||||||
Reference in New Issue
Block a user