Class: Rigor::Configuration

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

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Modules: SeverityProfile Classes: Dependencies

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" => {},
  "dependencies" => {
    "source_inference" => [],
    "budget_per_gem" => Configuration::Dependencies::DEFAULT_BUDGET_PER_GEM
  }
}.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



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/rigor/configuration.rb', line 218

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"))
  )
  @dependencies = Dependencies.from_h(
    data.fetch("dependencies", DEFAULTS.fetch("dependencies"))
  )
end

Instance Attribute Details

#cache_pathObject (readonly)

Returns the value of attribute cache_path.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def cache_path
  @cache_path
end

#dependenciesObject (readonly)

Returns the value of attribute dependencies.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def dependencies
  @dependencies
end

#disabled_rulesObject (readonly)

Returns the value of attribute disabled_rules.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def disabled_rules
  @disabled_rules
end

#exclude_patternsObject (readonly)

Returns the value of attribute exclude_patterns.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def exclude_patterns
  @exclude_patterns
end

#fold_platform_specific_pathsObject (readonly)

Returns the value of attribute fold_platform_specific_paths.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def fold_platform_specific_paths
  @fold_platform_specific_paths
end

#librariesObject (readonly)

Returns the value of attribute libraries.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def libraries
  @libraries
end

#pathsObject (readonly)

Returns the value of attribute paths.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def paths
  @paths
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def plugins
  @plugins
end

#plugins_io_allowed_pathsObject (readonly)

Returns the value of attribute plugins_io_allowed_paths.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

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.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def plugins_io_allowed_url_hosts
  @plugins_io_allowed_url_hosts
end

#plugins_io_networkObject (readonly)

Returns the value of attribute plugins_io_network.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def plugins_io_network
  @plugins_io_network
end

#severity_overridesObject (readonly)

Returns the value of attribute severity_overrides.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def severity_overrides
  @severity_overrides
end

#severity_profileObject (readonly)

Returns the value of attribute severity_profile.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def severity_profile
  @severity_profile
end

#signature_pathsObject (readonly)

Returns the value of attribute signature_paths.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

def signature_paths
  @signature_paths
end

#target_rubyObject (readonly)

Returns the value of attribute target_ruby.



76
77
78
# File 'lib/rigor/configuration.rb', line 76

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.



110
111
112
# File 'lib/rigor/configuration.rb', line 110

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).



98
99
100
101
102
103
104
# File 'lib/rigor/configuration.rb', line 98

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



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

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



169
170
171
172
173
174
175
176
# File 'lib/rigor/configuration.rb', line 169

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



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rigor/configuration.rb', line 252

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] },
    "dependencies" => dependencies.to_h
  }
end