spoom 1.1.15 → 1.2.0

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.
@@ -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,117 +11,33 @@ require "open3"
11
11
 
12
12
  module Spoom
13
13
  module Sorbet
14
- CONFIG_PATH = "sorbet/config"
15
- GEM_PATH = T.let(Gem::Specification.find_by_name("sorbet-static").full_gem_path, String)
16
- BIN_PATH = T.let((Pathname.new(GEM_PATH) / "libexec" / "sorbet").to_s, String)
17
-
18
- SEGFAULT_CODE = 139
19
-
20
- class << self
14
+ class Error < StandardError
21
15
  extend T::Sig
22
16
 
23
- sig do
24
- params(
25
- arg: String,
26
- path: String,
27
- capture_err: T::Boolean,
28
- sorbet_bin: T.nilable(String),
29
- ).returns(ExecResult)
30
- end
31
- def srb(*arg, path: ".", capture_err: false, sorbet_bin: nil)
32
- if sorbet_bin
33
- arg.prepend(sorbet_bin)
34
- else
35
- arg.prepend("bundle", "exec", "srb")
36
- end
37
- Spoom.exec(*T.unsafe(arg), path: path, capture_err: capture_err)
38
- end
17
+ class Killed < Error; end
18
+ class Segfault < Error; end
39
19
 
40
- sig do
41
- params(
42
- arg: String,
43
- path: String,
44
- capture_err: T::Boolean,
45
- sorbet_bin: T.nilable(String),
46
- ).returns(ExecResult)
47
- end
48
- def srb_tc(*arg, path: ".", capture_err: false, sorbet_bin: nil)
49
- arg.prepend("tc") unless sorbet_bin
50
- srb(*T.unsafe(arg), path: path, capture_err: capture_err, sorbet_bin: sorbet_bin)
51
- end
52
-
53
- # List all files typechecked by Sorbet from its `config`
54
- sig { params(config: Config, path: String).returns(T::Array[String]) }
55
- def srb_files(config, path: ".")
56
- regs = config.ignore.map { |string| Regexp.new(Regexp.escape(string)) }
57
- exts = config.allowed_extensions.empty? ? [".rb", ".rbi"] : config.allowed_extensions
58
- Dir.glob((Pathname.new(path) / "**/*{#{exts.join(",")}}").to_s).reject do |f|
59
- regs.any? { |re| re.match?(f) }
60
- end.sort
61
- end
20
+ sig { returns(ExecResult) }
21
+ attr_reader :result
62
22
 
63
23
  sig do
64
24
  params(
65
- arg: String,
66
- path: String,
67
- capture_err: T::Boolean,
68
- sorbet_bin: T.nilable(String),
69
- ).returns(T.nilable(String))
25
+ message: String,
26
+ result: ExecResult,
27
+ ).void
70
28
  end
71
- 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)
80
- return nil unless result.status
29
+ def initialize(message, result)
30
+ super(message)
81
31
 
82
- result.out.split(" ")[2]
32
+ @result = result
83
33
  end
34
+ end
84
35
 
