codeclimate 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be2909fa3f01d67de344c999494d63d650599c22
4
- data.tar.gz: f093673d099ddfbcca4e5ac63456097a76f20ce3
3
+ metadata.gz: 1cc07ab27995ff71abe7fcf3d17cdb26c0e7cab0
4
+ data.tar.gz: 0bde4385fe0659131b7254d595a3b87a2a099659
5
5
  SHA512:
6
- metadata.gz: b6efca56017e42e7991255f0e3acda6058f4c876dde88be14b8e9d113f9a61701e482fedc3b5ce47747f07a69c83b2963288c2ff985d6bc78e9d866b80f39204
7
- data.tar.gz: d1c74c3732550d6a001879a22538c9f0d6fdfb8ee15e3bebf6e8cd99776f866454f16ae524ce2a5198b2e6a53de1cb707b1214b2501ebcf6186ab682a9432b80
6
+ metadata.gz: 788524a629d27d9825bfd87bb3181da46444273395fbac5c16ac10254a7ac5d13f9c0bb86494f5f4710d605115a4ae97385ce6773713802fce2bc983dcbf3caf
7
+ data.tar.gz: def528a3546470b85c9200334286a1afd1a6c6c0eb8eca43189d3fb747745dd43f22db131e8b87b587438aa2b4379637200adca22c21be62aea9049617833af1
@@ -12,13 +12,10 @@ module CC
12
12
  autoload :EnginesRunner, "cc/analyzer/engines_runner"
13
13
  autoload :Filesystem, "cc/analyzer/filesystem"
14
14
  autoload :Formatters, "cc/analyzer/formatters"
15
- autoload :IncludePathsBuilder, "cc/analyzer/include_paths_builder"
16
15
  autoload :Issue, "cc/analyzer/issue"
17
16
  autoload :IssueSorter, "cc/analyzer/issue_sorter"
18
17
  autoload :LocationDescription, "cc/analyzer/location_description"
19
18
  autoload :LoggingContainerListener, "cc/analyzer/logging_container_listener"
20
- autoload :PathPatterns, "cc/analyzer/path_patterns"
21
- autoload :PathMinimizer, "cc/analyzer/path_minimizer"
22
19
  autoload :RaisingContainerListener, "cc/analyzer/raising_container_listener"
23
20
  autoload :SourceBuffer, "cc/analyzer/source_buffer"
24
21
  autoload :StatsdContainerListener, "cc/analyzer/statsd_container_listener"
@@ -172,6 +172,7 @@ module CC
172
172
  def reap_running_container(message)
173
173
  Analyzer.logger.warn("killing container name=#{@name} message=#{message.inspect}")
174
174
  POSIX::Spawn::Child.new("docker", "kill", @name)
175
+ POSIX::Spawn::Child.new("docker", "wait", @name, timeout: 5.minutes)
175
176
  end
176
177
 
177
178
  def timeout
@@ -29,23 +29,27 @@ module CC
29
29
 
30
30
  private
31
31
 
32
- attr_reader :include_paths
33
-
34
32
  def engine_config(raw_engine_config)
33
+ engine_workspace = engine_workspace(raw_engine_config)
35
34
  config = raw_engine_config.merge(
36
- exclude_paths: exclude_paths,
37
- include_paths: include_paths,
35
+ include_paths: engine_workspace.paths,
38
36
  )
39
- # The yaml gem turns a config file string into a hash, but engines
40
- # expect the string. So we (for now) need to turn it into a string in
41
- # that one scenario.
42
- # TODO: update the engines to expect the hash and then remove this.
43
- if config.fetch("config", {}).keys.size == 1 && config["config"].key?("file")
44
- config["config"] = config["config"]["file"]
45
- end
37
+
38
+ normalize_config_file(config)
39
+
46
40
  config
47
41
  end
48
42
 
