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