fastlane 2.158.0 → 2.163.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +74 -74
  3. data/cert/lib/cert/options.rb +28 -1
  4. data/cert/lib/cert/runner.rb +50 -33
  5. data/deliver/lib/deliver/app_screenshot_iterator.rb +4 -4
  6. data/deliver/lib/deliver/module.rb +2 -0
  7. data/deliver/lib/deliver/options.rb +20 -4
  8. data/deliver/lib/deliver/runner.rb +36 -8
  9. data/deliver/lib/deliver/upload_metadata.rb +49 -9
  10. data/deliver/lib/deliver/upload_price_tier.rb +7 -2
  11. data/deliver/lib/deliver/upload_screenshots.rb +25 -8
  12. data/{pilot/lib/pilot/.manager.rb.swp → fastlane/lib/fastlane/.erb_template_helper.rb.swp} +0 -0
  13. data/fastlane/lib/fastlane/actions/{.slack.rb.swp → .git_commit.rb.swp} +0 -0
  14. data/fastlane/lib/fastlane/actions/.register_device.rb.swp +0 -0
  15. data/fastlane/lib/fastlane/actions/.register_devices.rb.swp +0 -0
  16. data/fastlane/lib/fastlane/actions/actions_helper.rb +20 -1
  17. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +39 -3
  18. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +15 -1
  19. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +1 -0
  20. data/fastlane/lib/fastlane/actions/clean_build_artifacts.rb +1 -0
  21. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +2 -2
  22. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +1 -1
  23. data/fastlane/lib/fastlane/actions/docs/create_app_online.md +1 -1
  24. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +3 -2
  25. data/fastlane/lib/fastlane/actions/docs/run_tests.md +2 -2
  26. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +3 -3
  27. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -2
  28. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +2 -2
  29. data/fastlane/lib/fastlane/actions/download_dsyms.rb +32 -7
  30. data/fastlane/lib/fastlane/actions/ensure_git_status_clean.rb +13 -2
  31. data/fastlane/lib/fastlane/actions/get_certificates.rb +1 -0
  32. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +1 -0
  33. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +15 -0
  34. data/fastlane/lib/fastlane/actions/register_device.rb +46 -5
  35. data/fastlane/lib/fastlane/actions/register_devices.rb +46 -15
  36. data/fastlane/lib/fastlane/actions/sync_code_signing.rb +1 -0
  37. data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +3 -2
  38. data/fastlane/lib/fastlane/helper/git_helper.rb +2 -0
  39. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +6 -4
  40. data/fastlane/lib/fastlane/swift_fastlane_function.rb +1 -1
  41. data/fastlane/lib/fastlane/version.rb +1 -1
  42. data/fastlane/swift/Actions.swift +2 -1
  43. data/fastlane/swift/Appfile.swift +2 -4
  44. data/fastlane/swift/ArgumentProcessor.swift +2 -6
  45. data/fastlane/swift/ControlCommand.swift +2 -5
  46. data/fastlane/swift/Deliverfile.swift +5 -2
  47. data/fastlane/swift/DeliverfileProtocol.swift +18 -7
  48. data/fastlane/swift/Fastfile.swift +5 -1
  49. data/fastlane/swift/Fastlane.swift +2368 -2251
  50. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj +5 -5
  51. data/fastlane/swift/Gymfile.swift +5 -2
  52. data/fastlane/swift/GymfileProtocol.swift +6 -3
  53. data/fastlane/swift/LaneFileProtocol.swift +49 -40
  54. data/fastlane/swift/MainProcess.swift +77 -0
  55. data/fastlane/swift/Matchfile.swift +5 -2
  56. data/fastlane/swift/MatchfileProtocol.swift +21 -6
  57. data/fastlane/swift/Plugins.swift +2 -1
  58. data/fastlane/swift/Precheckfile.swift +5 -2
  59. data/fastlane/swift/PrecheckfileProtocol.swift +14 -3
  60. data/fastlane/swift/RubyCommand.swift +2 -6
  61. data/fastlane/swift/RubyCommandable.swift +2 -6
  62. data/fastlane/swift/Runner.swift +5 -9
  63. data/fastlane/swift/RunnerArgument.swift +2 -6
  64. data/fastlane/swift/Scanfile.swift +5 -2
  65. data/fastlane/swift/ScanfileProtocol.swift +6 -3
  66. data/fastlane/swift/Screengrabfile.swift +5 -2
  67. data/fastlane/swift/ScreengrabfileProtocol.swift +6 -3
  68. data/fastlane/swift/Snapshotfile.swift +5 -2
  69. data/fastlane/swift/SnapshotfileProtocol.swift +6 -3
  70. data/fastlane/swift/SocketClient.swift +3 -7
  71. data/fastlane/swift/SocketClientDelegateProtocol.swift +2 -6
  72. data/fastlane/swift/SocketResponse.swift +2 -6
  73. data/fastlane/swift/formatting/Brewfile.lock.json +18 -10
  74. data/fastlane/swift/main.swift +4 -8
  75. data/fastlane/swift/upgrade_manifest.json +1 -1
  76. data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +6 -7
  77. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +2 -2
  78. data/frameit/lib/frameit/editor.rb +1 -0
  79. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +2 -2
  80. data/match/lib/match/generator.rb +6 -1
  81. data/match/lib/match/importer.rb +44 -8
  82. data/match/lib/match/migrate.rb +13 -2
  83. data/match/lib/match/nuke.rb +65 -22
  84. data/match/lib/match/options.rb +27 -2
  85. data/match/lib/match/runner.rb +38 -10
  86. data/match/lib/match/spaceship_ensure.rb +27 -21
  87. data/match/lib/match/storage/google_cloud_storage.rb +20 -3
  88. data/match/lib/match/storage/s3_storage.rb +19 -3
  89. data/pilot/lib/pilot/options.rb +2 -2
  90. data/precheck/lib/precheck/options.rb +16 -0
  91. data/precheck/lib/precheck/runner.rb +20 -1
  92. data/sigh/lib/sigh/download_all.rb +16 -4
  93. data/sigh/lib/sigh/options.rb +21 -0
  94. data/sigh/lib/sigh/runner.rb +80 -38
  95. data/snapshot/lib/assets/SnapshotHelper.swift +21 -2
  96. data/spaceship/README.md +1 -1
  97. data/spaceship/lib/spaceship/{connect_api/.DS_Store → .DS_Store} +0 -0
  98. data/spaceship/lib/spaceship/client.rb +9 -4
  99. data/spaceship/lib/spaceship/connect_api.rb +25 -0
  100. data/spaceship/lib/spaceship/connect_api/api_client.rb +12 -3
  101. data/spaceship/lib/spaceship/connect_api/client.rb +38 -15
  102. data/{fastlane/lib/fastlane/actions/.update_project_provisioning.rb.swp → spaceship/lib/spaceship/connect_api/models/.app_store_version_submission.rb.swp} +0 -0
  103. data/spaceship/lib/spaceship/connect_api/models/app.rb +17 -9
  104. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +1 -0
  105. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +3 -1
  106. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +2 -2
  107. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +3 -5
  108. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +3 -5
  109. data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +2 -1
  110. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +42 -0
  111. data/spaceship/lib/spaceship/connect_api/models/device.rb +5 -0
  112. data/spaceship/lib/spaceship/connect_api/models/profile.rb +7 -1
  113. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +59 -0
  114. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +45 -2
  115. data/spaceship/lib/spaceship/connect_api/spaceship.rb +3 -2
  116. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
  117. data/spaceship/lib/spaceship/connect_api/token.rb +6 -1
  118. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +17 -9
  119. data/spaceship/lib/spaceship/connect_api/users/users.rb +40 -0
  120. data/spaceship/lib/spaceship/helper/net_http_generic_request.rb +11 -5
  121. metadata +27 -25
  122. data/fastlane/lib/fastlane/actions/.hockey.rb.swp +0 -0
  123. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/josh.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
