audition 0.3.0 → 0.4.1

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.
@@ -12,6 +12,12 @@ module Audition
12
12
  vendor node_modules tmp log coverage pkg .git .bundle
13
13
  ].freeze
14
14
 
15
+ # Directories holding the target's tests rather than the code
16
+ # a production boot loads. The default; `test_dirs` in
17
+ # .audition.yml replaces it for a project that files them
18
+ # elsewhere.
19
+ TEST_DIRS = %w[test spec features].freeze
20
+
15
21
  # Cargo and Zig build output: full of .so/.dylib artifacts that
16
22
  # are not the extension `require` loads.
17
23
  BUILD_DIRS = %w[target zig-out].freeze
@@ -20,6 +26,11 @@ module Audition
20
26
  # "bundle" on macOS, "so" everywhere else, Windows included).
21
27
  COMPILED = "*.{bundle,so}"
22
28
 
29
+ # Generated type stubs. The extension is the convention; where
30
+ # the generator writes them is not, so they are found by
31
+ # extension anywhere in the tree.
32
+ STUBS = "*.rbi"
33
+
23
34
  # @return [Symbol] one of `:script`, `:gem`, `:rack`, `:rails`,
24
35
  # `:directory`, `:bundle`
25
36
  attr_reader :type
@@ -34,6 +45,10 @@ module Audition
34
45
  # shipped with the target; empty for scripts and file lists
35
46
  attr_reader :compiled_files
36
47
 
48
+ # @return [Array<String>] generated type stubs (.rbi) found
49
+ # anywhere in the target's tree
50
+ attr_reader :stub_files
51
+
37
52
  # @return [Hash, nil] dynamic probe entry (`:mode` plus
38
53
  # mode-specific keys), nil for static-only targets
39
54
  attr_reader :entry
@@ -75,7 +90,8 @@ module Audition
75
90
  type: :files,
76
91
  root: Dir.pwd,
77
92
  ruby_files: paths,
78
- entry: nil
93
+ entry: nil,
94
+ stub_files: stubs(Dir.pwd)
79
95
  )
80
96
  end
81
97
 
@@ -96,10 +112,26 @@ module Audition
96
112
  type: :script,
97
113
  root: File.dirname(path),
98
114
  ruby_files: [path],
99
- entry: {mode: :script, path: path}
115
+ # A file inside a Rails root cannot run standalone; the
116
+ # script probe would report a misleading boot failure, so
117
+ # such files stay static-only.
118
+ entry: rails_member?(path) ? nil : {mode: :script, path: path},
119
+ stub_files: stubs(File.dirname(path))
100
120
  )
101
121
  end
102
122
 
123
+ def self.rails_member?(path)
124
+ dir = File.dirname(File.expand_path(path))
125
+ until dir == (parent = File.dirname(dir))
126
+ return true if File.file?(
127
+ File.join(dir, "config", "application.rb")
128
+ )
129
+
130
+ dir = parent
131
+ end
132
+ false
133
+ end
134
+
103
135
  def self.from_directory(dir)
104
136
  application_rb = File.join(dir, "config", "application.rb")
105
137
  config_ru = File.join(dir, "config.ru")
@@ -117,7 +149,8 @@ module Audition
117
149
  root: dir,
118
150
  ruby_files: glob(dir),
119
151
  entry: nil,
120
- compiled_files: compiled(dir)
152
+ compiled_files: compiled(dir),
153
+ stub_files: stubs(dir)
121
154
  )
122
155
  end
123
156
  end
@@ -131,16 +164,21 @@ module Audition
131
164
  glob(File.join(spec.full_gem_path, rp))
132
165
  end,
133
166
  entry: {mode: :require, feature: name,
167
+ load_paths: spec.full_require_paths,
134
168
  root: spec.full_gem_path},
135
- compiled_files: compiled_for(spec)
169
+ compiled_files: compiled_for(spec),
170
+ stub_files: stubs(spec.full_gem_path)
136
171
  )
137
172
  rescue Gem::MissingSpecError
138
173
  raise Error,
139
174
  "#{name} is not a file, directory, or installed gem"
140
175
  end
141
176
 
177
+ # An app's own Ruby is not confined to app/lib/config: local
178
+ # gems, engines and tests boot into the same process, so the
179
+ # whole root is scanned.
142
180
  def self.rails_target(dir)
143
- files = %w[app lib config].flat_map { |d| glob(File.join(dir, d)) }
181
+ files = glob(dir)
144
182
  files << File.join(dir, "config.ru")
145
183
  new(
146
184
  type: :rails,
@@ -151,7 +189,8 @@ module Audition
151
189
  environment: File.join(dir, "config", "environment.rb"),
152
190
  root: dir
153
191
  },
154
- compiled_files: compiled(dir)
192
+ compiled_files: compiled(dir),
193
+ stub_files: stubs(dir)
155
194
  )
156
195
  end
157
196
 
@@ -160,24 +199,31 @@ module Audition
160
199
  type: :rack,
161
200
  root: dir,
162
201
  ruby_files: [config_ru] + glob(dir),
163
- entry: {mode: :rack, config_ru: config_ru},
164
- compiled_files: compiled(dir)
202
+ entry: {mode: :rack, config_ru: config_ru, root: dir},
203
+ compiled_files: compiled(dir),
204
+ stub_files: stubs(dir)
165
205
  )
166
206
  end
167
207
 
208
+ # Reading require_paths off the gemspec would mean evaluating
209
+ # it, which the static pass never does; `lib` is the packaging
210
+ # default, and a gem that keeps its code elsewhere falls back to
211
+ # the whole checkout rather than scanning nothing.
168
212
  def self.gem_dir_target(dir, gemspec)
169
213
  lib = File.join(dir, "lib")
