lib/buildbox/command.rb in buildbox-0.0.4 vs lib/buildbox/command.rb in buildbox-0.1

- old
+ new

@@ -1,73 +1,67 @@ +require 'pty' + module Buildbox class Command - require 'pty' + class Result < Struct.new(:output, :exit_status) + end - class Error < StandardError; end + def self.run(command, options = {}, &block) + new(command, options).run(&block) + end - def initialize(path = nil, read_interval = nil) - @path = path || "." - @read_interval = read_interval || 5 + def initialize(command, options = {}) + @command = command + @directory = options[:directory] || "." + @read_interval = options[:read_interval] || 5 end - def run(command) - Buildbox.logger.debug(command) - + def run(&block) + output = "" read_io, write_io, pid = nil - result = Buildbox::Result.new(command) - # hack: this is so the observer class can raise a started event. - # instead of having a block passed to this command, we should implement - # a proper command observer - yield result + # spawn the process in a pseudo terminal so colors out outputted + read_io, write_io, pid = PTY.spawn("cd #{expanded_directory} && #{@command}") - begin - dir = File.expand_path(@path) - - # spawn the process in a pseudo terminal so colors out outputted - read_io, write_io, pid = PTY.spawn("cd #{dir} && #{command}") - rescue Errno::ENOENT => e - return Buildbox::Result.new(false, e.message) - end - + # we don't need to write to the spawned io write_io.close loop do - fds, = IO.select([read_io], nil, nil, @read_interval) + fds, = IO.select([read_io], nil, nil, read_interval) if fds # should have some data to read begin - chunk = read_io.read_nonblock(10240) - if block_given? - yield result, chunk - end - result.output += chunk + chunk = read_io.read_nonblock(10240) + cleaned_chunk = UTF8.clean(chunk) + + output << chunk + yield cleaned_chunk if block_given? rescue Errno::EAGAIN, Errno::EWOULDBLOCK # do select again rescue EOFError, Errno::EIO # EOFError from OSX, EIO is raised by ubuntu break end end # if fds are empty, timeout expired - run another iteration end + # we're done reading, yay! read_io.close + + # just wait until its finally finished closing Process.waitpid(pid) - # output may be invalid UTF-8, as it is produced by the build command. - result.finished = true - result.exit_status = $?.exitstatus - - result + # the final result! + Result.new(output.chomp, $?.exitstatus) end - def run!(command) - result = run(command) + private - unless result.success? - raise Error, "Failed to run '#{command}': #{result.output}" - end + def expanded_directory + File.expand_path(@directory) + end - result + def read_interval + @read_interval end end end