@@ -52,10 +52,10 @@ module Deliver
52
52
  end
53
53
  end
54
54
 
55
- # Iterate given local app_screenshot over localizations and app_screenshot_sets with index within each app_screenshot_set
55
+ # Iterate given local app_screenshot over localizations and app_screenshot_sets
56
56
  #
57
57
  # @param screenshots_per_language [Hash<String, Array<Deliver::AppScreenshot>]
58
- # @yield [localization, app_screenshot_set, app_screenshot, index]
58
+ # @yield [localization, app_screenshot_set, app_screenshot]
59
59
  # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreVersionLocalization] localization
60
60
  # @yieldparam [optional, Spaceship::ConnectAPI::AppStoreScreenshotSet] app_screenshot_set
61
61
  # @yieldparam [optional, Deliver::AppScreenshot] screenshot
@@ -85,8 +85,8 @@ module Deliver
85
85
  app_screenshot_set ||= localization.create_app_screenshot_set(attributes: { screenshotDisplayType: display_type })
86
86
 
87
87
  # iterate over screenshots per display size with index
88
- screenshots.each.with_index do |screenshot, index|
89
- yield(localization, app_screenshot_set, screenshot, index)
88
+ screenshots.each do |screenshot|
89
+ yield(localization, app_screenshot_set, screenshot)
90
90
  end
