spoom 1.1.15 → 1.1.16

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a61ae83fa56b7a78027c8954c5680d4784c5a3f8b2d717ed3ba49b36409ede2
4
- data.tar.gz: b339a398e57f92f71b797dd8d427e18749d1afd6246be42fe549baf96563706e
3
+ metadata.gz: 9c41426fe9cc0e4f20115b5ce81ff2256d00f255acf59f29beb7e6ffeef16b0c
4
+ data.tar.gz: ea751643663fc13175ad1ec23c39872a7b963b2f6f619a497e4bc6fc51052e8f
5
5
  SHA512:
6
- metadata.gz: 981c7b625e31b732ce731c600fd1291afb9635818918d77424750ba152633f06e8f4515037c5044badcc951ded1dd9fd768ae135ff2305359005a2cb7ad47a38
7
- data.tar.gz: 123baadb32b3341271b323eab29a1ddb4561c74b9afbe6cfa71af61ff1547783a24a1d439406f482b3c40920b960e4b04fc19ca7c0423c18407af00bc35fa692
6
+ metadata.gz: 4503195f8c603f0148f9393a2fdfdb5667ef6b6a9d43917484952409cfa69ce5edc86fda0275144451790d5e2928fe33b7fd19597ab0f9500678185e65f65b33
7
+ data.tar.gz: 4eb49038a48389c0ac8160c1f9b7e37f80275c8d39fda35a8d08b1e75bae0e763382f24419a1fc51a3e2c7972d4e4d8e2ef22996961bb9a003ee7ad4bbb1abbb
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  group :development do
9
- gem "pry-byebug"
9
+ gem "debug"
10
10
  gem "ruby-lsp"
11
11
  gem "rubocop-shopify", require: false
12
12
  gem "rubocop-sorbet", require: false
@@ -13,20 +13,36 @@ module Spoom
13
13
  default_task :bump
14
14
 
15
15
  desc "bump DIRECTORY", "Change Sorbet sigils from one strictness to another when no errors"
16
- option :from, type: :string, default: Spoom::Sorbet::Sigils::STRICTNESS_FALSE,
16
+ option :from,
17
+ type: :string,
18
+ default: Spoom::Sorbet::Sigils::STRICTNESS_FALSE,
17
19
  desc: "Change only files from this strictness"
18
- option :to, type: :string, default: Spoom::Sorbet::Sigils::STRICTNESS_TRUE,
20
+ option :to,
21
+ type: :string,
22
+ default: Spoom::Sorbet::Sigils::STRICTNESS_TRUE,
19
23
  desc: "Change files to this strictness"
20
- option :force, type: :boolean, default: false, aliases: :f,
24
+ option :force,
25
+ type: :boolean,
26
+ default: false,
27
+ aliases: :f,
21
28
  desc: "Change strictness without type checking"
22
29
  option :sorbet, type: :string, desc: "Path to custom Sorbet bin"
23
- option :dry, type: :boolean, default: false, aliases: :d,
30
+ option :dry,
31
+ type: :boolean,
32
+ default: false,
33
+ aliases: :d,
24
34
  desc: "Only display what would happen, do not actually change sigils"
25
- option :only, type: :string, default: nil, aliases: :o,
35
+ option :only,
36
+ type: :string,
37
+ default: nil,
38
+ aliases: :o,
26
39
  desc: "Only change specified list (one file by line)"
27
- option :suggest_bump_command, type: :string,
40
+ option :suggest_bump_command,
41
+ type: :string,
28
42
  desc: "Command to suggest if files can be bumped"
29
- option :count_errors, type: :boolean, default: false,
43
+ option :count_errors,
44
+ type: :boolean,
45
+ default: false,
30
46
  desc: "Count the number of errors if all files were bumped"
31
47
  option :sorbet_options, type: :string, default: "", desc: "Pass options to Sorbet"
32
48
  sig { params(directory: String).void }
@@ -85,20 +101,33 @@ module Spoom
85
101
  end
86
102
 
87
103
  error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
