Class: Necropsy::Project
- Inherits:
-
Object
- Object
- Necropsy::Project
- Defined in:
- lib/necropsy/project.rb
Constant Summary collapse
- SOURCE_SNAPSHOT_MAX_BYTES =
268_435_456- SOURCE_SNAPSHOT_MAX_FILES =
100_000- SOURCE_DIGEST_CHUNK_BYTES =
65_536- EXCLUDED_DIRECTORIES =
%w[ .bundle .git .necropsy_cache .ruby-lsp coverage doc node_modules pkg tmp vendor ].freeze
- NESTED_EXCLUDED_DIRECTORIES =
EXCLUDED_DIRECTORIES.freeze
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
Instance Method Summary collapse
- #cache_files ⇒ Object
- #changed_files(diff_base) ⇒ Object
- #fresh_source_snapshot ⇒ Object
-
#initialize(root:, config:) ⇒ Project
constructor
A new instance of Project.
- #non_ruby_reference_files ⇒ Object
- #reference_file?(file) ⇒ Boolean
- #reference_files ⇒ Object
- #reference_ruby_files ⇒ Object
- #relative_path(file) ⇒ Object
- #ruby_files ⇒ Object
- #scan_files ⇒ Object
- #scan_inventory_key ⇒ Object
- #scan_result ⇒ Object
- #scope_blockers ⇒ Object
- #scope_diagnostics ⇒ Object
- #source_domains ⇒ Object
- #source_snapshot ⇒ Object
- #test_file?(file) ⇒ Boolean
Constructor Details
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
26 27 28 |
# File 'lib/necropsy/project.rb', line 26 def config @config end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
26 27 28 |
# File 'lib/necropsy/project.rb', line 26 def root @root end |
Instance Method Details
#cache_files ⇒ Object
82 83 84 |
# File 'lib/necropsy/project.rb', line 82 def cache_files @cache_files ||= (scan_files + reference_files).uniq end |
#changed_files(diff_base) ⇒ Object
190 191 192 |
# File 'lib/necropsy/project.rb', line 190 def changed_files(diff_base) Guardrail::Diff.changed_files(root: root, diff_base: diff_base) end |
#fresh_source_snapshot ⇒ Object
98 99 100 |
# File 'lib/necropsy/project.rb', line 98 def fresh_source_snapshot self.class.new(root: root, config: config).source_snapshot end |
#non_ruby_reference_files ⇒ Object
68 69 70 |
# File 'lib/necropsy/project.rb', line 68 def non_ruby_reference_files @non_ruby_reference_files ||= (reference_files.to_set - reference_ruby_files.to_set).to_a.sort end |
#reference_file?(file) ⇒ Boolean
72 73 74 75 76 |
# File 'lib/necropsy/project.rb', line 72 def reference_file?(file) reference_file_set.include?(File.(file, root)) rescue ArgumentError false end |
#reference_files ⇒ Object
58 59 60 61 62 |
# File 'lib/necropsy/project.rb', line 58 def reference_files @reference_files ||= repository_files.select do |file| config.reference_paths.any? { |pattern| path_matches?(pattern, relative_path(file)) } end.sort end |
#reference_ruby_files ⇒ Object
64 65 66 |
# File 'lib/necropsy/project.rb', line 64 def reference_ruby_files @reference_ruby_files ||= reference_files.select { |file| ruby_source?(file) } end |
#relative_path(file) ⇒ Object
186 187 188 |
# File 'lib/necropsy/project.rb', line 186 def relative_path(file) Pathname.new(File.(file)).relative_path_from(Pathname.new(root)).to_s end |
#ruby_files ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/necropsy/project.rb', line 44 def ruby_files @ruby_files ||= begin candidates = if config.analyze_paths.any? ruby_candidates.dup else default_analyze_candidates.dup end warn_excluded_entry_points(candidates) if config.analyze_paths.any? || config.exclude_paths.any? candidates.select! { |file| analyzed_path?(relative_path(file)) } if config.analyze_paths.any? candidates.reject! { |file| excluded_path?(relative_path(file)) } candidates.sort end end |
#scan_files ⇒ Object
78 79 80 |
# File 'lib/necropsy/project.rb', line 78 def scan_files @scan_files ||= (ruby_files + reference_ruby_files).uniq end |
#scan_inventory_key ⇒ Object
86 87 88 89 90 91 92 |
# File 'lib/necropsy/project.rb', line 86 def scan_inventory_key { 'ignored_symlinks' => ignored_symlinks.sort, 'source_discovery_issues' => source_discovery_issues, 'ruby_files_outside_scopes' => ruby_files_outside_scopes.map { |file| relative_path(file) } } end |
#scan_result ⇒ Object
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/necropsy/project.rb', line 33 def scan_result @scan_result ||= Cache::ScanCache.new(project: self).fetch(cache_files) do AstScanner.new( project: self, files: scan_files, source_domains: source_domains, scope_diagnostics: scope_diagnostics ).scan end end |
#scope_blockers ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/necropsy/project.rb', line 143 def scope_blockers excluded = scope_diagnostics.fetch('potential_callers_outside_reference') blockers = [] if excluded.fetch('runtime_count').positive? blockers << Blocker.new( kind: :reference_scope_incomplete, scope_kind: :global, scope_value: '*', source: :source_discovery, reason: "paths.reference excludes #{excluded.fetch('runtime_count')} non-test Ruby caller candidates", suggested_action: :expand_reference_scope, metadata: { 'caller_domain' => 'runtime', 'excluded_file_count' => excluded.fetch('count'), 'excluded_runtime_file_count' => excluded.fetch('runtime_count'), 'files' => excluded.fetch('samples') } ) end runtime_issues = source_discovery_issues.reject { |issue| issue.fetch('domain') == 'test' } if runtime_issues.any? blockers << Blocker.new( kind: :source_discovery_incomplete, scope_kind: :global, scope_value: '*', source: :source_discovery, reason: "#{runtime_issues.length} runtime source paths could not be inspected safely", suggested_action: :review_source_discovery, metadata: { 'caller_domain' => 'runtime', 'issue_count' => runtime_issues.length, 'files' => runtime_issues.first(50) } ) end blockers end |
#scope_diagnostics ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/necropsy/project.rb', line 111 def scope_diagnostics @scope_diagnostics ||= begin analyzed = ruby_files.to_set references = reference_ruby_files.to_set reference_only = (references - analyzed).map { |file| relative_path(file) }.sort excluded_callers = ruby_files_outside_scopes.map { |file| relative_path(file) } runtime_excluded_callers = excluded_callers.reject { |file| test_source_path?(file) } potential = ruby_candidates.filter_map do |file| next if analyzed.include?(file) next unless potential_entry_point_path?(relative_path(file)) { 'file' => relative_path(file), 'reference_status' => references.include?(file) ? 'reference_only' : 'excluded' } end.sort_by { |entry| entry.fetch('file') } { 'analyze_file_count' => ruby_files.length, 'reference_file_count' => reference_files.length, 'reference_only_ruby_files' => reference_only, 'potential_callers_outside_reference' => { 'count' => excluded_callers.length, 'runtime_count' => runtime_excluded_callers.length, 'samples' => excluded_callers.first(20) }, 'potential_entry_points_outside_analyze' => potential, 'ignored_symlinks' => ignored_symlinks.sort, 'source_discovery_issues' => source_discovery_issues }.tap { warn_excluded_callers(runtime_excluded_callers) } end end |
#source_domains ⇒ Object
102 103 104 105 106 107 108 109 |
# File 'lib/necropsy/project.rb', line 102 def source_domains @source_domains ||= begin analyzed = ruby_files.to_set scan_files.to_h do |file| [relative_path(file), analyzed.include?(file) ? :analyze : :reference] end end end |
#source_snapshot ⇒ Object
94 95 96 |
# File 'lib/necropsy/project.rb', line 94 def source_snapshot @source_snapshot ||= build_source_snapshot end |
#test_file?(file) ⇒ Boolean
182 183 184 |
# File 'lib/necropsy/project.rb', line 182 def test_file?(file) test_source_path?(relative_path(file)) end |