codeclimate-fede 0.85.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/bin/check +18 -0
  3. data/bin/codeclimate +21 -0
  4. data/bin/prep-release +45 -0
  5. data/bin/publish +47 -0
  6. data/bin/release +41 -0
  7. data/bin/validate-release +18 -0
  8. data/config/engines.yml +322 -0
  9. data/lib/cc/analyzer/bridge.rb +106 -0
  10. data/lib/cc/analyzer/composite_container_listener.rb +21 -0
  11. data/lib/cc/analyzer/container/result.rb +74 -0
  12. data/lib/cc/analyzer/container.rb +208 -0
  13. data/lib/cc/analyzer/container_listener.rb +9 -0
  14. data/lib/cc/analyzer/engine.rb +125 -0
  15. data/lib/cc/analyzer/engine_output.rb +74 -0
  16. data/lib/cc/analyzer/engine_output_filter.rb +36 -0
  17. data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
  18. data/lib/cc/analyzer/filesystem.rb +50 -0
  19. data/lib/cc/analyzer/formatters/formatter.rb +53 -0
  20. data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
  21. data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
  22. data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
  23. data/lib/cc/analyzer/formatters/spinner.rb +35 -0
  24. data/lib/cc/analyzer/formatters.rb +21 -0
  25. data/lib/cc/analyzer/issue.rb +69 -0
  26. data/lib/cc/analyzer/issue_sorter.rb +30 -0
  27. data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
  28. data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
  29. data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
  30. data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
  31. data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
  32. data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
  33. data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
  34. data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
  35. data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
  36. data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
  37. data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
  38. data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
  39. data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
  40. data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
  41. data/lib/cc/analyzer/issue_validations.rb +26 -0
  42. data/lib/cc/analyzer/issue_validator.rb +11 -0
  43. data/lib/cc/analyzer/location_description.rb +45 -0
  44. data/lib/cc/analyzer/logging_container_listener.rb +24 -0
  45. data/lib/cc/analyzer/measurement.rb +22 -0
  46. data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
  47. data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
  48. data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
  49. data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
  50. data/lib/cc/analyzer/measurement_validations.rb +16 -0
  51. data/lib/cc/analyzer/measurement_validator.rb +11 -0
  52. data/lib/cc/analyzer/mounted_path.rb +80 -0
  53. data/lib/cc/analyzer/raising_container_listener.rb +32 -0
  54. data/lib/cc/analyzer/source_buffer.rb +47 -0
  55. data/lib/cc/analyzer/source_extractor.rb +79 -0
  56. data/lib/cc/analyzer/source_fingerprint.rb +40 -0
  57. data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
  58. data/lib/cc/analyzer/validator.rb +38 -0
  59. data/lib/cc/analyzer.rb +50 -0
  60. data/lib/cc/cli/analyze/engine_failure.rb +11 -0
  61. data/lib/cc/cli/analyze.rb +90 -0
  62. data/lib/cc/cli/command.rb +85 -0
  63. data/lib/cc/cli/console.rb +12 -0
  64. data/lib/cc/cli/engines/engine_command.rb +15 -0
  65. data/lib/cc/cli/engines/install.rb +35 -0
  66. data/lib/cc/cli/engines/list.rb +18 -0
  67. data/lib/cc/cli/engines.rb +5 -0
  68. data/lib/cc/cli/file_store.rb +42 -0
  69. data/lib/cc/cli/global_cache.rb +47 -0
  70. data/lib/cc/cli/global_config.rb +35 -0
  71. data/lib/cc/cli/help.rb +51 -0
  72. data/lib/cc/cli/output.rb +34 -0
  73. data/lib/cc/cli/prepare.rb +98 -0
  74. data/lib/cc/cli/runner.rb +75 -0
  75. data/lib/cc/cli/validate_config.rb +84 -0
  76. data/lib/cc/cli/version.rb +16 -0
  77. data/lib/cc/cli/version_checker.rb +107 -0
  78. data/lib/cc/cli.rb +39 -0
  79. data/lib/cc/config/checks_adapter.rb +40 -0
  80. data/lib/cc/config/default_adapter.rb +54 -0
  81. data/lib/cc/config/engine.rb +41 -0
  82. data/lib/cc/config/engine_set.rb +47 -0
  83. data/lib/cc/config/json_adapter.rb +17 -0
  84. data/lib/cc/config/prepare.rb +92 -0
  85. data/lib/cc/config/validation/check_validator.rb +34 -0
  86. data/lib/cc/config/validation/engine_validator.rb +93 -0
  87. data/lib/cc/config/validation/fetch_validator.rb +78 -0
  88. data/lib/cc/config/validation/file_validator.rb +112 -0
  89. data/lib/cc/config/validation/hash_validations.rb +52 -0
  90. data/lib/cc/config/validation/json.rb +31 -0
  91. data/lib/cc/config/validation/prepare_validator.rb +40 -0
  92. data/lib/cc/config/validation/yaml.rb +66 -0
  93. data/lib/cc/config/yaml_adapter.rb +73 -0
  94. data/lib/cc/config.rb +70 -0
  95. data/lib/cc/engine_registry.rb +74 -0
  96. data/lib/cc/resolv.rb +39 -0
  97. data/lib/cc/workspace/exclusion.rb +34 -0
  98. data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
  99. data/lib/cc/workspace/path_tree/file_node.rb +31 -0
  100. data/lib/cc/workspace/path_tree.rb +49 -0
  101. data/lib/cc/workspace.rb +39 -0
  102. metadata +279 -0