88
- result = Sorbet.srb_tc(
89
- *options[:sorbet_options].split(" "),
90
- "--error-url-base=#{error_url_base}",
91
- path: exec_path,
92
- capture_err: true,
93
- sorbet_bin: options[:sorbet],
94
- )
95
-
96
- check_sorbet_segfault(result.exit_code) do
104
+ result = begin
105
+ Sorbet.srb_tc(
106
+ *options[:sorbet_options].split(" "),
107
+ "--error-url-base=#{error_url_base}",
108
+ path: exec_path,
109
+ capture_err: true,
110
+ sorbet_bin: options[:sorbet],
111
+ )
112
+ rescue Spoom::Sorbet::Error::Segfault => error
97
113
  say_error(<<~ERR, status: nil)
114
+ !!! Sorbet exited with code #{Spoom::Sorbet::SEGFAULT_CODE} - SEGFAULT !!!
115
+
116
+ This is most likely related to a bug in Sorbet.
98
117
  It means one of the file bumped to `typed: #{to}` made Sorbet crash.
99
118
  Run `spoom bump -f` locally followed by `bundle exec srb tc` to investigate the problem.
100
119
  ERR
101
120
  undo_changes(files_to_bump, from)
121
+ exit(error.result.exit_code)
122
+ rescue Spoom::Sorbet::Error::Killed => error
123
+ say_error(<<~ERR, status: nil)
124
+ !!! Sorbet exited with code #{Spoom::Sorbet::KILLED_CODE} - KILLED !!!
125
+
126
+ It means Sorbet was killed while executing. Changes to files have not been applied.
127
+ Re-run `spoom bump` to try again.
128
+ ERR
129
+ undo_changes(files_to_bump, from)
130
+ exit(error.result.exit_code)
102
131
  end
103
132
 
104
133
  if result.status
@@ -115,17 +115,30 @@ module Spoom
115
115
 
116
116
  desc "report", "Produce a typing coverage report"
117
117
  option :data, type: :string, default: DATA_DIR, desc: "Snapshots JSON data"
118
- option :file, type: :string, default: "spoom_report.html", aliases: :f,
118
+ option :file,
119
+ type: :string,
120
+ default: "spoom_report.html",
121
+ aliases: :f,
119
122
  desc: "Save report to file"
120
- option :color_ignore, type: :string, default: Spoom::Coverage::D3::COLOR_IGNORE,
123
+ option :color_ignore,
124
+ type: :string,
125
+ default: Spoom::Coverage::D3::COLOR_IGNORE,
121
126
  desc: "Color used for typed: ignore"
122
- option :color_false, type: :string, default: Spoom::Coverage::D3::COLOR_FALSE,
127
+ option :color_false,
128
+ type: :string,
129
+ default: Spoom::Coverage::D3::COLOR_FALSE,
123
130
  desc: "Color used for typed: false"
124
- option :color_true, type: :string, default: Spoom::Coverage::D3::COLOR_TRUE,
131
+ option :color_true,
132
+ type: :string,
133
+ default: Spoom::Coverage::D3::COLOR_TRUE,
125
134
  desc: "Color used for typed: true"
126
- option :color_strict, type: :string, default: Spoom::Coverage::D3::COLOR_STRICT,
135
+ option :color_strict,
136
+ type: :string,
137
+ default: Spoom::Coverage::D3::COLOR_STRICT,
127
138
  desc: "Color used for typed: strict"
128
- option :color_strong, type: :string, default: Spoom::Coverage::D3::COLOR_STRONG,
139
+ option :color_strong,
140
+ type: :string,
141
+ default: Spoom::Coverage::D3::COLOR_STRONG,
129
142
  desc: "Color used for typed: strong"
130
143
  def report
131
144
  in_sorbet_project!
@@ -83,20 +83,6 @@ module Spoom
83
83
  Sorbet::Config.parse_file(sorbet_config_file)
84
84
  end
85
85
 
