Skip to content

Commit 86d94cd

Browse files
committed
feat!: upgrade process_executer dependency to 4.x
BREAKING CHANGE: RubyGit::CommandLine.run no longer accepts the option `raise_git_errors: <Boolean>`. Users should use `raise_errors: <Boolean>` instead. BREAKING CHANGE: this gem now raises `RubyGit::ArgumentError` where before it raised `::ArgumentError`
1 parent 9110533 commit 86d94cd

File tree

15 files changed

+156
-116
lines changed

15 files changed

+156
-116
lines changed

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22

33
source 'https://rubygems.org'
44

5-
# Specify your gem's dependencies in ruby_git.gemspec
65
gemspec

lib/ruby_git.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,7 @@ def self.clone(repository_url, to_path: '')
160160
# or git was not found on the path.
161161
#
162162
def self.binary_version
163-
command = %w[version]
164-
options = { out: StringIO.new, err: StringIO.new }
165-
version_string = RubyGit::CommandLine.run(*command, **options).stdout[/\d+\.\d+(\.\d+)+/]
163+
version_string = RubyGit::CommandLine.run('version').stdout[/\d+\.\d+(\.\d+)+/]
166164
version_string.split('.').collect(&:to_i)
167165
end
168166
end

lib/ruby_git/command_line/options.rb

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,37 @@ module CommandLine
99
#
1010
# @api public
1111
#
12-
class Options < ProcessExecuter::Options::RunOptions
12+
class Options < ProcessExecuter::Options::RunWithCaptureOptions
1313
# Alias for brevity
1414
OptionDefinition = ProcessExecuter::Options::OptionDefinition
1515

1616
private
1717

18-
# :nocov: SimpleCov on JRuby reports the last with the last argument line is not covered
19-
2018
# The options allowed for objects of this class
2119
# @return [Array<OptionDefinition>]
2220
# @api private
2321
def define_options
2422
[
2523
*super,
2624
OptionDefinition.new(:normalize_encoding, default: false, validator: method(:validate_normalize_encoding)),
27-
OptionDefinition.new(:chomp, default: false, validator: method(:validate_chomp)),
28-
OptionDefinition.new(:raise_git_errors, default: true, validator: method(:validate_raise_git_errors))
25+
OptionDefinition.new(:chomp, default: false, validator: method(:validate_chomp))
2926
].freeze
3027
end
31-
# :nocov:
3228

33-
# Validate the raise_git_errors option value
34-
# @return [String, nil] the error message if the value is not valid
29+
# Wrap ProcessExecuter::ArgumentError in a RubyGit::ArgumentError
30+
# @return [void]
31+
# @raise [RubyGit::ArgumentError] if the options are invalid
3532
# @api private
36-
def validate_raise_git_errors
37-
return if [true, false].include?(raise_git_errors)
38-
39-
errors << "raise_git_errors must be true or false but was #{raise_git_errors.inspect}"
33+
def validate_options
34+
super
35+
rescue ProcessExecuter::ArgumentError => e
36+
raise RubyGit::ArgumentError, e.message, cause: e
4037
end
4138

4239
# Validate the normalize_encoding option value
4340
# @return [String, nil] the error message if the value is not valid
4441
# @api private
45-
def validate_normalize_encoding
42+
def validate_normalize_encoding(_key, _value)
4643
return if [true, false].include?(normalize_encoding)
4744

4845
errors << "normalize_encoding must be true or false but was #{normalize_encoding.inspect}"
@@ -51,7 +48,7 @@ def validate_normalize_encoding
5148
# Validate the chomp option value
5249
# @return [String, nil] the error message if the value is not valid
5350
# @api private
54-
def validate_chomp
51+
def validate_chomp(_key, _value)
5552
return if [true, false].include?(chomp)
5653

5754
errors << "chomp must be true or false but was #{chomp.inspect}"

lib/ruby_git/command_line/result.rb

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,30 @@ module RubyGit
77
module CommandLine
88
# The result of running a git command
99
#
10-
# Adds stdout and stderr processing to the `ProcessExecuter::Result` class.
10+
# Adds stdout and stderr processing to the `ProcessExecuter::ResultWithCapture` class.
1111
#
1212
# @api public
1313
#
1414
class Result < SimpleDelegator
1515
# @!method initialize(result)
1616
# Initialize a new result object
17+
#
1718
# @example
18-
# result = ProcessExecuter.run('echo hello')
19+
# result = Git::CommandLine.run_with_capture('git', 'status')
1920
# RubyGit::CommandLine::Result.new(result)
20-
# @param [ProcessExecuter::Result] result The result of running the command
21+
#
22+
# @param [ProcessExecuter::ResultWithCapture] result The result of running the command
23+
#
2124
# @return [RubyGit::CommandLine::Result]
25+
#
2226
# @api public
2327