91
91
  end
92
92
  end
@@ -1,5 +1,6 @@
1
1
  require 'fastlane_core/helper'
2
2
  require 'fastlane_core/ui/ui'
3
+ require 'fastlane/boolean'
3
4
 
4
5
  module Deliver
5
6
  class << self
@@ -7,6 +8,7 @@ module Deliver
7
8
 
8
9
  Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
9
10
  UI = FastlaneCore::UI
11
+ Boolean = Fastlane::Boolean
10
12
 
11
13
  # Constant that captures the root Pathname for the project. Should be used for building paths to assets or other
12
14
  # resources that code needs to locate locally
@@ -12,6 +12,22 @@ module Deliver
12
12
  user ||= ENV["DELIVER_USER"]
13
13
 
14
14
  [
15
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
16
+ env_name: "DELIVER_API_KEY_PATH",
17
+ description: "Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)",
18
+ optional: true,
19
+ conflicting_options: [:api_key],
20
+ verify_block: proc do |value|
21
+ UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
22
+ end),
23
+ FastlaneCore::ConfigItem.new(key: :api_key,
24
+ env_name: "DELIVER_API_KEY",
25
+ description: "Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#use-return-value-and-pass-in-as-an-option)",
26
+ type: Hash,
27
+ optional: true,
28
+ sensitive: true,
29
+ conflicting_options: [:api_key_path]),
30
+
15
31
  FastlaneCore::ConfigItem.new(key: :username,
16
32
  short_option: "-u",
17
33
  env_name: "DELIVER_USERNAME",
@@ -129,7 +145,7 @@ module Deliver
129
145
  default_value: false),
130
146
  FastlaneCore::ConfigItem.new(key: :skip_app_version_update,
131
147
  env_name: "DELIVER_SKIP_APP_VERSION_UPDATE",
132
- description: "Don't update app version for submission",
148
+ description: "Dont create or update the app version that is being prepared for submission",
133
149
  is_string: false,
134
150
  default_value: false),
135
151
 
@@ -137,7 +153,7 @@ module Deliver
137
153
  FastlaneCore::ConfigItem.new(key: :force,
138
154
  short_option: "-f",
139
155
  env_name: "DELIVER_FORCE",
140
- description: "Skip the HTML report file verification",
156
+ description: "Skip verification of HTML preview file",
141
157
  is_string: false,
142
158
  default_value: false),