86
- sig { params(exit_code: Integer, block: T.nilable(T.proc.void)).void }
87
- def check_sorbet_segfault(exit_code, &block)
88
- return unless exit_code == Spoom::Sorbet::SEGFAULT_CODE
89
-
90
- say_error(<<~ERR, status: nil)
91
- #{red("!!! Sorbet exited with code #{exit_code} - SEGFAULT !!!")}
92
-
93
- This is most likely related to a bug in Sorbet.
94
- ERR
95
-
96
- block&.call
97
- exit(exit_code)
98
- end
99
-
100
86
  # Colors
101
87
 
102
88
  # Color used to highlight expressions in backticks
data/lib/spoom/cli/run.rb CHANGED
@@ -43,7 +43,6 @@ module Spoom
43
43
  sorbet_bin: sorbet,
44
44
  )
45
45
 
46
- check_sorbet_segfault(result.code)
47
46
  say_error(result.err, status: nil, nl: false)
48
47
  exit(result.status)
49
48
  end
@@ -57,8 +56,6 @@ module Spoom
57
56
  sorbet_bin: sorbet,
58
57
  )
59
58
 
60
- check_sorbet_segfault(result.exit_code)
61
-
62
59
  if result.status
63
60
  say_error(result.err, status: nil, nl: false)
64
61
  exit(0)
@@ -109,6 +106,20 @@ module Spoom
109
106
  end
110
107
 
111
108
  exit(1)
109
+ rescue Spoom::Sorbet::Error::Segfault => error
110
+ say_error(<<~ERR, status: nil)
111
+ #{red("!!! Sorbet exited with code #{error.result.exit_code} - SEGFAULT !!!")}
112
+
113
+ This is most likely related to a bug in Sorbet.
114
+ ERR
115
+
116
+ exit(error.result.exit_code)
117
+ rescue Spoom::Sorbet::Error::Killed => error
118
+ say_error(<<~ERR, status: nil)
119
+ #{red("!!! Sorbet exited with code #{error.result.exit_code} - KILLED !!!")}
120
+ ERR
121
+
122
+ exit(error.result.exit_code)
112
123
  end
113
124
 
114
125
  no_commands do
data/lib/spoom/context.rb CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "fileutils"
5
5
  require "open3"
6
+ require "tmpdir"
6
7
 
7
8
  module Spoom
8
9
  # An abstraction to a Ruby project context
@@ -132,7 +133,7 @@ module Spoom
132
133
  ExecResult.new(out: out, err: err, status: T.must(status.success?), exit_code: T.must(status.exitstatus))
133
134
  else
134
135
  out, status = Open3.capture2(command, opts)
135
- ExecResult.new(out: out, err: "", status: T.must(status.success?), exit_code: T.must(status.exitstatus))
136
+ ExecResult.new(out: out, err: nil, status: T.must(status.success?), exit_code: T.must(status.exitstatus))
136
137
  end
137
138
  end
138
139
  end
@@ -179,9 +180,16 @@ module Spoom
179
180
  end
180
181
 
181
182
  # Run `git init` in this context directory
182
- sig { params(branch: String).void }
183
- def git_init!(branch: "main")
184
- git("init -q -b #{branch}")
183
+ #
184
+ # Warning: passing a branch will run `git init -b <branch>` which is only available in git 2.28+.
185
+ # In older versions, use `git_init!` followed by `git("checkout -b <branch>")`.
186
+ sig { params(branch: T.nilable(String)).returns(ExecResult) }
187
+ def git_init!(branch: nil)
188
+ if branch
189
+ git("init -b #{branch}")
190
+ else
191
+ git("init")
192
+ end
185
193
  end
186
194
 
187
195
  # Run `git checkout` in this context directory
@@ -147,7 +147,7 @@ module Spoom
147
147
  def print_map(hash, total)
148
148
  indent
149
149
  hash.each do |key, value|
150
- next unless value > 0
150
+ next if value <= 0
151
151
 
152
152
  printl("#{key}: #{value}#{percent(value, total)}")
153
153
  end
@@ -63,8 +63,10 @@ module Spoom
63
63
 
64
64
  if metrics_without_rbis
