feat(config): support daemon.auto_* settings in config.yaml (GH#871)
Allow team-wide auto-sync configuration via config.yaml instead of SQLite. This enables teams to share auto-commit/auto-push settings through version control. Changes: - Add daemon.auto_commit, daemon.auto_push, daemon.auto_pull to YamlOnlyKeys - Add daemon.* prefix to YAML-only prefixes - Update daemon startup to read from config.yaml first, then fall back to SQLite - Update bd init --team to write daemon settings to config.yaml Usage: # In .beads/config.yaml (version controlled, shared by team) daemon.auto_commit: true daemon.auto_push: true # Or via bd config set bd config set daemon.auto_commit true 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/steveyegge/beads/cmd/bd/doctor"
|
"github.com/steveyegge/beads/cmd/bd/doctor"
|
||||||
"github.com/steveyegge/beads/internal/beads"
|
"github.com/steveyegge/beads/internal/beads"
|
||||||
|
"github.com/steveyegge/beads/internal/config"
|
||||||
"github.com/steveyegge/beads/internal/daemon"
|
"github.com/steveyegge/beads/internal/daemon"
|
||||||
"github.com/steveyegge/beads/internal/rpc"
|
"github.com/steveyegge/beads/internal/rpc"
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
@@ -67,10 +68,15 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
// GH#871: Read from config.yaml first (team-shared), then fall back to SQLite (legacy)
|
||||||
// (skip if --stop, --status, --health, --metrics)
|
// (skip if --stop, --status, --health, --metrics)
|
||||||
if start && !stop && !status && !health && !metrics {
|
if start && !stop && !status && !health && !metrics {
|
||||||
if !cmd.Flags().Changed("auto-commit") {
|
if !cmd.Flags().Changed("auto-commit") {
|
||||||
if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
// Check config.yaml first (GH#871: team-wide settings)
|
||||||
|
if config.GetBool("daemon.auto_commit") {
|
||||||
|
autoCommit = true
|
||||||
|
} else if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
||||||
|
// Fall back to SQLite for backwards compatibility
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
store, err := sqlite.New(ctx, dbPath)
|
store, err := sqlite.New(ctx, dbPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -82,7 +88,11 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !cmd.Flags().Changed("auto-push") {
|
if !cmd.Flags().Changed("auto-push") {
|
||||||
if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
// Check config.yaml first (GH#871: team-wide settings)
|
||||||
|
if config.GetBool("daemon.auto_push") {
|
||||||
|
autoPush = true
|
||||||
|
} else if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
||||||
|
// Fall back to SQLite for backwards compatibility
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
store, err := sqlite.New(ctx, dbPath)
|
store, err := sqlite.New(ctx, dbPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -97,8 +107,11 @@ Run 'bd daemon' with no flags to see available options.`,
|
|||||||
// Check environment variable first
|
// Check environment variable first
|
||||||
if envVal := os.Getenv("BEADS_AUTO_PULL"); envVal != "" {
|
if envVal := os.Getenv("BEADS_AUTO_PULL"); envVal != "" {
|
||||||
autoPull = envVal == "true" || envVal == "1"
|
autoPull = envVal == "true" || envVal == "1"
|
||||||
|
} else if config.GetBool("daemon.auto_pull") {
|
||||||
|
// Check config.yaml (GH#871: team-wide settings)
|
||||||
|
autoPull = true
|
||||||
} else if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
} else if dbPath := beads.FindDatabasePath(); dbPath != "" {
|
||||||
// Check database config
|
// Fall back to SQLite for backwards compatibility
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
store, err := sqlite.New(ctx, dbPath)
|
store, err := sqlite.New(ctx, dbPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/steveyegge/beads/internal/config"
|
||||||
"github.com/steveyegge/beads/internal/storage"
|
"github.com/steveyegge/beads/internal/storage"
|
||||||
"github.com/steveyegge/beads/internal/ui"
|
"github.com/steveyegge/beads/internal/ui"
|
||||||
)
|
)
|
||||||
@@ -117,11 +118,12 @@ func runTeamWizard(ctx context.Context, store storage.Storage) error {
|
|||||||
autoSync := !(response == "n" || response == "no")
|
autoSync := !(response == "n" || response == "no")
|
||||||
|
|
||||||
if autoSync {
|
if autoSync {
|
||||||
if err := store.SetConfig(ctx, "daemon.auto_commit", "true"); err != nil {
|
// GH#871: Write to config.yaml for team-wide settings (version controlled)
|
||||||
|
if err := config.SetYamlConfig("daemon.auto_commit", "true"); err != nil {
|
||||||
return fmt.Errorf("failed to enable auto-commit: %w", err)
|
return fmt.Errorf("failed to enable auto-commit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := store.SetConfig(ctx, "daemon.auto_push", "true"); err != nil {
|
if err := config.SetYamlConfig("daemon.auto_push", "true"); err != nil {
|
||||||
return fmt.Errorf("failed to enable auto-push: %w", err)
|
return fmt.Errorf("failed to enable auto-push: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ var YamlOnlyKeys = map[string]bool{
|
|||||||
"sync.branch": true,
|
"sync.branch": true,
|
||||||
"sync.require_confirmation_on_mass_delete": true,
|
"sync.require_confirmation_on_mass_delete": true,
|
||||||
|
|
||||||
|
// Daemon settings (GH#871: team-wide auto-sync config)
|
||||||
|
"daemon.auto_commit": true,
|
||||||
|
"daemon.auto_push": true,
|
||||||
|
"daemon.auto_pull": true,
|
||||||
|
|
||||||
// Routing settings
|
// Routing settings
|
||||||
"routing.mode": true,
|
"routing.mode": true,
|
||||||
"routing.default": true,
|
"routing.default": true,
|
||||||
@@ -70,7 +75,7 @@ func IsYamlOnlyKey(key string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check prefix matches for nested keys
|
// Check prefix matches for nested keys
|
||||||
prefixes := []string{"routing.", "sync.", "git.", "directory.", "repos.", "external_projects.", "validation."}
|
prefixes := []string{"routing.", "sync.", "git.", "directory.", "repos.", "external_projects.", "validation.", "daemon."}
|
||||||
for _, prefix := range prefixes {
|
for _, prefix := range prefixes {
|
||||||
if strings.HasPrefix(key, prefix) {
|
if strings.HasPrefix(key, prefix) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ func TestIsYamlOnlyKey(t *testing.T) {
|
|||||||
{"repos.primary", true},
|
{"repos.primary", true},
|
||||||
{"external_projects.beads", true},
|
{"external_projects.beads", true},
|
||||||
|
|
||||||
|
// Daemon settings (GH#871)
|
||||||
|
{"daemon.auto_commit", true},
|
||||||
|
{"daemon.auto_push", true},
|
||||||
|
{"daemon.auto_pull", true},
|
||||||
|
{"daemon.custom_setting", true}, // prefix match
|
||||||
|
|
||||||
// SQLite keys (should return false)
|
// SQLite keys (should return false)
|
||||||
{"jira.url", false},
|
{"jira.url", false},
|
||||||
{"jira.project", false},
|
{"jira.project", false},
|
||||||
|
|||||||
Reference in New Issue
Block a user