143
159
  FastlaneCore::ConfigItem.new(key: :overwrite_screenshots,
@@ -160,8 +176,8 @@ module Deliver
160
176
  FastlaneCore::ConfigItem.new(key: :automatic_release,
161
177
  env_name: "DELIVER_AUTOMATIC_RELEASE",
162
178
  description: "Should the app be automatically released once it's approved? (Can not be used together with `auto_release_date`)",
163
- is_string: false,
164
- default_value: false),
179
+ type: Boolean,
180
+ optional: true),
165
181
  FastlaneCore::ConfigItem.new(key: :auto_release_date,
166
182
  env_name: "DELIVER_AUTO_RELEASE_DATE",
167
183
  description: "Date in milliseconds for automatically releasing on pending approval (Can not be used together with `automatic_release`)",
@@ -26,11 +26,22 @@ module Deliver
26
26
  end
27
27
 
28
28
  def login
29
- # Team selection passed though FASTLANE_TEAM_ID and FASTLANE_TEAM_NAME environment variables
30
- # Prompts select team if multiple teams and none specified
31
- UI.message("Login to App Store Connect (#{options[:username]})")
32
- Spaceship::ConnectAPI.login(options[:username], nil, use_portal: false, use_tunes: true)
33
- UI.message("Login successful")
29
+ if api_token
30
+ UI.message("Creating authorization token for App Store Connect API")
31
+ Spaceship::ConnectAPI.token = api_token
32
+ else
33
+ # Team selection passed though FASTLANE_TEAM_ID and FASTLANE_TEAM_NAME environment variables
34
+ # Prompts select team if multiple teams and none specified
35
+ UI.message("Login to App Store Connect (#{options[:username]})")
36
+ Spaceship::ConnectAPI.login(options[:username], nil, use_portal: false, use_tunes: true)
37
+ UI.message("Login successful")
38
+ end
39
+ end
40
+
41
+ def api_token
42
+ @api_token ||= Spaceship::ConnectAPI::Token.create(options[:api_key]) if options[:api_key]
43
+ @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(options[:api_key_path]) if options[:api_key_path]
44
+ return @api_token
34
45
  end
35
46
 
36
47
  def run
@@ -67,11 +78,21 @@ module Deliver
67
78
  precheck_options = {
68
79
  default_rule_level: options[:precheck_default_rule_level],
69
80
  include_in_app_purchases: options[:precheck_include_in_app_purchases],
70
- app_identifier: options[:app_identifier],
71
- username: options[:username],
72
- platform: options[:platform]
81
+ app_identifier: options[:app_identifier]
73
82
  }
74
83
 
84
+ if options[:api_key] || options[:api_key_path]
85
+ if options[:precheck_include_in_app_purchases]
86
+ UI.user_error!("Precheck cannot check In-app purchases with the App Store Connect API Key (yet). Exclude In-app purchases from precheck or use Apple ID login")
87
+ end
88
+
89
+ precheck_options[:api_key] = options[:api_key]
90
+ precheck_options[:api_key_path] = options[:api_key_path]
91
+ else
92
+ precheck_options[:username] = options[:username]
93
+ precheck_options[:platform] = options[:platform]
94
+ end
95
+
75
96
  precheck_config = FastlaneCore::Configuration.create(Precheck::Options.available_options, precheck_options)
76
97
  Precheck.config = precheck_config
77
98
 
@@ -175,10 +196,17 @@ module Deliver
175
196
 
176
197
  private
177
198
 
199
+ # If App Store Connect API token, use token.
178
200
  # If itc_provider was explicitly specified, use it.
179
201
  # If there are multiple teams, infer the provider from the selected team name.
180
202
  # If there are fewer than two teams, don't infer the provider.
181
203
  def transporter_for_selected_team
204
+ # Use JWT auth
205
+ unless api_token.nil?
206
+ api_token.refresh! if api_token.expired?
207
+ return FastlaneCore::ItunesTransporter.new(nil, nil, false, nil, api_token.text)
208
+ end
209
+
182
210
  tunes_client = Spaceship::ConnectAPI.client.tunes_client
183
211
 
184
212
  generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider])
@@ -96,7 +96,7 @@ module Deliver
96
96
 
97
97
  if v.nil?
98
98
  UI.message("Couldn't find live version, editing the current version on App Store Connect instead")
99
- version = app.get_edit_app_store_version(platform: platform)
99
+ version = fetch_edit_app_store_version(app, platform)
100
100
  # we don't want to update the localised_options and non_localised_options
101
101
  # as we also check for `options[:edit_live]` at other areas in the code
102
102
  # by not touching those 2 variables, deliver is more consistent with what the option says
@@ -105,13 +105,17 @@ module Deliver
105
105
  UI.message("Found live version")
106
106
  end
107
107
  else
108
- version = app.get_edit_app_store_version(platform: platform)
108
+ version = fetch_edit_app_store_version(app, platform)
109
109
  localised_options = (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys)
110
110
  non_localised_options = NON_LOCALISED_VERSION_VALUES.keys
111
111
  end
112
112
 
113
113
  # Needed for to filter out release notes from being sent up
114
- number_of_versions = app.get_app_store_versions(filter: { platform: platform }, limit: 2).size
114
+ number_of_versions = Spaceship::ConnectAPI.get_app_store_versions(
115
+ app_id: app.id,
116
+ filter: { platform: platform },
117
+ limit: 2
118
+ ).count
115
119
  is_first_version = number_of_versions == 1
116
120
  UI.verbose("Version '#{version.version_string}' is the first version on App Store Connect") if is_first_version
117
121
 
@@ -171,17 +175,26 @@ module Deliver
171
175
 
