Skip to content

Change how the git CLI subprocess is executed #684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Change how the git CLI subprocess is executed
Signed-off-by: James Couball <jcouball@yahoo.com>
  • Loading branch information
jcouball committed Jan 15, 2024
commit 320c5481dbe61b6c22d59a3352aad591afc802e1
38 changes: 21 additions & 17 deletions .github/workflows/continuous_integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@ name: CI

on:
push:
branches: [master]
branches: [master,v1]
pull_request:
branches: [master]
branches: [master,v1]
workflow_dispatch:

jobs:
continuous_integration_build:
continue-on-error: true
build:
name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
continue-on-error: ${{ matrix.experimental == 'Yes' }}
env: { JAVA_OPTS: -Djdk.io.File.enableADS=true }

strategy:
fail-fast: false
matrix:
ruby: [3.0, 3.1, 3.2, 3.3]
# Only the latest versions of JRuby and TruffleRuby are tested
ruby: ["3.0", "3.1", "3.2", "3.3", "truffleruby-23.1.1", "jruby-9.4.5.0"]
operating-system: [ubuntu-latest]
experimental: [No]
include:
- ruby: head
- # Building against head version of Ruby is considered experimental
ruby: head
operating-system: ubuntu-latest
- ruby: truffleruby-23.1.1
operating-system: ubuntu-latest
- ruby: 3.0
operating-system: windows-latest
- ruby: jruby-9.4.5.0
operating-system: windows-latest
experimental: Yes

name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }}

runs-on: ${{ matrix.operating-system }}
- # Only test with minimal Ruby version on Windows
ruby: 3.0
operating-system: windows-latest

env:
JAVA_OPTS: -Djdk.io.File.enableADS=true
- # Since JRuby on Windows is known to not work, consider this experimental
ruby: jruby-9.4.5.0
operating-system: windows-latest
experimental: Yes

