refactor(config): improve sync config with warnings toggle and dedup
Code review improvements to internal/config/sync.go: 1. Warning suppression toggle - Add ConfigWarnings bool to enable/disable warnings - Add ConfigWarningWriter io.Writer for testable output 2. Consolidate sync mode constants - cmd/bd/sync_mode.go now imports from internal/config - Single source of truth for mode values - Uses shared IsValidSyncMode() for validation 3. Fix empty sovereignty semantics - Empty now returns SovereigntyNone (no restriction) - Only non-empty invalid values fall back to T1 with warning 4. Export validation helpers - IsValidSyncMode(), IsValidConflictStrategy(), IsValidSovereignty() - ValidSyncModes(), ValidConflictStrategies(), ValidSovereigntyTiers() - String() methods on all typed values 5. Logger interface - ConfigWarningWriter allows custom logging destinations - Tests can capture warnings without os.Stderr manipulation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -9,6 +10,21 @@ import (
|
||||
// Sync mode configuration values (from hq-ew1mbr.3)
|
||||
// These control how Dolt syncs with JSONL/remotes.
|
||||
|
||||
// ConfigWarnings controls whether warnings are logged for invalid config values.
|
||||
// Set to false to suppress warnings (useful for tests or scripts).
|
||||
var ConfigWarnings = true
|
||||
|
||||
// ConfigWarningWriter is the destination for config warnings.
|
||||
// Defaults to os.Stderr. Can be replaced for testing or custom logging.
|
||||
var ConfigWarningWriter io.Writer = os.Stderr
|
||||
|
||||
// logConfigWarning logs a warning message if ConfigWarnings is enabled.
|
||||
func logConfigWarning(format string, args ...interface{}) {
|
||||
if ConfigWarnings && ConfigWarningWriter != nil {
|
||||
fmt.Fprintf(ConfigWarningWriter, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// SyncMode represents the sync mode configuration
|
||||
type SyncMode string
|
||||
|
||||
@@ -31,6 +47,21 @@ var validSyncModes = map[SyncMode]bool{
|
||||
SyncModeBeltAndSuspenders: true,
|
||||
}
|
||||
|
||||
// ValidSyncModes returns the list of valid sync mode values.
|
||||
func ValidSyncModes() []string {
|
||||
return []string{
|
||||
string(SyncModeGitPortable),
|
||||
string(SyncModeRealtime),
|
||||
string(SyncModeDoltNative),
|
||||
string(SyncModeBeltAndSuspenders),
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidSyncMode returns true if the given string is a valid sync mode.
|
||||
func IsValidSyncMode(mode string) bool {
|
||||
return validSyncModes[SyncMode(strings.ToLower(strings.TrimSpace(mode)))]
|
||||
}
|
||||
|
||||
// ConflictStrategy represents the conflict resolution strategy
|
||||
type ConflictStrategy string
|
||||
|
||||
@@ -53,10 +84,27 @@ var validConflictStrategies = map[ConflictStrategy]bool{
|
||||
ConflictStrategyManual: true,
|
||||
}
|
||||
|
||||
// ValidConflictStrategies returns the list of valid conflict strategy values.
|
||||
func ValidConflictStrategies() []string {
|
||||
return []string{
|
||||
string(ConflictStrategyNewest),
|
||||
string(ConflictStrategyOurs),
|
||||
string(ConflictStrategyTheirs),
|
||||
string(ConflictStrategyManual),
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidConflictStrategy returns true if the given string is a valid conflict strategy.
|
||||
func IsValidConflictStrategy(strategy string) bool {
|
||||
return validConflictStrategies[ConflictStrategy(strings.ToLower(strings.TrimSpace(strategy)))]
|
||||
}
|
||||
|
||||
// Sovereignty represents the federation sovereignty tier
|
||||
type Sovereignty string
|
||||
|
||||
const (
|
||||
// SovereigntyNone means no sovereignty restriction (empty value)
|
||||
SovereigntyNone Sovereignty = ""
|
||||
// SovereigntyT1 is the most open tier (public repos)
|
||||
SovereigntyT1 Sovereignty = "T1"
|
||||
// SovereigntyT2 is organization-level
|
||||
@@ -67,7 +115,7 @@ const (
|
||||
SovereigntyT4 Sovereignty = "T4"
|
||||
)
|
||||
|
||||
// validSovereigntyTiers is the set of allowed sovereignty values
|
||||
// validSovereigntyTiers is the set of allowed sovereignty values (excluding empty)
|
||||
var validSovereigntyTiers = map[Sovereignty]bool{
|
||||
SovereigntyT1: true,
|
||||
SovereigntyT2: true,
|
||||
@@ -75,9 +123,28 @@ var validSovereigntyTiers = map[Sovereignty]bool{
|
||||
SovereigntyT4: true,
|
||||
}
|
||||
|
||||
// ValidSovereigntyTiers returns the list of valid sovereignty tier values.
|
||||
func ValidSovereigntyTiers() []string {
|
||||
return []string{
|
||||
string(SovereigntyT1),
|
||||
string(SovereigntyT2),
|
||||
string(SovereigntyT3),
|
||||
string(SovereigntyT4),
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidSovereignty returns true if the given string is a valid sovereignty tier.
|
||||
// Empty string is valid (means no restriction).
|
||||
func IsValidSovereignty(sovereignty string) bool {
|
||||
if sovereignty == "" {
|
||||
return true
|
||||
}
|
||||
return validSovereigntyTiers[Sovereignty(strings.ToUpper(strings.TrimSpace(sovereignty)))]
|
||||
}
|
||||
|
||||
// GetSyncMode retrieves the sync mode configuration.
|
||||
// Returns the configured mode, or SyncModeGitPortable (default) if not set or invalid.
|
||||
// Logs a warning to stderr if an invalid value is configured.
|
||||
// Logs a warning if an invalid value is configured (unless ConfigWarnings is false).
|
||||
//
|
||||
// Config key: sync.mode
|
||||
// Valid values: git-portable, realtime, dolt-native, belt-and-suspenders
|
||||
@@ -89,7 +156,8 @@ func GetSyncMode() SyncMode {
|
||||
|
||||
mode := SyncMode(strings.ToLower(strings.TrimSpace(value)))
|
||||
if !validSyncModes[mode] {
|
||||
fmt.Fprintf(os.Stderr, "Warning: invalid sync.mode %q in config (valid: git-portable, realtime, dolt-native, belt-and-suspenders), using default 'git-portable'\n", value)
|
||||
logConfigWarning("Warning: invalid sync.mode %q in config (valid: %s), using default 'git-portable'\n",
|
||||
value, strings.Join(ValidSyncModes(), ", "))
|
||||
return SyncModeGitPortable
|
||||
}
|
||||
|
||||
@@ -98,7 +166,7 @@ func GetSyncMode() SyncMode {
|
||||
|
||||
// GetConflictStrategy retrieves the conflict resolution strategy configuration.
|
||||
// Returns the configured strategy, or ConflictStrategyNewest (default) if not set or invalid.
|
||||
// Logs a warning to stderr if an invalid value is configured.
|
||||
// Logs a warning if an invalid value is configured (unless ConfigWarnings is false).
|
||||
//
|
||||
// Config key: conflict.strategy
|
||||
// Valid values: newest, ours, theirs, manual
|
||||
@@ -110,7 +178,8 @@ func GetConflictStrategy() ConflictStrategy {
|
||||
|
||||
strategy := ConflictStrategy(strings.ToLower(strings.TrimSpace(value)))
|
||||
if !validConflictStrategies[strategy] {
|
||||
fmt.Fprintf(os.Stderr, "Warning: invalid conflict.strategy %q in config (valid: newest, ours, theirs, manual), using default 'newest'\n", value)
|
||||
logConfigWarning("Warning: invalid conflict.strategy %q in config (valid: %s), using default 'newest'\n",
|
||||
value, strings.Join(ValidConflictStrategies(), ", "))
|
||||
return ConflictStrategyNewest
|
||||
}
|
||||
|
||||
@@ -118,23 +187,39 @@ func GetConflictStrategy() ConflictStrategy {
|
||||
}
|
||||
|
||||
// GetSovereignty retrieves the federation sovereignty tier configuration.
|
||||
// Returns the configured tier, or SovereigntyT1 (default) if not set or invalid.
|
||||
// Logs a warning to stderr if an invalid value is configured.
|
||||
// Returns the configured tier, or SovereigntyNone (empty, no restriction) if not set.
|
||||
// Returns SovereigntyT1 and logs a warning if an invalid non-empty value is configured.
|
||||
//
|
||||
// Config key: federation.sovereignty
|
||||
// Valid values: T1, T2, T3, T4
|
||||
// Valid values: T1, T2, T3, T4 (empty means no restriction)
|
||||
func GetSovereignty() Sovereignty {
|
||||
value := GetString("federation.sovereignty")
|
||||
if value == "" {
|
||||
return SovereigntyT1 // Default
|
||||
return SovereigntyNone // No restriction
|
||||
}
|
||||
|
||||
// Normalize to uppercase for comparison (T1, T2, etc.)
|
||||
tier := Sovereignty(strings.ToUpper(strings.TrimSpace(value)))
|
||||
if !validSovereigntyTiers[tier] {
|
||||
fmt.Fprintf(os.Stderr, "Warning: invalid federation.sovereignty %q in config (valid: T1, T2, T3, T4), using default 'T1'\n", value)
|
||||
logConfigWarning("Warning: invalid federation.sovereignty %q in config (valid: %s, or empty for no restriction), using 'T1'\n",
|
||||
value, strings.Join(ValidSovereigntyTiers(), ", "))
|
||||
return SovereigntyT1
|
||||
}
|
||||
|
||||
return tier
|
||||
}
|
||||
|
||||
// String returns the string representation of the SyncMode.
|
||||
func (m SyncMode) String() string {
|
||||
return string(m)
|
||||
}
|
||||
|
||||
// String returns the string representation of the ConflictStrategy.
|
||||
func (s ConflictStrategy) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// String returns the string representation of the Sovereignty.
|
||||
func (s Sovereignty) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user