2428
# Return the processed stdout output (or original if it was not processed)
2529
#
26-
# This output is only returned if a stdout redirection is a
27-
# `ProcessExecuter::MonitoredPipe`.
28-
#
2930
# @example
30-
# result = ProcessExecuter.run('echo hello': out: StringIO.new)
31+
# result = RubyGit::CommandLine::Result.new(
32+
# ProcessExecuter.run_with_capture('echo hello')
33+
# )
3134
# result.stdout #=> "hello\n"
3235
#
3336
# @return [String, nil]
@@ -39,20 +42,25 @@ def stdout
3942
# Process the captured stdout output
4043
#
4144
# @example
42-
# result = ProcessExecuter.run('echo hello', out: StringIO.new)
45+
# result = RubyGit::CommandLine::Result.new(
46+
# ProcessExecuter.run_with_capture('echo hello')
47+
# )
4348
# result.stdout #=> "hello\n"
4449
# result.process_stdout { |stdout, _result| stdout.upcase }
4550
# result.stdout #=> "HELLO\n"
4651
# result.unprocessed_stdout #=> "hello\n"
4752
#
4853
# @example Chain processing
49-
# result = ProcessExecuter.run('echo hello', out: StringIO.new)
54+
# result = RubyGit::CommandLine::Result.new(
55+
# ProcessExecuter.run_with_capture('echo hello')
56+
# )
5057
# result.stdout #=> "hello\n"
58+
# # Here is the chain processing:
5159
# result.process_stdout { |s| s.upcase }.process_stdout { |s| s.reverse }
5260
# result.stdout #=> "OLLEH\n"
5361
# result.unprocessed_stdout #=> "hello\n"
5462
#
55-
# @return [String, nil]
63+
# @return [self]
5664
#
5765
# @yield [stdout, result] Yields the stdout output and the result object
5866
# @yieldparam stdout [String] The output to process
@@ -62,16 +70,19 @@ def stdout
6270
# @api public
6371
#
6472
def process_stdout(&block)
65-
return if block.nil?
73+
return self if block.nil?
6674

6775
@processed_stdout = block.call(stdout, self)
76+
6877
self
6978
end
7079

7180
# Returns the original stdout output before it was processed
7281
#
7382
# @example
74-
# result = ProcessExecuter.run('echo hello', out: StringIO.new)
83+
# result = RubyGit::CommandLine::Result.new(
84+
# ProcessExecuter.run_with_capture('echo hello')
85+
# )
7586
# result.stdout #=> "hello\n"
7687
# result.unprocessed_stdout #=> "hello\n"
7788
# result.process_stdout { |s| s.upcase }
@@ -92,7 +103,9 @@ def unprocessed_stdout
92103
# `ProcessExecuter::MonitoredPipe`.
93104
#
94105
# @example
95-
# result = ProcessExecuter.run('echo hello 1>&2': err: StringIO.new)
106+
# result = RubyGit::CommandLine::Result.new(
107+
# ProcessExecuter.run_with_capture('echo hello >&2')
108+
# )
96109
# result.stderr #=> "hello\n"
97110
#
98111
# @return [String, nil]
@@ -104,20 +117,25 @@ def stderr
104117
# Process the captured stderr output
105118
#
106119
# @example
107-
# result = ProcessExecuter.run('echo hello 1>&2', err: StringIO.new)
120+
# result = RubyGit::CommandLine::Result.new(
121+
# ProcessExecuter.run_with_capture('echo hello >&2')
122+
# )
108123
# result.stderr #=> "hello\n"
109124
# result.process_stderr { |stderr, _result| stderr.upcase }
110125
# result.stderr #=> "HELLO\n"
111126
# result.unprocessed_stderr #=> "hello\n"
112127
#
113128
# @example Chain processing
114-
# result = ProcessExecuter.run('echo hello 1>&2', err: StringIO.new)
129+
# result = RubyGit::CommandLine::Result.new(
130+
# ProcessExecuter.run_with_capture('echo hello >&2')
131+
# )
115132
# result.stderr #=> "hello\n"
133+
# # Here is the chain processing:
116134
# result.process_stderr { |s| s.upcase }.process_stderr { |s| s.reverse }
117135
# result.stderr #=> "OLLEH\n"
118136
# result.unprocessed_stderr #=> "hello\n"
119137
#
120-
# @return [String, nil]
138+
# @return [self]
121139
#
122140
# @yield [stderr, result] Yields the stderr output and the result object
123141
# @yieldparam stderr [String] The output to process
@@ -127,16 +145,19 @@ def stderr
127145
# @api public
128146
#
129147
def process_stderr(&block)
130-
return if block.nil?
148+
return self if block.nil?
131149

