require 'json' require 'fileutils' require 'tmpdir' require 'bolt/transport/base' require 'bolt/result' module Bolt module Transport class Local < Base def initialize(config = nil) super(config) if Gem.win_platform? raise NotImplementedError, "The local transport is not yet implemented on Windows" else @conn = Shell.new end end def in_tmpdir(base) args = base ? [nil, base] : [] Dir.mktmpdir(*args) do |dir| Dir.chdir(dir) do yield dir end end rescue StandardError => e raise Bolt::Node::FileError.new("Could not make tempdir: #{e.message}", 'TEMPDIR_ERROR') end private :in_tmpdir def copy_file(source, destination) FileUtils.copy_file(source, destination) rescue StandardError => e raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR') end def with_tmpscript(script, base) in_tmpdir(base) do |dir| dest = File.join(dir, File.basename(script)) copy_file(script, dest) File.chmod(0o750, dest) yield dest end end private :with_tmpscript def upload(target, source, destination, _options = {}) copy_file(source, destination) Bolt::Result.for_upload(target, source, destination) end def run_command(target, command, _options = {}) in_tmpdir(target.options[:tmpdir]) do |_| output = @conn.execute(command, {}) Bolt::Result.for_command(target, output.stdout.string, output.stderr.string, output.exit_code) end end def run_script(target, script, arguments, _options = {}) with_tmpscript(File.absolute_path(script), target.options[:tmpdir]) do |file| logger.debug "Running '#{file}' with #{arguments}" if arguments.empty? # We will always provide separated arguments, so work-around Open3's handling of a single # argument as the entire command string for script paths containing spaces. arguments = [''] end output = @conn.execute(file, *arguments, {}) Bolt::Result.for_command(target, output.stdout.string, output.stderr.string, output.exit_code) end end def run_task(target, task, arguments, _options = {}) input_method = task.input_method stdin = STDIN_METHODS.include?(input_method) ? JSON.dump(arguments) : nil env = ENVIRONMENT_METHODS.include?(input_method) ? arguments : nil with_tmpscript(task.executable, target.options[:tmpdir]) do |script| logger.debug("Running '#{script}' with #{arguments}") output = @conn.execute(script, stdin: stdin, env: env) Bolt::Result.for_task(target, output.stdout.string, output.stderr.string, output.exit_code) end end end end end require 'bolt/transport/local/shell'