mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Merge pull request #361 from crystal-ameba/issue-275
Support hierarchical loading of the config file
This commit is contained in:
		
						commit
						3fbbe3986e
					
				
					 5 changed files with 62 additions and 18 deletions
				
			
		| 
						 | 
					@ -21,7 +21,7 @@ module Ameba::Cli
 | 
				
			||||||
      %w(-c --config).each do |f|
 | 
					      %w(-c --config).each do |f|
 | 
				
			||||||
        it "accepts #{f} flag" do
 | 
					        it "accepts #{f} flag" do
 | 
				
			||||||
          c = Cli.parse_args [f, "config.yml"]
 | 
					          c = Cli.parse_args [f, "config.yml"]
 | 
				
			||||||
          c.config.should eq "config.yml"
 | 
					          c.config.should eq Path["config.yml"]
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,12 +82,6 @@ module Ameba::Cli
 | 
				
			||||||
        c.colors?.should be_true
 | 
					        c.colors?.should be_true
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it "ignores --config if --gen-config flag passed" do
 | 
					 | 
				
			||||||
        c = Cli.parse_args %w(--gen-config --config my_config.yml)
 | 
					 | 
				
			||||||
        c.formatter.should eq :todo
 | 
					 | 
				
			||||||
        c.config.should eq ""
 | 
					 | 
				
			||||||
      end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      describe "-e/--explain" do
 | 
					      describe "-e/--explain" do
 | 
				
			||||||
        it "configures file/line/column" do
 | 
					        it "configures file/line/column" do
 | 
				
			||||||
          c = Cli.parse_args %w(--explain src/file.cr:3:5)
 | 
					          c = Cli.parse_args %w(--explain src/file.cr:3:5)
 | 
				
			||||||
