Class: Ocak::Config

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

Defined Under Namespace

Classes: ConfigError, ConfigNotFound

Constant Summary collapse

CONFIG_FILE =
'ocak.yml'
USER_CONFIG_DIR =
'ocak'
USER_CONFIG_FILE =
'config.yml'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, project_dir = Dir.pwd) ⇒ Config

Returns a new instance of Config.



32
33
34
35
36
37
# File 'lib/ocak/config.rb', line 32

def initialize(data, project_dir = Dir.pwd)
  @data = data || {}
  @project_dir = project_dir
  @overrides = {}
  validate!
end

Instance Attribute Details

#project_dirObject (readonly)

Returns the value of attribute project_dir.



11
12
13
# File 'lib/ocak/config.rb', line 11

def project_dir
  @project_dir
end

Class Method Details

.load(dir = Dir.pwd) ⇒ Object

Raises:



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/ocak/config.rb', line 18

def self.load(dir = Dir.pwd)
  path = File.join(dir, CONFIG_FILE)
  raise ConfigNotFound, "No ocak.yml found in #{dir}. Run `ocak init` first." unless File.exist?(path)

  project_data = YAML.safe_load_file(path, symbolize_names: true) || {}
  user_data = load_user_config

  merged = deep_merge(user_data, project_data)
  # repos: is user-only — always take from user config, never project
  merged[:repos] = user_data[:repos] if user_data[:repos]

  new(merged, dir)
end

.user_config_pathObject



13
14
15
16
# File 'lib/ocak/config.rb', line 13

def self.user_config_path
  base = ENV.fetch('XDG_CONFIG_HOME', File.expand_path('~/.config'))
  File.join(base, USER_CONFIG_DIR, USER_CONFIG_FILE)
end

Instance Method Details

#agent_path(name) ⇒ Object

Agent paths



142
143
144
145
146
147
# File 'lib/ocak/config.rb', line 142

def agent_path(name)
  custom = dig(:agents, name.to_sym)
  return File.join(@project_dir, validate_path(custom)) if custom

  File.join(@project_dir, '.claude', 'agents', "#{name.to_s.tr('_', '-')}.md")
end

#all_labelsObject



132
133
134
# File 'lib/ocak/config.rb', line 132

def all_labels
  [label_ready, label_in_progress, label_completed, label_failed, label_reready, label_awaiting_review]
end

#allowed_authorsObject

Safety



89
# File 'lib/ocak/config.rb', line 89

def allowed_authors    = dig(:safety, :allowed_authors) || []

#audit_modeObject



86
# File 'lib/ocak/config.rb', line 86

def audit_mode    = @overrides[:audit_mode] || dig(:pipeline, :audit_mode) || false

#cost_budgetObject



84
# File 'lib/ocak/config.rb', line 84

def cost_budget   = dig(:pipeline, :cost_budget)

#custom_commands?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/ocak/config.rb', line 68

def custom_commands?
  !!(test_command || lint_command || format_command || setup_command || security_commands.any?)
end

#format_commandObject



48
# File 'lib/ocak/config.rb', line 48

def format_command = dig(:stack, :format_command)

#frameworkObject



45
# File 'lib/ocak/config.rb', line 45

def framework     = dig(:stack, :framework)

#issue_backendObject

Issues



121
# File 'lib/ocak/config.rb', line 121

def issue_backend = dig(:issues, :backend)

#label_awaiting_reviewObject



130
# File 'lib/ocak/config.rb', line 130

def label_awaiting_review = dig(:labels, :awaiting_review) || 'auto-pending-human'

#label_completedObject



127
# File 'lib/ocak/config.rb', line 127

def label_completed  = dig(:labels, :completed) || 'completed'

#label_failedObject



128
# File 'lib/ocak/config.rb', line 128

def label_failed     = dig(:labels, :failed) || 'pipeline-failed'

#label_in_progressObject



126
# File 'lib/ocak/config.rb', line 126

def label_in_progress = dig(:labels, :in_progress) || 'auto-doing'

#label_readyObject

Labels



125
# File 'lib/ocak/config.rb', line 125

def label_ready = dig(:labels, :ready) || 'auto-ready'

#label_rereadyObject