43
+ def engine_workspace(raw_engine_config)
44
+ if raw_engine_config.key?("exclude_paths") && !@requested_paths.present?
45
+ base_workspace.clone.tap do |workspace|
46
+ workspace.remove(raw_engine_config["exclude_paths"])
47
+ end
48
+ else
49
+ base_workspace
50
+ end
51
+ end
52
+
49
53
  def names_and_raw_engine_configs
50
54
  {}.tap do |ret|
51
55
  (@config.engines || {}).each do |name, raw_engine_config|
@@ -56,20 +60,22 @@ module CC
56
60
  end
57
61
  end
58
62
 
59
- def include_paths
60
- IncludePathsBuilder.new(exclude_paths, Array(@requested_paths)).build
61
- end
62
-
63
- def exclude_paths
64
- PathPatterns.new(@config.exclude_paths || []).expanded +
65
- gitignore_paths
63
+ def base_workspace
64
+ @base_workspace ||= Workspace.new.tap do |workspace|
65
+ workspace.add(@requested_paths)
66
+ unless @requested_paths.present?
67
+ workspace.remove([".git"])
68
+ workspace.remove(@config.exclude_paths)
69
+ end
70
+ end
66
71
  end
67
72
 
68
- def gitignore_paths
69
- if File.exist?(".gitignore")
70
- `git ls-files --others -i -z --exclude-from .gitignore`.split("\0")
71
- else
72
- []
73
+ # The yaml gem turns a config file string into a hash, but engines expect
74
+ # the string. So we (for now) need to turn it into a string in that one
75
+ # scenario.
76
+ def normalize_config_file(config)
77
+ if config.fetch("config", {}).keys.size == 1 && config["config"].key?("file")
78
+ config["config"] = config["config"]["file"]
73
79
  end
74
80
  end
75
81
  end
@@ -1,6 +1,8 @@
1
1
  module CC
2
2
  module Analyzer
3
3
  class Filesystem
4
+ attr_reader :root
5
+
4
6
  def initialize(root)
5
7
  @root = root
6
8
  end
@@ -22,30 +24,14 @@ module CC
22
24
  File.chown(root_uid, root_gid, path_for(path))
23
25
  end
24
26
 
25
- def any?(&block)
26
- file_paths.any?(&block)
27
- end
28
-
29
- def files_matching(globs)
30
- Dir.chdir(@root) do
31
- globs.map do |glob|
32
- Dir.glob(glob)
33
- end.flatten.sort.uniq
34
- end
27
+ def ls
28
+ Dir.entries(root).reject { |entry| [".", ".."].include?(entry) }
35
29
  end
36
30
 
37
31
  private
38
32
 
39
- def file_paths
40
- @file_paths ||= Dir.chdir(@root) do
41
- `find . -type f -print0`.strip.split("\0").map do |path|
42
- path.sub(%r{^\.\/}, "")
43
- end
44
- end
45
- end
46
-
47
33
  def path_for(path)
48
- File.join(@root, path)
34
+ File.join(root, path)
49
35
  end
50
36
 
51
37
  def root_uid
@@ -57,7 +43,7 @@ module CC
57
43
  end
58
44
 
59
45
  def root_stat
60
- @root_stat ||= File.stat(@root)
46
+ @root_stat ||= File.stat(root)
61
47
  end
62
48
  end
63
49
  end
@@ -1,6 +1,7 @@
1
1
  require "active_support"
2
2
  require "active_support/core_ext"
3
3
  require "cc/analyzer"
4
+ require "cc/workspace"
4
5
  require "cc/yaml"
5
6
 
6
7
  module CC
@@ -15,5 +16,16 @@ module CC
15
16
  autoload :Test, "cc/cli/test"
16
17
  autoload :ValidateConfig, "cc/cli/validate_config"
17
18
  autoload :Version, "cc/cli/version"
19
+
20
+ def self.debug(message, values = {})
21
+ if ENV["CODECLIMATE_DEBUG"]
22
+ if values.any?
23
+ message << " "
24
+ message << values.map { |k, v| "#{k}=#{v.inspect}" }.join(" ")
25
+ end
26
+
27
+ $stderr.puts("[DEBUG] #{message}")
28
+ end
29
+ end
18
30
  end