132150
@processed_stderr = block.call(stderr, self)
151+
133152
self
134153
end
135154

136155
# Returns the original stderr output before it was processed
137156
#
138157
# @example
139-
# result = ProcessExecuter.run('echo hello 1>&2', err: StringIO.new)
158+
# result = RubyGit::CommandLine::Result.new(
159+
# ProcessExecuter.run_with_capture('echo hello >&2')
160+
# )
140161
# result.stderr #=> "hello\n"
141162
# result.unprocessed_stderr #=> "hello\n"
142163
# result.process_stderr { |stderr| stderr.upcase }

lib/ruby_git/command_line/runner.rb

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def initialize(env, binary_path, global_options, logger)
154154
#
155155
# @return [RubyGit::CommandLine::Result] the result of the command
156156
#
157-
# @raise [ArgumentError] if `args` or `options_hash` are not valid
157+
# @raise [RubyGit::ArgumentError] if `args` or `options_hash` are not valid
158158
#
159159
# @raise [RubyGit::FailedError] if the command returned a non-zero exitstatus
160160
#
@@ -165,18 +165,27 @@ def initialize(env, binary_path, global_options, logger)
165165
# @raise [RubyGit::ProcessIOError] if an exception was raised while collecting subprocess output
166166
#
167167
def call(*args, **options_hash)
168-
options_hash[:raise_errors] = false
169168
options = RubyGit::CommandLine::Options.new(logger: logger, **options_hash)
170-
begin
171-
result = run_with_chdir([env, *build_git_cmd(args)], options)
169+
result = run(*args, options)
170+
process_result(result, options)
171+
end
172+
173+
private
174+
175+
# Run the git command with the given arguments and options
176+
# @return [ProcessExecuter::ResultWithCapture] the result of the command
177+
# @api private
178+
def run(*args, options)
179+
# We don't want ProcessExecuter to raise an error if the command fails, but
180+
# we want to preserve the value of the raise_errors option to use in
181+
# process_result.
182+
options.merge(raise_errors: false).then do |options|
183+
run_with_chdir([env, *build_git_cmd(args)], options)
172184
rescue ProcessExecuter::ProcessIOError => e
173185
raise RubyGit::ProcessIOError.new(e.message), cause: e.exception.cause
174186
end
175-
process_result(result)
176187
end
177188

178-
private
179-
180189
# Run command with options with special handling for the `chdir` option on JRuby
181190
#
182191
# JRuby does not support the `chdir` option in `Process.spawn`. Note that this
@@ -185,7 +194,7 @@ def call(*args, **options_hash)
185194
# @param args [Array<String>] the command to run
186195
# @param options [RubyGit::CommandLine::Options] the options to pass to `Process.spawn`
187196
#
188-
# @return [ProcessExecuter::Result] the result of the command
197+
# @return [ProcessExecuter::ResultWithCapture] the result of the command
189198
#
190199
# @api private
191200
#
@@ -197,7 +206,9 @@ def run_with_chdir(args, options) # rubocop:disable Metrics/MethodLength
197206
Dir.chdir(options.chdir) do
198207
saved_chdir = options.chdir
199208
options.merge!(chdir: :not_set)
200-
run_and_handle_spawn_error(args, options).tap do
209+
begin
210+
run_and_handle_spawn_error(args, options)
211+
ensure
201212
options.merge!(chdir: saved_chdir)
202213
end
203214
end
@@ -212,14 +223,14 @@ def run_with_chdir(args, options) # rubocop:disable Metrics/MethodLength
212223
# @param args [Array<String>] the command to run
213224
# @param options [RubyGit::CommandLine::Options] the options to pass to `Process.spawn`
214225
#
215-
# @return [ProcessExecuter::Result] the result of the command
226+
# @return [ProcessExecuter::ResultWithCapture] the result of the command
216227
#
217228
# @api private
218229
#
219230
def run_and_handle_spawn_error(args, options)
220-
ProcessExecuter.run_with_options(args, options)
231+
ProcessExecuter.run_with_capture(*args, options)
221232
rescue ProcessExecuter::SpawnError => e
222-
raise RubyGit::SpawnError, e.message
233+
raise RubyGit::SpawnError, e.message, cause: e
223234
end
224235