129
# File 'lib/ocak/config.rb', line 129

def label_reready         = dig(:labels, :reready) || 'auto-reready'

#languageObject

Stack



44
# File 'lib/ocak/config.rb', line 44

def language      = dig(:stack, :language) || 'unknown'

#lint_check_commandObject

Returns the lint command suitable for check-only verification. Uses explicit lint_check_command config if provided; otherwise strips known fix flags from lint_command.



53
54
55
56
57
58
59
60
61
62
# File 'lib/ocak/config.rb', line 53

def lint_check_command
  explicit = dig(:stack, :lint_check_command)
  return explicit if explicit && !explicit.empty?

  cmd = lint_command
  return nil unless cmd

  # Longer --fix-* variants must precede --fix in the alternation due to \b matching after 'fix'
  cmd.gsub(/\s+(?:-A|--fix-dry-run|--fix-type\s+\S+|--unsafe-fix|--fix|--write|--allow-dirty)\b/, '').strip
end

#lint_commandObject



47
# File 'lib/ocak/config.rb', line 47

def lint_command   = dig(:stack, :lint_command)

#local_issues?Boolean

Returns:

  • (Boolean)


122
# File 'lib/ocak/config.rb', line 122

def local_issues? = issue_backend == 'local'

#log_dirObject



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

def log_dir
  validate_path(dig(:pipeline, :log_dir) || 'logs/pipeline')
end

#manual_reviewObject



85
# File 'lib/ocak/config.rb', line 85

def manual_review = @overrides[:manual_review] || dig(:pipeline, :manual_review) || false

#max_issues_per_runObject



91
# File 'lib/ocak/config.rb', line 91

def max_issues_per_run = dig(:safety, :max_issues_per_run) || 5

#max_parallelObject

Pipeline



73
# File 'lib/ocak/config.rb', line 73

def max_parallel  = @overrides[:max_parallel] || dig(:pipeline, :max_parallel) || 5

#multi_repo?Boolean

Returns:

  • (Boolean)


98
99
100
101
# File 'lib/ocak/config.rb', line 98

def multi_repo?
  r = repos
  r.is_a?(Hash) && !r.empty?
end

#override(key, value) ⇒ Object



39
40
41
# File 'lib/ocak/config.rb', line 39

def override(key, value)
  @overrides[key] = value
end

#poll_intervalObject



74
# File 'lib/ocak/config.rb', line 74

def poll_interval = @overrides[:poll_interval] || dig(:pipeline, :poll_interval) || 60

#reposObject

Multi-repo



94
95
96
# File 'lib/ocak/config.rb', line 94

def repos
  @data[:repos]
end

#require_commentObject



90
# File 'lib/ocak/config.rb', line 90

def require_comment    = dig(:safety, :require_comment)

#resolve_repo(name) ⇒ Object

Raises:



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ocak/config.rb', line 107

def resolve_repo(name)
  raise ConfigError, 'No repos configured in ocak.yml' unless multi_repo?

  key = name.to_sym
  path_value = repos[key]
  raise ConfigError, "Unknown repo '#{name}'. Known repos: #{repos.keys.join(', ')}" unless path_value

  expanded = File.expand_path(path_value.to_s)
  raise ConfigError, "Repo path does not exist: #{expanded}" unless Dir.exist?(expanded)

  { name: name.to_s, path: expanded }
end

#security_commandsObject



64
65
66
# File 'lib/ocak/config.rb', line 64

def security_commands
  dig(:stack, :security_commands) || []
end

#setup_commandObject



49
# File 'lib/ocak/config.rb', line 49

def setup_command  = dig(:stack, :setup_command)

#stepsObject

Steps



137
138
139
# File 'lib/ocak/config.rb', line 137

def steps
  @data[:steps] || default_steps
end

#target_fieldObject



103
104
105
# File 'lib/ocak/config.rb', line 103

def target_field
  dig(:target_field) || 'target_repo'
end

#test_commandObject



46
# File 'lib/ocak/config.rb', line 46

def test_command   = dig(:stack, :test_command)

#worktree_dirObject



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

def worktree_dir
  validate_path(dig(:pipeline, :worktree_dir) || '.claude/worktrees')
end