19
31
  end
@@ -21,13 +21,7 @@ module CC
21
21
 
22
22
  def add_exclude_paths(paths)
23
23
  config["exclude_paths"] ||= []
24
- config["exclude_paths"] += paths.map do |path|
25
- if path.ends_with?("/")
26
- "#{path}**/*"
27
- else
28
- path
29
- end
30
- end
24
+ config["exclude_paths"] |= paths
31
25
  end
32
26
 
33
27
  private
@@ -1,3 +1,5 @@
1
+ require "shellwords"
2
+
1
3
  module CC
2
4
  module CLI
3
5
  class ConfigGenerator
@@ -37,7 +39,7 @@ module CC
37
39
  end
38
40
 
39
41
  def exclude_paths
40
- AUTO_EXCLUDE_PATHS.select { |path| filesystem.exist?(path) }
42
+ @exclude_paths ||= AUTO_EXCLUDE_PATHS.select { |path| filesystem.exist?(path) }
41
43
  end
42
44
 
43
45
  def post_generation_verb
@@ -59,10 +61,32 @@ module CC
59
61
  end
60
62
 
61
63
  def files_exist?(engine)
62
- filesystem.any? do |path|
64
+ workspace_files.any? do |path|
63
65
  engine["enable_regexps"].any? { |re| Regexp.new(re).match(path) }
64
66
  end
65
67
  end
68
+
69
+ def non_excluded_paths
70
+ @non_excluded_paths ||= begin
71
+ excludes = exclude_paths.map { |path| path.chomp("/") }
72
+ filesystem.ls.reject do |path|
73
+ path.starts_with?(".") || excludes.include?(path)
74
+ end
75
+ end
76
+ end
77
+
78
+ def workspace_files
79
+ @workspace_files ||= Dir.chdir(filesystem.root) do
80
+ if non_excluded_paths.empty?
81
+ []
82
+ else
83
+ find_cmd = "find #{non_excluded_paths.map(&:shellescape).join(' ')} -type f -print0"
84
+ `#{find_cmd}`.strip.split("\0").map do |path|
85
+ path.sub(%r{^\.\/}, "")
86
+ end
87
+ end
88
+ end
89
+ end
66
90
  end
67
91
  end
68
92
  end
@@ -10,11 +10,11 @@ module CC
10
10
  def run
11
11
  if !upgrade? && filesystem.exist?(CODECLIMATE_YAML)
12
12
  warn "Config file .codeclimate.yml already present.\nTry running 'validate-config' to check configuration."
13
- create_default_configs
13
+ create_default_engine_configs if engines_enabled?
14
14
  elsif upgrade? && engines_enabled?
15
15
  fatal "--upgrade should not be used on a .codeclimate.yml configured for the Platform.\nTry running 'validate-config' to check configuration."
16
16
  else
17
- generate_config
17
+ generate_all_config
18
18
  end
19
19
  end
20
20
 
@@ -24,7 +24,7 @@ module CC
24
24
  @args.include?("--upgrade")
25
25
  end
26
26
 
27
- def generate_config
27
+ def generate_all_config
28
28
  unless config_generator.can_generate?
29
29
  config_generator.errors.each do |error|
30
30
  $stderr.puts colorize("ERROR: #{error}", :red)
@@ -34,10 +34,11 @@ module CC
34
34
 
35
35
  create_codeclimate_yaml
36
36
  success "Config file .codeclimate.yml successfully #{config_generator.post_generation_verb}.\nEdit and then try running 'validate-config' to check configuration."
37
- create_default_configs
37
+ create_default_engine_configs
38
38
  end
39
39
 
40
40
  def create_codeclimate_yaml
41
+ say "Generating .codeclimate.yml..."
41
42
  config = CC::CLI::Config.new
42
43
 
43
44
  config_generator.eligible_engines.each do |(engine_name, engine_config)|
@@ -48,8 +49,9 @@ module CC
48
49
  filesystem.write_path(CODECLIMATE_YAML, config.to_yaml)