85
- sig do
86
- params(
87
- arg: String,
88
- path: String,
89
- capture_err: T::Boolean,
90
- sorbet_bin: T.nilable(String),
91
- ).returns(T.nilable(T::Hash[String, Integer]))
92
- end
93
- def srb_metrics(*arg, path: ".", capture_err: false, sorbet_bin: nil)
94
- metrics_file = "metrics.tmp"
95
- metrics_path = "#{path}/#{metrics_file}"
96
- T.unsafe(self).srb_tc(
97
- "--metrics-file",
98
- metrics_file,
99
- *arg,
100
- path: path,
101
- capture_err: capture_err,
102
- sorbet_bin: sorbet_bin,
103
- )
104
- if File.exist?(metrics_path)
105
- metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
106
- File.delete(metrics_path)
107
- return metrics
108
- end
109
- nil
110
- end
111
-
112
- # Get `gem` version from the `Gemfile.lock` content
113
- #
114
- # Returns `nil` if `gem` cannot be found in the Gemfile.
115
- sig { params(gem: String, path: String).returns(T.nilable(String)) }
116
- def version_from_gemfile_lock(gem: "sorbet", path: ".")
117
- gemfile_path = "#{path}/Gemfile.lock"
118
- return nil unless File.exist?(gemfile_path)
119
-
120
- content = File.read(gemfile_path).match(/^ #{gem} \(.*(\d+\.\d+\.\d+).*\)/)
121
- return nil unless content
36
+ CONFIG_PATH = "sorbet/config"
37
+ GEM_PATH = T.let(Gem::Specification.find_by_name("sorbet-static").full_gem_path, String)
38
+ BIN_PATH = T.let((Pathname.new(GEM_PATH) / "libexec" / "sorbet").to_s, String)
122
39
 
123
- content[1]
124
- end
125
- end
40
+ KILLED_CODE = 137
41
+ SEGFAULT_CODE = 139
126
42
  end
127
43
  end
@@ -1,17 +1,15 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative "git"
5
-
6
4
  module Spoom
7
5
  class Timeline
8
6
  extend T::Sig
9
7
 
10
- sig { params(from: Time, to: Time, path: String).void }
11
- def initialize(from, to, path: ".")
8
+ sig { params(context: Context, from: Time, to: Time).void }
9
+ def initialize(context, from, to)
10
+ @context = context
12
11
  @from = from
13
12
  @to = to
14
- @path = path
15
13
  end
16
14
 
17
15
  # Return one commit for each month between `from` and `to`
@@ -37,17 +35,16 @@ module Spoom
37
35
  sig { params(dates: T::Array[Time]).returns(T::Array[Git::Commit]) }
38
36
  def commits_for_dates(dates)
39
37
  dates.map do |t|
40
- result = Spoom::Git.log(
38
+ result = @context.git_log(
41
39
  "--since='#{t}'",
42
40
  "--until='#{t.to_date.next_month}'",
43
41
  "--format='format:%h %at'",
44
42
  "--author-date-order",
45
43
  "-1",
46
- path: @path,
47
44
  )
48
45
  next if result.out.empty?
49
46
 
50
- Git.parse_commit(result.out.strip)
47
+ Spoom::Git::Commit.parse_line(result.out.strip)
51
48
  end.compact.uniq(&:sha)
52
49
  end
53
50
  end
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.2.0"
6
6
  end
data/lib/spoom.rb CHANGED
@@ -10,58 +10,6 @@ module Spoom
10
10
  SPOOM_PATH = T.let((Pathname.new(__FILE__) / ".." / "..").to_s, String)
11
11
 
12
12
  class Error < StandardError; end
13
-
14
- class ExecResult < T::Struct
15
- extend T::Sig
16
-
17
- const :out, String
18
- const :err, String
19
- const :status, T::Boolean
20
- const :exit_code, Integer
21
-
22
- sig { returns(String) }
23
- def to_s
24
- <<~STR
25
- ########## STDOUT ##########
26
- #{out.empty? ? "<empty>" : out}
27
- ########## STDERR ##########
28
- #{err.empty? ? "<empty>" : err}
29
- ########## STATUS: #{status} ##########
30
- STR
31
- end
32
- end
33
-
34
- class << self
35
- extend T::Sig
36
-
37
- sig do
38
- params(
39
- cmd: String,
40
- arg: String,
41
- path: String,
42
- capture_err: T::Boolean,
43
- ).returns(ExecResult)
44
- end
45
- def exec(cmd, *arg, path: ".", capture_err: false)
46
- if capture_err
47
- stdout, stderr, status = T.unsafe(Open3).capture3([cmd, *arg].join(" "), chdir: path)
48
- ExecResult.new(
49
- out: stdout,
50
- err: stderr,
51
- status: status.success?,
52
- exit_code: status.exitstatus,
53
- )
54
- else
55
- stdout, status = T.unsafe(Open3).capture2([cmd, *arg].join(" "), chdir: path)
56
- ExecResult.new(
57
- out: stdout,
58
- err: "",
59
- status: status.success?,
60
- exit_code: status.exitstatus,
61
- )
62
- end
63
- end
64
- end
65
13
  end
66
14
 
67
15
  require "spoom/context"
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.2.0
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-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -130,6 +130,11 @@ files:
130
130
  - lib/spoom/cli/run.rb
131
131
  - lib/spoom/colors.rb
132
132
  - lib/spoom/context.rb
133
+ - lib/spoom/context/bundle.rb
134
+ - lib/spoom/context/exec.rb
135
+ - lib/spoom/context/file_system.rb
136
+ - lib/spoom/context/git.rb
137
+ - lib/spoom/context/sorbet.rb
133
138
  - lib/spoom/coverage.rb
134
139
  - lib/spoom/coverage/d3.rb
135
140
  - lib/spoom/coverage/d3/base.rb
@@ -139,7 +144,6 @@ files:
139
144
  - lib/spoom/coverage/report.rb
140
145
  - lib/spoom/coverage/snapshot.rb
141
146
  - lib/spoom/file_tree.rb
142
- - lib/spoom/git.rb
143
147
  - lib/spoom/printer.rb
144
148
  - lib/spoom/sorbet.rb
145
149
  - lib/spoom/sorbet/config.rb
@@ -175,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
179
  - !ruby/object:Gem::Version
176
180
  version: '0'
177
181
  requirements: []
178
- rubygems_version: 3.3.3
182
+ rubygems_version: 3.4.9
179
183
  signing_key:
180
184
  specification_version: 4
181
185
  summary: Useful tools for Sorbet enthusiasts.
data/lib/spoom/git.rb DELETED
@@ -1,130 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "time"
5
-
6
- module Spoom
7
- # Execute git commands
8
- module Git
9
- class Commit < T::Struct
10
- extend T::Sig
11
-
12
- const :sha, String
13
- const :time, Time
14
-
15
- sig { returns(Integer) }
16
- def timestamp
17
- time.to_i
18
- end
19
- end
20
-
21
- class << self
22
- extend T::Sig
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
- # Git commands
46
-
47
- sig { params(arg: String, path: String).returns(ExecResult) }
48
- def checkout(*arg, path: ".")
49
- exec("git checkout -q #{arg.join(" ")}", path: path)
50
- end
51
-
52
- sig { params(arg: String, path: String).returns(ExecResult) }
53
- def diff(*arg, path: ".")
54
- exec("git diff #{arg.join(" ")}", path: path)
55
- end
56
-
57
- sig { params(arg: String, path: String).returns(ExecResult) }
58
- def log(*arg, path: ".")
59
- exec("git log #{arg.join(" ")}", path: path)
60
- end
61
-
62
- sig { params(arg: String, path: String).returns(ExecResult) }
63
- def show(*arg, path: ".")
64
- exec("git show #{arg.join(" ")}", path: path)
65
- end
66
-
67
- sig { params(path: String).returns(T.nilable(String)) }
68
- def current_branch(path: ".")
69
- result = exec("git branch --show-current", path: path)
70
- return nil unless result.status
71
-
72
- result.out.strip
73
- end
74
-
75
- # Utils
76
-
77
- # Get the last commit in the currently checked out branch
78
- sig { params(path: String, short_sha: T::Boolean).returns(T.nilable(Commit)) }
79
- def last_commit(path: ".", short_sha: true)
80
- result = log("HEAD --format='%#{short_sha ? "h" : "H"} %at' -1", path: path)
81
- return nil unless result.status
82
-
83
- out = result.out.strip
84
- return nil if out.empty?
85
-
86
- parse_commit(out)
87
- end
88
-
89
- # Is there uncommited changes in `path`?
90
- sig { params(path: String).returns(T::Boolean) }
91
- def workdir_clean?(path: ".")
92
- diff("HEAD", path: path).out.empty?
93
- end
94
-
95
- # Get the commit introducing the `sorbet/config` file
96
- sig { params(path: String).returns(T.nilable(Commit)) }
97
- def sorbet_intro_commit(path: ".")
98
- result = log("--diff-filter=A --format='%h %at' -1 -- sorbet/config", path: path)
99
- return nil unless result.status
100
-
101
- out = result.out.strip
102
- return nil if out.empty?
103
-
104
- parse_commit(out)
105
- end
106
-
107
- # Get the commit removing the `sorbet/config` file
108
- sig { params(path: String).returns(T.nilable(Commit)) }
109
- def sorbet_removal_commit(path: ".")
110
- result = log("--diff-filter=D --format='%h %at' -1 -- sorbet/config", path: path)
111
- return nil unless result.status
112
-
113
- out = result.out.strip
114
- return nil if out.empty?
115
-
116
- parse_commit(out)
117
- end
118
-
119
- # Parse a line formated as `%h %at` into a `Commit`
120
- sig { params(string: String).returns(T.nilable(Commit)) }
121
- def parse_commit(string)
122
- sha, epoch = string.split(" ", 2)
123
- return nil unless sha && epoch
124
-
125
- time = Time.strptime(epoch, "%s")
126
- Commit.new(sha: sha, time: time)
127
- end
128
- end
129
- end
130
- end