lib/buildbox/command.rb in buildbox-0.3.2 vs lib/buildbox/command.rb in buildbox-0.3.3
- old
+ new
@@ -11,11 +11,11 @@
# An error which occurs when the process doesn't end within
# the given timeout.
class TimeoutExceeded < StandardError; end
- attr_reader :output, :exit_status
+ attr_reader :pid, :output, :exit_status
def self.run(*args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
arguments = args.dup
@@ -34,20 +34,27 @@
def start(&block)
# Get the timeout, if we have one
timeout = @options[:timeout]
# Build the command we're going to run
- arguments = [ *runner, *@arguments ].compact.map(&:to_s) # all arguments must be a string
+ arguments = [ *@arguments ].compact.map(&:to_s) # all arguments must be a string
# Build the ChildProcess
@logger.info("Starting process: #{arguments}")
process = ChildProcess.build(*arguments)
process.cwd = File.expand_path(@options[:directory] || Dir.pwd)
- # Create the pipes so we can read the output in real tim
- read_pipe, write_pipe = IO.pipe
+ # Create the pipes so we can read the output in real time. PTY
+ # isn't avaible on all platforms (heroku) so we just fallback to IO.pipe
+ # if it's not presetnt.
+ read_pipe, write_pipe = begin
+ PTY.open
+ rescue
+ IO.pipe
+ end
+
process.io.stdout = write_pipe
process.io.stderr = write_pipe
process.duplex = true
# Set the environment on the process
@@ -68,10 +75,13 @@
# Otherwise, we close the writer right here, since we're
# not on the writing side.
write_pipe.close
end
+ # Store the process id for later cancelling!
+ @pid = process.pid
+
# Record the start time for timeout purposes
start_time = Time.now.to_i
# Track the output as it goes
output = ""
@@ -95,11 +105,11 @@
# We don't need to do anything if the data is empty
next if data.empty?
output << cleaned_data = UTF8.clean(data)
- yield cleaned_data if block_given?
+ yield self, cleaned_data if block_given?
end
end
# Break out if the process exited. We have to do this before
# attempting to write to stdin otherwise we'll get a broken pipe
@@ -128,11 +138,11 @@
extra_data = read_io(read_pipe)
# If there's some that we missed
if extra_data != ""
output << cleaned_data = UTF8.clean(extra_data)
- yield cleaned_data if block_given?
+ yield self, cleaned_data if block_given?
end
if RUBY_PLATFORM == "java"
# On JRuby, we need to close the writers after the process,
# for some reason. See https://github.com/mitchellh/vagrant/pull/711
@@ -143,22 +153,10 @@
@exit_status = process.exit_code
end
private
- # on heroku, tty isn't avaiable. so we result to just running command through
- # bash. the downside to this, is that stuff colors aren't outputted because
- # processes don't think they're being in a terminal.
- def runner
- require 'pty'
- PTY.spawn('whoami')
-
- [ File.join(Buildbox.gem_root, "bin", "buildbox-pty") ]
- rescue
- [ "bash", "-c" ]
- end
-
# Reads data from an IO object while it can, returning the data it reads.
# When it encounters a case when it can't read anymore, it returns the
# data.
#
# @return [String]
@@ -188,20 +186,22 @@
rescue Exception => e
# The catch-all rescue here is to support multiple Ruby versions,
# since we use some Ruby 1.9 specific exceptions.
breakable = false
- if e.is_a?(EOFError)
+
+ # EOFError from OSX, EIO is raised by ubuntu
+ if e.is_a?(EOFError) || e.is_a?(Errno::EIO)
# An `EOFError` means this IO object is done!
breakable = true
elsif defined?(IO::WaitReadable) && e.is_a?(IO::WaitReadable)
# IO::WaitReadable is only available on Ruby 1.9+
# An IO::WaitReadable means there may be more IO but this
# IO object is not ready to be read from yet. No problem,
# we read as much as we can, so we break.
breakable = true
- elsif e.is_a?(Errno::EAGAIN)
+ elsif e.is_a?(Errno::EAGAIN) || e.is_a?(Errno::EWOULDBLOCK)
# Otherwise, we just look for the EAGAIN error which should be
# all that IO::WaitReadable does in Ruby 1.9.
breakable = true
end