spoom 1.1.16 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,136 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Spoom
5
+ class Context
6
+ # Sorbet features for a context
7
+ module Sorbet
8
+ extend T::Sig
9
+ extend T::Helpers
10
+
11
+ requires_ancestor { Context }
12
+
13
+ # Run `bundle exec srb` in this context directory
14
+ sig { params(arg: String, sorbet_bin: T.nilable(String), capture_err: T::Boolean).returns(ExecResult) }
15
+ def srb(*arg, sorbet_bin: nil, capture_err: true)
16
+ res = if sorbet_bin
17
+ exec("#{sorbet_bin} #{arg.join(" ")}", capture_err: capture_err)
18
+ else
19
+ bundle_exec("srb #{arg.join(" ")}", capture_err: capture_err)
20
+ end
21
+
22
+ case res.exit_code
23
+ when Spoom::Sorbet::KILLED_CODE
24
+ raise Spoom::Sorbet::Error::Killed.new("Sorbet was killed.", res)
25
+ when Spoom::Sorbet::SEGFAULT_CODE
26
+ raise Spoom::Sorbet::Error::Segfault.new("Sorbet segfaulted.", res)
27
+ end
28
+
29
+ res
30
+ end
31
+
32
+ sig { params(arg: String, sorbet_bin: T.nilable(String), capture_err: T::Boolean).returns(ExecResult) }
33
+ def srb_tc(*arg, sorbet_bin: nil, capture_err: true)
34
+ arg.prepend("tc") unless sorbet_bin
35
+ T.unsafe(self).srb(*arg, sorbet_bin: sorbet_bin, capture_err: capture_err)
36
+ end
37
+
38
+ sig do
39
+ params(
40
+ arg: String,
41
+ sorbet_bin: T.nilable(String),
42
+ capture_err: T::Boolean,
43
+ ).returns(T.nilable(T::Hash[String, Integer]))
44
+ end
45
+ def srb_metrics(*arg, sorbet_bin: nil, capture_err: true)
46
+ metrics_file = "metrics.tmp"
47
+
48
+ T.unsafe(self).srb_tc(
49
+ "--metrics-file",
50
+ metrics_file,
51
+ *arg,
52
+ sorbet_bin: sorbet_bin,
53
+ capture_err: capture_err,
54
+ )
55
+ return nil unless file?(metrics_file)
56
+
57
+ metrics_path = absolute_path_to(metrics_file)
58
+ metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
59
+ remove!(metrics_file)
60
+ metrics
61
+ end
62
+
63
+ # List all files typechecked by Sorbet from its `config`
64
+ sig { params(with_config: T.nilable(Spoom::Sorbet::Config)).returns(T::Array[String]) }
65
+ def srb_files(with_config: nil)
66
+ config = with_config || sorbet_config
67
+ regs = config.ignore.map { |string| Regexp.new(Regexp.escape(string)) }
68
+ exts = config.allowed_extensions.empty? ? [".rb", ".rbi"] : config.allowed_extensions
69
+ glob("**/*{#{exts.join(",")}}").reject do |f|
70
+ regs.any? { |re| re.match?(f) }
71
+ end.sort
72
+ end
73
+
74
+ sig { params(arg: String, sorbet_bin: T.nilable(String), capture_err: T::Boolean).returns(T.nilable(String)) }
75
+ def srb_version(*arg, sorbet_bin: nil, capture_err: true)
76
+ res = T.unsafe(self).srb_tc("--no-config", "--version", *arg, sorbet_bin: sorbet_bin, capture_err: capture_err)
77
+ return nil unless res.status
78
+
79
+ res.out.split(" ")[2]
80
+ end
81
+
82
+ # Does this context has a `sorbet/config` file?
83
+ sig { returns(T::Boolean) }
84
+ def has_sorbet_config?
85
+ file?(Spoom::Sorbet::CONFIG_PATH)
86
+ end
87
+
88
+ sig { returns(Spoom::Sorbet::Config) }
89
+ def sorbet_config
90
+ Spoom::Sorbet::Config.parse_string(read_sorbet_config)
91
+ end
92
+
93
+ # Read the contents of `sorbet/config` in this context directory
94
+ sig { returns(String) }
95
+ def read_sorbet_config
96
+ read(Spoom::Sorbet::CONFIG_PATH)
97
+ end
98
+
99
+ # Set the `contents` of `sorbet/config` in this context directory
100
+ sig { params(contents: String, append: T::Boolean).void }
101
+ def write_sorbet_config!(contents, append: false)
102
+ write!(Spoom::Sorbet::CONFIG_PATH, contents, append: append)
103
+ end
104
+
105
+ # Read the strictness sigil from the file at `relative_path` (returns `nil` if no sigil)
106
+ sig { params(relative_path: String).returns(T.nilable(String)) }
107
+ def read_file_strictness(relative_path)
108
+ Spoom::Sorbet::Sigils.file_strictness(absolute_path_to(relative_path))
109
+ end
110
+
111
+ # Get the commit introducing the `sorbet/config` file
112
+ sig { returns(T.nilable(Spoom::Git::Commit)) }
113
+ def sorbet_intro_commit
114
+ res = git_log("--diff-filter=A --format='%h %at' -1 -- sorbet/config")
115
+ return nil unless res.status
116
+
117
+ out = res.out.strip
118
+ return nil if out.empty?
119
+
120
+ Spoom::Git::Commit.parse_line(out)
121
+ end
122
+
123
+ # Get the commit removing the `sorbet/config` file
124
+ sig { returns(T.nilable(Spoom::Git::Commit)) }
125
+ def sorbet_removal_commit
126
+ res = git_log("--diff-filter=D --format='%h %at' -1 -- sorbet/config")
127
+ return nil unless res.status
128
+
129
+ out = res.out.strip
130
+ return nil if out.empty?
131
+
132
+ Spoom::Git::Commit.parse_line(out)
133
+ end
134
+ end
135
+ end
136
+ end
data/lib/spoom/context.rb CHANGED
@@ -3,8 +3,15 @@
3
3
 
