Class: Mysigner::Upload::Uploader

Inherits:
Object
  • Object
show all
Defined in:
lib/mysigner/upload/uploader.rb

Defined Under Namespace

Classes: AuthenticationError, TransporterNotFoundError, UploadError

Constant Summary collapse

TRANSPORTER_PATHS =
[
  '/Applications/Xcode.app/Contents/Developer/usr/bin/iTMSTransporter', # Bundled with Xcode (primary)
  '/Applications/Transporter.app/Contents/itms/bin/iTMSTransporter',    # Standalone Transporter app
  '/usr/local/itms/bin/iTMSTransporter' # Custom installation
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ipa_path, api_key:, api_issuer:, private_key:) ⇒ Uploader

Returns a new instance of Uploader.



45
46
47
48
49
50
51
52
53
54
# File 'lib/mysigner/upload/uploader.rb', line 45

def initialize(ipa_path, api_key:, api_issuer:, private_key:)
  @ipa_path = File.expand_path(ipa_path)
  @api_key = api_key
  @api_issuer = api_issuer
  @private_key = private_key

  validate_ipa!
  detect_transporter!
  setup_private_key!
end

Class Method Details

.extract_ipa_info(ipa_path) ⇒ Object

Parses CFBundleVersion + CFBundleShortVersionString from an .ipa (reads Info.plist from the Payload/*.app/ inside the zip). Used by the new ASC REST upload flow in build_commands.rb.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/mysigner/upload/uploader.rb', line 24

def self.extract_ipa_info(ipa_path)
  require 'open3'
  result = { cf_bundle_version: nil, cf_bundle_short_version_string: nil, bundle_id: nil }
  Dir.mktmpdir('mysigner-ipa-inspect-') do |tmp|
    plist_path = File.join(tmp, 'Info.plist')
    zip_out, status = Open3.capture2e('unzip', '-p', ipa_path, 'Payload/*.app/Info.plist')
    return result unless status.success? && !zip_out.empty?

    File.binwrite(plist_path, zip_out)
    xml_out, xml_status = Open3.capture2e('plutil', '-convert', 'xml1', '-o', '-', plist_path)
    return result unless xml_status.success?

    result[:cf_bundle_version] = xml_out[%r{<key>CFBundleVersion</key>\s*<string>([^<]+)</string>}, 1]
    result[:cf_bundle_short_version_string] = xml_out[%r{<key>CFBundleShortVersionString</key>\s*<string>([^<]+)</string>}, 1]
    result[:bundle_id] = xml_out[%r{<key>CFBundleIdentifier</key>\s*<string>([^<]+)</string>}, 1]
  end
  result
rescue StandardError
  { cf_bundle_version: nil, cf_bundle_short_version_string: nil, bundle_id: nil }
end

Instance Method Details

#upload!(wait_for_processing: false) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/mysigner/upload/uploader.rb', line 56

def upload!(wait_for_processing: false)
  say_uploading

  begin
    # Validate IPA first
    validate_result = validate_ipa
    raise UploadError, "IPA validation failed: #{validate_result[:error]}" unless validate_result[:success]

    # Upload to App Store Connect
    upload_result = upload_ipa
    raise UploadError, "Upload failed: #{upload_result[:error]}" unless upload_result[:success]

    say_success

    # Optionally wait for processing
    if wait_for_processing
      say_waiting_for_processing
      # NOTE: Build processing status is polled via the dashboard's sync API
      # in build_commands.rb, not directly here. This flag is reserved for
      # future use or manual uploads outside the `ship` command flow.
    end

    {
      success: true,
      message: 'Upload completed successfully'
    }
  rescue StandardError => e
    raise UploadError, "Upload failed: #{e.message}"
  ensure
    cleanup_private_key!
  end
end