49
50
  end
50
51
 
51
- def create_default_configs
52
- available_configs.each do |config_path|
52
+ def create_default_engine_configs
53
+ say "Generating default configuration for engines..."
54
+ available_engine_configs.each do |config_path|
53
55
  file_name = File.basename(config_path)
54
56
  if filesystem.exist?(file_name)
55
57
  say "Skipping generating #{file_name} file (already exists)."
@@ -60,29 +62,34 @@ module CC
60
62
  end
61
63
  end
62
64
 
63
- def available_configs
64
- all_paths = config_generator.eligible_engines.flat_map do |engine_name, _|
65
+ def available_engine_configs
66
+ engine_names = existing_cc_config.engines.select do |_, config|
67
+ config.enabled?
68
+ end.keys
69
+
70
+ all_paths = engine_names.flat_map do |engine_name|
65
71
  engine_directory = File.expand_path("../../../../config/#{engine_name}", __FILE__)
66
72
  Dir.glob("#{engine_directory}/*", File::FNM_DOTMATCH)
67
73
  end
68
-
69
- all_paths.reject { |path| [".", ".."].include?(File.basename(path)) }
74
+ all_paths.reject do |path|
75
+ %w[. ..].include?(File.basename(path))
76
+ end
70
77
  end
71
78
 
72
79
  def engines_enabled?
73
- unless @engines_enabled.nil?
74
- return @engines_enabled
75
- end
76
-
77
- if filesystem.exist?(CODECLIMATE_YAML)
78
- config = CC::Analyzer::Config.new(File.read(CODECLIMATE_YAML))
79
- @engines_enabled ||= config.engine_names.any?
80
- end
80
+ cc_config = existing_cc_config
81
+ cc_config.present? && cc_config.engines.present?
81
82
  end
82
83
 
83
84
  def config_generator
84
85
  @config_generator ||= ConfigGenerator.for(filesystem, engine_registry, upgrade?)
85
86
  end
87
+
88
+ def existing_cc_config
89
+ if filesystem.exist?(CODECLIMATE_YAML)
90
+ CC::Yaml.parse(filesystem.read_path(CODECLIMATE_YAML))
91
+ end
92
+ end
86
93
  end
87
94
  end
88
95
  end
@@ -10,9 +10,7 @@ module CC
10
10
  rescue => ex
11
11
  $stderr.puts("error: (#{ex.class}) #{ex.message}")
12
12
 
13
- if ENV["CODECLIMATE_DEBUG"]
14
- $stderr.puts("backtrace: #{ex.backtrace.join("\n\t")}")
15
- end
13
+ CLI.debug("backtrace: #{ex.backtrace.join("\n\t")}")
16
14
  end
17
15
 
18
16
  def initialize(args)