4
4
  require "fileutils"
5
5
  require "open3"
6
+ require "time"
6
7
  require "tmpdir"
7
8
 
9
+ require_relative "context/bundle"
10
+ require_relative "context/exec"
11
+ require_relative "context/file_system"
12
+ require_relative "context/git"
13
+ require_relative "context/sorbet"
14
+
8
15
  module Spoom
9
16
  # An abstraction to a Ruby project context
10
17
  #
@@ -13,9 +20,11 @@ module Spoom
13
20
  class Context
14
21
  extend T::Sig
15
22
 
16
- # The absolute path to the directory this context is about
17
- sig { returns(String) }
18
- attr_reader :absolute_path
23
+ include Bundle
24
+ include Exec
25
+ include FileSystem
26
+ include Git
27
+ include Sorbet
19
28
 
20
29
  class << self
21
30
  extend T::Sig
@@ -30,6 +39,10 @@ module Spoom
30
39
  end
31
40
  end
32
41
 
42
+ # The absolute path to the directory this context is about
43
+ sig { returns(String) }
44
+ attr_reader :absolute_path
45
+
33
46
  # Create a new context about `absolute_path`
34
47
  #
35
48
  # The directory will not be created if it doesn't exist.
@@ -38,202 +51,5 @@ module Spoom
38
51
  def initialize(absolute_path)
39
52
  @absolute_path = T.let(::File.expand_path(absolute_path), String)
40
53
  end