65
65
  snapshot.methods_with_sig_excluding_rbis = metrics_without_rbis.fetch("types.sig.count", 0)
66
- snapshot.methods_without_sig_excluding_rbis = metrics_without_rbis.fetch("types.input.methods.total",
67
- 0) - snapshot.methods_with_sig_excluding_rbis
66
+ snapshot.methods_without_sig_excluding_rbis = metrics_without_rbis.fetch(
67
+ "types.input.methods.total",
68
+ 0,
69
+ ) - snapshot.methods_with_sig_excluding_rbis
68
70
  end
69
71
 
70
72
  Snapshot::STRICTNESSES.each do |strictness|
data/lib/spoom/git.rb CHANGED
@@ -21,52 +21,31 @@ module Spoom
21
21
  class << self
22
22
  extend T::Sig
23
23
 
24
- # Execute a `command`
25
- sig { params(command: String, arg: String, path: String).returns(ExecResult) }
26
- def exec(command, *arg, path: ".")
27
- return ExecResult.new(
28
- out: "",
29
- err: "Error: `#{path}` is not a directory.",
30
- status: false,
31
- exit_code: 1,
32
- ) unless File.directory?(path)
33
-
34
- T.unsafe(Open3).popen3(command, *arg, chdir: path) do |_, stdout, stderr, thread|
35
- status = T.cast(thread.value, Process::Status)
36
- ExecResult.new(
37
- out: stdout.read,
38
- err: stderr.read,
39
- status: T.must(status.success?),
40
- exit_code: T.must(status.exitstatus),
41
- )
42
- end
43
- end
44
-
45
24
  # Git commands
46
25
 
47
26
  sig { params(arg: String, path: String).returns(ExecResult) }
48
27
  def checkout(*arg, path: ".")
49
- exec("git checkout -q #{arg.join(" ")}", path: path)
28
+ Spoom.exec("git checkout -q #{arg.join(" ")}", path: path)
50
29
  end
51
30
 
52
31
  sig { params(arg: String, path: String).returns(ExecResult) }
53
32
  def diff(*arg, path: ".")
54
- exec("git diff #{arg.join(" ")}", path: path)
33
+ Spoom.exec("git diff #{arg.join(" ")}", path: path)
55
34
  end
56
35
 
57
36
  sig { params(arg: String, path: String).returns(ExecResult) }
58
37
  def log(*arg, path: ".")
59
- exec("git log #{arg.join(" ")}", path: path)
38
+ Spoom.exec("git log #{arg.join(" ")}", path: path)
60
39
  end
61
40
 
62
41
  sig { params(arg: String, path: String).returns(ExecResult) }
63
42
  def show(*arg, path: ".")
64
- exec("git show #{arg.join(" ")}", path: path)
43
+ Spoom.exec("git show #{arg.join(" ")}", path: path)
65
44
  end
66
45
 
67
46
  sig { params(path: String).returns(T.nilable(String)) }
68
47
  def current_branch(path: ".")
69
- result = exec("git branch --show-current", path: path)
48
+ result = Spoom.exec("git branch --show-current", path: path)
70
49
  return nil unless result.status
71
50
 
72
51
  result.out.strip
@@ -18,13 +18,16 @@ module Spoom
18
18
  class Parser
19
19
  extend T::Sig
20
20
 
21
- HEADER = T.let([
22
- "👋 Hey there! Heads up that this is not a release build of sorbet.",
23
- "Release builds are faster and more well-supported by the Sorbet team.",
24
- "Check out the README to learn how to build Sorbet in release mode.",
25
- "To forcibly silence this error, either pass --silence-dev-message,",
26
- "or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment.",
27
- ], T::Array[String])
21
+ HEADER = T.let(
22
+ [
23
+ "👋 Hey there! Heads up that this is not a release build of sorbet.",
24
+ "Release builds are faster and more well-supported by the Sorbet team.",
25
+ "Check out the README to learn how to build Sorbet in release mode.",
26
+ "To forcibly silence this error, either pass --silence-dev-message,",
27
+ "or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment.",
28
+ ],
29
+ T::Array[String],
30
+ )
28
31
 