172
176
  non_localized_version_attributes['earliestReleaseDate'] = date
173
177
  Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::SCHEDULED
174
- elsif options[:automatic_release]
178
+ elsif options[:automatic_release] == true
175
179
  Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::AFTER_APPROVAL
176
- else
180
+ elsif options[:automatic_release] == false
177
181
  Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::MANUAL
178
182
  end
179
- non_localized_version_attributes['releaseType'] = release_type
183
+ if release_type.nil?
184
+ UI.important("Release type will not be set because neither `automatic_release` nor `auto_release_date` were provided. Please explicitly set one of these options if you need a release type set")
185
+ else
186
+ non_localized_version_attributes['releaseType'] = release_type
187
+ end
180
188
 
181
189
  # Update app store version
182
190
  # This needs to happen before updating localizations (https://openradar.appspot.com/radar?id=4925914991296512)
191
+ #
192
+ # Adding some sleeps because the API will sometimes be in a state where releaseType can't be modified
193
+ # https://github.com/fastlane/fastlane/issues/16911
183
194
  UI.message("Uploading metadata to App Store Connect for version")
195
+ sleep(2)
184
196
  version.update(attributes: non_localized_version_attributes)
197
+ sleep(1)
185
198
 
186
199
  # Update app store version localizations
187
200
  app_store_version_localizations.each do |app_store_version_localization|
@@ -202,7 +215,7 @@ module Deliver
202
215
  end
203
216
 
204
217
  # Update categories
205
- app_info = app.fetch_edit_app_info
218
+ app_info = fetch_edit_app_info(app)
206
219
  if app_info
207
220
  category_id_map = {}
208
221
 
@@ -410,9 +423,35 @@ module Deliver
410
423
  .uniq
411
424
  end
412
425
 
426
+ def fetch_edit_app_store_version(app, platform, wait_time: 10)
427
+ retry_if_nil("Cannot find edit app store version", wait_time: wait_time) do
428
+ app.get_edit_app_store_version(platform: platform)
429
+ end
430
+ end
431
+
432
+ def fetch_edit_app_info(app, wait_time: 10)
433
+ retry_if_nil("Cannot find edit app info", wait_time: wait_time) do
434
+ app.fetch_edit_app_info
435
+ end
436
+ end
437
+
438
+ def retry_if_nil(message, tries: 5, wait_time: 10)
439
+ loop do
440
+ tries -= 1
441
+
442
+ value = yield
443
+ return value if value
444
+
445
+ UI.message("#{message}... Retrying after #{wait_time} seconds (remaining: #{tries})")
446
+ sleep(wait_time)
447
+
448
+ return nil if tries.zero?
449
+ end
450
+ end
451
+
413
452
  # Finding languages to enable
414
453
  def verify_available_info_languages!(options, app, languages)
415
- app_info = app.fetch_edit_app_info
454
+ app_info = fetch_edit_app_info(app)
416
455
 
417
456
  unless app_info
418
457
  UI.user_error!("Cannot update languages - could not find an editable info")
@@ -447,7 +486,7 @@ module Deliver
447
486
  # Finding languages to enable
448
487
  def verify_available_version_languages!(options, app, languages)
449
488
  platform = Spaceship::ConnectAPI::Platform.map(options[:platform])
450
- version = app.get_edit_app_store_version(platform: platform)
489
+ version = fetch_edit_app_store_version(app, platform)
451
490
 
452
491
  unless version
453
492
  UI.user_error!("Cannot update languages - could not find an editable version for '#{platform}'")
@@ -554,6 +593,7 @@ module Deliver
554
593
  def set_review_information(version, options)
555
594
  return unless options[:app_review_information]
556
595
  info = options[:app_review_information]
596
+ info = info.collect { |k, v| [k.to_sym, v] }.to_h
557
597
  UI.user_error!("`app_review_information` must be a hash", show_github_issues: true) unless info.kind_of?(Hash)
558
598
 
559
599
  attributes = {}
@@ -14,9 +14,14 @@ module Deliver
14
14
  attributes = {}
15
15
  territory_ids = []
16
16
 
17
- app_prices = app.fetch_app_prices
17
+ # As of 2020-09-14:
18
+ # Official App Store Connect does not have an endpoint to get app prices for an app
19
+ # Need to get prices from the app's relationships
20
+ # Prices from app's relationship doess not have price tier so need to fetch app price with price tier relationship
21
+ app_prices = app.prices
18
22
  if app_prices.first
