class FastlaneCore::ItunesTransporter
Constants
- PROVIDER_REGEX
Matches a line in the provider table: “12 Initech Systems Inc LG89CQY559”
- TWO_FACTOR_ENV_VARIABLE
- TWO_STEP_HOST_PREFIX
Public Class Methods
This will be called from the Deliverfile, and disables the logging of the transporter output
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 396 def self.hide_transporter_output @hide_transporter_output = !FastlaneCore::Globals.verbose? end
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 400 def self.hide_transporter_output? @hide_transporter_output end
Returns a new instance of the iTunesTransporter. If no username or password given, it will be taken from the #{CredentialsManager::AccountManager} @param use_shell_script if true, forces use of the iTMSTransporter shell script.
if false, allows a direct call to the iTMSTransporter Java app (preferred). see: https://github.com/fastlane/fastlane/pull/4003
@param provider_short_name The provider short name to be given to the iTMSTransporter to identify the
correct team for this work. The provider short name is usually your Developer Portal team ID, but in certain cases it is different! see: https://github.com/fastlane/fastlane/issues/1524#issuecomment-196370628 for more information about how to use the iTMSTransporter to list your provider short names
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 416 def initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil) # Xcode 6.x doesn't have the same iTMSTransporter Java setup as later Xcode versions, so # we can't default to using the newer direct Java invocation strategy for those versions. use_shell_script ||= Helper.is_mac? && Helper.xcode_version.start_with?('6.') use_shell_script ||= Helper.windows? use_shell_script ||= Feature.enabled?('FASTLANE_ITUNES_TRANSPORTER_USE_SHELL_SCRIPT') if jwt.to_s.empty? @user = user @password = password || load_password_for_transporter end @jwt = jwt @transporter_executor = use_shell_script ? ShellScriptTransporterExecutor.new : JavaTransporterExecutor.new @provider_short_name = provider_short_name end
Public Instance Methods
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 509 def displayable_errors @transporter_executor.displayable_errors end
Downloads the latest version of the app metadata package from iTC. @param app_id [Integer] The unique App ID @param dir [String] the path in which the package file should be stored @return (Bool) True if everything worked fine @raise [Deliver::TransporterTransferError] when something went wrong
when transferring
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 440 def download(app_id, dir = nil) dir ||= "/tmp" password_placeholder = @jwt.nil? ? 'YourPassword' : nil jwt_placeholder = @jwt.nil? ? nil : 'YourJWT' UI.message("Going to download app metadata from App Store Connect") command = @transporter_executor.build_download_command(@user, @password, app_id, dir, @provider_short_name, @jwt) UI.verbose(@transporter_executor.build_download_command(@user, password_placeholder, app_id, dir, @provider_short_name, jwt_placeholder)) begin result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?) rescue TransporterRequiresApplicationSpecificPasswordError => ex handle_two_step_failure(ex) return download(app_id, dir) end return result if Helper.test? itmsp_path = File.join(dir, "#{app_id}.itmsp") successful = result && File.directory?(itmsp_path) if successful UI.success("✅ Successfully downloaded the latest package from App Store Connect to #{itmsp_path}") else handle_error(@password) end successful end
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 513 def provider_ids password_placeholder = @jwt.nil? ? 'YourPassword' : nil jwt_placeholder = @jwt.nil? ? nil : 'YourJWT' command = @transporter_executor.build_provider_ids_command(@user, @password, @jwt) UI.verbose(@transporter_executor.build_provider_ids_command(@user, password_placeholder, jwt_placeholder)) lines = [] begin result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?) { |xs| lines = xs } return result if Helper.test? rescue TransporterRequiresApplicationSpecificPasswordError => ex handle_two_step_failure(ex) return provider_ids end lines.map { |line| provider_pair(line) }.compact.to_h end
Uploads the modified package back to App Store Connect @param app_id [Integer] The unique App ID @param dir [String] the path in which the package file is located @param package_path [String] the path to the package file (used instead of app_id and dir) @return (Bool) True if everything worked fine @raise [Deliver::TransporterTransferError] when something went wrong
when transferring
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 478 def upload(app_id = nil, dir = nil, package_path: nil) raise "app_id and dir are required or package_path is required" if (app_id.nil? || dir.nil?) && package_path.nil? actual_dir = package_path || File.join(dir, "#{app_id}.itmsp") UI.message("Going to upload updated app to App Store Connect") UI.success("This might take a few minutes. Please don't interrupt the script.") password_placeholder = @jwt.nil? ? 'YourPassword' : nil jwt_placeholder = @jwt.nil? ? nil : 'YourJWT' command = @transporter_executor.build_upload_command(@user, @password, actual_dir, @provider_short_name, @jwt) UI.verbose(@transporter_executor.build_upload_command(@user, password_placeholder, actual_dir, @provider_short_name, jwt_placeholder)) begin result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?) rescue TransporterRequiresApplicationSpecificPasswordError => ex handle_two_step_failure(ex) return upload(app_id, dir, package_path: package_path) end if result UI.header("Successfully uploaded package to App Store Connect. It might take a few minutes until it's visible online.") FileUtils.rm_rf(actual_dir) unless Helper.test? # we don't need the package any more, since the upload was successful else handle_error(@password) end return result end
Private Instance Methods
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 590 def handle_error(password) @transporter_executor.handle_error(password) end
Tells the user how to get an application specific password
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 556 def handle_two_step_failure(ex) if ENV[TWO_FACTOR_ENV_VARIABLE].to_s.length > 0 # Password provided, however we already used it UI.error("") UI.error("Application specific password you provided using") UI.error("environment variable #{TWO_FACTOR_ENV_VARIABLE}") UI.error("is invalid, please make sure it's correct") UI.error("") UI.user_error!("Invalid application specific password provided") end a = CredentialsManager::AccountManager.new(user: @user, prefix: TWO_STEP_HOST_PREFIX, note: "application-specific") if a.password(ask_if_missing: false).to_s.length > 0 # user already entered one.. delete the old one UI.error("Application specific password seems wrong") UI.error("Please make sure to follow the instructions") a.remove_from_keychain end UI.error("") UI.error("Your account has 2 step verification enabled") UI.error("Please go to https://appleid.apple.com/account/manage") UI.error("and generate an application specific password for") UI.error("the iTunes Transporter, which is used to upload builds") UI.error("") UI.error("To set the application specific password on a CI machine using") UI.error("an environment variable, you can set the") UI.error("#{TWO_FACTOR_ENV_VARIABLE} variable") @password = a.password(ask_if_missing: true) # to ask the user for the missing value return true end
Returns the password to be used with the transporter
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 537 def load_password_for_transporter # 3 different sources for the password # 1) ENV variable for application specific password if ENV[TWO_FACTOR_ENV_VARIABLE].to_s.length > 0 UI.message("Fetching password for transporter from environment variable named `#{TWO_FACTOR_ENV_VARIABLE}`") return ENV[TWO_FACTOR_ENV_VARIABLE] end # 2) TWO_STEP_HOST_PREFIX from keychain account_manager = CredentialsManager::AccountManager.new(user: @user, prefix: TWO_STEP_HOST_PREFIX, note: "application-specific") password = account_manager.password(ask_if_missing: false) return password if password.to_s.length > 0 # 3) standard iTC password account_manager = CredentialsManager::AccountManager.new(user: @user) return account_manager.password(ask_if_missing: true) end
# File fastlane_core/lib/fastlane_core/itunes_transporter.rb, line 594 def provider_pair(line) line = line.strip return nil unless line =~ PROVIDER_REGEX line.split(/\s{2,}/).drop(1) end