29
32
  class << self
30
33
  extend T::Sig
@@ -273,34 +273,37 @@ module Spoom
273
273
  SYMBOL_KINDS[kind] || "<unknown:#{kind}>"
274
274
  end
275
275
 
276
- SYMBOL_KINDS = T.let({
277
- 1 => "file",
278
- 2 => "module",
279
- 3 => "namespace",
280
- 4 => "package",
281
- 5 => "class",
282
- 6 => "def",
283
- 7 => "property",
284
- 8 => "field",
285
- 9 => "constructor",
286
- 10 => "enum",
287
- 11 => "interface",
288
- 12 => "function",
289
- 13 => "variable",
290
- 14 => "const",
291
- 15 => "string",
292
- 16 => "number",
293
- 17 => "boolean",
294
- 18 => "array",
295
- 19 => "object",
296
- 20 => "key",
297
- 21 => "null",
298
- 22 => "enum_member",
299
- 23 => "struct",
300
- 24 => "event",
301
- 25 => "operator",
302
- 26 => "type_parameter",
303
- }, T::Hash[Integer, String])
276
+ SYMBOL_KINDS = T.let(
277
+ {
278
+ 1 => "file",
279
+ 2 => "module",
280
+ 3 => "namespace",
281
+ 4 => "package",
282
+ 5 => "class",
283
+ 6 => "def",
284
+ 7 => "property",
285
+ 8 => "field",
286
+ 9 => "constructor",
287
+ 10 => "enum",
288
+ 11 => "interface",
289
+ 12 => "function",
290
+ 13 => "variable",
291
+ 14 => "const",
292
+ 15 => "string",
293
+ 16 => "number",
294
+ 17 => "boolean",
295
+ 18 => "array",
296
+ 19 => "object",
297
+ 20 => "key",
298
+ 21 => "null",
299
+ 22 => "enum_member",
300
+ 23 => "struct",
301
+ 24 => "event",
302
+ 25 => "operator",
303
+ 26 => "type_parameter",
304
+ },
305
+ T::Hash[Integer, String],
306
+ )
304
307
  end
305
308
 
306
309
  class SymbolPrinter < Printer
@@ -16,14 +16,17 @@ module Spoom
16
16
  STRICTNESS_STRONG = "strong"
17
17
  STRICTNESS_INTERNAL = "__STDLIB_INTERNAL"
18
18
 
19
- VALID_STRICTNESS = T.let([
20
- STRICTNESS_IGNORE,
21
- STRICTNESS_FALSE,
22
- STRICTNESS_TRUE,
23
- STRICTNESS_STRICT,
24
- STRICTNESS_STRONG,
25
- STRICTNESS_INTERNAL,
26
- ].freeze, T::Array[String])
19
+ VALID_STRICTNESS = T.let(
20
+ [
21
+ STRICTNESS_IGNORE,
22
+ STRICTNESS_FALSE,
23
+ STRICTNESS_TRUE,
24
+ STRICTNESS_STRICT,
25
+ STRICTNESS_STRONG,
26
+ STRICTNESS_INTERNAL,
27
+ ].freeze,
28
+ T::Array[String],
29
+ )
27
30
 