19
- old_price = app_prices.first.price_tier.id
23
+ app_price = Spaceship::ConnectAPI.get_app_price(app_price_id: app_prices.first.id, includes: "priceTier").first
24
+ old_price = app_price.price_tier.id
20
25
  else
21
26
  UI.message("App has no prices yet... Enabling all countries in App Store Connect")
22
27
  territory_ids = Spaceship::ConnectAPI::Territory.all.map(&:id)
@@ -126,10 +126,16 @@ module Deliver
126
126
  end
127
127
  end
128
128
 
129
- number_of_screenshots = 0
129
+ # Each app_screenshot_set can have only 10 images
130
+ number_of_screenshots_per_set = {}
131
+ total_number_of_screenshots = 0
132
+
130
133
  iterator = AppScreenshotIterator.new(localizations)
131
- iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot, index|
132
- if index >= 10
134
+ iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot|
135
+ # Initialize counter on each app screenshot set
136
+ number_of_screenshots_per_set[app_screenshot_set] ||= (app_screenshot_set.app_screenshots || []).count
137
+
138
+ if number_of_screenshots_per_set[app_screenshot_set] >= 10
133
139
  UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})")
134
140
  next
135
141
  end
@@ -141,10 +147,12 @@ module Deliver
141
147
  if duplicate
142
148
  UI.message("Previous uploaded. Skipping '#{screenshot.path}'...")
143
149
  else
150
+ UI.verbose("Queued uplaod sceeenshot job for #{localization.locale} #{app_screenshot_set.screenshot_display_type} #{screenshot.path}")
144
151
  worker.enqueue(UploadScreenshotJob.new(app_screenshot_set, screenshot.path))
152
+ number_of_screenshots_per_set[app_screenshot_set] += 1
145
153
  end
146
154
 
147
- number_of_screenshots += 1
155
+ total_number_of_screenshots += 1
148
156
  end
149
157
 
150
158
  worker.start
@@ -154,7 +162,7 @@ module Deliver
154
162
  Helper.show_loading_indicator("Waiting for all the screenshots processed...")
155
163
  states = wait_for_complete(iterator)
156
164
  Helper.hide_loading_indicator
157
- retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, localizations, screenshots_per_language)
165
+ retry_upload_screenshots_if_needed(iterator, states, total_number_of_screenshots, tries, localizations, screenshots_per_language)
158
166
 
159
167
  UI.message("Successfully uploaded all screenshots")
160
168
  end
@@ -203,12 +211,21 @@ module Deliver
203
211
  # Check if local screenshots' checksum exist on App Store Connect
204
212
  checksum_to_app_screenshot = iterator.each_app_screenshot.map { |_, _, app_screenshot| [app_screenshot.source_file_checksum, app_screenshot] }.to_h
205
213
 
206
- missing_local_screenshots = iterator.each_local_screenshot(screenshots_per_language).select do |_, _, local_screenshot, index|
214
+ number_of_screenshots_per_set = {}
215
+ missing_local_screenshots = iterator.each_local_screenshot(screenshots_per_language).select do |_, app_screenshot_set, local_screenshot|
216
+ number_of_screenshots_per_set[app_screenshot_set] ||= (app_screenshot_set.app_screenshots || []).count
207
217
  checksum = UploadScreenshots.calculate_checksum(local_screenshot.path)
208
- checksum_to_app_screenshot[checksum].nil? && index < 10 # if index is more than 10, it's skipped
218
+
219
+ if checksum_to_app_screenshot[checksum]
220
+ next(false)
221
+ else
222
+ is_missing = number_of_screenshots_per_set[app_screenshot_set] < 10 # if it's more than 10, it's skipped
223
+ number_of_screenshots_per_set[app_screenshot_set] += 1
224
+ next(is_missing)
225
+ end
209
226
  end
210
227
 
211
- missing_local_screenshots.each do |_, _, screenshot, _|
228
+ missing_local_screenshots.each do |_, _, screenshot|
212
229
  UI.error("#{screenshot.path} is missing on App Store Connect.")
213
230
  end
214
231
 
@@ -4,6 +4,25 @@ module Fastlane
4
4
  LANE_NAME = :LANE_NAME
5
5
  PLATFORM_NAME = :PLATFORM_NAME