214
+ paths = File.directory?(lib) ? [lib] : [dir]
170
215
  new(
171
216
  type: :gem,
172
217
  root: dir,
173
- ruby_files: glob(lib),
218
+ ruby_files: paths.flat_map { |path| glob(path) },
174
219
  entry: {
175
220
  mode: :require,
176
221
  feature: File.basename(gemspec, ".gemspec"),
177
- load_paths: [lib],
222
+ load_paths: paths,
178
223
  root: dir
179
224
  },
180
- compiled_files: compiled(dir)
225
+ compiled_files: compiled(dir),
226
+ stub_files: stubs(dir)
181
227
  )
182
228
  end
183
229
 
@@ -199,6 +245,14 @@ module Audition
199
245
  end.sort
200
246
  end
201
247
 
248
+ # Generated type stubs anywhere under a directory.
249
+ #
250
+ # @param dir [String]
251
+ # @return [Array<String>]
252
+ def self.stubs(dir)
253
+ glob(dir, STUBS)
254
+ end
255
+
202
256
  # macOS debug-symbol bundles (x.bundle.dSYM/...) carry a file
203
257
  # with the extension's name that nothing ever loads.
204
258
  def self.compiled(dir)
@@ -222,15 +276,30 @@ module Audition
222
276
 
223
277
  private_class_method :from_file, :from_directory, :from_gem_name,
224
278
  :rails_target, :rack_target, :gem_dir_target,
225
- :glob, :compiled, :normalize
279
+ :rails_member?, :glob, :compiled, :normalize
226
280
 
227
281
  def initialize(type:, root:, ruby_files:, entry:,
228
- compiled_files: [])
282
+ compiled_files: [], stub_files: [])
229
283
  @type = type
230
284
  @root = root
231
285
  @ruby_files = ruby_files
232
286
  @entry = entry
233
287
  @compiled_files = compiled_files
288
+ @stub_files = stub_files
289
+ end
290
+
291
+ # Whether a path is test/spec code rather than code the
292
+ # production boot loads.
293
+ #
294
+ # @param path [String] absolute or root-relative
295
+ # @param dirs [Array<String>] directory names that hold tests
296
+ # @return [Boolean]
297
+ def test_file?(path, dirs: TEST_DIRS)
298
+ relative = File.expand_path(path, root)
299
+ .delete_prefix("#{root}/")
300
+ parts = relative.split("/")
301
+ parts[0..-2].any? { |p| dirs.include?(p) } ||
302
+ parts.last.to_s.end_with?("_test.rb", "_spec.rb")
234
303
  end
235
304
  end
236
305
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Audition
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.1"
5
5
  end
data/lib/audition.rb CHANGED
@@ -1,15 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "audition/version"
4
+ require_relative "audition/progress"
4
5
  require_relative "audition/finding"
5
6
  require_relative "audition/target"
6
7
  require_relative "audition/static/source_file"
7
8
  require_relative "audition/static/literal_classifier"
8
9
  require_relative "audition/static/checks"
9
10
  require_relative "audition/static/graph_audit"
11
+ require_relative "audition/static/gem_calls"
10
12
  require_relative "audition/static/native_extensions"
11
13
  require_relative "audition/static/analyzer"
12
14
  require_relative "audition/dynamic/prober"
15
+ require_relative "audition/reconciliation"
13
16
  require_relative "audition/report"
14
17
  require_relative "audition/config"
15
18
  require_relative "audition/baseline"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: audition
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Markin
@@ -43,14 +43,14 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '0.2'
46
+ version: '0.4'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '0.2'
53
+ version: '0.4'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: table_tennis
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -102,24 +102,32 @@ files:
102
102
  - lib/audition/dynamic/prober.rb
103
103
  - lib/audition/finding.rb
104
104
  - lib/audition/fixer.rb
105
+ - lib/audition/progress.rb
106
+ - lib/audition/reconciliation.rb
105
107
  - lib/audition/report.rb
106
108
  - lib/audition/report/github.rb
107
109
  - lib/audition/report/json.rb
108
110
  - lib/audition/report/style.rb
111
+ - lib/audition/report/sweep.rb
109
112
  - lib/audition/report/text.rb
110
113
  - lib/audition/rewriters.rb
111
114
  - lib/audition/static/analyzer.rb
112
115
  - lib/audition/static/checks.rb
113
116
  - lib/audition/static/checks/base.rb
117
+ - lib/audition/static/checks/dependency_class_state.rb
114
118
  - lib/audition/static/checks/global_variables.rb
119
+ - lib/audition/static/checks/instance_memoization.rb
115
120
  - lib/audition/static/checks/mutable_constants.rb
116
121
  - lib/audition/static/checks/ractor_isolation.rb
117
122
  - lib/audition/static/checks/runtime_require.rb
118
123
  - lib/audition/static/checks/unsafe_calls.rb
124
+ - lib/audition/static/checks/unshareable_reads.rb
125
+ - lib/audition/static/gem_calls.rb
119
126
  - lib/audition/static/graph_audit.rb
120
127
  - lib/audition/static/literal_classifier.rb
121
128
  - lib/audition/static/native_extensions.rb
122
129
  - lib/audition/static/source_file.rb
130
+ - lib/audition/static/work_split.rb
123
131
  - lib/audition/target.rb
124
132
  - lib/audition/version.rb
125
133
  homepage: https://github.com/yaroslav/audition
@@ -142,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
150
  - !ruby/object:Gem::Version
143
151
  version: '0'
144
152
  requirements: []
145
- rubygems_version: 4.0.16
153
+ rubygems_version: 4.0.20
146
154
  specification_version: 4
147
155
  summary: Probe Ruby code for Ractor-readiness
148
156
  test_files: []