require_relative 'ui/ui' require_relative 'globals' require_relative 'fastlane_pty' module FastlaneCore # Executes commands and takes care of error handling and more class CommandExecutor class << self # Cross-platform way of finding an executable in the $PATH. Respects the $PATHEXT, which lists # valid file extensions for executables on Windows. # # which('ruby') #=> /usr/bin/ruby # # Derived from https://stackoverflow.com/a/5471032/3005 def which(cmd) # PATHEXT contains the list of file extensions that Windows considers executable, semicolon separated. # e.g. ".COM;.EXE;.BAT;.CMD" exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [] exts << '' # Always have an empty string (= no file extension) ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| exts.each do |ext| cmd_path = File.join(path, "#{cmd}#{ext}") return cmd_path if Helper.executable?(cmd_path) end end return nil end # @param command [String] The command to be executed # @param print_all [Boolean] Do we want to print out the command output while running? # @param print_command [Boolean] Should we print the command that's being executed # @param error [Block] A block that's called if an error occurs # @param prefix [Array] An array containing a prefix + block which might get applied to the output # @param loading [String] A loading string that is shown before the first output # @param suppress_output [Boolean] Should we print the command's output? # @return [String] All the output as string def execute(command: nil, print_all: false, print_command: true, error: nil, prefix: nil, loading: nil, suppress_output: false) print_all = true if FastlaneCore::Globals.verbose? prefix ||= {} output = [] command = command.join(" ") if command.kind_of?(Array) UI.command(command) if print_command if print_all && loading # this is only used to show the "Loading text"... UI.command_output(loading) end begin status = FastlaneCore::FastlanePty.spawn(command) do |command_stdout, command_stdin, pid| command_stdout.each do |l| line = l.strip # strip so that \n gets removed output << line next unless print_all # Prefix the current line with a string prefix.each do |element| line = element[:prefix] + line if element[:block] && element[:block].call(line) end UI.command_output(line) unless suppress_output end end rescue => ex # FastlanePty adds exit_status on to StandardError so every error will have a status code status = ex.exit_status # This could happen when the environment is wrong: # > invalid byte sequence in US-ASCII (ArgumentError) output << ex.to_s o = output.join("\n") puts(o) if error error.call(o, nil) else raise ex end end # Exit status for build command, should be 0 if build succeeded if status != 0 o = output.join("\n") puts(o) unless suppress_output # the user has the right to see the raw output UI.error("Exit status: #{status}") if error error.call(o, status) else UI.user_error!("Exit status: #{status}") end end return output.join("\n") end end end end