6
6
  ENVIRONMENT = :ENVIRONMENT
7
+
8
+ # A slighly decorated hash that will store and fetch sensitive data
9
+ # but not display it while iterating keys and values
10
+ class LaneContextValues < Hash
11
+ def initialize
12
+ @sensitive_context = {}
13
+ end
14
+
15
+ def set_sensitive(key, value)
16
+ @sensitive_context[key] = value
17
+ end
18
+
19
+ def [](key)
20
+ if @sensitive_context.key?(key)
21
+ return @sensitive_context[key]
22
+ end
23
+ super
24
+ end
25
+ end
7
26
  end
8
27
 
9
28
  def self.reset_aliases
@@ -27,7 +46,7 @@ module Fastlane
27
46
 
28
47
  # The shared hash can be accessed by any action and contains information like the screenshots path or beta URL
29
48
  def self.lane_context
30
- @lane_context ||= {}
49
+ @lane_context ||= SharedValues::LaneContextValues.new
31
50
  end
32
51
 
33
52
  # Used in tests to get a clear lane before every test
@@ -27,9 +27,15 @@ module Fastlane
27
27
 
28
28
  def self.get_build_number(params)
29
29
  # Prompts select team if multiple teams and none specified
30
- UI.message("Login to App Store Connect (#{params[:username]})")
31
- Spaceship::ConnectAPI.login(params[:username], use_portal: false, use_tunes: true, tunes_team_id: params[:team_id], team_name: params[:team_name])
32
- UI.message("Login successful")
30
+ token = self.api_token(params)
31
+ if token
32
+ UI.message("Using App Store Connect API token...")
33
+ Spaceship::ConnectAPI.token = token
34
+ else
35
+ UI.message("Login to App Store Connect (#{params[:username]})")
36
+ Spaceship::ConnectAPI.login(params[:username], use_portal: false, use_tunes: true, tunes_team_id: params[:team_id], team_name: params[:team_name])
37
+ UI.message("Login successful")
38
+ end
33
39
 
34
40
  platform = Spaceship::ConnectAPI::Platform.map(params[:platform])
35
41
 
@@ -92,6 +98,13 @@ module Fastlane
92
98
  versions.map(&:to_s).sort_by { |v| Gem::Version.new(v) }
93
99
  end
94
100
 
101
+ def self.api_token(params)
102
+ params[:api_key] ||= Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
103
+ api_token ||= Spaceship::ConnectAPI::Token.create(params[:api_key]) if params[:api_key]
104
+ api_token ||= Spaceship::ConnectAPI::Token.from_json_file(params[:api_key_path]) if params[:api_key_path]
105
+ return api_token
106
+ end
107
+
95
108
  #####################################################
96
109
  # @!group Documentation
97
110
  #####################################################
@@ -104,6 +117,21 @@ module Fastlane
104
117
  user = CredentialsManager::AppfileConfig.try_fetch_value(:itunes_connect_id)
105
118
  user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
106
119
  [
120
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
121
+ env_name: "APPSTORE_BUILD_NUMBER_API_KEY_PATH",
122
+ description: "Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)",
123
+ optional: true,
124
+ conflicting_options: [:api_key],
125
+ verify_block: proc do |value|
126
+ UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
127
+ end),
128
+ FastlaneCore::ConfigItem.new(key: :api_key,
129
+ env_name: "APPSTORE_BUILD_NUMBER_API_KEY",
130
+ description: "Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#use-return-value-and-pass-in-as-an-option)",
131
+ type: Hash,
132
+ optional: true,
133
+ sensitive: true,
134
+ conflicting_options: [:api_key_path]),
107
135
  FastlaneCore::ConfigItem.new(key: :initial_build_number,
108
136
  env_name: "INITIAL_BUILD_NUMBER",
109
137
  description: "sets the build number to given value if no build is in current train",
@@ -193,6 +221,14 @@ module Fastlane
193
221
  live: false,
194
222
  app_identifier: "app.identifier",
195
223
  version: "1.2.9"
224
+ )',
225
+ 'api_key = app_store_connect_api_key(
226
+ key_id: "MyKeyID12345",
227
+ issuer_id: "00000000-0000-0000-0000-000000000000",
228
+ key_filepath: "./AuthKey.p8"
229
+ )
230
+ build_num = app_store_build_number(
231
+ api_key: api_key
196
232
  )'
197
233
  ]
198
234
  end