Class: Rigor::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/configuration.rb,
lib/rigor/configuration/severity_profile.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Modules: SeverityProfile

Constant Summary collapse

DISCOVERY_ORDER =

File-discovery order for ‘Configuration.load(nil)`.

The first file present is loaded; the others are NOT implicitly merged. To extend a base config explicitly the winning file MUST list the base via ‘includes:`.

‘.rigor.yml` is a developer-local override (typically gitignored); `.rigor.dist.yml` is the project default (committed to the repo). When both are present the developer’s local override wins outright — there is no implicit auto-merge.

%w[.rigor.yml .rigor.dist.yml].freeze
DEFAULT_PATH =

Back-compat alias. Keep here so external callers that read ‘Configuration::DEFAULT_PATH` for help text / fixture paths still work; the discovery list is the canonical source.

DISCOVERY_ORDER.first
BUILTIN_EXCLUDES =

Built-in exclusion patterns appended to ‘exclude:` so vendored dependencies, Bundler artefacts, and JavaScript node_modules are never analysed by accident when a directory glob expands. Users cannot disable these defaults; the trade-off is that analysing any of these paths is essentially never what the user wants (they’re build outputs / external dependencies, not source).

We deliberately keep this list narrow. ‘tmp/` and similar directories vary across project layouts (Rails has `tmp/`, libraries usually don’t); user-supplied ‘exclude:` entries in `.rigor.yml` cover the project-specific cases.

%w[
  **/vendor/bundle/**
  **/.bundle/**
  **/node_modules/**
].freeze
DEFAULTS =
{
  "target_ruby" => "4.0",
  "paths" => ["lib"],
  "exclude" => [],
  "plugins" => [],
  "disable" => [],
  "libraries" => [],
  "signature_paths" => nil,
  "fold_platform_specific_paths" => false,
  "cache" => {
    "path" => ".rigor/cache"
  },
  "plugins_io" => {
    "network" => "disabled",
    "allowed_paths" => [],
    "allowed_url_hosts" => []
  },
  "severity_profile" => "balanced",
  "severity_overrides" => {}
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = DEFAULTS) ⇒ Configuration

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/rigor/configuration.rb', line 188

def initialize(data = DEFAULTS)
  cache = DEFAULTS.fetch("cache").merge(data.fetch("cache", {}))
  plugins_io = DEFAULTS.fetch("plugins_io").merge(data.fetch("plugins_io", {}))

  @target_ruby = coerce_target_ruby(data.fetch("target_ruby", DEFAULTS.fetch("target_ruby")))
  @paths = Array(data.fetch("paths", DEFAULTS.fetch("paths"))).map(&:to_s)
  user_excludes = Array(data.fetch("exclude", DEFAULTS.fetch("exclude"))).map(&:to_s)
  @exclude_patterns = (BUILTIN_EXCLUDES + user_excludes).uniq.freeze
  @plugins = Array(data.fetch("plugins", DEFAULTS.fetch("plugins"))).map do |entry|
    coerce_plugin_entry(entry)
  end.freeze
  @disabled_rules = Array(data.fetch("disable", DEFAULTS.fetch("disable"))).map(&:to_s).freeze
  @libraries = Array(data.fetch("libraries", DEFAULTS.fetch("libraries"))).map(&:to_s).freeze
  sig_paths = data.fetch("signature_paths", DEFAULTS.fetch("signature_paths"))
  @signature_paths = sig_paths.nil? ? nil : Array(sig_paths).map(&:to_s).freeze
  @fold_platform_specific_paths = data.fetch(
    "fold_platform_specific_paths", DEFAULTS.fetch("fold_platform_specific_paths")
  ) == true
  @cache_path = cache.fetch("path").to_s
  @plugins_io_network = coerce_network_policy(plugins_io.fetch("network"))
  @plugins_io_allowed_paths = Array(plugins_io.fetch("allowed_paths")).map(&:to_s).freeze
  @plugins_io_allowed_url_hosts = Array(plugins_io.fetch("allowed_url_hosts")).map(&:to_s).freeze
  @severity_profile = coerce_severity_profile(
    data.fetch("severity_profile", DEFAULTS.fetch("severity_profile"))
  )
  @severity_overrides = coerce_severity_overrides(
    data.fetch("severity_overrides", DEFAULTS.fetch("severity_overrides"))
  )
end

Instance Attribute Details

#cache_pathObject (readonly)

Returns the value of attribute cache_path.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def cache_path
  @cache_path
end

#disabled_rulesObject (readonly)

Returns the value of attribute disabled_rules.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def disabled_rules
  @disabled_rules
end

#exclude_patternsObject (readonly)

Returns the value of attribute exclude_patterns.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def exclude_patterns
  @exclude_patterns
end

#fold_platform_specific_pathsObject (readonly)

Returns the value of attribute fold_platform_specific_paths.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def fold_platform_specific_paths
  @fold_platform_specific_paths
end

#librariesObject (readonly)

Returns the value of attribute libraries.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def libraries
  @libraries
end

#pathsObject (readonly)

Returns the value of attribute paths.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def paths
  @paths
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def plugins
  @plugins
end

#plugins_io_allowed_pathsObject (readonly)

Returns the value of attribute plugins_io_allowed_paths.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def plugins_io_allowed_paths
  @plugins_io_allowed_paths
end

#plugins_io_allowed_url_hostsObject (readonly)

Returns the value of attribute plugins_io_allowed_url_hosts.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def plugins_io_allowed_url_hosts
  @plugins_io_allowed_url_hosts
end

#plugins_io_networkObject (readonly)

Returns the value of attribute plugins_io_network.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def plugins_io_network
  @plugins_io_network
end

#severity_overridesObject (readonly)

Returns the value of attribute severity_overrides.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def severity_overrides
  @severity_overrides
end

#severity_profileObject (readonly)

Returns the value of attribute severity_profile.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def severity_profile
  @severity_profile
end

#signature_pathsObject (readonly)

Returns the value of attribute signature_paths.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def signature_paths
  @signature_paths
end

#target_rubyObject (readonly)

Returns the value of attribute target_ruby.



71
72
73
# File 'lib/rigor/configuration.rb', line 71

def target_ruby
  @target_ruby
end

Class Method Details

.discoverObject

Returns the path to the config file Rigor would load under auto-discovery, or ‘nil` when neither candidate exists. Public so the CLI / spec drift checks can introspect the resolved file.



