lib/cli/kit/system.rb in cli-kit-3.3.0 vs lib/cli/kit/system.rb in cli-kit-4.0.0
- old
+ new
@@ -4,11 +4,11 @@
require 'English'
module CLI
module Kit
module System
- SUDO_PROMPT = CLI::UI.fmt("{{info:(sudo)}} Password: ")
+ SUDO_PROMPT = CLI::UI.fmt('{{info:(sudo)}} Password: ')
class << self
# Ask for sudo access with a message explaning the need for it
# Will make subsequent commands capable of running with sudo for a period of time
#
# #### Parameters
@@ -17,11 +17,11 @@
# #### Usage
# `ctx.sudo_reason("We need to do a thing")`
#
def sudo_reason(msg)
# See if sudo has a cached password
- `env SUDO_ASKPASS=/usr/bin/false sudo -A true`
+ %x(env SUDO_ASKPASS=/usr/bin/false sudo -A true)
return if $CHILD_STATUS.success?
CLI::UI.with_frame_color(:blue) do
puts(CLI::UI.fmt("{{i}} #{msg}"))
end
end
@@ -88,21 +88,33 @@
#
def capture3(*a, sudo: false, env: ENV, **kwargs)
delegate_open3(*a, sudo: sudo, env: env, method: :capture3, **kwargs)
end
+ def popen2(*a, sudo: false, env: ENV, **kwargs, &block)
+ delegate_open3(*a, sudo: sudo, env: env, method: :popen2, **kwargs, &block)
+ end
+
+ def popen2e(*a, sudo: false, env: ENV, **kwargs, &block)
+ delegate_open3(*a, sudo: sudo, env: env, method: :popen2e, **kwargs, &block)
+ end
+
+ def popen3(*a, sudo: false, env: ENV, **kwargs, &block)
+ delegate_open3(*a, sudo: sudo, env: env, method: :popen3, **kwargs, &block)
+ end
+
# Execute a command in the user's environment
# Outputs result of the command without capturing it
#
# #### Parameters
# - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
# - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
# - `env`: process environment with which to execute this command
# - `**kwargs`: additional keyword arguments to pass to Process.spawn
#
# #### Returns
- # - `status`: boolean success status of the command execution
+ # - `status`: The `Process:Status` result for the command execution
#
# #### Usage
# `stat = CLI::Kit::System.system('ls', 'a_folder')`
#
def system(*a, sudo: false, env: ENV, **kwargs)
@@ -114,31 +126,33 @@
pid = Process.spawn(env, *resolve_path(a, env), 0 => in_stream, :out => out_w, :err => err_w, **kwargs)
out_w.close
err_w.close
handlers = if block_given?
- { out_r => ->(data) { yield(data.force_encoding(Encoding::UTF_8), '') },
- err_r => ->(data) { yield('', data.force_encoding(Encoding::UTF_8)) } }
+ {
+ out_r => ->(data) { yield(data.force_encoding(Encoding::UTF_8), '') },
+ err_r => ->(data) { yield('', data.force_encoding(Encoding::UTF_8)) },
+ }
else
- { out_r => ->(data) { STDOUT.write(data) },
- err_r => ->(data) { STDOUT.write(data) } }
+ {
+ out_r => ->(data) { STDOUT.write(data) },
+ err_r => ->(data) { STDOUT.write(data) },
+ }
end
previous_trailing = Hash.new('')
loop do
ios = [err_r, out_r].reject(&:closed?)
break if ios.empty?
readers, = IO.select(ios)
readers.each do |io|
- begin
- data, trailing = split_partial_characters(io.readpartial(4096))
- handlers[io].call(previous_trailing[io] + data)
- previous_trailing[io] = trailing
- rescue IOError
- io.close
- end
+ data, trailing = split_partial_characters(io.readpartial(4096))
+ handlers[io].call(previous_trailing[io] + data)
+ previous_trailing[io] = trailing
+ rescue IOError
+ io.close
end
end
Process.wait(pid)
$CHILD_STATUS
@@ -161,23 +175,31 @@
partial_character_index = min_bound + partial_character_sub_index
[data.byteslice(0...partial_character_index), data.byteslice(partial_character_index..-1)]
end
+ def os
+ return :mac if /darwin/.match(RUBY_PLATFORM)
+ return :linux if /linux/.match(RUBY_PLATFORM)
+ return :windows if /mingw32/.match(RUBY_PLATFORM)
+
+ raise "Could not determine OS from platform #{RUBY_PLATFORM}"
+ end
+
private
def apply_sudo(*a, sudo)
a.unshift('sudo', '-S', '-p', SUDO_PROMPT, '--') if sudo
sudo_reason(sudo) if sudo.is_a?(String)
a
end
- def delegate_open3(*a, sudo: raise, env: raise, method: raise, **kwargs)
+ def delegate_open3(*a, sudo: raise, env: raise, method: raise, **kwargs, &block)
a = apply_sudo(*a, sudo)
- Open3.send(method, env, *resolve_path(a, env), **kwargs)
+ Open3.send(method, env, *resolve_path(a, env), **kwargs, &block)
rescue Errno::EINTR
- raise(Errno::EINTR, "command interrupted: #{a.join(' ')}")
+ raise(Errno::EINTR, "command interrupted: #{a.join(" ")}")
end
# Ruby resolves the program to execute using its own PATH, but we want it to
# use the provided one, so we ensure ruby chooses to spawn a shell, which will
# parse our command and properly spawn our target using the provided environment.
@@ -187,20 +209,33 @@
# project.
#
# See https://github.com/Shopify/dev/pull/625 for more details.
def resolve_path(a, env)
# If only one argument was provided, make sure it's interpreted by a shell.
- return ["true ; " + a[0]] if a.size == 1
+ if a.size == 1
+ if os == :windows
+ return ['break && ' + a[0]]
+ else
+ return ['true ; ' + a[0]]
+ end
+ end
return a if a.first.include?('/')
- paths = env.fetch('PATH', '').split(':')
- item = paths.detect do |f|
- command_path = "#{f}/#{a.first}"
- File.executable?(command_path) && File.file?(command_path)
+ item = which(a.first, env)
+ a[0] = item if item
+ a
+ end
+
+ def which(cmd, env)
+ exts = os == :windows ? env.fetch('PATHEXT').split(';') : ['']
+ env.fetch('PATH', '').split(File::PATH_SEPARATOR).each do |path|
+ exts.each do |ext|
+ exe = File.join(path, "#{cmd}#{ext}")
+ return exe if File.executable?(exe) && !File.directory?(exe)
+ end
end
- a[0] = "#{item}/#{a.first}" if item
- a
+ nil
end
end
end
end
end