require 'open3' require 'izanami/channel' require 'izanami/worker' require 'izanami/mappers/command' module Izanami module Workers # Worker that executes the {cap} commands and stores the results. # # @param [Hash] redis_options Options for the redis mapper. # @param [Hash] command the command to execute. class Command < Struct.new(:redis_options, :command) Izanami::Worker self attr_writer :shell # Executes the cap command with the correct environment and handles the # IO streams. def call Open3.popen2e(shell_env, cmd, shell_options) do |stdin, stdout, thread| handle_streams(stdin, stdout, thread) end end # ENV vars for the {cap} command. # # This worker is executed inside a ruby process initiated by {bundler}. # Because the {cap} process is executed in a sandbox with its own {bundler} # ecosystem, we need to remove all the {bundler} environment variables to have # a clean execution. # # @return [Hash] def shell_env bundler_keys = ENV.select { |var, _| var.to_s.match(/\ABUNDLE/) }.keys bundler_keys.reduce({}) do |hash, (k,_)| hash[k] = nil hash end end # Options for the new process. # # Here we define the directory for the {cap} process, which is the # ENV['IZANAMI_SANDBOX'] configuration variable. # # @return [Hash] def shell_options { chdir: ENV['IZANAMI_SANDBOX'] } end # The {cap} command # # @return [String] def cmd "bundle exec cap #{command['tasks']}" end # Handle the process IO streams. # # It writes the process output to a {Izanami::Channel} and # saves the status of the command. # # @see {Open3.popen2e} def handle_streams(stdin, stdout, thread) mapper = Izanami::Mappers::Command.new(redis_options) channel = Izanami::Channel.new(mapper) channel.write(command['id']) do |input| while (line = stdout.gets) input << line.chomp end end status = thread.value.success? ? 'success' : 'fail' mapper.update(command['id'], 'status', status) stdin.close stdout.close end end end end