104
105
106
# File 'lib/rigor/configuration.rb', line 104

def self.discover
  DISCOVERY_ORDER.find { |candidate| File.exist?(candidate) }
end

.load(path = nil) ⇒ Object

Loads a configuration file.

‘path == nil` triggers auto-discovery against DISCOVERY_ORDER. The first present file in that list is loaded; if none exist the built-in DEFAULTS are used.

When a path is supplied (whether by auto-discovery or by the caller) the YAML body is processed for ‘includes:` recursively, and every relative path inside path-bearing keys (`paths:`, `signature_paths:`, `plugins_io.allowed_paths:`, `includes:`) is resolved against THAT file’s directory. The resolution is per-file: an included file’s relative paths resolve against the included file’s directory, not the top-level file. Path resolution mirrors [PHPStan](phpstan.org/config-reference#paths).



92
93
94
95
96
97
98
# File 'lib/rigor/configuration.rb', line 92

def self.load(path = nil)
  resolved = path || discover
  return new(DEFAULTS) if resolved.nil? || !File.exist?(resolved)

  data = load_with_includes(resolved)
  new(DEFAULTS.merge(data))
end

.resolve_path_key!(out, key, base_dir) ⇒ Object



157
158
159
160
161
# File 'lib/rigor/configuration.rb', line 157

def self.resolve_path_key!(out, key, base_dir)
  return unless out.key?(key) && !out[key].nil?

  out[key] = Array(out[key]).map { |p| File.expand_path(p.to_s, base_dir) }
end

.resolve_plugins_io_paths!(out, base_dir) ⇒ Object



163
164
165
166
167
168
169
170
# File 'lib/rigor/configuration.rb', line 163

def self.resolve_plugins_io_paths!(out, base_dir)
  plugins_io = out["plugins_io"]
  return unless plugins_io.is_a?(Hash) && plugins_io["allowed_paths"]

  duped = plugins_io.dup
  duped["allowed_paths"] = Array(plugins_io["allowed_paths"]).map { |p| File.expand_path(p.to_s, base_dir) }
  out["plugins_io"] = duped
end

Instance Method Details

#to_hObject

rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/rigor/configuration.rb', line 219

def to_h
  {
    "target_ruby" => target_ruby,
    "paths" => paths,
    "exclude" => exclude_patterns - BUILTIN_EXCLUDES,
    "plugins" => plugins,
    "disable" => disabled_rules,
    "libraries" => libraries,
    "signature_paths" => signature_paths,
    "fold_platform_specific_paths" => fold_platform_specific_paths,
    "cache" => {
      "path" => cache_path
    },
    "plugins_io" => {
      "network" => plugins_io_network.to_s,
      "allowed_paths" => plugins_io_allowed_paths,
      "allowed_url_hosts" => plugins_io_allowed_url_hosts
    },
    "severity_profile" => severity_profile.to_s,
    "severity_overrides" => severity_overrides.to_h { |k, v| [k, v.to_s] }
  }
end