lib/utils/probe_server.rb
require 'tins/xt' require 'term/ansicolor' class String include Term::ANSIColor end module Utils class ProbeServer class Job class << self attr_writer :colorize def colorize? !!@colorize end end self.colorize = false def initialize(probe_server, args) @id = probe_server.next_job_id @args = args end attr_reader :id attr_reader :args attr_writer :ok def ok case @ok when false then 'n' when true then 'y' else '-' end end def ok_colorize(string) return string unless self.class.colorize? case @ok when false then string.white.on_red when true then string.black.on_green else string.black.on_yellow end end def inspect ok_colorize( "#<#{self.class}: id=#{id} args=#{args.inspect} ok=#{ok}>" ) end alias to_s inspect end def initialize @history = [].freeze @jobs_queue = Queue.new @current_job_id = 0 Thread.new { work_loop } end annotate :doc def docs annotations = self.class.doc_annotations.sort_by(&:first) max_size = annotations.map { |a| a.first.size }.max annotations.map { |n, v| "#{n.to_s.ljust(max_size + 1)}#{v}" } end doc 'Return the currently running job.' def job queue_synchronize do @job end end def next_job_id @current_job_id += 1 end doc 'Enqueue a new job with the argument array <job_args>.' def job_enqueue(job_args) job = Job.new(self, job_args) output_message "#{job.inspect} enqueued." @jobs_queue.push job end alias enqueue job_enqueue doc 'Stop the process that is working on the current job, if any.' def job_stop @pid and Process.kill :STOP, @pid end doc 'Continue the process that is working on the current job, if any.' def job_continue @pid and Process.kill :CONT, @pid end doc 'Shutdown the server.' def server_shutdown output_message "Server was shutdown down – HARD!", :type => :warn exit! 23 end doc 'List the currently pending jobs waiting to be run.' def jobs_list @jobs_queue.instance_variable_get(:@que).dup end doc 'Clear all pending jobs.' def jobs_clear queue_synchronize do unless @jobs_queue.empty? @jobs_queue.clear output_message "Cleared all queued jobs.", :type => :warn true else false end end end doc 'Repeat the job with <job_id>, it will be assigned a new id, though.' def job_repeat(job_id) Job === job_id and job_id = job.id if old_job = @history.find { |job| job.id == job_id } job_enqueue old_job.args true else false end end doc 'List the history of run jobs.' def history_list @history.dup end doc 'Clear the history of run jobs.' def history_clear @history = [] true end doc "The environment of the server process, use env['a'] = 'b' and env['a']." def env ENV end private def queue_synchronize(&block) @jobs_queue.instance_variable_get(:@mutex).synchronize(&block) end def output_message(msg, opts = { :type => :info }) msg = case opts[:type] when :success msg.on_green.black when :info, nil msg.on_color(118).black when :warn msg.on_color(166).black when :failure msg.on_red.blink.white end STDOUT.puts msg STDOUT.flush end def run_job(job) output_message "#{job.inspect} about to run now.", :type => :info @pid = fork { exec(*cmd(job.args)) } output_message "#{job.inspect} now running with pid #@pid.", :type => :info Process.wait @pid message = "#{job.inspect} was just run" if $?.success? job.ok = true message << " successfully." output_message message, :type => :success else job.ok = false message << " and failed with exit status #{$?.exitstatus}!" output_message message, :type => :failure end @history += [ @job.freeze ] @history.freeze @job = nil end def work_loop loop do @job = @jobs_queue.shift run_job @job end end def cmd(job) call = [] if ENV.key?('BUNDLE_GEMFILE') and bundle = `which bundle`.full?(:chomp) call << bundle << 'exec' end call.push($0, *job) output_message "Executing #{call.inspect} now.", :type => :info call end end end