| 
						 | 
					@ -166,7 +160,7 @@ module Ameba::Cli
 | 
				
			||||||
        c.globs.should be_nil
 | 
					        c.globs.should be_nil
 | 
				
			||||||
        c.only.should be_nil
 | 
					        c.only.should be_nil
 | 
				
			||||||
        c.except.should be_nil
 | 
					        c.except.should be_nil
 | 
				
			||||||
        c.config.should eq Config::PATH
 | 
					        c.config.should be_nil
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ module Ameba
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe Formatter::TODOFormatter do
 | 
					  describe Formatter::TODOFormatter do
 | 
				
			||||||
    ::Spec.after_each do
 | 
					    ::Spec.after_each do
 | 
				
			||||||
      FileUtils.rm_rf(Ameba::Config::PATH)
 | 
					      FileUtils.rm_rf(Ameba::Config::DEFAULT_PATH)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    context "problems not found" do
 | 
					    context "problems not found" do
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ module Ameba
 | 
				
			||||||
          s = Source.new "a = 1", "source.cr"
 | 
					          s = Source.new "a = 1", "source.cr"
 | 
				
			||||||
          s.add_issue DummyRule.new, {1, 2}, "message"
 | 
					          s.add_issue DummyRule.new, {1, 2}, "message"
 | 
				
			||||||
          formatter.finished([s])
 | 
					          formatter.finished([s])
 | 
				
			||||||
          io.to_s.should contain "Created .ameba.yml"
 | 
					          io.to_s.should contain "Created #{Config::DEFAULT_PATH}"
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,7 +44,7 @@ module Ameba::Cli
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class Opts
 | 
					  private class Opts
 | 
				
			||||||
    property config = Config::PATH
 | 
					    property config : Path?
 | 
				
			||||||
    property formatter : Symbol | String | Nil
 | 
					    property formatter : Symbol | String | Nil
 | 
				
			||||||
    property globs : Array(String)?
 | 
					    property globs : Array(String)?
 | 
				
			||||||
    property only : Array(String)?
 | 
					    property only : Array(String)?
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ module Ameba::Cli
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      parser.on("-c", "--config PATH",
 | 
					      parser.on("-c", "--config PATH",
 | 
				
			||||||
        "Specify a configuration file") do |path|
 | 
					        "Specify a configuration file") do |path|
 | 
				
			||||||
        opts.config = path unless opts.config.empty?
 | 
					        opts.config = Path[path] unless path.empty?
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      parser.on("-f", "--format FORMATTER",
 | 
					      parser.on("-f", "--format FORMATTER",
 | 
				
			||||||
| 
						 | 
					@ -105,7 +105,6 @@ module Ameba::Cli
 | 
				
			||||||
      parser.on("--gen-config",
 | 
					      parser.on("--gen-config",
 | 
				
			||||||
        "Generate a configuration file acting as a TODO list") do
 | 
					        "Generate a configuration file acting as a TODO list") do
 | 
				
			||||||
        opts.formatter = :todo
 | 
					        opts.formatter = :todo
 | 
				
			||||||
        opts.config = ""
 | 
					 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      parser.on("--fail-level SEVERITY",
 | 
					      parser.on("--fail-level SEVERITY",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,8 +10,30 @@ require "./glob_utils"
 | 
				
			||||||
# config.formatter = my_formatter
 | 
					# config.formatter = my_formatter
 | 
				
			||||||
# ```
 | 
					# ```
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# By default config loads `.ameba.yml` file in a current directory.
 | 
					# By default config loads `.ameba.yml` file located in a current
 | 
				
			||||||
 | 
					# working directory.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					# If it cannot be found until reaching the root directory, then it will be
 | 
				
			||||||
 | 
					# searched for in the user’s global config locations, which consists of a
 | 
				
			||||||
 | 
					# dotfile or a config file inside the XDG Base Directory specification.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# - `~/.ameba.yml`
 | 
				
			||||||
 | 
					# - `$XDG_CONFIG_HOME/ameba/config.yml` (expands to `~/.config/ameba/config.yml`
 | 
				
			||||||
 | 
					#   if `$XDG_CONFIG_HOME` is not set)
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# If both files exist, the dotfile will be selected.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# As an example, if Ameba is invoked from inside `/path/to/project/lib/utils`,
 | 
				
			||||||
 | 
					# then it will use the config as specified inside the first of the following files:
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# - `/path/to/project/lib/utils/.ameba.yml`
 | 
				
			||||||
 | 
					# - `/path/to/project/lib/.ameba.yml`
 | 
				
			||||||
 | 
					# - `/path/to/project/.ameba.yml`
 | 
				
			||||||
 | 
					# - `/path/to/.ameba.yml`
 | 
				
			||||||
 | 
					# - `/path/.ameba.yml`
 | 
				
			||||||
 | 
					# - `/.ameba.yml`
 | 
				
			||||||
 | 
					# - `~/.ameba.yml`
 | 
				
			||||||
 | 
					# - `~/.config/ameba/config.yml`
 | 
				
			||||||
class Ameba::Config
 | 
					class Ameba::Config
 | 
				
			||||||
  include GlobUtils
 | 
					  include GlobUtils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +46,14 @@ class Ameba::Config
 | 
				
			||||||
    json:     Formatter::JSONFormatter,
 | 
					    json:     Formatter::JSONFormatter,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  PATH = ".ameba.yml"
 | 
					  XDG_CONFIG_HOME = ENV.fetch("XDG_CONFIG_HOME", "~/.config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FILENAME      = ".ameba.yml"
 | 
				
			||||||
 | 
					  DEFAULT_PATH  = Path[Dir.current] / FILENAME
 | 
				
			||||||
 | 
					  DEFAULT_PATHS = {
 | 
				
			||||||
 | 
					    Path["~"] / FILENAME,
 | 
				
			||||||
 | 
					    Path[XDG_CONFIG_HOME] / "ameba/config.yml",
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  DEFAULT_GLOBS = %w(
 | 
					  DEFAULT_GLOBS = %w(
 | 
				
			||||||
    **/*.cr
 | 
					    **/*.cr
 | 
				
			||||||
| 
						 | 
					@ -77,14 +106,36 @@ class Ameba::Config
 | 
				
			||||||
  # ```
 | 
					  # ```
 | 
				
			||||||
  # config = Ameba::Config.load
 | 
					  # config = Ameba::Config.load
 | 
				
			||||||
  # ```
 | 
					  # ```
 | 
				
			||||||
  def self.load(path = PATH, colors = true)
 | 
					  def self.load(path = nil, colors = true)
 | 
				
			||||||
    Colorize.enabled = colors
 | 
					    Colorize.enabled = colors
 | 
				
			||||||
    content = File.exists?(path) ? File.read path : "{}"
 | 
					    content = read_config(path) || "{}"
 | 
				
			||||||
    Config.new YAML.parse(content)
 | 
					    Config.new YAML.parse(content)
 | 
				
			||||||
  rescue e
 | 
					  rescue e
 | 
				
			||||||
    raise "Config file is invalid: #{e.message}"
 | 
					    raise "Config file is invalid: #{e.message}"
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected def self.read_config(path = nil)
 | 
				
			||||||
 | 
					    if path
 | 
				
			||||||
 | 
					      return File.exists?(path) ? File.read(path) : nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    each_config_path do |config_path|
 | 
				
			||||||
 | 
					      return File.read(config_path) if File.exists?(config_path)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected def self.each_config_path(&)
 | 
				
			||||||
 | 
					    path = Path[DEFAULT_PATH].expand(home: true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    search_paths = path.parents
 | 
				
			||||||
 | 
					    search_paths.reverse_each do |search_path|
 | 
				
			||||||
 | 
					      yield search_path / FILENAME
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DEFAULT_PATHS.each do |default_path|
 | 
				
			||||||
 | 
					      yield default_path
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def self.formatter_names
 | 
					  def self.formatter_names
 | 
				
			||||||
    AVAILABLE_FORMATTERS.keys.join('|')
 | 
					    AVAILABLE_FORMATTERS.keys.join('|')
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ module Ameba::Formatter
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private def generate_todo_config(issues)
 | 
					    private def generate_todo_config(issues)
 | 
				
			||||||
      file = File.new(Config::PATH, mode: "w")
 | 
					      file = File.new(Config::DEFAULT_PATH, mode: "w")
 | 
				
			||||||
      file << header
 | 
					      file << header
 | 
				
			||||||
      rule_issues_map(issues).each do |rule, rule_issues|
 | 
					      rule_issues_map(issues).each do |rule, rule_issues|
 | 
				
			||||||
        file << "\n# Problems found: #{rule_issues.size}"
 | 
					        file << "\n# Problems found: #{rule_issues.size}"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue