class FastlaneCore::TransporterExecutor

Base class for executing the iTMSTransporter

Constants

ERROR_REGEX
OUTPUT_REGEX
RETURN_VALUE_REGEX
SKIP_ERRORS
WARNING_REGEX

Public Instance Methods

displayable_errors() click to toggle source
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 97
def displayable_errors
  @errors.map { |error| "[Transporter Error Output]: #{error}" }.join("\n").gsub!(/"/, "")
end
execute(command, hide_output) { |nil| ... } click to toggle source
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 33
def execute(command, hide_output)
  if Helper.test?
    yield(nil) if block_given?
    return command
  end

  @errors = []
  @warnings = []
  @all_lines = []

  if hide_output
    # Show a one time message instead
    UI.success("Waiting for App Store Connect transporter to be finished.")
    UI.success("iTunes Transporter progress... this might take a few minutes...")
  end

  begin
    exit_status = FastlaneCore::FastlanePty.spawn(command) do |command_stdout, command_stdin, pid|
      begin
        command_stdout.each do |line|
          @all_lines << line
          parse_line(line, hide_output) # this is where the parsing happens
        end
      end
    end
  rescue => ex
    # FastlanePty adds exit_status on to StandardError so every error will have a status code
    exit_status = ex.exit_status
    @errors << ex.to_s
  end

  unless exit_status.zero?
    @errors << "The call to the iTMSTransporter completed with a non-zero exit status: #{exit_status}. This indicates a failure."
  end

  if @warnings.count > 0
    UI.important(@warnings.join("\n"))
  end

  if @errors.join("").include?("app-specific")
    raise TransporterRequiresApplicationSpecificPasswordError
  end

  if @errors.count > 0 && @all_lines.count > 0
    # Print out the last 15 lines, this is key for non-verbose mode
    @all_lines.last(15).each do |line|
      UI.important("[iTMSTransporter] #{line}")
    end
    UI.message("iTunes Transporter output above ^")
    UI.error(@errors.join("\n"))
  end

  # this is to handle GitHub issue #1896, which occurs when an
  #  iTMSTransporter file transfer fails; iTMSTransporter will log an error
  #  but will then retry; if that retry is successful, we will see the error
  #  logged, but since the status code is zero, we want to return success
  if @errors.count > 0 && exit_status.zero?
    UI.important("Although errors occurred during execution of iTMSTransporter, it returned success status.")
  end

  yield(@all_lines) if block_given?
  return exit_status.zero?
end

Private Instance Methods

additional_upload_parameters() click to toggle source
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 151
def additional_upload_parameters
  # As Apple recommends in Transporter User Guide we shouldn't specify the -t transport parameter
  # and instead allow Transporter to use automatic transport discovery
  # to determine the best transport mode for packages.
  # It became crucial after WWDC 2020 as it leaded to "Broken pipe (Write failed)" exception
  # More information https://github.com/fastlane/fastlane/issues/16749
  env_deliver_additional_params = ENV["DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS"]
  if env_deliver_additional_params.to_s.strip.empty?
    return nil
  end

  deliver_additional_params = env_deliver_additional_params.to_s.strip
  if deliver_additional_params.include?("-t ")
    UI.important("Apple recommends you don’t specify the -t transport and instead allow Transporter to use automatic transport discovery to determine the best transport mode for your packages. For more information, please read Apple's Transporter User Guide 2.1: https://help.apple.com/itc/transporteruserguide/#/apdATD1E1288-D1E1A1303-D1E1288A1126")
  end
  return deliver_additional_params
end
parse_line(line, hide_output) click to toggle source
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 103
def parse_line(line, hide_output)
  # Taken from https://github.com/sshaw/itunes_store_transporter/blob/master/lib/itunes/store/transporter/output_parser.rb

  output_done = false

  re = Regexp.union(SKIP_ERRORS)
  if line.match(re)
    # Those lines will not be handled like errors or warnings

  elsif line =~ ERROR_REGEX
    @errors << $1

    # Check if it's a login error
    if $1.include?("Your Apple ID or password was entered incorrectly") ||
       $1.include?("This Apple ID has been locked for security reasons")

      unless Helper.test?
        CredentialsManager::AccountManager.new(user: @user).invalid_credentials
        UI.error("Please run this tool again to apply the new password")
      end
    end

    output_done = true
  elsif line =~ WARNING_REGEX
    @warnings << $1
    UI.important("[Transporter Warning Output]: #{$1}")
    output_done = true
  end

  if line =~ RETURN_VALUE_REGEX
    if $1.to_i != 0
      UI.error("Transporter transfer failed.")
      UI.important(@warnings.join("\n"))
      UI.error(@errors.join("\n"))
      UI.crash!("Return status of iTunes Transporter was #{$1}: #{@errors.join('\n')}")
    else
      UI.success("iTunes Transporter successfully finished its job")
    end
  end

  if !hide_output && line =~ OUTPUT_REGEX
    # General logging for debug purposes
    unless output_done
      UI.verbose("[Transporter]: #{$1}")
    end
  end
end