ruby_git 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.commitlintrc.yml +16 -0
- data/.github/CODEOWNERS +4 -0
- data/.github/workflows/continuous_integration.yml +87 -0
- data/.github/workflows/enforce_conventional_commits.yml +21 -0
- data/.github/workflows/experimental_ruby_builds.yml +65 -0
- data/.gitignore +3 -0
- data/.husky/commit-msg +1 -0
- data/.markdownlint.yml +25 -0
- data/.rubocop.yml +13 -15
- data/.yardopts +6 -1
- data/CHANGELOG.md +50 -0
- data/CONTRIBUTING.md +5 -5
- data/{LICENSE.md → LICENSE.txt} +1 -1
- data/PLAN.md +67 -0
- data/README.md +58 -6
- data/RELEASING.md +5 -54
- data/Rakefile +31 -38
- data/RubyGit Class Diagram.svg +1 -0
- data/bin/command-line-test +189 -0
- data/bin/console +2 -0
- data/bin/setup +13 -2
- data/lib/ruby_git/command_line/options.rb +61 -0
- data/lib/ruby_git/command_line/result.rb +155 -0
- data/lib/ruby_git/command_line/runner.rb +296 -0
- data/lib/ruby_git/command_line.rb +95 -0
- data/lib/ruby_git/encoding_normalizer.rb +49 -0
- data/lib/ruby_git/errors.rb +169 -0
- data/lib/ruby_git/repository.rb +33 -0
- data/lib/ruby_git/status/branch.rb +92 -0
- data/lib/ruby_git/status/entry.rb +162 -0
- data/lib/ruby_git/status/ignored_entry.rb +44 -0
- data/lib/ruby_git/status/ordinary_entry.rb +207 -0
- data/lib/ruby_git/status/parser.rb +203 -0
- data/lib/ruby_git/status/renamed_entry.rb +257 -0
- data/lib/ruby_git/status/report.rb +143 -0
- data/lib/ruby_git/status/stash.rb +33 -0
- data/lib/ruby_git/status/submodule_status.rb +85 -0
- data/lib/ruby_git/status/unmerged_entry.rb +248 -0
- data/lib/ruby_git/status/untracked_entry.rb +52 -0
- data/lib/ruby_git/status.rb +33 -0
- data/lib/ruby_git/version.rb +1 -1
- data/lib/ruby_git/worktree.rb +175 -33
- data/lib/ruby_git.rb +91 -28
- data/package.json +11 -0
- data/ruby_git.gemspec +32 -20
- metadata +145 -47
- data/.travis.yml +0 -26
- data/CODEOWNERS +0 -3
- data/MAINTAINERS.md +0 -8
- data/lib/ruby_git/error.rb +0 -8
- data/lib/ruby_git/file_helpers.rb +0 -42
- data/lib/ruby_git/git_binary.rb +0 -106
- /data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +0 -0
- /data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +0 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'command_line_boss'
|
5
|
+
|
6
|
+
# A script used to test calling a command line program from Ruby
|
7
|
+
#
|
8
|
+
# This script is used to test the `Git::CommandLine` class. It is called
|
9
|
+
# from the `test_command_line` unit test.
|
10
|
+
#
|
11
|
+
# --stdout: string to output to stdout
|
12
|
+
# --stderr: string to output to stderr
|
13
|
+
# --exitstatus: exit status to return (default is zero)
|
14
|
+
# --signal: uncaught signal to raise (default is not to signal)
|
15
|
+
# --duration: number of seconds to sleep before exiting (default is zero)
|
16
|
+
#
|
17
|
+
# Both --stdout and --stderr can be given.
|
18
|
+
#
|
19
|
+
# If --signal is given, --exitstatus is ignored.
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
# Output "Hello, world!" to stdout and exit with status 0
|
23
|
+
# $ bin/command-line-test --stdout="Hello, world!" --exitstatus=0
|
24
|
+
#
|
25
|
+
# Output "ERROR: timeout" to stderr and exit with status 1
|
26
|
+
# $ bin/command-line-test --stderr="ERROR: timeout" --exitstatus=1
|
27
|
+
#
|
28
|
+
# Output "Fatal: killed by parent" to stderr and signal 9
|
29
|
+
# $ bin/command-line-test --stderr="Fatal: killed by parent" --signal=9
|
30
|
+
#
|
31
|
+
# Output to both stdout and stderr return default exitstatus 0
|
32
|
+
# $ bin/command-line-test --stdout="Hello, world!" --stderr="ERROR: timeout"
|
33
|
+
#
|
34
|
+
|
35
|
+
# The command line parser for this script
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# parser = CommandLineParser.new
|
39
|
+
# options = parser.parse(['--exitstatus', '1', '--stderr', 'ERROR: timeout', '--duration', '5'])
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
class CommandLineParser < CommandLineBoss
|
43
|
+
attr_reader :duration, :stdout, :stderr, :exitstatus, :signal, :env_vars, :remaining_args
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def set_defaults
|
48
|
+
@duration = 0.0
|
49
|
+
@stdout = nil
|
50
|
+
@stderr = nil
|
51
|
+
@exitstatus = 0
|
52
|
+
@signal = nil
|
53
|
+
@env_vars = []
|
54
|
+
@remaining_args = []
|
55
|
+
end
|
56
|
+
|
57
|
+
include CommandLineBoss::HelpOption
|
58
|
+
|
59
|
+
def parse_arguments
|
60
|
+
@remaining_args = args.shift(args.length)
|
61
|
+
end
|
62
|
+
|
63
|
+
def banner = <<~HEADER
|
64
|
+
A script used to test calling a command line program from Ruby
|
65
|
+
|
66
|
+
Command line options allow control of the output, exit status, signal
|
67
|
+
raised, and the duration of the command.
|
68
|
+
|
69
|
+
Usage: #{$PROGRAM_NAME} [options]
|
70
|
+
|
71
|
+
HEADER
|
72
|
+
|
73
|
+
def footer = <<~HEADER
|
74
|
+
The default exitstatus is 0.
|
75
|
+
The default duration is 0.
|
76
|
+
Both --stdout and --stderr can be given.
|
77
|
+
If --signal is given, --exitstatus is ignored.
|
78
|
+
|
79
|
+
If no options are given, the script will exit with exitstatus 0.
|
80
|
+
HEADER
|
81
|
+
|
82
|
+
# Define the stdout option
|
83
|
+
# @return [void]
|
84
|
+
# @api private
|
85
|
+
def define_stdout_option
|
86
|
+
parser.on('--stdout="string to stdout"', 'A string to send to stdout') do |string|
|
87
|
+
@stdout = string
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Define the stdout-file option
|
92
|
+
# @return [void]
|
93
|
+
# @api private
|
94
|
+
def define_stdout_file_option
|
95
|
+
parser.on('--stdout-file="file"', 'Send contents of file to stdout') do |filename|
|
96
|
+
@stdout = File.binread(filename)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Define the stderr option
|
101
|
+
# @return [void]
|
102
|
+
# @api private
|
103
|
+
def define_stderr_option
|
104
|
+
parser.on('--stderr="string to stderr"', 'A string to send to stderr') do |string|
|
105
|
+
@stderr = string
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Define the stderr-file option
|
110
|
+
# @return [void]
|
111
|
+
# @api private
|
112
|
+
def define_stderr_file_option
|
113
|
+
parser.on('--stderr-file="file"', 'Send contents of file to stderr') do |filename|
|
114
|
+
@stderr = File.binread(filename)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Define the exitstatus option
|
119
|
+
# @return [void]
|
120
|
+
# @api private
|
121
|
+
def define_exitstatus_option
|
122
|
+
parser.on('--exitstatus=1', 'The exitstatus to return') do |exitstatus|
|
123
|
+
@exitstatus = Integer(exitstatus)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Define the signal option
|
128
|
+
# @return [void]
|
129
|
+
# @api private
|
130
|
+
def define_signal_option
|
131
|
+
parser.on('--signal=9', 'The signal to raise') do |signal|
|
132
|
+
@signal = Integer(signal)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Define the duration option
|
137
|
+
# @return [void]
|
138
|
+
# @api private
|
139
|
+
def define_duration_option
|
140
|
+
parser.on('--duration=0', 'The number of seconds the command should take') do |duration|
|
141
|
+
@duration = Float(duration)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Define the envvar option
|
146
|
+
# @return [void]
|
147
|
+
# @api private
|
148
|
+
def define_env_var_option
|
149
|
+
parser.on('--env-var=name', 'Display an environment variable') do |name|
|
150
|
+
@env_vars << name
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
options = CommandLineParser.new.parse(ARGV)
|
156
|
+
|
157
|
+
if options.error_messages.any?
|
158
|
+
warn options.error_messages.join("\n")
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
|
162
|
+
options.remaining_args.each do |arg|
|
163
|
+
warn "Found argument: #{arg}"
|
164
|
+
end
|
165
|
+
|
166
|
+
options.env_vars.each do |name|
|
167
|
+
print "Environment variable #{name}="
|
168
|
+
if ENV.key?(name)
|
169
|
+
puts ENV[name].inspect
|
170
|
+
else
|
171
|
+
puts '<not set>'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
if options.stdout
|
176
|
+
$stdout.binmode
|
177
|
+
$stdout.puts options.stdout
|
178
|
+
end
|
179
|
+
|
180
|
+
if options.stderr
|
181
|
+
$stderr.binmode
|
182
|
+
$stderr.puts options.stderr # rubocop:disable Style/StderrPuts
|
183
|
+
end
|
184
|
+
|
185
|
+
sleep options.duration unless options.duration.zero?
|
186
|
+
|
187
|
+
Process.kill(options.signal, Process.pid) if options.signal
|
188
|
+
|
189
|
+
exit(options.exitstatus) if options.exitstatus
|
data/bin/console
CHANGED
data/bin/setup
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# Same setup script for Ruby projects that also installs the conventional commit git hook
|
4
|
+
|
2
5
|
set -euo pipefail
|
3
6
|
IFS=$'\n\t'
|
4
|
-
set -vx
|
5
7
|
|
6
|
-
|
8
|
+
# set -vx
|
9
|
+
|
10
|
+
bundle install
|
11
|
+
|
12
|
+
if [ -x "$(command -v npm)" ]; then
|
13
|
+
npm install
|
14
|
+
else
|
15
|
+
echo "npm is not installed"
|
16
|
+
echo "Install npm then re-run this script to enable the conventional commit git hook."
|
17
|
+
fi
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
require 'process_executer'
|
5
|
+
|
6
|
+
module RubyGit
|
7
|
+
module CommandLine
|
8
|
+
# Defines the options for RubyGit::CommandLine::Runner
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
#
|
12
|
+
class Options < ProcessExecuter::Options::RunOptions
|
13
|
+
# Alias for brevity
|
14
|
+
OptionDefinition = ProcessExecuter::Options::OptionDefinition
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# :nocov: SimpleCov on JRuby reports the last with the last argument line is not covered
|
19
|
+
|
20
|
+
# The options allowed for objects of this class
|
21
|
+
# @return [Array<OptionDefinition>]
|
22
|
+
# @api private
|
23
|
+
def define_options
|
24
|
+
[
|
25
|
+
*super,
|
26
|
+
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))
|
29
|
+
].freeze
|
30
|
+
end
|
31
|
+
# :nocov:
|
32
|
+
|
33
|
+
# Validate the raise_git_errors option value
|
34
|
+
# @return [String, nil] the error message if the value is not valid
|
35
|
+
# @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}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Validate the normalize_encoding option value
|
43
|
+
# @return [String, nil] the error message if the value is not valid
|
44
|
+
# @api private
|
45
|
+
def validate_normalize_encoding
|
46
|
+
return if [true, false].include?(normalize_encoding)
|
47
|
+
|
48
|
+
errors << "normalize_encoding must be true or false but was #{normalize_encoding.inspect}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Validate the chomp option value
|
52
|
+
# @return [String, nil] the error message if the value is not valid
|
53
|
+
# @api private
|
54
|
+
def validate_chomp
|
55
|
+
return if [true, false].include?(chomp)
|
56
|
+
|
57
|
+
errors << "chomp must be true or false but was #{chomp.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
require 'process_executer'
|
5
|
+
|
6
|
+
module RubyGit
|
7
|
+
module CommandLine
|
8
|
+
# The result of running a git command
|
9
|
+
#
|
10
|
+
# Adds stdout and stderr processing to the `ProcessExecuter::Result` class.
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
#
|
14
|
+
class Result < SimpleDelegator
|
15
|
+
# @!method initialize(result)
|
16
|
+
# Initialize a new result object
|
17
|
+
# @example
|
18
|
+
# result = ProcessExecuter.run('echo hello')
|
19
|
+
# RubyGit::CommandLine::Result.new(result)
|
20
|
+
# @param [ProcessExecuter::Result] result The result of running the command
|
21
|
+
# @return [RubyGit::CommandLine::Result]
|
22
|
+
# @api public
|
23
|
+
|
24
|
+
# Return the processed stdout output (or original if it was not processed)
|
25
|
+
#
|
26
|
+
# This output is only returned if a stdout redirection is a
|
27
|
+
# `ProcessExecuter::MonitoredPipe`.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# result = ProcessExecuter.run('echo hello': out: StringIO.new)
|
31
|
+
# result.stdout #=> "hello\n"
|
32
|
+
#
|
33
|
+
# @return [String, nil]
|
34
|
+
#
|
35
|
+
def stdout
|
36
|
+
defined?(@processed_stdout) ? @processed_stdout : unprocessed_stdout
|
37
|
+
end
|
38
|
+
|
39
|
+
# Process the captured stdout output
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# result = ProcessExecuter.run('echo hello', out: StringIO.new)
|
43
|
+
# result.stdout #=> "hello\n"
|
44
|
+
# result.process_stdout { |stdout, _result| stdout.upcase }
|
45
|
+
# result.stdout #=> "HELLO\n"
|
46
|
+
# result.unprocessed_stdout #=> "hello\n"
|
47
|
+
#
|
48
|
+
# @example Chain processing
|
49
|
+
# result = ProcessExecuter.run('echo hello', out: StringIO.new)
|
50
|
+
# result.stdout #=> "hello\n"
|
51
|
+
# result.process_stdout { |s| s.upcase }.process_stdout { |s| s.reverse }
|
52
|
+
# result.stdout #=> "OLLEH\n"
|
53
|
+
# result.unprocessed_stdout #=> "hello\n"
|
54
|
+
#
|
55
|
+
# @return [String, nil]
|
56
|
+
#
|
57
|
+
# @yield [stdout, result] Yields the stdout output and the result object
|
58
|
+
# @yieldparam stdout [String] The output to process
|
59
|
+
# @yieldparam result [RubyGit::CommandLine::Result] This object (aka self)
|
60
|
+
# @yieldreturn [String] The processed stdout output
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
#
|
64
|
+
def process_stdout(&block)
|
65
|
+
return if block.nil?
|
66
|
+
|
67
|
+
@processed_stdout = block.call(stdout, self)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the original stdout output before it was processed
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# result = ProcessExecuter.run('echo hello', out: StringIO.new)
|
75
|
+
# result.stdout #=> "hello\n"
|
76
|
+
# result.unprocessed_stdout #=> "hello\n"
|
77
|
+
# result.process_stdout { |s| s.upcase }
|
78
|
+
# result.stdout #=> "HELLO\n"
|
79
|
+
# result.unprocessed_stdout #=> "hello\n"
|
80
|
+
#
|
81
|
+
# @return [String, nil]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
#
|
85
|
+
def unprocessed_stdout
|
86
|
+
__getobj__.stdout
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return the processed stderr output (or original if it was not processed)
|
90
|
+
#
|
91
|
+
# This output is only returned if a stderr redirection is a
|
92
|
+
# `ProcessExecuter::MonitoredPipe`.
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# result = ProcessExecuter.run('echo hello 1>&2': err: StringIO.new)
|
96
|
+
# result.stderr #=> "hello\n"
|
97
|
+
#
|
98
|
+
# @return [String, nil]
|
99
|
+
#
|
100
|
+
def stderr
|
101
|
+
defined?(@processed_stderr) ? @processed_stderr : unprocessed_stderr
|
102
|
+
end
|
103
|
+
|
104
|
+
# Process the captured stderr output
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# result = ProcessExecuter.run('echo hello 1>&2', err: StringIO.new)
|
108
|
+
# result.stderr #=> "hello\n"
|
109
|
+
# result.process_stderr { |stderr, _result| stderr.upcase }
|
110
|
+
# result.stderr #=> "HELLO\n"
|
111
|
+
# result.unprocessed_stderr #=> "hello\n"
|
112
|
+
#
|
113
|
+
# @example Chain processing
|
114
|
+
# result = ProcessExecuter.run('echo hello 1>&2', err: StringIO.new)
|
115
|
+
# result.stderr #=> "hello\n"
|
116
|
+
# result.process_stderr { |s| s.upcase }.process_stderr { |s| s.reverse }
|
117
|
+
# result.stderr #=> "OLLEH\n"
|
118
|
+
# result.unprocessed_stderr #=> "hello\n"
|
119
|
+
#
|
120
|
+
# @return [String, nil]
|
121
|
+
#
|
122
|
+
# @yield [stderr, result] Yields the stderr output and the result object
|
123
|
+
# @yieldparam stderr [String] The output to process
|
124
|
+
# @yieldparam result [RubyGit::CommandLine::Result] This object (aka self)
|
125
|
+
# @yieldreturn [String] The processed stderr output
|
126
|
+
#
|
127
|
+
# @api public
|
128
|
+
#
|
129
|
+
def process_stderr(&block)
|
130
|
+
return if block.nil?
|
131
|
+
|
132
|
+
@processed_stderr = block.call(stderr, self)
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the original stderr output before it was processed
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# result = ProcessExecuter.run('echo hello 1>&2', err: StringIO.new)
|
140
|
+
# result.stderr #=> "hello\n"
|
141
|
+
# result.unprocessed_stderr #=> "hello\n"
|
142
|
+
# result.process_stderr { |stderr| stderr.upcase }
|
143
|
+
# result.stderr #=> "HELLO\n"
|
144
|
+
# result.unprocessed_stderr #=> "hello\n"
|
145
|
+
#
|
146
|
+
# @return [String, nil]
|
147
|
+
#
|
148
|
+
# @api public
|
149
|
+
#
|
150
|
+
def unprocessed_stderr
|
151
|
+
__getobj__.stderr
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|