@@ -0,0 +1,34 @@
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.new("."))
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
+ path_tree.exclude_paths(Exclusion.new(pattern).expand)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :path_tree
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ module CC
2
+ class Workspace
3
+ class Exclusion
4
+ def initialize(pattern)
5
+ @pattern = simplify(pattern)
6
+ end
7
+
8
+ def expand
9
+ if glob?
10
+ Dir.glob(pattern)
11
+ else
12
+ [pattern]
13
+ end
14
+ end
15
+
16
+ def glob?
17
+ pattern.include?("*")
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :pattern
23
+
24
+ def simplify(pattern)
25
+ pattern.to_s.sub(%r{(/\*\*)?(/\*)?$}, "")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,110 @@
1
+ require "pathname"
2
+ require "set"
3
+
4
+ module CC
5
+ class Workspace
6
+ class PathTree
7
+ def self.create(pathname)
8
+ if pathname.directory?
9
+ new(pathname.to_s)
10
+ else
11
+ FileNode.new(pathname.to_s)
12
+ end
13
+ end
14
+
15
+ def initialize(root_path, children = {})
16
+ @root_path = root_path.dup.freeze
17
+ @children = children
18
+ end
19
+
20
+ def clone
21
+ self.class.new(root_path, children.dup)
22
+ end
23
+
24
+ def exclude_paths(paths)
25
+ paths.each { |path| remove(*normalized_path_pieces(path)) }
26
+ end
27
+
28
+ def include_paths(paths)
29
+ paths.each { |path| add(*normalized_path_pieces(path)) }
30
+ end
31
+
32
+ def all_paths
33
+ if populated?
34
+ children.values.flat_map(&:all_paths)
35
+ else
36
+ [File.join(root_path, File::SEPARATOR)]
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def populated?
43
+ children.present?
44
+ end
45
+
46
+ def remove(head = nil, *tail)
47
+ return if head.nil? && tail.empty?
48
+ populate_direct_children
49
+
50
+ if (child = children[head])
51
+ child.remove(*tail)
52
+ children.delete(head) unless child.populated?
53
+ end
54
+ end
55
+
56
+ def add(head = nil, *tail)
57
+ return if head.nil? && tail.empty?
58
+
59
+ if (entry = find_direct_child(head))
60
+ children[entry.basename.to_s] = self.class.create(entry)
61
+ children[entry.basename.to_s].add(*tail)
62
+ else
63
+ CLI.debug("Couldn't include because part of path doesn't exist.", path: File.join(root_path, head))
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ attr_reader :children, :root_path
70
+
71
+ def populate_direct_children
72
+ return if populated?
73
+
74
+ Pathname.new(root_path).each_child do |child_path|
75
+ children[child_path.basename.to_s] = self.class.create(child_path)
76
+ end
77
+ end
78
+
79
+ def find_direct_child(name)
80
+ Pathname.new(root_path).children.detect { |c| c.basename.to_s == name }
81
+ end
82
+
83
+ def normalized_path_pieces(path)
84
+ Pathname.new(path).cleanpath.to_s.split(File::SEPARATOR).reject(&:blank?)
85
+ end
86
+
87
+ class FileNode
88
+ def initialize(root_path)
89
+ @root_path = root_path.dup.freeze
90
+ end
91
+
92
+ def all_paths
93
+ [@root_path]
94
+ end
95
+
96
+ def populated?
97
+ false
98
+ end
99
+
100
+ def remove(*)
101
+ # this space intentionally left blank
102
+ end
103
+
104
+ def add(*)
105
+ # this space intentionally left blank
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: codeclimate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code Climate
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-21 00:00:00.000000000 Z
11
+ date: 2016-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 0.6.1
53
+ version: 0.7.0
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: 0.6.1
60
+ version: 0.7.0
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: faraday
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -216,15 +216,10 @@ files:
216
216
  - lib/cc/analyzer/formatters/json_formatter.rb
217
217
  - lib/cc/analyzer/formatters/plain_text_formatter.rb
218
218
  - lib/cc/analyzer/formatters/spinner.rb
219
- - lib/cc/analyzer/include_paths_builder.rb
220
219
  - lib/cc/analyzer/issue.rb
221
220
  - lib/cc/analyzer/issue_sorter.rb
222
221
  - lib/cc/analyzer/location_description.rb
223
222
  - lib/cc/analyzer/logging_container_listener.rb
224
- - lib/cc/analyzer/path_entries.rb
225
- - lib/cc/analyzer/path_filter.rb
226
- - lib/cc/analyzer/path_minimizer.rb
227
- - lib/cc/analyzer/path_patterns.rb
228
223
  - lib/cc/analyzer/raising_container_listener.rb
229
224
  - lib/cc/analyzer/source_buffer.rb
230
225
  - lib/cc/analyzer/statsd_container_listener.rb
@@ -248,7 +243,9 @@ files:
248
243
  - lib/cc/cli/upgrade_config_generator.rb
249
244
  - lib/cc/cli/validate_config.rb
250
245
  - lib/cc/cli/version.rb
251
- - lib/file_utils_ext.rb
246
+ - lib/cc/workspace.rb
247
+ - lib/cc/workspace/exclusion.rb
248
+ - lib/cc/workspace/path_tree.rb
252
249
  homepage: https://codeclimate.com