41
-
42
- # Returns the absolute path to `relative_path` in the context's directory
43
- sig { params(relative_path: String).returns(String) }
44
- def absolute_path_to(relative_path)
45
- File.join(@absolute_path, relative_path)
46
- end
47
-
48
- # File System
49
-
50
- # Does the context directory at `absolute_path` exist and is a directory?
51
- sig { returns(T::Boolean) }
52
- def exist?
53
- File.directory?(@absolute_path)
54
- end
55
-
56
- # Create the context directory at `absolute_path`
57
- sig { void }
58
- def mkdir!
59
- FileUtils.rm_rf(@absolute_path)
60
- FileUtils.mkdir_p(@absolute_path)
61
- end
62
-
63
- # List all files in this context matching `pattern`
64
- sig { params(pattern: String).returns(T::Array[String]) }
65
- def glob(pattern = "**/*")
66
- Dir.glob(absolute_path_to(pattern)).map do |path|
67
- Pathname.new(path).relative_path_from(@absolute_path).to_s
68
- end.sort
69
- end
70
-
71
- # List all files at the top level of this context directory
72
- sig { returns(T::Array[String]) }
73
- def list
74
- glob("*")
75
- end
76
-
77
- # Does `relative_path` point to an existing file in this context directory?
78
- sig { params(relative_path: String).returns(T::Boolean) }
79
- def file?(relative_path)
80
- File.file?(absolute_path_to(relative_path))
81
- end
82
-
83
- # Return the contents of the file at `relative_path` in this context directory
84
- #
85
- # Will raise if the file doesn't exist.
86
- sig { params(relative_path: String).returns(String) }
87
- def read(relative_path)
88
- File.read(absolute_path_to(relative_path))
89
- end
90
-
91
- # Write `contents` in the file at `relative_path` in this context directory
92
- #
93
- # Append to the file if `append` is true.
94
- sig { params(relative_path: String, contents: String, append: T::Boolean).void }
95
- def write!(relative_path, contents = "", append: false)
96
- absolute_path = absolute_path_to(relative_path)
97
- FileUtils.mkdir_p(File.dirname(absolute_path))
98
- File.write(absolute_path, contents, mode: append ? "a" : "w")
99
- end
100
-
101
- # Remove the path at `relative_path` (recursive + force) in this context directory
102
- sig { params(relative_path: String).void }
103
- def remove!(relative_path)
104
- FileUtils.rm_rf(absolute_path_to(relative_path))
105
- end
106
-
107
- # Move the file or directory from `from_relative_path` to `to_relative_path`
108
- sig { params(from_relative_path: String, to_relative_path: String).void }
109
- def move!(from_relative_path, to_relative_path)
110
- destination_path = absolute_path_to(to_relative_path)
111
- FileUtils.mkdir_p(File.dirname(destination_path))
112
- FileUtils.mv(absolute_path_to(from_relative_path), destination_path)
113
- end
114
-
115
- # Delete this context and its content
116
- #
117
- # Warning: it will `rm -rf` the context directory on the file system.
118
- sig { void }
119
- def destroy!
120
- FileUtils.rm_rf(@absolute_path)
121
- end
122
-
123
- # Execution
124
-
125
- # Run a command in this context directory
126
- sig { params(command: String, capture_err: T::Boolean).returns(ExecResult) }
127
- def exec(command, capture_err: true)
128
- Bundler.with_unbundled_env do
129
- opts = T.let({ chdir: @absolute_path }, T::Hash[Symbol, T.untyped])
130
-
131
- if capture_err
132
- out, err, status = Open3.capture3(command, opts)
133
- ExecResult.new(out: out, err: err, status: T.must(status.success?), exit_code: T.must(status.exitstatus))
134
- else
135
- out, status = Open3.capture2(command, opts)
136
- ExecResult.new(out: out, err: nil, status: T.must(status.success?), exit_code: T.must(status.exitstatus))
137
- end
138
- end
139
- end
140
-
141
- # Bundle
142
-
143
- # Read the `contents` of the Gemfile in this context directory
144
- sig { returns(T.nilable(String)) }
145
- def read_gemfile
146
- read("Gemfile")
147
- end
148
-
149
- # Set the `contents` of the Gemfile in this context directory
150
- sig { params(contents: String, append: T::Boolean).void }
151
- def write_gemfile!(contents, append: false)
152
- write!("Gemfile", contents, append: append)
153
- end
154
-
155
- # Run a command with `bundle` in this context directory
156
- sig { params(command: String, version: T.nilable(String)).returns(ExecResult) }
157
- def bundle(command, version: nil)
158
- command = "_#{version}_ #{command}" if version
159
- exec("bundle #{command}")
160
- end
161
-
162
- # Run `bundle install` in this context directory
163
- sig { params(version: T.nilable(String)).returns(ExecResult) }
164
- def bundle_install!(version: nil)
165
- bundle("install", version: version)
166
- end
167
-
168
- # Run a command `bundle exec` in this context directory
169
- sig { params(command: String, version: T.nilable(String)).returns(ExecResult) }
170
- def bundle_exec(command, version: nil)
171
- bundle("exec #{command}", version: version)
172
- end
173
-
174
- # Git
175
-
176
- # Run a command prefixed by `git` in this context directory
177
- sig { params(command: String).returns(ExecResult) }
178
- def git(command)
179
- exec("git #{command}")
180
- end
181
-
182
- # Run `git init` in this context directory
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
193
- end
194
-
195
- # Run `git checkout` in this context directory
196
- sig { params(ref: String).returns(ExecResult) }
197
- def git_checkout!(ref: "main")
198
- git("checkout #{ref}")
199
- end
200
-
201
- # Get the current git branch in this context directory
202
- sig { returns(T.nilable(String)) }
203
- def git_current_branch
204
- Spoom::Git.current_branch(path: @absolute_path)
205
- end
206
-
207
- # Get the last commit in the currently checked out branch
208
- sig { params(short_sha: T::Boolean).returns(T.nilable(Git::Commit)) }
209
- def git_last_commit(short_sha: true)
210
- Spoom::Git.last_commit(path: @absolute_path, short_sha: short_sha)
211
- end
212
-
213
- # Sorbet
214
-
215
- # Run `bundle exec srb` in this context directory
216
- sig { params(command: String).returns(ExecResult) }
217
- def srb(command)
218
- bundle_exec("srb #{command}")
219
- end
220
-
221
- # Read the contents of `sorbet/config` in this context directory
222
- sig { returns(String) }
223
- def read_sorbet_config
224
- read(Spoom::Sorbet::CONFIG_PATH)
225
- end
226
-
227
- # Set the `contents` of `sorbet/config` in this context directory
228
- sig { params(contents: String, append: T::Boolean).void }
229
- def write_sorbet_config!(contents, append: false)
230
- write!(Spoom::Sorbet::CONFIG_PATH, contents, append: append)
231
- end
232
-
233
- # Read the strictness sigil from the file at `relative_path` (returns `nil` if no sigil)
234
- sig { params(relative_path: String).returns(T.nilable(String)) }
235
- def read_file_strictness(relative_path)
236
- Spoom::Sorbet::Sigils.file_strictness(absolute_path_to(relative_path))
237
- end
238
54
  end
