feat: Respect BEADS_SYNC_BRANCH environment variable

Fixes daemon and bd sync to honor BEADS_SYNC_BRANCH environment variable
as documented in PROTECTED_BRANCHES.md for CI/CD temporary overrides.

Changes:
- Updated internal/syncbranch.Get() to prioritize env var over DB config
- Both daemon sync and bd sync CLI now use syncbranch.Get()
- Added comprehensive tests for env var override behavior
- Validates branch names using git-style rules

This enables CI/CD workflows to override sync branch per-job without
mutating database config.

Based on PR #364 by Charles P. Cross <cpdata@users.noreply.github.com>
Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
This commit is contained in:
Steve Yegge
2025-11-22 18:17:19 -08:00
parent 72aa0d1097
commit 0dc8452c56
6 changed files with 220 additions and 69 deletions

View File

@@ -7,6 +7,9 @@ import (
"path/filepath"
"strings"
"testing"
"github.com/steveyegge/beads/internal/storage/sqlite"
"github.com/steveyegge/beads/internal/syncbranch"
)
func TestIsGitRepo_InGitRepo(t *testing.T) {
@@ -386,3 +389,55 @@ func TestMergeSyncBranch_DirtyWorkingTree(t *testing.T) {
t.Error("expected dirty working tree for test setup")
}
}
func TestGetSyncBranch_EnvOverridesDB(t *testing.T) {
ctx := context.Background()
// Save and restore global store state
oldStore := store
storeMutex.Lock()
oldStoreActive := storeActive
storeMutex.Unlock()
oldDBPath := dbPath
// Use an in-memory SQLite store for testing
testStore, err := sqlite.New(context.Background(), "file::memory:?mode=memory&cache=private")
if err != nil {
t.Fatalf("failed to create test store: %v", err)
}
defer testStore.Close()
// Seed DB config and globals
if err := testStore.SetConfig(ctx, "sync.branch", "db-branch"); err != nil {
t.Fatalf("failed to set sync.branch in db: %v", err)
}
storeMutex.Lock()
store = testStore
storeActive = true
storeMutex.Unlock()
dbPath = "" // avoid FindDatabasePath in ensureStoreActive
// Set environment override
if err := os.Setenv(syncbranch.EnvVar, "env-branch"); err != nil {
t.Fatalf("failed to set %s: %v", syncbranch.EnvVar, err)
}
defer os.Unsetenv(syncbranch.EnvVar)
// Ensure we restore globals after the test
defer func() {
storeMutex.Lock()
store = oldStore
storeActive = oldStoreActive
storeMutex.Unlock()
dbPath = oldDBPath
}()
branch, err := getSyncBranch(ctx)
if err != nil {
t.Fatalf("getSyncBranch() error = %v", err)
}
if branch != "env-branch" {
t.Errorf("getSyncBranch() = %q, want %q (env override)", branch, "env-branch")
}
}