codeclimate-fede 0.85.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/check +18 -0
- data/bin/codeclimate +21 -0
- data/bin/prep-release +45 -0
- data/bin/publish +47 -0
- data/bin/release +41 -0
- data/bin/validate-release +18 -0
- data/config/engines.yml +322 -0
- data/lib/cc/analyzer/bridge.rb +106 -0
- data/lib/cc/analyzer/composite_container_listener.rb +21 -0
- data/lib/cc/analyzer/container/result.rb +74 -0
- data/lib/cc/analyzer/container.rb +208 -0
- data/lib/cc/analyzer/container_listener.rb +9 -0
- data/lib/cc/analyzer/engine.rb +125 -0
- data/lib/cc/analyzer/engine_output.rb +74 -0
- data/lib/cc/analyzer/engine_output_filter.rb +36 -0
- data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
- data/lib/cc/analyzer/filesystem.rb +50 -0
- data/lib/cc/analyzer/formatters/formatter.rb +53 -0
- data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
- data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
- data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
- data/lib/cc/analyzer/formatters/spinner.rb +35 -0
- data/lib/cc/analyzer/formatters.rb +21 -0
- data/lib/cc/analyzer/issue.rb +69 -0
- data/lib/cc/analyzer/issue_sorter.rb +30 -0
- data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
- data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
- data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
- data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
- data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
- data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
- data/lib/cc/analyzer/issue_validations.rb +26 -0
- data/lib/cc/analyzer/issue_validator.rb +11 -0
- data/lib/cc/analyzer/location_description.rb +45 -0
- data/lib/cc/analyzer/logging_container_listener.rb +24 -0
- data/lib/cc/analyzer/measurement.rb +22 -0
- data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
- data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
- data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
- data/lib/cc/analyzer/measurement_validations.rb +16 -0
- data/lib/cc/analyzer/measurement_validator.rb +11 -0
- data/lib/cc/analyzer/mounted_path.rb +80 -0
- data/lib/cc/analyzer/raising_container_listener.rb +32 -0
- data/lib/cc/analyzer/source_buffer.rb +47 -0
- data/lib/cc/analyzer/source_extractor.rb +79 -0
- data/lib/cc/analyzer/source_fingerprint.rb +40 -0
- data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
- data/lib/cc/analyzer/validator.rb +38 -0
- data/lib/cc/analyzer.rb +50 -0
- data/lib/cc/cli/analyze/engine_failure.rb +11 -0
- data/lib/cc/cli/analyze.rb +90 -0
- data/lib/cc/cli/command.rb +85 -0
- data/lib/cc/cli/console.rb +12 -0
- data/lib/cc/cli/engines/engine_command.rb +15 -0
- data/lib/cc/cli/engines/install.rb +35 -0
- data/lib/cc/cli/engines/list.rb +18 -0
- data/lib/cc/cli/engines.rb +5 -0
- data/lib/cc/cli/file_store.rb +42 -0
- data/lib/cc/cli/global_cache.rb +47 -0
- data/lib/cc/cli/global_config.rb +35 -0
- data/lib/cc/cli/help.rb +51 -0
- data/lib/cc/cli/output.rb +34 -0
- data/lib/cc/cli/prepare.rb +98 -0
- data/lib/cc/cli/runner.rb +75 -0
- data/lib/cc/cli/validate_config.rb +84 -0
- data/lib/cc/cli/version.rb +16 -0
- data/lib/cc/cli/version_checker.rb +107 -0
- data/lib/cc/cli.rb +39 -0
- data/lib/cc/config/checks_adapter.rb +40 -0
- data/lib/cc/config/default_adapter.rb +54 -0
- data/lib/cc/config/engine.rb +41 -0
- data/lib/cc/config/engine_set.rb +47 -0
- data/lib/cc/config/json_adapter.rb +17 -0
- data/lib/cc/config/prepare.rb +92 -0
- data/lib/cc/config/validation/check_validator.rb +34 -0
- data/lib/cc/config/validation/engine_validator.rb +93 -0
- data/lib/cc/config/validation/fetch_validator.rb +78 -0
- data/lib/cc/config/validation/file_validator.rb +112 -0
- data/lib/cc/config/validation/hash_validations.rb +52 -0
- data/lib/cc/config/validation/json.rb +31 -0
- data/lib/cc/config/validation/prepare_validator.rb +40 -0
- data/lib/cc/config/validation/yaml.rb +66 -0
- data/lib/cc/config/yaml_adapter.rb +73 -0
- data/lib/cc/config.rb +70 -0
- data/lib/cc/engine_registry.rb +74 -0
- data/lib/cc/resolv.rb +39 -0
- data/lib/cc/workspace/exclusion.rb +34 -0
- data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
- data/lib/cc/workspace/path_tree/file_node.rb +31 -0
- data/lib/cc/workspace/path_tree.rb +49 -0
- data/lib/cc/workspace.rb +39 -0
- 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
|
data/lib/cc/workspace.rb
ADDED
@@ -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
|