225236
# Returns true if running on JRuby
@@ -230,11 +241,15 @@ def run_and_handle_spawn_error(args, options)
230241
def jruby? = RUBY_ENGINE == 'jruby'
231242

232243
# Build the git command line from the available sources to send to `Process.spawn`
244+
#
245+
# @raise [RubyGit::ArgumentError] if the args array contains an array
246+
#
233247
# @return [Array<String>]
248+
#
234249
# @api private
235250
#
236251
def build_git_cmd(args)
237-
raise ArgumentError, 'The args array can not contain an array' if args.any? { |a| a.is_a?(Array) }
252+
raise RubyGit::ArgumentError, 'The args array can not contain an array' if args.any? { |a| a.is_a?(Array) }
238253

239254
[binary_path, *global_options, *args].map(&:to_s)
240255
end
@@ -250,18 +265,21 @@ def build_git_cmd(args)
250265
# @return [RubyGit::CommandLineResult] the result of the command to return to the caller
251266
#
252267
# @raise [RubyGit::FailedError] if the command failed
268+
#
253269
# @raise [RubyGit::SignaledError] if the command was signaled
270+
#
254271
# @raise [RubyGit::TimeoutError] if the command times out
272+
#
255273
# @raise [RubyGit::ProcessIOError] if an exception was raised while collecting subprocess output
256274
#
257275
# @api private
258276
#
259-
def process_result(result)
277+
def process_result(result, options)
260278
RubyGit::CommandLine::Result.new(result).tap do |processed_result|
261-
raise_any_errors(processed_result) if processed_result.options.raise_git_errors
279+
raise_any_errors(processed_result) if options.raise_errors
262280

263-
processed_result.process_stdout { |s, r| process_output(s, r) }
264-
processed_result.process_stderr { |s, r| process_output(s, r) }
281+
processed_result.process_stdout { |output, result| process_output(output, result) }
282+
processed_result.process_stderr { |output, result| process_output(output, result) }
265283
end
266284
end
267285

lib/ruby_git/errors.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module RubyGit
1616
# ```text
1717
# StandardError
1818
# └─> RubyGit::Error
19+
# ├─> RubyGit::ArgumentError
1920
# ├─> RubyGit::CommandLineError
2021
# │ ├─> RubyGit::FailedError
2122
# │ └─> RubyGit::SignaledError
@@ -28,6 +29,7 @@ module RubyGit
2829
# | Error Class | Description |
2930
# | --- | --- |
3031
# | `Error` | This catch-all error serves as the base class for other custom errors raised by the git gem. |
32+
# | `ArgumentError` | Raised when an invalid argument is passed to a method. |
3133
# | `CommandLineError` | A subclass of this error is raised when there is a problem executing the git command line. |
3234
# | `FailedError` | This error is raised when the git command line exits with a non-zero status code that is not expected by the git gem. |
3335
# | `SignaledError` | This error is raised when the git command line is terminated as a result of receiving a signal. This could happen if the process is forcibly terminated or if there is a serious system error. |
@@ -66,6 +68,19 @@ class Error < StandardError; end
6668

6769
# rubocop:enable Layout/LineLength
6870

71+
# Raised when an invalid argument is passed to a method
72+
#
73+
# @example Raising RubyGit::ArgumentError due to invalid option value
74+
# begin
75+
# RubyGit::CommandLine.run('status', timeout_after: 'not_a_number')
76+
# rescue RubyGit::ArgumentError => e
77+
# e.message #=> 'timeout_after must be nil or a non-negative real number but was "not_a_number"'
78+
# end
79+
#
80+
# @api public
81+
#
82+
class ArgumentError < RubyGit::Error; end
83+
6984
# Raised when a git command fails or exits because of an uncaught signal
7085
#
7186
# The git command executed, status, stdout, and stderr are available from this

ruby_git.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Gem::Specification.new do |spec|
5656
spec.add_development_dependency 'yardstick', '~> 0.9'
5757
end
5858

59-
spec.add_dependency 'process_executer', '~> 3.2'
59+
spec.add_dependency 'process_executer', '~> 4.0'
6060
spec.add_dependency 'rchardet', '~> 1.9'
6161

6262
spec.metadata['rubygems_mfa_required'] = 'true'

0 commit comments

Comments
 (0)