steps:
- name: Checkout Code
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The changes coming in this major release include:
* Update the required Git command line version to at least 2.28
* Update how CLI commands are called to use the [process_executer](https://github.com/main-branch/process_executer)
gem which is built on top of [Kernel.spawn](https://ruby-doc.org/3.3.0/Kernel.html#method-i-spawn).
See [PR #617](https://github.com/ruby-git/ruby-git/pull/617) for more details
See [PR #684](https://github.com/ruby-git/ruby-git/pull/684) for more details
on the motivation for this implementation.

The tentative plan is to release `2.0.0` near the end of March 2024 depending on
Expand Down
180 changes: 180 additions & 0 deletions bin/command_line_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'optparse'

# A script used to test calling a command line program from Ruby
#
# This script is used to test the `Git::CommandLine` class. It is called
# from the `test_command_line` unit test.
#
# --stdout: string to output to stdout
# --stderr: string to output to stderr
# --exitstatus: exit status to return (default is zero)
# --signal: uncaught signal to raise (default is not to signal)
#
# Both --stdout and --stderr can be given.
#
# If --signal is given, --exitstatus is ignored.
#
# Examples:
# Output "Hello, world!" to stdout and exit with status 0
# $ bin/command_line_test --stdout="Hello, world!" --exitstatus=0
#
# Output "ERROR: timeout" to stderr and exit with status 1
# $ bin/command_line_test --stderr="ERROR: timeout" --exitstatus=1
#
# Output "Fatal: killed by parent" to stderr and signal 9
# $ bin/command_line_test --stderr="Fatal: killed by parent" --signal=9
#
# Output to both stdout and stderr return default exitstatus 0
# $ bin/command_line_test --stdout="Hello, world!" --stderr="ERROR: timeout"
#


class CommandLineParser
def initialize
@option_parser = OptionParser.new
define_options
end

attr_reader :stdout, :stderr, :exitstatus, :signal

# Parse the command line arguements returning the options
#
# @example
# parser = CommandLineParser.new
# options = parser.parse(['major'])
#
# @param args [Array<String>] the command line arguments
#
# @return [CreateGithubRelease::Options] the options
#
def parse(*args)
begin
option_parser.parse!(remaining_args = args.dup)
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
report_errors(e.message)
end
parse_remaining_args(remaining_args)
# puts options unless options.quiet
# report_errors(*options.errors) unless options.valid?
self
end

private

# @!attribute [rw] option_parser
#
# The option parser
#
# @return [OptionParser] the option parser
#
# @api private
#
attr_reader :option_parser

def define_options
option_parser.banner = "Usage:\n#{command_template}"
option_parser.separator ''
option_parser.separator "Both --stdout and --stderr can be given."
option_parser.separator 'If --signal is given, --exitstatus is ignored.'
option_parser.separator 'If nothing is given, the script will exit with exitstatus 0.'
option_parser.separator ''
option_parser.separator 'Options:'
%i[
define_help_option define_stdout_option define_stderr_option
define_exitstatus_option define_signal_option
].each { |m| send(m) }
end

# The command line template as a string
# @return [String]
# @api private
def command_template
<<~COMMAND
#{File.basename($PROGRAM_NAME)} \
--help | \
[--stdout="string to stdout"] [--stderr="string to stderr"] [--exitstatus=1] [--signal=9]
COMMAND
end

# Define the stdout option
# @return [void]
# @api private
def define_stdout_option
option_parser.on('--stdout="string to stdout"', 'A string to send to stdout') do |string|
@stdout = string
end
end

# Define the stderr option
# @return [void]
# @api private
def define_stderr_option
option_parser.on('--stderr="string to stderr"', 'A string to send to stderr') do |string|
@stderr = string
end
end

# Define the exitstatus option
# @return [void]
# @api private
def define_exitstatus_option
option_parser.on('--exitstatus=1', 'The exitstatus to return') do |exitstatus|
@exitstatus = Integer(exitstatus)
end
end

# Define the signal option
# @return [void]
# @api private
def define_signal_option
option_parser.on('--signal=9', 'The signal to raise') do |signal|
@signal = Integer(signal)
end
end

# Define the help option
# @return [void]
# @api private
def define_help_option
option_parser.on_tail('-h', '--help', 'Show this message') do
puts option_parser
exit 0
end
end

# An error message constructed from the given errors array
# @return [String]
# @api private
def error_message(errors)
<<~MESSAGE
#{errors.map { |e| "ERROR: #{e}" }.join("\n")}
Use --help for usage
MESSAGE
end

# Output an error message and useage to stderr and exit
# @return [void]
# @api private
def report_errors(*errors)
warn error_message(errors)
exit 1
end

# Parse non-option arguments (there are none for this parser)
# @return [void]
# @api private
def parse_remaining_args(remaining_args)
report_errors('Too many args') unless remaining_args.empty?
end
end

options = CommandLineParser.new.parse(*ARGV)

STDOUT.puts options.stdout if options.stdout
STDERR.puts options.stderr if options.stderr
Process.kill(options.signal, Process.pid) if options.signal
exit(options.exitstatus) if options.exitstatus
1 change: 1 addition & 0 deletions git.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Gem::Specification.new do |s|
s.requirements = ['git 2.28.0 or greater']

s.add_runtime_dependency 'addressable', '~> 2.8'
s.add_runtime_dependency 'process_executer', '~> 0.7'
s.add_runtime_dependency 'rchardet', '~> 1.8'

s.add_development_dependency 'minitar', '~> 0.9'
Expand Down
2 changes: 2 additions & 0 deletions lib/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require 'git/branch'
require 'git/branches'
require 'git/command_line_result'
require 'git/command_line'
require 'git/config'
require 'git/diff'
require 'git/encoding_utils'
Expand All @@ -23,6 +24,7 @@
require 'git/repository'
require 'git/signaled_error'
require 'git/status'
require 'git/signaled_error'
require 'git/stash'
require 'git/stashes'
require 'git/url'
Expand Down
Loading