Class: FastlaneCore::ItunesTransporter

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

Constant Summary collapse

PROVIDER_REGEX =

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

/^\d+\s{2,}.+\s{2,}[^\s]+$/
TWO_STEP_HOST_PREFIX =
"deliver.appspecific"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil) ⇒ ItunesTransporter

Returns a new instance of the iTunesTransporter. If no username or password given, it will be taken from the #CredentialsManager::AccountManager

Parameters:

  • use_shell_script (defaults to: false)

    if true, forces use of the iTMSTransporter shell script. if false, allows a direct call to the iTMSTransporter Java app (preferred). see: github.com/fastlane/fastlane/pull/4003

  • provider_short_name (defaults to: nil)

    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: github.com/fastlane/fastlane/issues/1524#issuecomment-196370628 for more information about how to use the iTMSTransporter to list your provider short names



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 479

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

Class Method Details

.hide_transporter_outputObject

This will be called from the Deliverfile, and disables the logging of the transporter output



459
460
461
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 459

def self.hide_transporter_output
  @hide_transporter_output = !FastlaneCore::Globals.verbose?
end

.hide_transporter_output?Boolean

Returns:



463
464
465
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 463

def self.hide_transporter_output?
  @hide_transporter_output
end

Instance Method Details

#displayable_errorsObject



633
634
635
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 633

def displayable_errors
  @transporter_executor.displayable_errors
end

#download(app_id, dir = nil) ⇒ Bool

Downloads the latest version of the app metadata package from iTC.

Parameters:

  • app_id (Integer)

    The unique App ID

  • dir (String) (defaults to: nil)

    the path in which the package file should be stored

Returns:

  • (Bool)

    True if everything worked fine

Raises:

  • (Deliver::TransporterTransferError)

    when something went wrong when transferring



503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 503

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

#provider_idsObject



637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 637

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

#upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil) ⇒ Bool

Uploads the modified package back to App Store Connect

Parameters:

  • app_id (Integer) (defaults to: nil)

    The unique App ID

  • dir (String) (defaults to: nil)

    the path in which the package file is located

  • package_path (String) (defaults to: nil)

    the path to the package file (used instead of app_id and dir)

  • asset_path (String) (defaults to: nil)

    the path to the ipa/dmg/pkg file (used instead of package_path if running on macOS)

Returns:

  • (Bool)

    True if everything worked fine

Raises:

  • (Deliver::TransporterTransferError)

    when something went wrong when transferring



542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 542

def upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil)
  raise "app_id and dir are required or package_path or asset_path is required" if (app_id.nil? || dir.nil?) && package_path.nil? && asset_path.nil?

  # Transport can upload .ipa, .dmg, and .pkg files directly with -assetFile
  # However, -assetFile requires -assetDescription if Linux or Windows
  # This will give the asset directly if macOS and asset_path exists
  # otherwise it will use the .itmsp package

  force_itmsp = FastlaneCore::Env.truthy?("ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD")
  can_use_asset_path = Helper.is_mac? && asset_path

  actual_dir = if can_use_asset_path && !force_itmsp
                 # The asset gets deleted upon completion so copying to a temp directory
                 # (with randomized filename, for multibyte-mixed filename upload fails)
                 new_file_name = "#{SecureRandom.uuid}#{File.extname(asset_path)}"
                 tmp_asset_path = File.join(Dir.tmpdir, new_file_name)
                 FileUtils.cp(asset_path, tmp_asset_path)
                 tmp_asset_path
               elsif package_path
                 package_path
               else
                 File.join(dir, "#{app_id}.itmsp")
               end

  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, asset_path: asset_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

#verify(app_id = nil, dir = nil, package_path: nil) ⇒ Bool

Verifies the given binary with App Store Connect

Parameters:

  • app_id (Integer) (defaults to: nil)

    The unique App ID

  • dir (String) (defaults to: nil)

    the path in which the package file is located

  • package_path (String) (defaults to: nil)

    the path to the package file (used instead of app_id and dir)

Returns:

  • (Bool)

    True if everything worked fine

Raises:

  • (Deliver::TransporterTransferError)

    when something went wrong when transferring



600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
# File 'fastlane_core/lib/fastlane_core/itunes_transporter.rb', line 600

def verify(app_id = nil, dir = nil, package_path: nil)
  raise "Either a combination of app id and directory or a package_path are required" if (app_id.nil? || dir.nil?) && package_path.nil?

  actual_dir = if package_path
                 package_path
               else
                 File.join(dir, "#{app_id}.itmsp")
               end

  password_placeholder = @jwt.nil? ? 'YourPassword' : nil
  jwt_placeholder = @jwt.nil? ? nil : 'YourJWT'

  command = @transporter_executor.build_verify_command(@user, @password, actual_dir, @provider_short_name, @jwt)
  UI.verbose(@transporter_executor.build_verify_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 verify(app_id, dir, package_path: package_path)
  end

  if result
    UI.header("Successfully verified package on App Store Connect")

    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