253
250
  licenses:
254
251
  - MIT
@@ -1,62 +0,0 @@
1
- require "file_utils_ext"
2
- require "cc/analyzer/path_minimizer"
3
- require "cc/analyzer/path_filter"
4
-
5
- module CC
6
- module Analyzer
7
- class IncludePathsBuilder
8
- IGNORE_PATHS = [".", "..", ".git"].freeze
9
-
10
- attr_reader :cc_include_paths
11
-
12
- def initialize(cc_exclude_paths, cc_include_paths = [])
13
- @cc_exclude_paths = cc_exclude_paths
14
- @cc_include_paths = cc_include_paths
15
- end
16
-
17
- def build
18
- PathMinimizer.new(paths_filter.paths).minimize.uniq
19
- end
20
-
21
- private
22
-
23
- def paths_filter
24
- @_paths =
25
- PathFilter.new(include_paths).
26
- reject_paths(ignored_files).
27
- reject_unreadable_paths.
28
- select_readable_files.
29
- reject_symlinks
30
- end
31
-
32
- def include_paths
33
- if @cc_include_paths.empty?
34
- all_paths
35
- else
36
- @cc_include_paths.flat_map do |path|
37
- PathEntries.new(path).entries
38
- end
39
- end
40
- end
41
-
42
- def all_paths
43
- Dir.glob("*", File::FNM_DOTMATCH).
44
- reject { |path| IncludePathsBuilder::IGNORE_PATHS.include?(path) }.
45
- flat_map { |path| PathEntries.new(path).entries }
46
- end
47
-
48
- def ignored_files
49
- return @_ignored_files if @_ignored_files
50
-
51
- Tempfile.open(".cc_gitignore") do |tmp|
52
- tmp.write(File.read(".gitignore")) if File.file?(".gitignore")
53
- tmp << @cc_exclude_paths.join("\n")
54
- tmp.close
55
- tracked_and_ignored = `git ls-files -zi -X #{tmp.path} 2>/dev/null`.split("\0")
56
- untracked_and_ignored = `git ls-files -zio -X #{tmp.path} 2>/dev/null`.split("\0")
57
- @_ignored_files = tracked_and_ignored + untracked_and_ignored
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,27 +0,0 @@
1
- module CC
2
- module Analyzer
3
- class PathEntries
4
- def initialize(initial_path)
5
- @initial_path = initial_path.gsub(%r{/$}, "")
6
- end
7
-
8
- def entries
9
- if File.directory?(initial_path)
10
- all_entries.reject do |path|
11
- path.end_with?("/.") || path.start_with?(".git/")
12
- end
13
- else
14
- initial_path
15
- end
16
- end
17
-
18
- private
19
-
20
- attr_reader :initial_path
21
-
22
- def all_entries
23
- Dir.glob("#{initial_path}/**/*", File::FNM_DOTMATCH).push(initial_path)
24
- end
25
- end
26
- end
27
- end
@@ -1,50 +0,0 @@
1
- module CC
2
- module Analyzer
3
- class PathFilter
4
- attr_reader :paths
5
-
6
- def initialize(paths)
7
- @paths = paths
8
- end
9
-
10
- def reject_unreadable_paths
11
- @paths = paths - unreadable_path_entries
12
- self
13
- end
14
-
15
- def reject_paths(ignore_paths)
16
- @paths = paths - ignore_paths
17
- self
18
- end
19
-
20
- def select_readable_files
21
- @paths = paths.select { |path| File.exist?(path) && FileUtils.readable_by_all?(path) }
22
- self
23
- end
24
-
25
- def reject_symlinks
26
- @paths = paths.reject { |path| File.symlink?(path) }
27
- self
28
- end
29
-
30
- def reject_globs(globs)
31
- patterns = PathPatterns.new(globs)
32
- @paths = paths.reject { |path| patterns.match?(pathpatterns.match?(path)) }
33
- self
34
- end
35
-
36
- private
37
-
38
- def unreadable_path_entries
39
- @_unreadable_path_entries ||=
40
- unreadable_paths.flat_map { |path| PathEntries.new(path).entries }
41
- end
42
-
43
- def unreadable_paths
44
- paths.select do |path|
45
- File.directory?(path) && !FileUtils.readable_by_all?(path)
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,84 +0,0 @@
1
- require "cc/analyzer/path_entries"
2
- require "cc/analyzer/include_paths_builder"
3
-
4
- module CC
5
- module Analyzer
6
- class PathMinimizer
7
- def initialize(paths)
8
- @paths = paths
9
- @to_remove = []
10
- end
11
-
12
- def minimize
13
- if diff.empty?
14
- ["./"]
15
- else
16
- filtered_paths
17
- end
18
- end
19
-
20
- private
21
-
22
- attr_reader :paths
23
-
24
- def diff
25
- @_diff ||=
26
- (all_files - paths).
27
- reject { |path| File.symlink?(path) }.
28
- flat_map { |path| build_entry_combinations(path) }
29
- end
30
-
31
- def filtered_paths
32
- filtered_paths = @paths - paths_to_remove
33
- filtered_paths.map { |path| add_trailing_slash(path) }
34
- end
35
-
36
- def paths_to_remove
37
- @paths.reduce([]) do |to_remove, path|
38
- if File.directory?(path)
39
- to_remove + removable_paths_for(path)
40
- else
41
- to_remove
42
- end
43
- end
44
- end
45
-
46
- def removable_paths_for(path)
47
- file_paths = PathEntries.new(path).entries
48
-
49
- if all_paths_match?(file_paths)
50
- file_paths - [path]
51
- else
52
- [path]
53
- end
54
- end
55
-
56
- def all_paths_match?(paths)
57
- paths.all? { |path| @paths.include?(path) }
58
- end
59
-
60
- def add_trailing_slash(path)
61
- if File.directory?(path) && !path.end_with?("/")
62
- "#{path}/"
63
- else
64
- path
65
- end
66
- end
67
-
68
- def build_entry_combinations(path)
69
- split = path.split("/")
70
-
71
- 0.upto(split.length - 1).map do |n|
72
- split[0..n].join("/")
73
- end
74
- end
75
-
76
- def all_files
77
- @_all_files ||=
78
- Dir.glob("*", File::FNM_DOTMATCH).
79
- reject { |path| IncludePathsBuilder::IGNORE_PATHS.include?(path) }.
80
- flat_map { |path| PathEntries.new(path).entries }
81
- end
82
- end
83
- end
84
- end
@@ -1,46 +0,0 @@
1
- module CC
2
- module Analyzer
3
- class PathPatterns
4
- def initialize(patterns, root = Dir.pwd)
5
- @patterns = patterns
6
- @root = root
7
- end
8
-
9
- def match?(path)
10
- expanded.include?(path)
11
- end
12
-
13
- def expanded
14
- @expanded ||= expand
15
- end
16
-
17
- private
18
-
19
- def expand
20
- results = Dir.chdir(@root) do
21
- @patterns.flat_map do |pattern|
22
- value = glob_value(pattern)
23
- Dir.glob(value)
24
- end
25
- end
26
-
27
- results.sort.uniq
28
- end
29
-
30
- def glob_value(pattern)
31
- # FIXME: there exists a temporary workaround whereby **-style globs
32
- # are translated to **/*-style globs within cc-yaml's custom
33
- # Glob#value method. It was thought that that would work correctly
34
- # with Dir.glob but it turns out we have to actually invoke #value
35
- # directrly for this to work. We need to guard this on class (not
36
- # respond_to?) because our mocking framework adds a #value method to
37
- # all objects, apparently.
38
- if pattern.is_a?(CC::Yaml::Nodes::Glob)
39
- pattern.value
40
- else
41
- pattern
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,7 +0,0 @@
1
- require "fileutils"
2
-
3
- module FileUtils
4
- def self.readable_by_all?(path)
5
- (File.stat(path).mode & 004) != 0
6
- end
7
- end