28
31
  SIGIL_REGEXP = T.let(/^#[\ t]*typed[\ t]*:[ \t]*(\w*)[ \t]*/.freeze, Regexp)
29
32
 
data/lib/spoom/sorbet.rb CHANGED
@@ -11,10 +11,33 @@ require "open3"
11
11
 
12
12
  module Spoom
13
13
  module Sorbet
14
+ class Error < StandardError
15
+ extend T::Sig
16
+
17
+ class Killed < Error; end
18
+ class Segfault < Error; end
19
+
20
+ sig { returns(ExecResult) }
21
+ attr_reader :result
22
+
23
+ sig do
24
+ params(
25
+ message: String,
26
+ result: ExecResult,
27
+ ).void
28
+ end
29
+ def initialize(message, result)
30
+ super(message)
31
+
32
+ @result = result
33
+ end
34
+ end
35
+
14
36
  CONFIG_PATH = "sorbet/config"
15
37
  GEM_PATH = T.let(Gem::Specification.find_by_name("sorbet-static").full_gem_path, String)
16
38
  BIN_PATH = T.let((Pathname.new(GEM_PATH) / "libexec" / "sorbet").to_s, String)
17
39
 
40
+ KILLED_CODE = 137
18
41
  SEGFAULT_CODE = 139
19
42
 
20
43
  class << self
@@ -34,7 +57,16 @@ module Spoom
34
57
  else
35
58
  arg.prepend("bundle", "exec", "srb")
36
59
  end
37
- Spoom.exec(*T.unsafe(arg), path: path, capture_err: capture_err)
60
+ result = Spoom.exec(*T.unsafe(arg), path: path, capture_err: capture_err)
61
+
62
+ case result.exit_code
63
+ when KILLED_CODE
64
+ raise Error::Killed.new("Sorbet was killed.", result)
65
+ when SEGFAULT_CODE
66
+ raise Error::Segfault.new("Sorbet segfaulted.", result)
67
+ end
68
+
69
+ result
38
70
  end
39
71
 
40
72
  sig do
@@ -69,14 +101,17 @@ module Spoom
69
101
  ).returns(T.nilable(String))
70
102
  end
71
103
  def srb_version(*arg, path: ".", capture_err: false, sorbet_bin: nil)
72
- result = T.let(T.unsafe(self).srb_tc(
73
- "--no-config",
74
- "--version",
75
- *arg,
76
- path: path,
77
- capture_err: capture_err,
78
- sorbet_bin: sorbet_bin,
79
- ), ExecResult)
104
+ result = T.let(
105
+ T.unsafe(self).srb_tc(
106
+ "--no-config",
107
+ "--version",
108
+ *arg,
109
+ path: path,
110
+ capture_err: capture_err,
111
+ sorbet_bin: sorbet_bin,
112
+ ),
113
+ ExecResult,
114
+ )
80
115
  return nil unless result.status
81
116
 
82
117
  result.out.split(" ")[2]
data/lib/spoom/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.1.15"
5
+ VERSION = "1.1.16"
6
6
  end
data/lib/spoom.rb CHANGED
@@ -15,7 +15,7 @@ module Spoom
15
15
  extend T::Sig
16
16
 
17
17
  const :out, String
18
- const :err, String
18
+ const :err, T.nilable(String)
19
19
  const :status, T::Boolean
20
20
  const :exit_code, Integer
21
21
 
@@ -25,7 +25,7 @@ module Spoom
25
25
  ########## STDOUT ##########
26
26
  #{out.empty? ? "<empty>" : out}
27
27
  ########## STDERR ##########
28
- #{err.empty? ? "<empty>" : err}
28
+ #{err&.empty? ? "<empty>" : err}
29
29
  ########## STATUS: #{status} ##########
30
30
  STR
31
31
  end
@@ -42,7 +42,7 @@ module Spoom
42
42
  capture_err: T::Boolean,
43
43
  ).returns(ExecResult)
44
44
  end
45
- def exec(cmd, *arg, path: ".", capture_err: false)
45
+ def exec(cmd, *arg, path: ".", capture_err: true)
46
46
  if capture_err
47
47
  stdout, stderr, status = T.unsafe(Open3).capture3([cmd, *arg].join(" "), chdir: path)
48
48
  ExecResult.new(
@@ -55,7 +55,7 @@ module Spoom
55
55
  stdout, status = T.unsafe(Open3).capture2([cmd, *arg].join(" "), chdir: path)
56
56
  ExecResult.new(
57
57
  out: stdout,
58
- err: "",
58
+ err: nil,
59
59
  status: status.success?,
60
60
  exit_code: status.exitstatus,
61
61
  )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spoom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.15
4
+ version: 1.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-14 00:00:00.000000000 Z
11
+ date: 2023-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler