fix: prevent gt down --all from respawning bd daemon (#457)

CountBdDaemons() was using `bd daemon list --json` which triggers
daemon auto-start as a side effect. During shutdown verification,
this caused a new daemon to spawn after all daemons were killed,
resulting in "bd daemon shutdown incomplete: 1 still running" error.

Replaced all `bd daemon killall` calls with pkill in:
- stopBdDaemons()
- restartBdDaemons()

Changed CountBdDaemons() to use pgrep instead of bd daemon list.
Also removed the now-unused parseBdDaemonCount helper function and its tests.
This commit is contained in:
dustin
2026-01-14 04:28:16 +07:00
committed by GitHub
parent bedccb1634
commit 66805079de
2 changed files with 13 additions and 81 deletions

View File

@@ -109,9 +109,8 @@ func EnsureBdDaemonHealth(workDir string) string {
// restartBdDaemons restarts all bd daemons.
func restartBdDaemons() error { //nolint:unparam // error return kept for future use
// Stop all daemons first
stopCmd := exec.Command("bd", "daemon", "killall")
_ = stopCmd.Run() // Ignore errors - daemons might not be running
// Stop all daemons first using pkill to avoid auto-start side effects
_ = exec.Command("pkill", "-TERM", "-f", "bd daemon").Run()
// Give time for cleanup
time.Sleep(200 * time.Millisecond)
@@ -159,39 +158,20 @@ func StopAllBdProcesses(dryRun, force bool) (int, int, error) {
}
// CountBdDaemons returns count of running bd daemons.
// Uses pgrep instead of "bd daemon list" to avoid triggering daemon auto-start
// during shutdown verification.
func CountBdDaemons() int {
listCmd := exec.Command("bd", "daemon", "list", "--json")
output, err := listCmd.Output()
// Use pgrep -f with wc -l for cross-platform compatibility
// (macOS pgrep doesn't support -c flag)
cmd := exec.Command("sh", "-c", "pgrep -f 'bd daemon' 2>/dev/null | wc -l")
output, err := cmd.Output()
if err != nil {
return 0
}
return parseBdDaemonCount(output)
count, _ := strconv.Atoi(strings.TrimSpace(string(output)))
return count
}
// parseBdDaemonCount parses bd daemon list --json output.
func parseBdDaemonCount(output []byte) int {
if len(output) == 0 {
return 0
}
var daemons []any
if err := json.Unmarshal(output, &daemons); err == nil {
return len(daemons)
}
var wrapper struct {
Daemons []any `json:"daemons"`
Count int `json:"count"`
}
if err := json.Unmarshal(output, &wrapper); err == nil {
if wrapper.Count > 0 {
return wrapper.Count
}
return len(wrapper.Daemons)
}
return 0
}
func stopBdDaemons(force bool) (int, int) {
before := CountBdDaemons()
@@ -199,19 +179,11 @@ func stopBdDaemons(force bool) (int, int) {
return 0, 0
}
killCmd := exec.Command("bd", "daemon", "killall")
_ = killCmd.Run()
time.Sleep(100 * time.Millisecond)
after := CountBdDaemons()
if after == 0 {
return before, 0
}
// Use pkill directly instead of "bd daemon killall" to avoid triggering
// daemon auto-start as a side effect of running bd commands.
// Note: pkill -f pattern may match unintended processes in rare cases
// (e.g., editors with "bd daemon" in file content). This is acceptable
// as a fallback when bd daemon killall fails.
// given the alternative of respawning daemons during shutdown.
if force {
_ = exec.Command("pkill", "-9", "-f", "bd daemon").Run()
} else {

View File

@@ -5,46 +5,6 @@ import (
"testing"
)
func TestParseBdDaemonCount_Array(t *testing.T) {
input := []byte(`[{"pid":1234},{"pid":5678}]`)
count := parseBdDaemonCount(input)
if count != 2 {
t.Errorf("expected 2, got %d", count)
}
}
func TestParseBdDaemonCount_ObjectWithCount(t *testing.T) {
input := []byte(`{"count":3,"daemons":[{},{},{}]}`)
count := parseBdDaemonCount(input)
if count != 3 {
t.Errorf("expected 3, got %d", count)
}
}
func TestParseBdDaemonCount_ObjectWithDaemons(t *testing.T) {
input := []byte(`{"daemons":[{},{}]}`)
count := parseBdDaemonCount(input)
if count != 2 {
t.Errorf("expected 2, got %d", count)
}
}
func TestParseBdDaemonCount_Empty(t *testing.T) {
input := []byte(``)
count := parseBdDaemonCount(input)
if count != 0 {
t.Errorf("expected 0, got %d", count)
}
}
func TestParseBdDaemonCount_Invalid(t *testing.T) {
input := []byte(`not json`)
count := parseBdDaemonCount(input)
if count != 0 {
t.Errorf("expected 0 for invalid JSON, got %d", count)
}
}
func TestCountBdActivityProcesses(t *testing.T) {
count := CountBdActivityProcesses()
if count < 0 {