Class: FastlaneCore::TransporterExecutor

Inherits:
Object
  • Object
show all
Defined in:
fastlane_core/lib/fastlane_core/itunes_transporter.rb

Overview

Base class for executing the iTMSTransporter

Constant Summary collapse

ITMS_PROVIDER_REGEX =

Matches a line in the iTMSTransporter provider table: “12 Initech Systems Inc LG89CQY559”

/^\d+\s{2,}.+\s{2,}[^\s]+$/

Instance Method Summary collapse

Instance Method Details

#build_credential_params(username = nil, password = nil, jwt = nil, api_key = nil) ⇒ String

Builds a string array of credentials parameters based on the provided authentication details.

Parameters:

  • username (String, nil) (defaults to: nil)

    The username for authentication (optional).

  • password (String, nil) (defaults to: nil)

    The password for authentication (optional).

  • jwt (String, nil) (defaults to: nil)

    A JSON Web Token for token-based authentication (optional).

  • api_key (Hash, nil) (defaults to: nil)

    An API key for authentication (optional).

Returns:

  • (String)

    A string containing the appropriate credentials for authentication.



63
64
65
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 63

def build_credential_params(username = nil, password = nil, jwt = nil, api_key = nil)
  not_implemented(__method__)
end

#build_download_command(username, password, apple_id, destination = "/tmp", provider_short_name = "", jwt = nil) ⇒ Object



39
40
41
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 39

def build_download_command(username, password, apple_id, destination = "/tmp", provider_short_name = "", jwt = nil)
  not_implemented(__method__)
end

#build_provider_ids_command(username, password, jwt = nil, api_key = nil) ⇒ Object



43
44
45
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 43

def build_provider_ids_command(username, password, jwt = nil, api_key = nil)
  not_implemented(__method__)
end

#build_upload_command(username, password, source = "/tmp", options = {}) ⇒ Object



47
48
49
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 47

def build_upload_command(username, password, source = "/tmp", options = {})
  not_implemented(__method__)
end

#build_verify_command(username, password, source = "/tmp", provider_short_name = "", **kwargs) ⇒ Object



51
52
53
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 51

def build_verify_command(username, password, source = "/tmp", provider_short_name = "", **kwargs)
  not_implemented(__method__)
end

#displayable_errorsObject



163
164
165
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 163

def displayable_errors
  @errors.map { |error| "[Transporter Error Output]: #{error}" }.join("\n").gsub!(/"/, "")
end

#execute(command, hide_output) {|@all_lines| ... } ⇒ Object

Yields:

  • (@all_lines)


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 99

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

#parse_provider_info(lines) ⇒ Object



167
168
169
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 167

def parse_provider_info(lines)
  lines.map { |line| itms_provider_pair(line) }.compact.to_h
end

#prepare(original_api_key:) ⇒ Hash

Runs preparations before executing any command from the executor.

Parameters:

  • original_api_key (Hash)

    api key containing the issuer id and private key

Returns:

  • (Hash)

    copy of ‘api_key` which includes an extra `key_dir` with the location of the .p8 file on disk



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 71

def prepare(original_api_key:)
  return if original_api_key.nil?
  # Create .p8 file from api_key and provide api key info which contains .p8 file path
  api_key = original_api_key.dup
  if self.kind_of?(ShellScriptTransporterExecutor)
    # as of Transporter v3.3.0, the app is unable to detect the private keys under the 'private_keys' folder in current directory
    # so we must rely on the other search paths in the Home dir:
    # https://help.apple.com/itc/transporteruserguide/en.lproj/static.html#itc803b7be80
    private_keys_dir = File.join(Dir.home, ".appstoreconnect/private_keys")
    unless Dir.exist?(private_keys_dir)
      FileUtils.mkdir_p(private_keys_dir)
    end
    api_key[:key_dir] = private_keys_dir
  else
    api_key[:key_dir] = Dir.mktmpdir("deliver-")
  end
  # Specified p8 needs to be generated to call altool or iTMSTransporter.
  # The key may be Base64 encoded (e.g. when passed via an ENV var); altool and
  # iTMSTransporter expect the raw .p8 contents, so decode it first. Spaceship
  # decodes on its own, but the transporter wrote the value verbatim before this,
  # producing an unparsable key and a "bearer token invalid" error.
  key_content = api_key[:is_key_content_base64] ? Base64.decode64(api_key[:key]) : api_key[:key]
  File.open(File.join(api_key[:key_dir], "AuthKey_#{api_key[:key_id]}.p8"), "wb") do |p8|
    p8.write(key_content)
  end
  api_key
end