feat(config): add validation.on-create and validation.on-sync config options (bd-t7jq)
Add .beads/config.yaml support for template validation settings: - validation.on-create: warn|error|none (default: none) - validation.on-sync: warn|error|none (default: none) When set to "warn", issues missing required sections (based on type) show warnings but operations proceed. When set to "error", operations fail. Implementation: - Add validation keys to YamlOnlyKeys in yaml_config.go - Add defaults in config.go - Wire up bd create to check validation.on-create config - Wire up bd sync to run validation before export - Add tests for config loading - Update CONFIG.md documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -114,6 +114,14 @@ func Initialize() error {
|
||||
// Create command defaults
|
||||
v.SetDefault("create.require-description", false)
|
||||
|
||||
// Validation configuration defaults (bd-t7jq)
|
||||
// Values: "warn" | "error" | "none"
|
||||
// - "none": no validation (default, backwards compatible)
|
||||
// - "warn": validate and print warnings but proceed
|
||||
// - "error": validate and fail on missing sections
|
||||
v.SetDefault("validation.on-create", "none")
|
||||
v.SetDefault("validation.on-sync", "none")
|
||||
|
||||
// Git configuration defaults (GH#600)
|
||||
v.SetDefault("git.author", "") // Override commit author (e.g., "beads-bot <beads@example.com>")
|
||||
v.SetDefault("git.no-gpg-sign", false) // Disable GPG signing for beads commits
|
||||
|
||||
@@ -786,3 +786,61 @@ func TestConfigSourceConstants(t *testing.T) {
|
||||
t.Errorf("SourceFlag = %q, want \"flag\"", SourceFlag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationConfigDefaults(t *testing.T) {
|
||||
// Isolate from environment variables
|
||||
restore := envSnapshot(t)
|
||||
defer restore()
|
||||
|
||||
// Initialize config
|
||||
if err := Initialize(); err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Test validation.on-create default is "none"
|
||||
if got := GetString("validation.on-create"); got != "none" {
|
||||
t.Errorf("GetString(validation.on-create) = %q, want \"none\"", got)
|
||||
}
|
||||
|
||||
// Test validation.on-sync default is "none"
|
||||
if got := GetString("validation.on-sync"); got != "none" {
|
||||
t.Errorf("GetString(validation.on-sync) = %q, want \"none\"", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationConfigFromFile(t *testing.T) {
|
||||
// Create a temporary directory for config file
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// Create a config file with validation settings
|
||||
configContent := `
|
||||
validation:
|
||||
on-create: error
|
||||
on-sync: warn
|
||||
`
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0750); err != nil {
|
||||
t.Fatalf("failed to create .beads directory: %v", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(beadsDir, "config.yaml")
|
||||
if err := os.WriteFile(configPath, []byte(configContent), 0600); err != nil {
|
||||
t.Fatalf("failed to write config file: %v", err)
|
||||
}
|
||||
|
||||
// Change to tmp directory
|
||||
t.Chdir(tmpDir)
|
||||
|
||||
// Initialize viper
|
||||
if err := Initialize(); err != nil {
|
||||
t.Fatalf("Initialize() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Test that validation settings are loaded correctly
|
||||
if got := GetString("validation.on-create"); got != "error" {
|
||||
t.Errorf("GetString(validation.on-create) = %q, want \"error\"", got)
|
||||
}
|
||||
if got := GetString("validation.on-sync"); got != "warn" {
|
||||
t.Errorf("GetString(validation.on-sync) = %q, want \"warn\"", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ var YamlOnlyKeys = map[string]bool{
|
||||
|
||||
// Create command settings
|
||||
"create.require-description": true,
|
||||
|
||||
// Validation settings (bd-t7jq)
|
||||
// Values: "warn" | "error" | "none"
|
||||
"validation.on-create": true,
|
||||
"validation.on-sync": true,
|
||||
}
|
||||
|
||||
// IsYamlOnlyKey returns true if the given key should be stored in config.yaml
|
||||
@@ -65,7 +70,7 @@ func IsYamlOnlyKey(key string) bool {
|
||||
}
|
||||
|
||||
// Check prefix matches for nested keys
|
||||
prefixes := []string{"routing.", "sync.", "git.", "directory.", "repos.", "external_projects."}
|
||||
prefixes := []string{"routing.", "sync.", "git.", "directory.", "repos.", "external_projects.", "validation."}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(key, prefix) {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user