@@ -0,0 +1,66 @@
1
+ module CC
2
+ class Config
3
+ module Validation
4
+ class YAML < FileValidator
5
+ private
6
+
7
+ def validate
8
+ @data = ::YAML.safe_load(File.read(path))
9
+
10
+ return unless validate_hash_data
11
+
12
+ validate_version
13
+ validate_prepare
14
+
15
+ validate_one_of(%w[engines plugins])
16
+ validate_one_of(%w[exclude_paths exclude_patterns])
17
+
18
+ validate_engines("engines", legacy: true)
19
+ validate_engines("plugins")
20
+
21
+ validate_checks
22
+
23
+ validate_exclude_pattern("exclude_patterns")
24
+ validate_exclude_pattern("exclude_paths", legacy: true)
25
+
26
+ deprecated_key_warnings
27
+ warn_unrecognized_keys(%w[checks prepare engines plugins ratings languages exclude_paths exclude_patterns version])
28
+ rescue Psych::SyntaxError => ex
29
+ errors << "Unable to parse: #{ex.message}"
30
+ end
31
+
32
+ def validate_version
33
+ if !data.key?("version") && (data.key?("plugins") || data.key?("exclude_patterns"))
34
+ warnings << %(missing 'version' key. Please add `version: "2"`)
35
+ end
36
+ end
37
+
38
+ def validate_one_of(keys)
39
+ num = keys.map { |k| data.key?(k) }.select(&:present?).count
40
+ if num > 1
41
+ wrapped_keys = keys.map { |k| "'#{k}'" }
42
+ errors << "only use one of #{wrapped_keys.join(", ")}"
43
+ end
44
+ end
45
+
46
+ def deprecated_key_warnings
47
+ deprecate_key("engines", "plugins")
48
+ deprecate_key("exclude_paths", "exclude_patterns")
49
+ deprecate_key("languages")
50
+ deprecate_key("ratings")
51
+ end
52
+
53
+ def deprecate_key(key, new_key = nil)
54
+ if data.key?(key)
55
+ warnings <<
56
+ if new_key.nil?
57
+ "'#{key}' has been deprecated, and will not be used"
58
+ else
59
+ "'#{key}' has been deprecated, please use '#{new_key}' instead"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,73 @@
1
+ module CC
2
+ class Config
3
+ class YAMLAdapter
4
+ DEFAULT_PATH = ".codeclimate.yml".freeze
5
+
6
+ attr_reader :config
7
+
8
+ def self.load(path = DEFAULT_PATH)
9
+ new(::YAML.safe_load(File.read(path)))
10
+ end
11
+
12
+ def initialize(yaml = {})
13
+ @config = yaml || {}
14
+
15
+ upconvert_legacy_yaml!
16
+ end
17
+
18
+ private
19
+
20
+ def coerce_engine(data)
21
+ if [true, false].include?(data)
22
+ { "enabled" => data }
23
+ elsif data.is_a?(Hash)
24
+ data
25
+ else
26
+ {}
27
+ end
28
+ end
29
+
30
+ # Many of our plugins still expect:
31
+ #
32
+ # { config: PATH }
33
+ #
34
+ # But we document, and hope to eventually move to:
35
+ #
36
+ # { config: { file: PATH } }
37
+ #
38
+ # We need to munge from the latter to the former when/if we encounter it
39
+ def convert_to_legacy_file_config(config)
40
+ if config.is_a?(Hash) && config.keys.one? && config.key?("file")
41
+ config["file"]
42
+ else
43
+ config
44
+ end
45
+ end
46
+
47
+ def upconvert_legacy_yaml!
48
+ config.delete("ratings")
49
+
50
+ if config.key?("engines")
51
+ config["plugins"] ||= config.delete("engines")
52
+ end
53
+
54
+ plugins = config.fetch("plugins", {})
55
+ plugins.each do |engine, data|
56
+ plugins[engine] = coerce_engine(data)
57
+ if plugins.fetch(engine)["exclude_paths"]
58
+ plugins.fetch(engine)["exclude_patterns"] ||= Array(plugins.fetch(engine).delete("exclude_paths"))
59
+ end
60
+ if plugins.fetch(engine)["config"]
61
+ plugins.fetch(engine)["config"] = convert_to_legacy_file_config(
62
+ plugins.fetch(engine).fetch("config"),
63
+ )
64
+ end
65
+ end
66
+
67
+ if config.key?("exclude_paths")
68
+ config["exclude_patterns"] ||= Array(config.delete("exclude_paths"))
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
data/lib/cc/config.rb ADDED
@@ -0,0 +1,70 @@
1
+ require "cc/config/checks_adapter"
2
+ require "cc/config/default_adapter"
3
+ require "cc/config/engine"
4
+ require "cc/config/engine_set"
5
+ require "cc/config/json_adapter"
6
+ require "cc/config/prepare"
7
+ require "cc/config/validation/hash_validations"
8
+ require "cc/config/validation/check_validator"
9
+ require "cc/config/validation/engine_validator"
10
+ require "cc/config/validation/fetch_validator"
11
+ require "cc/config/validation/file_validator"
12
+ require "cc/config/validation/json"
13
+ require "cc/config/validation/prepare_validator"
14
+ require "cc/config/validation/yaml"
15
+ require "cc/config/yaml_adapter"
16
+
17
+ module CC
18
+ class Config
19
+ attr_reader \
20
+ :analysis_paths,
21
+ :engines,
22
+ :exclude_patterns,
23
+ :prepare
24
+
25
+ attr_writer \
26
+ :development
27
+
28
+ def self.load
29
+ config =
30
+ if File.exist?(JSONAdapter::DEFAULT_PATH)
31
+ JSONAdapter.load.config
32
+ elsif File.exist?(YAMLAdapter::DEFAULT_PATH)
33
+ YAMLAdapter.load.config
34
+ else
35
+ {}
36
+ end
37
+ config = DefaultAdapter.new(config).config
38
+ config = ChecksAdapter.new(config).config
39
+ build(config)
40
+ end
41
+
42
+ def self.build(data)
43
+ new(
44
+ engines: EngineSet.new(data.fetch("plugins", {})).engines,
45
+ exclude_patterns: data.fetch("exclude_patterns", DefaultAdapter::EXCLUDE_PATTERNS),
46
+ prepare: Prepare.from_data(data["prepare"]),
47
+ )
48
+ end
49
+
50
+ def initialize(analysis_paths: [], development: false, engines: [], exclude_patterns: [], prepare: Prepare.new)
51
+ @analysis_paths = analysis_paths
52
+ @development = development
53
+ @engines = engines
54
+ @exclude_patterns = exclude_patterns
55
+ @prepare = prepare
56
+ end
57
+
58
+ def merge(other)
59
+ Merge.new(self, other).run
60
+ end
61
+
62
+ def development?
63
+ @development
64
+ end
65
+
66
+ def disable_plugins!
67
+ @engines.delete_if(&:plugin?)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,74 @@
1
+ module CC
2
+ class EngineRegistry
3
+ include Enumerable
4
+
5
+ DEFAULT_MEMORY_LIMIT = 1_024_000_000
6
+ DEFAULT_COMMAND = nil
7
+ DEFAULT_MANIFEST_PATH = File.expand_path("../../../config/engines.yml", __FILE__)
8
+
9
+ EngineDetails = Struct.new(:image, :command, :description, :memory)
10
+ EngineDetailsNotFoundError = Class.new(StandardError)
11
+
12
+ def initialize(path = DEFAULT_MANIFEST_PATH, prefix = nil)
13
+ @yaml = YAML.safe_load(File.read(path))
14
+ @prefix = prefix || ENV["CODECLIMATE_PREFIX"] || ""
15
+ end
16
+
17
+ def each
18
+ yaml.each do |name, metadata|
19
+ engine = Config::Engine.new(
20
+ name,
21
+ channel: metadata.fetch("channels").keys.first,
22
+ )
23
+ engine_details = fetch_engine_details(engine)
24
+
25
+ yield(engine, engine_details)
26
+ end
27
+ end
28
+
29
+ def fetch_engine_details(engine, development: false)
30
+ if development
31
+ EngineDetails.new("codeclimate/codeclimate-#{engine.name}", nil, "")
32
+ else
33
+ metadata = yaml.fetch(engine.name)
34
+ channels = metadata.fetch("channels")
35
+
36
+ EngineDetails.new(
37
+ [prefix, channels.fetch(engine.channel)].join,
38
+ metadata.fetch("command", DEFAULT_COMMAND),
39
+ metadata.fetch("description", "(No description available)"),
40
+ memory_limit(metadata["minimum_memory_limit"]),
41
+ )
42
+ end
43
+ rescue KeyError
44
+ raise EngineDetailsNotFoundError, not_found_message(engine, channels)
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :yaml, :prefix
50
+
51
+ def memory_limit(minimum_memory_limit)
52
+ [
53
+ minimum_memory_limit.to_i,
54
+ default_memory_limit.to_i,
55
+ ].max
56
+ end
57
+
58
+ def default_memory_limit
59
+ ENV["ENGINE_MEMORY_LIMIT_BYTES"] || DEFAULT_MEMORY_LIMIT
60
+ end
61
+
62
+ def not_found_message(engine, available_channels)
63
+ if available_channels
64
+ # Known engine, unknown channel
65
+ "Channel #{engine.channel} not found" \
66
+ " for #{engine.name}," \
67
+ " available channels: #{available_channels.keys.inspect}"
68
+ else
69
+ # Unknown engine
70
+ "No engine named #{engine.name} found"
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/cc/resolv.rb ADDED
@@ -0,0 +1,39 @@
1
+ require "resolv-replace"
2
+
3
+ module CC
4
+ class Resolv
5
+ def self.with_fixed_dns(dns = ::Resolv::DNS.new)
6
+ ::Resolv::DefaultResolver.replace_resolvers([Fixed.new(dns)])
7
+
8
+ yield if block_given?
9
+ ensure
10
+ # There's no way to ask what the current values are before we override
11
+ # them; hopefully going by the source is good enough.
12
+ # https://docs.ruby-lang.org/en/2.0.0/Resolv.html#method-c-new
13
+ default_resolvers = [::Resolv::Hosts.new, ::Resolv::DNS.new]
14
+ ::Resolv::DefaultResolver.replace_resolvers(default_resolvers)
15
+ end
16
+
17
+ class Fixed
18
+ def initialize(fallback)
19
+ @addresses = {}
20
+ @fallback = fallback
21
+ end
22
+
23
+ def each_address(name)
24
+ if addresses.key?(name)
25
+ yield addresses.fetch(name)
26
+ else
27
+ fallback.each_address(name) do |address|
28
+ addresses[name] ||= address
29
+ yield address
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :addresses, :fallback
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,34 @@
1
+ module CC
2
+ class Workspace
3
+ class Exclusion
4
+ def initialize(pattern)
5
+ @negated = pattern.starts_with?("!")
6
+ @pattern = simplify(pattern)
7
+ end
8
+
9
+ def expand
10
+ if glob?
11
+ Dir.glob(pattern)
12
+ else
13
+ [pattern]
14
+ end
15
+ end
16
+
17
+ def glob?
18
+ pattern.include?("*")
19
+ end
20
+
21
+ def negated?
22
+ negated
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :negated, :pattern
28
+
29
+ def simplify(pattern)
30
+ pattern.to_s.sub(%r{(/\*\*)?(/\*)?$}, "").sub(/^\!/, "")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ module CC
2
+ class Workspace
3
+ class PathTree
4
+ class DirNode
5
+ def initialize(root_path, children = {})
6
+ @root_path = root_path.dup.freeze
7
+ @children = children.each_with_object({}) do |(k, v), memo|
8
+ memo[k.clone] = v.clone
9
+ end
10
+ end
11
+
12
+ def clone
13
+ self.class.new(root_path, children)
14
+ end
15
+
16
+ def all_paths
17
+ if populated?
18
+ children.values.flat_map(&:all_paths)
19
+ else
20
+ [File.join(root_path, File::SEPARATOR)]
21
+ end
22
+ end
23
+
24
+ def populated?
25
+ children.present?
26
+ end
27
+
28
+ def remove(head = nil, *tail)
29
+ return if head.nil? && tail.empty?
30
+ populate_direct_children
31
+
32
+ if (child = children[head])
33
+ child.remove(*tail)
34
+ children.delete(head) if !child.populated? || tail.empty?
35
+ end
36
+ end
37
+
38
+ def add(head = nil, *tail)
39
+ return if head.nil? && tail.empty?
40
+
41
+ if (entry = find_direct_child(head))
42
+ children[entry.basename.to_s.dup.freeze] ||= PathTree.node_for_pathname(entry)
43
+ children[entry.basename.to_s.dup.freeze].add(*tail)
44
+ else
45
+ Analyzer.logger.debug("Couldn't include because part of path doesn't exist: #{File.join(root_path, head)}")
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :children, :root_path
52
+
53
+ def populate_direct_children
54
+ return if populated?
55
+
56
+ Pathname.new(root_path).each_child do |child_path|
57
+ children[child_path.basename.to_s] = PathTree.node_for_pathname(child_path)
58
+ end
59
+ end
60
+
61
+ def find_direct_child(name)
62
+ Pathname.new(root_path).children.detect { |c| c.basename.to_s == name }
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,31 @@
1
+ module CC
2
+ class Workspace
3
+ class PathTree
4
+ class FileNode
5
+ def initialize(root_path)
6
+ @root_path = root_path.dup.freeze
7
+ end
8
+
9
+ def all_paths
10
+ [root_path]
11
+ end
12
+
13
+ def populated?
14
+ false
15
+ end
16
+
17
+ def remove(*)
18
+ # this space intentionally left blank
19
+ end
20
+
21
+ def add(*)
22
+ # this space intentionally left blank
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :root_path
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ require "pathname"
2
+ require "set"
3
+
4
+ module CC
5
+ class Workspace
6
+ class PathTree
7
+ autoload :DirNode, "cc/workspace/path_tree/dir_node"
8
+ autoload :FileNode, "cc/workspace/path_tree/file_node"
9
+
10
+ def self.node_for_pathname(pathname)
11
+ if pathname.directory?
12
+ DirNode.new(pathname.to_s)
13
+ else
14
+ FileNode.new(pathname.to_s)
15
+ end
16
+ end
17
+
18
+ def self.for_path(path)
19
+ new(node_for_pathname(Pathname.new(path)))
20
+ end
21
+
22
+ def initialize(root_node)
23
+ @root_node = root_node
24
+ end
25
+
26
+ def clone
27
+ self.class.new(root_node.clone)
28
+ end
29
+
30
+ def exclude_paths(paths)
31
+ paths.each { |path| root_node.remove(*normalized_path_pieces(path)) }
32
+ end
33
+
34
+ def include_paths(paths)
35
+ paths.each { |path| root_node.add(*normalized_path_pieces(path)) }
36
+ end
37
+
38
+ delegate :all_paths, to: :root_node
39
+
40
+ private
41
+
42
+ attr_reader :root_node
43
+
44
+ def normalized_path_pieces(path)
45
+ Pathname.new(path).cleanpath.to_s.split(File::SEPARATOR).reject(&:blank?)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,39 @@
1
+ module CC
2
+ class Workspace
3
+ autoload :Exclusion, "cc/workspace/exclusion"
4
+ autoload :PathTree, "cc/workspace/path_tree"
5
+
6
+ def initialize(path_tree = PathTree.for_path("."))
7
+ @path_tree = path_tree
8
+ end
9
+
10
+ def clone
11
+ self.class.new(path_tree.clone)
12
+ end
13
+
14
+ def paths
15
+ path_tree.all_paths
16
+ end
17
+
18
+ def add(paths)
19
+ if paths.present?
20
+ path_tree.include_paths(paths)
21
+ end
22
+ end
23
+
24
+ def remove(patterns)
25
+ Array(patterns).each do |pattern|
26
+ exclusion = Exclusion.new(pattern)
27
+ if exclusion.negated?
28
+ path_tree.include_paths(exclusion.expand)
29
+ else
30
+ path_tree.exclude_paths(exclusion.expand)
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :path_tree
38
+ end
39
+ end