Class: Zwischen::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/zwischen/config.rb

Constant Summary collapse

DEFAULT_CONFIG =
{
  "ai" => {
    "enabled" => true,
    "pre_push_enabled" => false,
    "provider" => "claude",
    "api_key" => nil,
    "ollama" => {
      "model" => "llama3",
      "url" => "http://localhost:11434/api/chat"
    },
    "openai" => {
      "model" => "gpt-4"
    }
  },
  "blocking" => {
    "severity" => "high"  # high, critical, or none
  },
  "scanners" => {
    "gitleaks" => { "enabled" => true },
    "semgrep" => { "enabled" => true, "config" => "p/security-audit" }
  },
  "ignore" => [
    "**/node_modules/**",
    "**/vendor/**",
    "**/.git/**",
    "**/dist/**",
    "**/build/**",
    "**/test/fixtures/**"
  ],
  "severity" => {
    "fail_on" => ["critical", "high"]
  }
}.freeze
CONFIG_FILE =
".zwischen.yml"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = {}) ⇒ Config

Returns a new instance of Config.



76
77
78
# File 'lib/zwischen/config.rb', line 76

def initialize(config = {})
  @config = DEFAULT_CONFIG.merge(config)
end

Class Method Details

.deep_merge(base, override) ⇒ Object



149
150
151
152
153
154
155
156
157
# File 'lib/zwischen/config.rb', line 149

def self.deep_merge(base, override)
  base.merge(override) do |_key, base_val, override_val|
    if base_val.is_a?(Hash) && override_val.is_a?(Hash)
      deep_merge(base_val, override_val)
    else
      override_val
    end
  end
end

.init(project_root = Dir.pwd, quiet: false) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/zwischen/config.rb', line 56

def self.init(project_root = Dir.pwd, quiet: false)
  config_path = File.join(project_root, CONFIG_FILE)
  example_path = File.join(File.dirname(__dir__), "..", ".zwischen.yml.example")

  if File.exist?(config_path)
    puts "Configuration file already exists at #{config_path}" unless quiet
    return false
  end

  if File.exist?(example_path)
    FileUtils.cp(example_path, config_path)
    puts "Created #{config_path} from example" unless quiet
  else
    File.write(config_path, DEFAULT_CONFIG.to_yaml)
    puts "Created #{config_path} with default configuration" unless quiet
  end

  true
end

.load(project_root = Dir.pwd) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/zwischen/config.rb', line 44

def self.load(project_root = Dir.pwd)
  config_path = File.join(project_root, CONFIG_FILE)
  config = DEFAULT_CONFIG.dup

  if File.exist?(config_path)
    user_config = YAML.safe_load(File.read(config_path))
    config = deep_merge(config, user_config) if user_config
  end

  new(config)
end

Instance Method Details

#ai_api_key(provider = ai_provider) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/zwischen/config.rb', line 95

def ai_api_key(provider = ai_provider)
  # Check credentials first, then config
  begin
    require_relative "credentials"
    api_key = Credentials.get_api_key(provider)
    return api_key if api_key
  rescue LoadError, NameError
    # Credentials not available, fall through to config
  end
  @config.dig("ai", "api_key")
end

#ai_enabled?Boolean

Returns:

  • (Boolean)


84
85
86
87
88
89
# File 'lib/zwischen/config.rb', line 84

def ai_enabled?
  # Default to true if provider is set, otherwise check explicit enabled flag
  provider = ai_provider
  enabled = @config.dig("ai", "enabled")
  enabled.nil? ? !provider.nil? : enabled
end

#ai_pre_push_enabled?Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/zwischen/config.rb', line 91

def ai_pre_push_enabled?
  @config.dig("ai", "pre_push_enabled") == true
end

#ai_providerObject



80
81
82
# File 'lib/zwischen/config.rb', line 80

def ai_provider
  @config.dig("ai", "provider") || "claude"
end

#ai_provider_config(provider = ai_provider) ⇒ Object



107
108
109
# File 'lib/zwischen/config.rb', line 107

def ai_provider_config(provider = ai_provider)
  @config.dig("ai", provider) || {}
end

#blocking_severityObject



111
112
113
# File 'lib/zwischen/config.rb', line 111

def blocking_severity
  @config.dig("blocking", "severity") || "high"
end

#fail_on_severitiesObject



143
144
145
# File 'lib/zwischen/config.rb', line 143

def fail_on_severities
  @config.dig("severity", "fail_on") || ["critical", "high"]
end

#ignored_pathsObject



139
140
141
# File 'lib/zwischen/config.rb', line 139

def ignored_paths
  @config["ignore"] || []
end

#scanner_enabled?(scanner) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/zwischen/config.rb', line 115

def scanner_enabled?(scanner)
  scanner_config = @config.dig("scanners", scanner.to_s)
  # Handle both formats: "gitleaks: true" (boolean) and "gitleaks: { enabled: true }" (hash)
  case scanner_config
  when true, false
    scanner_config
  when Hash
    scanner_config["enabled"] != false
  else
    # Default to enabled if not specified
    true
  end
end

#semgrep_configObject



129
130
131
132
133
134
135
136
137
# File 'lib/zwischen/config.rb', line 129

def semgrep_config
  semgrep_config = @config.dig("scanners", "semgrep")
  # Handle both formats: "semgrep: true" (boolean) and "semgrep: { enabled: true, config: '...' }" (hash)
  if semgrep_config.is_a?(Hash)
    semgrep_config["config"] || "p/security-audit"
  else
    "p/security-audit"
  end
end