239
55
  end
@@ -12,9 +12,9 @@ module Spoom
12
12
  class << self
13
13
  extend T::Sig
14
14
 
15
- sig { params(path: String, rbi: T::Boolean, sorbet_bin: T.nilable(String)).returns(Snapshot) }
16
- def snapshot(path: ".", rbi: true, sorbet_bin: nil)
17
- config = sorbet_config(path: path)
15
+ sig { params(context: Context, rbi: T::Boolean, sorbet_bin: T.nilable(String)).returns(Snapshot) }
16
+ def snapshot(context, rbi: true, sorbet_bin: nil)
17
+ config = context.sorbet_config
18
18
  config.allowed_extensions.push(".rb", ".rbi") if config.allowed_extensions.empty?
19
19
 
20
20
  new_config = config.copy
@@ -27,25 +27,16 @@ module Spoom
27
27
  new_config.options_string,
28
28
  ]
29
29
 
30
- metrics = Spoom::Sorbet.srb_metrics(
31
- *flags,
32
- path: path,
33
- capture_err: true,
34
- sorbet_bin: sorbet_bin,
35
- )
30
+ metrics = context.srb_metrics(*flags, sorbet_bin: sorbet_bin)
31
+
36
32
  # Collect extra information using a different configuration
37
33
  flags << "--ignore sorbet/rbi/"
38
- metrics_without_rbis = Spoom::Sorbet.srb_metrics(
39
- *flags,
40
- path: path,
41
- capture_err: true,
42
- sorbet_bin: sorbet_bin,
43
- )
34
+ metrics_without_rbis = context.srb_metrics(*flags, sorbet_bin: sorbet_bin)
44
35
 
45
36
  snapshot = Snapshot.new
46
37
  return snapshot unless metrics
47
38
 
48
- last_commit = Spoom::Git.last_commit(path: path)
39
+ last_commit = context.git_last_commit
49
40
  snapshot.commit_sha = last_commit&.sha
50
41
  snapshot.commit_timestamp = last_commit&.timestamp
51
42
 
@@ -79,40 +70,34 @@ module Spoom
79
70
  end
80
71
  end
81
72
 
82
- snapshot.version_static = Spoom::Sorbet.version_from_gemfile_lock(gem: "sorbet-static", path: path)
83
- snapshot.version_runtime = Spoom::Sorbet.version_from_gemfile_lock(gem: "sorbet-runtime", path: path)
73
+ snapshot.version_static = context.gem_version_from_gemfile_lock("sorbet-static")
74
+ snapshot.version_runtime = context.gem_version_from_gemfile_lock("sorbet-runtime")
84
75
 
85
- files = Spoom::Sorbet.srb_files(new_config, path: path)
76
+ files = context.srb_files(with_config: new_config)
86
77
  snapshot.rbi_files = files.count { |file| file.end_with?(".rbi") }
87
78
 
88
79
  snapshot
89
80
  end
90
81
 
91
- sig { params(snapshots: T::Array[Snapshot], palette: D3::ColorPalette, path: String).returns(Report) }
92
- def report(snapshots, palette:, path: ".")
93
- intro_commit = Git.sorbet_intro_commit(path: path)
82
+ sig { params(context: Context, snapshots: T::Array[Snapshot], palette: D3::ColorPalette).returns(Report) }
83
+ def report(context, snapshots, palette:)
84
+ intro_commit = context.sorbet_intro_commit
94
85
 
95
86
  Report.new(
96
- project_name: File.basename(File.expand_path(path)),
87
+ project_name: File.basename(context.absolute_path),
97
88
  palette: palette,
98
89
  snapshots: snapshots,
99
- sigils_tree: sigils_tree(path: path),
90
+ sigils_tree: sigils_tree(context),
100
91
  sorbet_intro_commit: intro_commit&.sha,
101
92
  sorbet_intro_date: intro_commit&.time,
102
93
  )
103
94
  end
104
95
 
105
- sig { params(path: String).returns(Sorbet::Config) }
106
- def sorbet_config(path: ".")
107
- Sorbet::Config.parse_file("#{path}/#{Spoom::Sorbet::CONFIG_PATH}")
108
- end
109
-
110
- sig { params(path: String).returns(FileTree) }
111
- def sigils_tree(path: ".")
112
- config = sorbet_config(path: path)
113
- files = Sorbet.srb_files(config, path: path)
96
+ sig { params(context: Context).returns(FileTree) }
97
+ def sigils_tree(context)
98
+ files = context.srb_files
114
99
 
115
- extensions = config.allowed_extensions
100
+ extensions = context.sorbet_config.allowed_extensions
116
101
  extensions = [".rb"] if extensions.empty?
117
102
  extensions -= [".rbi"]
118
103
 
@@ -120,7 +105,7 @@ module Spoom
120
105
  files.select! { |file| file =~ pattern }
121
106
  files.reject! { |file| file =~ %r{/test/} }
122
107
 
123
- FileTree.new(files, strip_prefix: path)
108
+ FileTree.new(files, strip_prefix: context.absolute_path)
124
109
  end
