module Deplomat class Node attr_accessor :log_to, :raise_exceptions, :logfile attr_reader :current_path def initialize(logfile: "#{Dir.pwd}/deplomat.log", log_to: [:stdout], path: nil, raise_exceptions: true) @log_to = log_to @logfile = logfile @raise_exceptions = raise_exceptions self.cd(path) if path end def execute(command, path=@current_path, message: [], stdout_source: :stdout, log_command: true, _raise_exceptions: @raise_exceptions) message = [message] if message.kind_of?(String) log(message[0] + "\n", color: 'white') unless message.empty? || message.nil? # Respect current_path command_to_log = command if path command_to_log = "#{command}\n(in #{path})" command = "cd #{path} && #{command}" end out = "" status = nil Open3.popen3(command) do |stdin, stdout, stderr, thread| # Sometimes, programs write in stderr, although there are no errors. # rake assets:precompile does that, for example. stdout_source_object = (stdout_source == :stderr ? stderr : stdout) log("--> " + command_to_log + "\n", color: "white") if log_command while line=stdout_source_object.gets do out << line log(" #{line}") if log_command end error_out = "" status = thread.value.exitstatus.to_i if status > 0 while o = stderr.gets error_out += o end log(error_out + "\n", color: 'red') if _raise_exceptions self.close if self.respond_to?(:close) raise Deplomat::ExecutionError end end yield if block_given? end log(message[1] + "\n", color: 'white') unless message.empty? || message.nil? return { status: status, out: out } end def copy(what, where) execute("rsync -ar #{what} #{where}") end alias :cp :copy def move(what, where) execute("mv #{what} #{where}") end alias :mv :move def remove(what) execute("rm -rf #{what}") end alias :rm :remove def create_file(filename) execute("touch #{filename}") end alias :touch :create_file def create_dir(dirname) execute("mkdir -p #{dirname}") end alias :mkdir :create_dir def create_symlink(source, target) execute("ln -s #{source} #{target}") end alias :ln :create_symlink def path_exist?(path) execute("[ -e #{path} ]", @current_path, log_command: false, _raise_exceptions: true) && true rescue Deplomat::ExecutionError # returned 1, file doesn't exist false end alias :path_exists? :path_exist? alias :file_exists? :path_exist? def cd(path) raise Deplomat::NoSuchPathError, path if !path.nil? && !path_exist?(path) @current_path = if path =~ /\A[\/~]/ || path.nil? path else File.expand_path("#{@current_path}#{path}") end # Making sure we don't end up with double // at the end of the path @current_path = @current_path.chomp("/") + "/" end def git_push(remote="origin", branch="master") execute("git push #{remote} #{branch}") end def git_pull(remote="origin", branch="master") execute("git pull #{remote} #{branch}") end def git_merge(source="origin", target="master") execute("git merge #{source} #{target}") end def git_checkout(target) execute("git checkout #{target}") end def clean(path: @current_path, except: [], leave: [0, :last]) # Gets us all entries sorted by date, most recent ones first entries_by_date = execute("ls -t", path, log_command: false)[:out].split("\n") if entries_by_date # Don't do anything with entries listed in :except entries_by_date = entries_by_date - except if leave entries_by_date.reverse! if leave[1] == :first entries_by_date = entries_by_date[leave[0]..entries_by_date.length] end entries_by_date.each { |entry| remove("#{path}#{entry}") } if entries_by_date end end def log(line, color: 'light_black') @message_color = color # Only calls log methods mentioned in the @log_to property @log_to.each { |logger| self.send("log_to_#{logger}", line) } end private def log_to_file(line) if @logfile open(@logfile, 'a') { |f| f.puts line } end end def log_to_stdout(line) print_to_terminal(line, color: @message_color, newline: false) end end end