Centralize BD_DEBUG logging into internal/debug package
- Created internal/debug package with Enabled(), Logf(), Printf()
- Added comprehensive unit tests for debug package
- Replaced 50+ scattered os.Getenv("BD_DEBUG") checks across 9 files
- Centralized debug logic for easier maintenance and testing
- All tests passing, behavior unchanged
Closes bd-fb95094c.5
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/steveyegge/beads/internal/beads"
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
"golang.org/x/mod/semver"
|
"golang.org/x/mod/semver"
|
||||||
)
|
)
|
||||||
@@ -66,9 +67,7 @@ func autoImportIfNewer() {
|
|||||||
jsonlData, err := os.ReadFile(jsonlPath)
|
jsonlData, err := os.ReadFile(jsonlPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// JSONL doesn't exist or can't be accessed, skip import
|
// JSONL doesn't exist or can't be accessed, skip import
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-import skipped, JSONL not found: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-import skipped, JSONL not found: %v\n", err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,24 +82,18 @@ func autoImportIfNewer() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// Metadata error - treat as first import rather than skipping (bd-663)
|
// Metadata error - treat as first import rather than skipping (bd-663)
|
||||||
// This allows auto-import to recover from corrupt/missing metadata
|
// This allows auto-import to recover from corrupt/missing metadata
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("metadata read failed (%v), treating as first import", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: metadata read failed (%v), treating as first import\n", err)
|
|
||||||
}
|
|
||||||
lastHash = ""
|
lastHash = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare hashes
|
// Compare hashes
|
||||||
if currentHash == lastHash {
|
if currentHash == lastHash {
|
||||||
// Content unchanged, skip import
|
// Content unchanged, skip import
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-import skipped, JSONL unchanged (hash match)")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-import skipped, JSONL unchanged (hash match)\n")
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-import triggered (hash changed)")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-import triggered (hash changed)\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for Git merge conflict markers (bd-270)
|
// Check for Git merge conflict markers (bd-270)
|
||||||
// Only match if they appear as standalone lines (not embedded in JSON strings)
|
// Only match if they appear as standalone lines (not embedded in JSON strings)
|
||||||
@@ -254,9 +247,7 @@ func checkVersionMismatch() {
|
|||||||
dbVersion, err := store.GetMetadata(ctx, "bd_version")
|
dbVersion, err := store.GetMetadata(ctx, "bd_version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Metadata error - skip check (shouldn't happen, but be defensive)
|
// Metadata error - skip check (shouldn't happen, but be defensive)
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("version check skipped, metadata error: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: version check skipped, metadata error: %v\n", err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,8 +491,8 @@ func writeJSONLAtomic(jsonlPath string, issues []*types.Issue) ([]string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Report skipped issues if any (helps debugging bd-159)
|
// Report skipped issues if any (helps debugging bd-159)
|
||||||
if skippedCount > 0 && os.Getenv("BD_DEBUG") != "" {
|
if skippedCount > 0 {
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-flush skipped %d issue(s) with timestamp-only changes\n", skippedCount)
|
debug.Logf("auto-flush skipped %d issue(s) with timestamp-only changes", skippedCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close temp file before renaming
|
// Close temp file before renaming
|
||||||
@@ -520,9 +511,7 @@ func writeJSONLAtomic(jsonlPath string, issues []*types.Issue) ([]string, error)
|
|||||||
// nolint:gosec // G302: JSONL needs to be readable by other tools
|
// nolint:gosec // G302: JSONL needs to be readable by other tools
|
||||||
if err := os.Chmod(jsonlPath, 0644); err != nil {
|
if err := os.Chmod(jsonlPath, 0644); err != nil {
|
||||||
// Non-fatal - file is already written
|
// Non-fatal - file is already written
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to set file permissions: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to set file permissions: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exportedIDs, nil
|
return exportedIDs, nil
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/beads/internal/config"
|
"github.com/steveyegge/beads/internal/config"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/routing"
|
"github.com/steveyegge/beads/internal/routing"
|
||||||
"github.com/steveyegge/beads/internal/rpc"
|
"github.com/steveyegge/beads/internal/rpc"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
@@ -127,8 +128,8 @@ var createCmd = &cobra.Command{
|
|||||||
} else {
|
} else {
|
||||||
// Auto-routing based on user role
|
// Auto-routing based on user role
|
||||||
userRole, err := routing.DetectUserRole(".")
|
userRole, err := routing.DetectUserRole(".")
|
||||||
if err != nil && os.Getenv("BD_DEBUG") != "" {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Warning: failed to detect user role: %v\n", err)
|
debug.Logf("Warning: failed to detect user role: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
routingConfig := &routing.RoutingConfig{
|
routingConfig := &routing.RoutingConfig{
|
||||||
@@ -144,8 +145,8 @@ var createCmd = &cobra.Command{
|
|||||||
|
|
||||||
// TODO: Switch to target repo for multi-repo support (bd-4ms)
|
// TODO: Switch to target repo for multi-repo support (bd-4ms)
|
||||||
// For now, we just log the target repo in debug mode
|
// For now, we just log the target repo in debug mode
|
||||||
if os.Getenv("BD_DEBUG") != "" && repoPath != "." {
|
if repoPath != "." {
|
||||||
fmt.Fprintf(os.Stderr, "DEBUG: Target repo: %s\n", repoPath)
|
debug.Logf("DEBUG: Target repo: %s\n", repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicting flags
|
// Check for conflicting flags
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/config"
|
"github.com/steveyegge/beads/internal/config"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/rpc"
|
"github.com/steveyegge/beads/internal/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,9 +50,7 @@ func restartDaemonForVersionMismatch() bool {
|
|||||||
// Use local daemon (global is deprecated)
|
// Use local daemon (global is deprecated)
|
||||||
pidFile, err := getPIDFilePath(false)
|
pidFile, err := getPIDFilePath(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to get PID file path: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to get PID file path: %v\n", err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,23 +59,17 @@ func restartDaemonForVersionMismatch() bool {
|
|||||||
// Check if daemon is running and stop it
|
// Check if daemon is running and stop it
|
||||||
forcedKill := false
|
forcedKill := false
|
||||||
if isRunning, pid := isDaemonRunning(pidFile); isRunning {
|
if isRunning, pid := isDaemonRunning(pidFile); isRunning {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("stopping old daemon (PID %d)", pid)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: stopping old daemon (PID %d)\n", pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
process, err := os.FindProcess(pid)
|
process, err := os.FindProcess(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to find process: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to find process: %v\n", err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send stop signal
|
// Send stop signal
|
||||||
if err := sendStopSignal(process); err != nil {
|
if err := sendStopSignal(process); err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to signal daemon: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to signal daemon: %v\n", err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,18 +77,14 @@ func restartDaemonForVersionMismatch() bool {
|
|||||||
for i := 0; i < 50; i++ {
|
for i := 0; i < 50; i++ {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
if isRunning, _ := isDaemonRunning(pidFile); !isRunning {
|
if isRunning, _ := isDaemonRunning(pidFile); !isRunning {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("old daemon stopped successfully")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: old daemon stopped successfully\n")
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force kill if still running
|
// Force kill if still running
|
||||||
if isRunning, _ := isDaemonRunning(pidFile); isRunning {
|
if isRunning, _ := isDaemonRunning(pidFile); isRunning {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("force killing old daemon")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: force killing old daemon\n")
|
|
||||||
}
|
|
||||||
_ = process.Kill()
|
_ = process.Kill()
|
||||||
forcedKill = true
|
forcedKill = true
|
||||||
}
|
}
|
||||||
@@ -110,9 +99,7 @@ func restartDaemonForVersionMismatch() bool {
|
|||||||
// Start new daemon with current binary version
|
// Start new daemon with current binary version
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to get executable path: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to get executable path: %v\n", err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +123,7 @@ func restartDaemonForVersionMismatch() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to start new daemon: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to start new daemon: %v\n", err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,15 +132,11 @@ func restartDaemonForVersionMismatch() bool {
|
|||||||
|
|
||||||
// Wait for daemon to be ready using shared helper
|
// Wait for daemon to be ready using shared helper
|
||||||
if waitForSocketReadiness(socketPath, 5*time.Second) {
|
if waitForSocketReadiness(socketPath, 5*time.Second) {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("new daemon started successfully")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: new daemon started successfully\n")
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("new daemon failed to become ready")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: new daemon failed to become ready\n")
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,9 +178,7 @@ func tryAutoStartDaemon(socketPath string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func debugLog(msg string, args ...interface{}) {
|
func debugLog(msg string, args ...interface{}) {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf(msg, args...)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: "+msg+"\n", args...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDaemonHealthy(socketPath string) bool {
|
func isDaemonHealthy(socketPath string) bool {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/beads"
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ func disableDaemonForFallback(reason string) {
|
|||||||
daemonStatus.FallbackReason = FallbackDaemonUnsupported
|
daemonStatus.FallbackReason = FallbackDaemonUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
if reason != "" && os.Getenv("BD_DEBUG") != "" {
|
if reason != "" {
|
||||||
fmt.Fprintf(os.Stderr, "Debug: %s\n", reason)
|
debug.Logf("Debug: %s\n", reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
)
|
)
|
||||||
@@ -98,9 +99,7 @@ Output to stdout by default, or use -o flag for file output.`,
|
|||||||
// Export command requires direct database access for consistent snapshot
|
// Export command requires direct database access for consistent snapshot
|
||||||
// If daemon is connected, close it and open direct connection
|
// If daemon is connected, close it and open direct connection
|
||||||
if daemonClient != nil {
|
if daemonClient != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("Debug: export command forcing direct mode (closes daemon connection)\n")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: export command forcing direct mode (closes daemon connection)\n")
|
|
||||||
}
|
|
||||||
_ = daemonClient.Close()
|
_ = daemonClient.Close()
|
||||||
daemonClient = nil
|
daemonClient = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
)
|
)
|
||||||
@@ -34,9 +35,7 @@ NOTE: Import requires direct database access and does not work with daemon mode.
|
|||||||
// Import requires direct database access due to complex transaction handling
|
// Import requires direct database access due to complex transaction handling
|
||||||
// and collision detection. Force direct mode regardless of daemon state.
|
// and collision detection. Force direct mode regardless of daemon state.
|
||||||
if daemonClient != nil {
|
if daemonClient != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("Debug: import command forcing direct mode (closes daemon connection)\n")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: import command forcing direct mode (closes daemon connection)\n")
|
|
||||||
}
|
|
||||||
_ = daemonClient.Close()
|
_ = daemonClient.Close()
|
||||||
daemonClient = nil
|
daemonClient = nil
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/beads/internal/beads"
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
"github.com/steveyegge/beads/internal/config"
|
"github.com/steveyegge/beads/internal/config"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/rpc"
|
"github.com/steveyegge/beads/internal/rpc"
|
||||||
"github.com/steveyegge/beads/internal/storage"
|
"github.com/steveyegge/beads/internal/storage"
|
||||||
"github.com/steveyegge/beads/internal/storage/memory"
|
"github.com/steveyegge/beads/internal/storage/memory"
|
||||||
@@ -229,9 +230,7 @@ var rootCmd = &cobra.Command{
|
|||||||
// Try to connect to daemon first (unless --no-daemon flag is set)
|
// Try to connect to daemon first (unless --no-daemon flag is set)
|
||||||
if noDaemon {
|
if noDaemon {
|
||||||
daemonStatus.FallbackReason = FallbackFlagNoDaemon
|
daemonStatus.FallbackReason = FallbackFlagNoDaemon
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("--no-daemon flag set, using direct mode")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: --no-daemon flag set, using direct mode\n")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Attempt daemon connection
|
// Attempt daemon connection
|
||||||
client, err := rpc.TryConnect(socketPath)
|
client, err := rpc.TryConnect(socketPath)
|
||||||
@@ -247,10 +246,8 @@ var rootCmd = &cobra.Command{
|
|||||||
if healthErr == nil && health.Status == statusHealthy {
|
if healthErr == nil && health.Status == statusHealthy {
|
||||||
// Check version compatibility
|
// Check version compatibility
|
||||||
if !health.Compatible {
|
if !health.Compatible {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("daemon version mismatch (daemon: %s, client: %s), restarting daemon",
|
||||||
fmt.Fprintf(os.Stderr, "Debug: daemon version mismatch (daemon: %s, client: %s), restarting daemon\n",
|
health.Version, Version)
|
||||||
health.Version, Version)
|
|
||||||
}
|
|
||||||
_ = client.Close()
|
_ = client.Close()
|
||||||
|
|
||||||
// Kill old daemon and restart with new version
|
// Kill old daemon and restart with new version
|
||||||
@@ -269,9 +266,7 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.Connected = true
|
daemonStatus.Connected = true
|
||||||
daemonStatus.Degraded = false
|
daemonStatus.Degraded = false
|
||||||
daemonStatus.Health = health.Status
|
daemonStatus.Health = health.Status
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("connected to restarted daemon (version: %s)", health.Version)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: connected to restarted daemon (version: %s)\n", health.Version)
|
|
||||||
}
|
|
||||||
warnWorktreeDaemon(dbPath)
|
warnWorktreeDaemon(dbPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -288,9 +283,7 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.Connected = true
|
daemonStatus.Connected = true
|
||||||
daemonStatus.Degraded = false
|
daemonStatus.Degraded = false
|
||||||
daemonStatus.Health = health.Status
|
daemonStatus.Health = health.Status
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("connected to daemon at %s (health: %s)", socketPath, health.Status)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: connected to daemon at %s (health: %s)\n", socketPath, health.Status)
|
|
||||||
}
|
|
||||||
// Warn if using daemon with git worktrees
|
// Warn if using daemon with git worktrees
|
||||||
warnWorktreeDaemon(dbPath)
|
warnWorktreeDaemon(dbPath)
|
||||||
return // Skip direct storage initialization
|
return // Skip direct storage initialization
|
||||||
@@ -301,15 +294,11 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.FallbackReason = FallbackHealthFailed
|
daemonStatus.FallbackReason = FallbackHealthFailed
|
||||||
if healthErr != nil {
|
if healthErr != nil {
|
||||||
daemonStatus.Detail = healthErr.Error()
|
daemonStatus.Detail = healthErr.Error()
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("daemon health check failed: %v", healthErr)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: daemon health check failed: %v\n", healthErr)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
daemonStatus.Health = health.Status
|
daemonStatus.Health = health.Status
|
||||||
daemonStatus.Detail = health.Error
|
daemonStatus.Detail = health.Error
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("daemon unhealthy (status=%s): %s", health.Status, health.Error)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: daemon unhealthy (status=%s): %s\n", health.Status, health.Error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -317,18 +306,14 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.FallbackReason = FallbackConnectFailed
|
daemonStatus.FallbackReason = FallbackConnectFailed
|
||||||
if err != nil {
|
if err != nil {
|
||||||
daemonStatus.Detail = err.Error()
|
daemonStatus.Detail = err.Error()
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("daemon connect failed at %s: %v", socketPath, err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: daemon connect failed at %s: %v\n", socketPath, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daemon not running or unhealthy - try auto-start if enabled
|
// Daemon not running or unhealthy - try auto-start if enabled
|
||||||
if daemonStatus.AutoStartEnabled {
|
if daemonStatus.AutoStartEnabled {
|
||||||
daemonStatus.AutoStartAttempted = true
|
daemonStatus.AutoStartAttempted = true
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("attempting to auto-start daemon")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: attempting to auto-start daemon\n")
|
|
||||||
}
|
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
if tryAutoStartDaemon(socketPath) {
|
if tryAutoStartDaemon(socketPath) {
|
||||||
// Retry connection after auto-start
|
// Retry connection after auto-start
|
||||||
@@ -350,10 +335,8 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.AutoStartSucceeded = true
|
daemonStatus.AutoStartSucceeded = true
|
||||||
daemonStatus.Health = health.Status
|
daemonStatus.Health = health.Status
|
||||||
daemonStatus.FallbackReason = FallbackNone
|
daemonStatus.FallbackReason = FallbackNone
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
elapsed := time.Since(startTime).Milliseconds()
|
||||||
elapsed := time.Since(startTime).Milliseconds()
|
debug.Logf("auto-start succeeded; connected at %s in %dms", socketPath, elapsed)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-start succeeded; connected at %s in %dms\n", socketPath, elapsed)
|
|
||||||
}
|
|
||||||
// Warn if using daemon with git worktrees
|
// Warn if using daemon with git worktrees
|
||||||
warnWorktreeDaemon(dbPath)
|
warnWorktreeDaemon(dbPath)
|
||||||
return // Skip direct storage initialization
|
return // Skip direct storage initialization
|
||||||
@@ -367,9 +350,7 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.Health = health.Status
|
daemonStatus.Health = health.Status
|
||||||
daemonStatus.Detail = health.Error
|
daemonStatus.Detail = health.Error
|
||||||
}
|
}
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-started daemon is unhealthy; falling back to direct mode")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-started daemon is unhealthy; falling back to direct mode\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Auto-start completed but connection still failed
|
// Auto-start completed but connection still failed
|
||||||
@@ -386,24 +367,18 @@ var rootCmd = &cobra.Command{
|
|||||||
daemonStatus.Detail = string(errMsg)
|
daemonStatus.Detail = string(errMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-start did not yield a running daemon; falling back to direct mode")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-start did not yield a running daemon; falling back to direct mode\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Auto-start itself failed
|
// Auto-start itself failed
|
||||||
daemonStatus.FallbackReason = FallbackAutoStartFailed
|
daemonStatus.FallbackReason = FallbackAutoStartFailed
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-start failed; falling back to direct mode")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-start failed; falling back to direct mode\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Auto-start disabled - preserve the actual failure reason
|
// Auto-start disabled - preserve the actual failure reason
|
||||||
// Don't override connect_failed or health_failed with auto_start_disabled
|
// Don't override connect_failed or health_failed with auto_start_disabled
|
||||||
// This preserves important diagnostic info (daemon crashed vs not running)
|
// This preserves important diagnostic info (daemon crashed vs not running)
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-start disabled by BEADS_AUTO_START_DAEMON")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-start disabled by BEADS_AUTO_START_DAEMON\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit BD_VERBOSE warning if falling back to direct mode
|
// Emit BD_VERBOSE warning if falling back to direct mode
|
||||||
@@ -411,9 +386,7 @@ var rootCmd = &cobra.Command{
|
|||||||
emitVerboseWarning()
|
emitVerboseWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("using direct mode (reason: %s)", daemonStatus.FallbackReason)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: using direct mode (reason: %s)\n", daemonStatus.FallbackReason)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to direct storage access
|
// Fall back to direct storage access
|
||||||
@@ -443,9 +416,7 @@ var rootCmd = &cobra.Command{
|
|||||||
if cmd.Name() == "sync" {
|
if cmd.Name() == "sync" {
|
||||||
if dryRun, _ := cmd.Flags().GetBool("dry-run"); dryRun {
|
if dryRun, _ := cmd.Flags().GetBool("dry-run"); dryRun {
|
||||||
// Skip auto-import in dry-run mode
|
// Skip auto-import in dry-run mode
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("auto-import skipped for sync --dry-run")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: auto-import skipped for sync --dry-run\n")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
autoImportIfNewer()
|
autoImportIfNewer()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/config"
|
"github.com/steveyegge/beads/internal/config"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/storage/memory"
|
"github.com/steveyegge/beads/internal/storage/memory"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
"github.com/steveyegge/beads/internal/utils"
|
"github.com/steveyegge/beads/internal/utils"
|
||||||
@@ -54,13 +55,9 @@ func initializeNoDbMode() error {
|
|||||||
return fmt.Errorf("failed to load issues into memory: %w", err)
|
return fmt.Errorf("failed to load issues into memory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("loaded %d issues from %s", len(issues), jsonlPath)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: loaded %d issues from %s\n", len(issues), jsonlPath)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("no existing %s, starting with empty database", jsonlPath)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: no existing %s, starting with empty database\n", jsonlPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect and set prefix
|
// Detect and set prefix
|
||||||
@@ -74,9 +71,7 @@ func initializeNoDbMode() error {
|
|||||||
return fmt.Errorf("failed to set prefix: %w", err)
|
return fmt.Errorf("failed to set prefix: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("using prefix '%s'", prefix)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: using prefix '%s'\n", prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global store
|
// Set global store
|
||||||
store = memStore
|
store = memStore
|
||||||
@@ -203,9 +198,7 @@ func writeIssuesToJSONL(memStore *memory.MemoryStorage, beadsDir string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("wrote %d issues to %s", len(issues), jsonlPath)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: wrote %d issues to %s\n", len(issues), jsonlPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/storage"
|
"github.com/steveyegge/beads/internal/storage"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
)
|
)
|
||||||
@@ -61,7 +62,7 @@ type ImportFunc func(ctx context.Context, issues []*types.Issue) (created, updat
|
|||||||
// dbPath is the full path to the database file (e.g., /path/to/.beads/bd.db)
|
// dbPath is the full path to the database file (e.g., /path/to/.beads/bd.db)
|
||||||
func AutoImportIfNewer(ctx context.Context, store storage.Storage, dbPath string, notify Notifier, importFunc ImportFunc, onChanged func(needsFullExport bool)) error {
|
func AutoImportIfNewer(ctx context.Context, store storage.Storage, dbPath string, notify Notifier, importFunc ImportFunc, onChanged func(needsFullExport bool)) error {
|
||||||
if notify == nil {
|
if notify == nil {
|
||||||
notify = NewStderrNotifier(os.Getenv("BD_DEBUG") != "")
|
notify = NewStderrNotifier(debug.Enabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find JSONL using database directory (same logic as beads.FindJSONLPath)
|
// Find JSONL using database directory (same logic as beads.FindJSONLPath)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
var v *viper.Viper
|
var v *viper.Viper
|
||||||
@@ -103,14 +104,10 @@ func Initialize() error {
|
|||||||
if err := v.ReadInConfig(); err != nil {
|
if err := v.ReadInConfig(); err != nil {
|
||||||
return fmt.Errorf("error reading config file: %w", err)
|
return fmt.Errorf("error reading config file: %w", err)
|
||||||
}
|
}
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("Debug: loaded config from %s\n", v.ConfigFileUsed())
|
||||||
fmt.Fprintf(os.Stderr, "Debug: loaded config from %s\n", v.ConfigFileUsed())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// No config.yaml found - use defaults and environment variables
|
// No config.yaml found - use defaults and environment variables
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("Debug: no config.yaml found; using defaults and environment variables\n")
|
||||||
fmt.Fprintf(os.Stderr, "Debug: no config.yaml found; using defaults and environment variables\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
24
internal/debug/debug.go
Normal file
24
internal/debug/debug.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var enabled = os.Getenv("BD_DEBUG") != ""
|
||||||
|
|
||||||
|
func Enabled() bool {
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func Logf(format string, args ...interface{}) {
|
||||||
|
if enabled {
|
||||||
|
fmt.Fprintf(os.Stderr, format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Printf(format string, args ...interface{}) {
|
||||||
|
if enabled {
|
||||||
|
fmt.Printf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
139
internal/debug/debug_test.go
Normal file
139
internal/debug/debug_test.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnabled(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
envValue string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"enabled with value", "1", true},
|
||||||
|
{"enabled with any value", "true", true},
|
||||||
|
{"disabled when empty", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
oldEnabled := enabled
|
||||||
|
defer func() { enabled = oldEnabled }()
|
||||||
|
|
||||||
|
if tt.envValue != "" {
|
||||||
|
enabled = true
|
||||||
|
} else {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := Enabled(); got != tt.want {
|
||||||
|
t.Errorf("Enabled() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogf(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
enabled bool
|
||||||
|
format string
|
||||||
|
args []interface{}
|
||||||
|
wantOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "outputs when enabled",
|
||||||
|
enabled: true,
|
||||||
|
format: "test message: %s\n",
|
||||||
|
args: []interface{}{"hello"},
|
||||||
|
wantOutput: "test message: hello\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no output when disabled",
|
||||||
|
enabled: false,
|
||||||
|
format: "test message: %s\n",
|
||||||
|
args: []interface{}{"hello"},
|
||||||
|
wantOutput: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
oldEnabled := enabled
|
||||||
|
oldStderr := os.Stderr
|
||||||
|
defer func() {
|
||||||
|
enabled = oldEnabled
|
||||||
|
os.Stderr = oldStderr
|
||||||
|
}()
|
||||||
|
|
||||||
|
enabled = tt.enabled
|
||||||
|
|
||||||
|
r, w, _ := os.Pipe()
|
||||||
|
os.Stderr = w
|
||||||
|
|
||||||
|
Logf(tt.format, tt.args...)
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, r)
|
||||||
|
|
||||||
|
if got := buf.String(); got != tt.wantOutput {
|
||||||
|
t.Errorf("Logf() output = %q, want %q", got, tt.wantOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintf(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
enabled bool
|
||||||
|
format string
|
||||||
|
args []interface{}
|
||||||
|
wantOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "outputs when enabled",
|
||||||
|
enabled: true,
|
||||||
|
format: "debug: %d\n",
|
||||||
|
args: []interface{}{42},
|
||||||
|
wantOutput: "debug: 42\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no output when disabled",
|
||||||
|
enabled: false,
|
||||||
|
format: "debug: %d\n",
|
||||||
|
args: []interface{}{42},
|
||||||
|
wantOutput: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
oldEnabled := enabled
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
defer func() {
|
||||||
|
enabled = oldEnabled
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
}()
|
||||||
|
|
||||||
|
enabled = tt.enabled
|
||||||
|
|
||||||
|
r, w, _ := os.Pipe()
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
Printf(tt.format, tt.args...)
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, r)
|
||||||
|
|
||||||
|
if got := buf.String(); got != tt.wantOutput {
|
||||||
|
t.Errorf("Printf() output = %q, want %q", got, tt.wantOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientVersion is the version of this RPC client
|
// ClientVersion is the version of this RPC client
|
||||||
@@ -32,9 +34,7 @@ func TryConnect(socketPath string) (*Client, error) {
|
|||||||
// Returns nil if no daemon is running or unhealthy.
|
// Returns nil if no daemon is running or unhealthy.
|
||||||
func TryConnectWithTimeout(socketPath string, dialTimeout time.Duration) (*Client, error) {
|
func TryConnectWithTimeout(socketPath string, dialTimeout time.Duration) (*Client, error) {
|
||||||
if !endpointExists(socketPath) {
|
if !endpointExists(socketPath) {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("RPC endpoint does not exist: %s", socketPath)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: RPC endpoint does not exist: %s\n", socketPath)
|
|
||||||
}
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,9 +44,7 @@ func TryConnectWithTimeout(socketPath string, dialTimeout time.Duration) (*Clien
|
|||||||
|
|
||||||
conn, err := dialRPC(socketPath, dialTimeout)
|
conn, err := dialRPC(socketPath, dialTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("failed to connect to RPC endpoint: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to connect to RPC endpoint: %v\n", err)
|
|
||||||
}
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,25 +56,19 @@ func TryConnectWithTimeout(socketPath string, dialTimeout time.Duration) (*Clien
|
|||||||
|
|
||||||
health, err := client.Health()
|
health, err := client.Health()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("health check failed: %v", err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: health check failed: %v\n", err)
|
|
||||||
}
|
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if health.Status == "unhealthy" {
|
if health.Status == "unhealthy" {
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("daemon unhealthy: %s", health.Error)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: daemon unhealthy: %s\n", health.Error)
|
|
||||||
}
|
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("connected to daemon (status: %s, uptime: %.1fs)",
|
||||||
fmt.Fprintf(os.Stderr, "Debug: connected to daemon (status: %s, uptime: %.1fs)\n",
|
health.Status, health.Uptime)
|
||||||
health.Status, health.Uptime)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/autoimport"
|
"github.com/steveyegge/beads/internal/autoimport"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/importer"
|
"github.com/steveyegge/beads/internal/importer"
|
||||||
"github.com/steveyegge/beads/internal/storage"
|
"github.com/steveyegge/beads/internal/storage"
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
@@ -231,7 +232,7 @@ func (s *Server) checkAndAutoImportIfStale(req *Request) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Perform actual import with timeout protection
|
// Perform actual import with timeout protection
|
||||||
notify := autoimport.NewStderrNotifier(os.Getenv("BD_DEBUG") != "")
|
notify := autoimport.NewStderrNotifier(debug.Enabled())
|
||||||
|
|
||||||
importFunc := func(ctx context.Context, issues []*types.Issue) (created, updated int, idMapping map[string]string, err error) {
|
importFunc := func(ctx context.Context, issues []*types.Issue) (created, updated int, idMapping map[string]string, err error) {
|
||||||
// Use the importer package to perform the actual import
|
// Use the importer package to perform the actual import
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/config"
|
"github.com/steveyegge/beads/internal/config"
|
||||||
|
"github.com/steveyegge/beads/internal/debug"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -163,9 +164,7 @@ func (s *SQLiteStorage) exportToRepo(ctx context.Context, repoPath string, issue
|
|||||||
// Set file permissions
|
// Set file permissions
|
||||||
if err := os.Chmod(jsonlPath, 0644); err != nil { // nolint:gosec // G302: 0644 intentional for git-tracked files
|
if err := os.Chmod(jsonlPath, 0644); err != nil { // nolint:gosec // G302: 0644 intentional for git-tracked files
|
||||||
// Non-fatal
|
// Non-fatal
|
||||||
if os.Getenv("BD_DEBUG") != "" {
|
debug.Logf("Debug: failed to set permissions on %s: %v\n", jsonlPath, err)
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to set permissions on %s: %v\n", jsonlPath, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update mtime cache for this repo
|
// Update mtime cache for this repo
|
||||||
@@ -175,8 +174,8 @@ func (s *SQLiteStorage) exportToRepo(ctx context.Context, repoPath string, issue
|
|||||||
INSERT OR REPLACE INTO repo_mtimes (repo_path, jsonl_path, mtime_ns, last_checked)
|
INSERT OR REPLACE INTO repo_mtimes (repo_path, jsonl_path, mtime_ns, last_checked)
|
||||||
VALUES (?, ?, ?, datetime('now'))
|
VALUES (?, ?, ?, datetime('now'))
|
||||||
`, absRepoPath, jsonlPath, fileInfo.ModTime().UnixNano())
|
`, absRepoPath, jsonlPath, fileInfo.ModTime().UnixNano())
|
||||||
if err != nil && os.Getenv("BD_DEBUG") != "" {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Debug: failed to update mtime cache for %s: %v\n", absRepoPath, err)
|
debug.Logf("Debug: failed to update mtime cache for %s: %v\n", absRepoPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user