125
110
  end
126
111
  end
data/lib/spoom/sorbet.rb CHANGED
@@ -39,124 +39,5 @@ module Spoom
39
39
 
40
40
  KILLED_CODE = 137
41
41
  SEGFAULT_CODE = 139
42
-
43
- class << self
44
- extend T::Sig
45
-
46
- sig do
47
- params(
48
- arg: String,
49
- path: String,
50
- capture_err: T::Boolean,
51
- sorbet_bin: T.nilable(String),
52
- ).returns(ExecResult)
53
- end
54
- def srb(*arg, path: ".", capture_err: false, sorbet_bin: nil)
55
- if sorbet_bin
56
- arg.prepend(sorbet_bin)
57
- else
58
- arg.prepend("bundle", "exec", "srb")
59
- end
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
70
- end
71
-
72
- sig do
73
- params(
74
- arg: String,
75
- path: String,
76
- capture_err: T::Boolean,
77
- sorbet_bin: T.nilable(String),
78
- ).returns(ExecResult)
79
- end
80
- def srb_tc(*arg, path: ".", capture_err: false, sorbet_bin: nil)
81
- arg.prepend("tc") unless sorbet_bin
82
- srb(*T.unsafe(arg), path: path, capture_err: capture_err, sorbet_bin: sorbet_bin)
83
- end
84
-
85
- # List all files typechecked by Sorbet from its `config`
86
- sig { params(config: Config, path: String).returns(T::Array[String]) }
87
- def srb_files(config, path: ".")
88
- regs = config.ignore.map { |string| Regexp.new(Regexp.escape(string)) }
89
- exts = config.allowed_extensions.empty? ? [".rb", ".rbi"] : config.allowed_extensions
90
- Dir.glob((Pathname.new(path) / "**/*{#{exts.join(",")}}").to_s).reject do |f|
91
- regs.any? { |re| re.match?(f) }
92
- end.sort
93
- end
94
-
95
- sig do
96
- params(
97
- arg: String,
98
- path: String,
99
- capture_err: T::Boolean,
100
- sorbet_bin: T.nilable(String),
101
- ).returns(T.nilable(String))
102
- end
103
- def srb_version(*arg, path: ".", capture_err: false, sorbet_bin: nil)
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
- )
115
- return nil unless result.status
116
-
117
- result.out.split(" ")[2]
118
- end
119
-
120
- sig do
121
- params(
122
- arg: String,
123
- path: String,
124
- capture_err: T::Boolean,
125
- sorbet_bin: T.nilable(String),
126
- ).returns(T.nilable(T::Hash[String, Integer]))
127
- end
128
- def srb_metrics(*arg, path: ".", capture_err: false, sorbet_bin: nil)
129
- metrics_file = "metrics.tmp"
130
- metrics_path = "#{path}/#{metrics_file}"
131
- T.unsafe(self).srb_tc(
132
- "--metrics-file",
133
- metrics_file,
134
- *arg,
135
- path: path,
136
- capture_err: capture_err,
137
- sorbet_bin: sorbet_bin,
138
- )
139
- if File.exist?(metrics_path)
140
- metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
141
- File.delete(metrics_path)
142
- return metrics
143
- end
144
- nil
145
- end
146
-
147
- # Get `gem` version from the `Gemfile.lock` content
148
- #
149
- # Returns `nil` if `gem` cannot be found in the Gemfile.
150
- sig { params(gem: String, path: String).returns(T.nilable(String)) }
151
- def version_from_gemfile_lock(gem: "sorbet", path: ".")
152
- gemfile_path = "#{path}/Gemfile.lock"
153
- return nil unless File.exist?(gemfile_path)
154
-
155
- content = File.read(gemfile_path).match(/^ #{gem} \(.*(\d+\.\d+\.\d+).*\)/)
156
- return nil unless content
157
-
158
- content[1]
159
- end
160
- end
161
42
  end
162
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.16"
5
+ VERSION = "1.2.0"
6
6
  end