fastlane 2.159.0 → 2.164.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) 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/queue_worker.rb +14 -29
  9. data/deliver/lib/deliver/runner.rb +36 -8
  10. data/deliver/lib/deliver/upload_metadata.rb +51 -10
  11. data/deliver/lib/deliver/upload_price_tier.rb +7 -2
  12. data/deliver/lib/deliver/upload_screenshots.rb +28 -13
  13. data/fastlane/lib/fastlane/actions/actions_helper.rb +20 -1
  14. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +39 -3
  15. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +15 -1
  16. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +1 -0
  17. data/fastlane/lib/fastlane/actions/clean_build_artifacts.rb +1 -0
  18. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +2 -2
  19. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +2 -2
  20. data/fastlane/lib/fastlane/actions/docs/create_app_online.md +1 -1
  21. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +3 -2
  22. data/fastlane/lib/fastlane/actions/docs/run_tests.md +2 -2
  23. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +12 -3
  24. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -2
  25. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +1 -1
  26. data/fastlane/lib/fastlane/actions/download_dsyms.rb +31 -6
  27. data/fastlane/lib/fastlane/actions/ensure_git_status_clean.rb +13 -2
  28. data/fastlane/lib/fastlane/actions/get_certificates.rb +1 -0
  29. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +1 -0
  30. data/fastlane/lib/fastlane/actions/import_from_git.rb +9 -1
  31. data/fastlane/lib/fastlane/actions/is_ci.rb +1 -1
  32. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +15 -0
  33. data/fastlane/lib/fastlane/actions/register_device.rb +46 -5
  34. data/fastlane/lib/fastlane/actions/register_devices.rb +50 -16
  35. data/fastlane/lib/fastlane/actions/set_changelog.rb +31 -3
  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/fast_file.rb +74 -23
  39. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -0
  40. data/fastlane/lib/fastlane/version.rb +1 -1
  41. data/fastlane/swift/Deliverfile.swift +1 -1
  42. data/fastlane/swift/DeliverfileProtocol.swift +13 -5
  43. data/fastlane/swift/Fastlane.swift +170 -44
  44. data/fastlane/swift/Gymfile.swift +1 -1
  45. data/fastlane/swift/GymfileProtocol.swift +1 -1
  46. data/fastlane/swift/LaneFileProtocol.swift +28 -36
  47. data/fastlane/swift/MainProcess.swift +1 -1
  48. data/fastlane/swift/Matchfile.swift +1 -1
  49. data/fastlane/swift/MatchfileProtocol.swift +20 -4
  50. data/fastlane/swift/Precheckfile.swift +1 -1
  51. data/fastlane/swift/PrecheckfileProtocol.swift +9 -1
  52. data/fastlane/swift/Scanfile.swift +1 -1
  53. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  54. data/fastlane/swift/Screengrabfile.swift +1 -1
  55. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  56. data/fastlane/swift/Snapshotfile.swift +1 -1
  57. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  58. data/fastlane/swift/main.swift +1 -1
  59. data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +6 -7
  60. data/fastlane_core/lib/fastlane_core/device_manager.rb +8 -4
  61. data/fastlane_core/lib/fastlane_core/helper.rb +1 -1
  62. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +3 -3
  63. data/frameit/lib/frameit/editor.rb +1 -0
  64. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +2 -2
  65. data/match/lib/match/.commands_generator.rb.swp +0 -0
  66. data/match/lib/match/.importer.rb.swp +0 -0
  67. data/{spaceship/lib/spaceship/connect_api/models/.app.rb.swp → match/lib/match/.options.rb.swp} +0 -0
  68. data/match/lib/match/generator.rb +6 -1
  69. data/match/lib/match/importer.rb +63 -18
  70. data/match/lib/match/migrate.rb +13 -2
  71. data/match/lib/match/nuke.rb +65 -22
  72. data/match/lib/match/options.rb +33 -2
  73. data/match/lib/match/runner.rb +38 -10
  74. data/match/lib/match/spaceship_ensure.rb +27 -21
  75. data/match/lib/match/storage/google_cloud_storage.rb +20 -3
  76. data/match/lib/match/storage/s3_storage.rb +19 -3
  77. data/pilot/lib/pilot/options.rb +2 -2
  78. data/precheck/lib/precheck/options.rb +16 -0
  79. data/precheck/lib/precheck/runner.rb +20 -1
  80. data/scan/lib/scan/detect_values.rb +5 -8
  81. data/scan/lib/scan/runner.rb +2 -1
  82. data/sigh/lib/assets/resign.sh +1 -1
  83. data/sigh/lib/sigh/download_all.rb +16 -4
  84. data/sigh/lib/sigh/options.rb +21 -0
  85. data/sigh/lib/sigh/runner.rb +81 -39
  86. data/snapshot/lib/assets/SnapshotHelper.swift +4 -0
  87. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +2 -1
  88. data/spaceship/README.md +1 -1
  89. data/spaceship/lib/spaceship/client.rb +9 -4
  90. data/spaceship/lib/spaceship/connect_api.rb +25 -0
  91. data/spaceship/lib/spaceship/connect_api/api_client.rb +12 -3
  92. data/spaceship/lib/spaceship/connect_api/client.rb +13 -3
  93. data/spaceship/lib/spaceship/connect_api/models/app.rb +17 -9
  94. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +1 -0
  95. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +3 -1
  96. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +2 -2
  97. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +3 -5
  98. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +3 -5
  99. data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +2 -1
  100. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +42 -0
  101. data/spaceship/lib/spaceship/connect_api/models/device.rb +5 -0
  102. data/spaceship/lib/spaceship/connect_api/models/profile.rb +7 -1
  103. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +59 -0
  104. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +45 -2
  105. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
  106. data/spaceship/lib/spaceship/connect_api/token.rb +6 -1
  107. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +15 -7
  108. data/spaceship/lib/spaceship/connect_api/users/users.rb +40 -0
  109. data/spaceship/lib/spaceship/helper/net_http_generic_request.rb +11 -5
  110. data/supply/lib/supply/uploader.rb +1 -1
  111. metadata +21 -19
  112. data/spaceship/lib/spaceship/connect_api/models/.app_store_version.rb.swp +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`)",
@@ -6,9 +6,11 @@ module Deliver
6
6
  # Use this when you have all the items that you'll process in advance.
7
7
  # Simply enqueue them to this and call `QueueWorker#start`.
8
8
  class QueueWorker
9
+ NUMBER_OF_THREADS = Helper.test? ? 1 : [ENV.fetch("DELIVER_NUMBER_OF_THREADS", 10).to_i, 10].min
10
+
9
11
  # @param concurrency (Numeric) - A number of threads to be created
10
12
  # @param block (Proc) - A task you want to execute with enqueued items
11
- def initialize(concurrency, &block)
13
+ def initialize(concurrency = NUMBER_OF_THREADS, &block)
12
14
  @concurrency = concurrency
13
15
  @block = block
14
16
  @queue = Queue.new
@@ -19,46 +21,29 @@ module Deliver
19
21
  @queue.push(job)
20
22
  end
21
23
 
24
+ # @param jobs (Array<Object>) - An array of arbitary object that keeps parameters
25
+ def batch_enqueue(jobs)
26
+ raise(ArgumentError, "Enqueue Array instead of #{jobs.class}") unless jobs.kind_of?(Array)
27
+ jobs.each { |job| enqueue(job) }
28
+ end
29
+
22
30
  # Call this after you enqueuned all the jobs you want to process
23
31
  # This method blocks current thread until all the enqueued jobs are processed
24
32
  def start
33
+ @queue.close
34
+
25
35
  threads = []
26
36
  @concurrency.times do
27
37
  threads << Thread.new do
28
- while running? && !empty?
38
+ job = @queue.pop
39
+ while job
40
+ @block.call(job)
29
41
  job = @queue.pop
30
- @block.call(job) if job
31
42
  end
32
43
  end
33
44
  end
34
45
 
35
- wait_for_complete
36
46
  threads.each(&:join)
37
47
  end
38
-
39
- private
40
-
41
- def running?
42
- !@queue.closed?
43
- end
44
-
45
- def empty?
46
- @queue.empty?
47
- end
48
-
49
- def wait_for_complete
50
- wait_thread = Thread.new do
51
- loop do
52
- if @queue.empty?
53
- @queue.close
54
- break
55
- end
56
-
57
- sleep(1)
58
- end
59
- end
60
-
61
- wait_thread.join
62
- end
63
48
  end
64
49
  end
@@ -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])
@@ -1,4 +1,5 @@
1
1
  require_relative 'module'
2
+ require_relative 'queue_worker'
2
3
 
3
4
  module Deliver
4
5
  # upload description, rating, etc.
@@ -96,7 +97,7 @@ module Deliver
96
97
 
97
98
  if v.nil?
98
99
  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)
100
+ version = fetch_edit_app_store_version(app, platform)
100
101
  # we don't want to update the localised_options and non_localised_options
101
102
  # as we also check for `options[:edit_live]` at other areas in the code
102
103
  # by not touching those 2 variables, deliver is more consistent with what the option says
@@ -105,7 +106,7 @@ module Deliver
105
106
  UI.message("Found live version")
106
107
  end
107
108
  else
108
- version = app.get_edit_app_store_version(platform: platform)
109
+ version = fetch_edit_app_store_version(app, platform)
109
110
  localised_options = (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys)
110
111
  non_localised_options = NON_LOCALISED_VERSION_VALUES.keys
111
112
  end
@@ -175,38 +176,51 @@ module Deliver
175
176
 
176
177
  non_localized_version_attributes['earliestReleaseDate'] = date
177
178
  Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::SCHEDULED
178
- elsif options[:automatic_release]
179
+ elsif options[:automatic_release] == true
179
180
  Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::AFTER_APPROVAL
180
- else
181
+ elsif options[:automatic_release] == false
181
182
  Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::MANUAL
182
183
  end
183
- non_localized_version_attributes['releaseType'] = release_type
184
+ if release_type.nil?
185
+ 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")
186
+ else
187
+ non_localized_version_attributes['releaseType'] = release_type
188
+ end
184
189
 
185
190
  # Update app store version
186
191
  # This needs to happen before updating localizations (https://openradar.appspot.com/radar?id=4925914991296512)
192
+ #
193
+ # Adding some sleeps because the API will sometimes be in a state where releaseType can't be modified
194
+ # https://github.com/fastlane/fastlane/issues/16911
187
195
  UI.message("Uploading metadata to App Store Connect for version")
196
+ sleep(2)
188
197
  version.update(attributes: non_localized_version_attributes)
198
+ sleep(1)
189
199
 
190
200
  # Update app store version localizations
191
- app_store_version_localizations.each do |app_store_version_localization|
201
+ store_version_worker = Deliver::QueueWorker.new do |app_store_version_localization|
192
202
  attributes = localized_version_attributes_by_locale[app_store_version_localization.locale]
193
203
  if attributes
194
204
  UI.message("Uploading metadata to App Store Connect for localized version '#{app_store_version_localization.locale}'")
195
205
  app_store_version_localization.update(attributes: attributes)
196
206
  end
197
207
  end
208
+ store_version_worker.batch_enqueue(app_store_version_localizations)
209
+ store_version_worker.start
198
210
 
199
211
  # Update app info localizations
200
- app_info_localizations.each do |app_info_localization|
212
+ app_info_worker = Deliver::QueueWorker.new do |app_info_localization|
201
213
  attributes = localized_info_attributes_by_locale[app_info_localization.locale]
202
214
  if attributes
203
215
  UI.message("Uploading metadata to App Store Connect for localized info '#{app_info_localization.locale}'")
204
216
  app_info_localization.update(attributes: attributes)
205
217
  end
206
218
  end
219
+ app_info_worker.batch_enqueue(app_info_localizations)
220
+ app_info_worker.start
207
221
 
208
222
  # Update categories
209
- app_info = app.fetch_edit_app_info
223
+ app_info = fetch_edit_app_info(app)
210
224
  if app_info
211
225
  category_id_map = {}
212
226
 
@@ -414,9 +428,35 @@ module Deliver
414
428
  .uniq
415
429
  end
416
430
 
431
+ def fetch_edit_app_store_version(app, platform, wait_time: 10)
432
+ retry_if_nil("Cannot find edit app store version", wait_time: wait_time) do
433
+ app.get_edit_app_store_version(platform: platform)
434
+ end
435
+ end
436
+
437
+ def fetch_edit_app_info(app, wait_time: 10)
438
+ retry_if_nil("Cannot find edit app info", wait_time: wait_time) do
439
+ app.fetch_edit_app_info
440
+ end
441
+ end
442
+
443
+ def retry_if_nil(message, tries: 5, wait_time: 10)
444
+ loop do
445
+ tries -= 1
446
+
447
+ value = yield
448
+ return value if value
449
+
450
+ UI.message("#{message}... Retrying after #{wait_time} seconds (remaining: #{tries})")
451
+ sleep(wait_time)
452
+
453
+ return nil if tries.zero?
454
+ end
455
+ end
456
+
417
457
  # Finding languages to enable
418
458
  def verify_available_info_languages!(options, app, languages)
419
- app_info = app.fetch_edit_app_info
459
+ app_info = fetch_edit_app_info(app)
420
460
 
421
461
  unless app_info
422
462
  UI.user_error!("Cannot update languages - could not find an editable info")
@@ -451,7 +491,7 @@ module Deliver
451
491
  # Finding languages to enable
452
492
  def verify_available_version_languages!(options, app, languages)
453
493
  platform = Spaceship::ConnectAPI::Platform.map(options[:platform])
454
- version = app.get_edit_app_store_version(platform: platform)
494
+ version = fetch_edit_app_store_version(app, platform)
455
495
 
456
496
  unless version
457
497
  UI.user_error!("Cannot update languages - could not find an editable version for '#{platform}'")
@@ -558,6 +598,7 @@ module Deliver
558
598
  def set_review_information(version, options)
559
599
  return unless options[:app_review_information]
560
600
  info = options[:app_review_information]
601
+ info = info.collect { |k, v| [k.to_sym, v] }.to_h
561
602
  UI.user_error!("`app_review_information` must be a hash", show_github_issues: true) unless info.kind_of?(Hash)
562
603
 
563
604
  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)
@@ -13,8 +13,6 @@ module Deliver
13
13
  DeleteScreenshotJob = Struct.new(:app_screenshot, :localization, :app_screenshot_set)
14
14
  UploadScreenshotJob = Struct.new(:app_screenshot_set, :path)
15
15
 
16
- NUMBER_OF_THREADS = Helper.test? ? 1 : [ENV.fetch("DELIVER_NUMBER_OF_THREADS", 10).to_i, 10].min
17
-
18
16
  def upload(options, screenshots)
19
17
  return if options[:skip_screenshots]
20
18
  return if options[:edit_live]
@@ -69,7 +67,7 @@ module Deliver
69
67
  def delete_screenshots(localizations, screenshots_per_language, tries: 5)
70
68
  tries -= 1
71
69
 
72
- worker = QueueWorker.new(NUMBER_OF_THREADS) do |job|
70
+ worker = QueueWorker.new do |job|
73
71
  start_time = Time.now
74
72
  target = "#{job.localization.locale} #{job.app_screenshot_set.screenshot_display_type} #{job.app_screenshot.id}"
75
73
  begin
@@ -115,7 +113,7 @@ module Deliver
115
113
  tries -= 1
116
114
 
117
115
  # Upload screenshots
118
- worker = QueueWorker.new(NUMBER_OF_THREADS) do |job|
116
+ worker = QueueWorker.new do |job|
119
117
  begin
120
118
  UI.verbose("Uploading '#{job.path}'...")
121
119
  start_time = Time.now
@@ -126,10 +124,16 @@ module Deliver
126
124
  end
127
125
  end
128
126
 
129
- number_of_screenshots = 0
127
+ # Each app_screenshot_set can have only 10 images
128
+ number_of_screenshots_per_set = {}
129
+ total_number_of_screenshots = 0
130
+
130
131
  iterator = AppScreenshotIterator.new(localizations)
131
- iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot, index|
132
- if index >= 10
132
+ iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot|
133
+ # Initialize counter on each app screenshot set
134
+ number_of_screenshots_per_set[app_screenshot_set] ||= (app_screenshot_set.app_screenshots || []).count
135
+
136
+ if number_of_screenshots_per_set[app_screenshot_set] >= 10
133
137
  UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})")
134
138
  next
135
139
  end
@@ -141,10 +145,12 @@ module Deliver
141
145
  if duplicate
142
146
  UI.message("Previous uploaded. Skipping '#{screenshot.path}'...")
143
147
  else
148
+ UI.verbose("Queued uplaod sceeenshot job for #{localization.locale} #{app_screenshot_set.screenshot_display_type} #{screenshot.path}")
144
149
  worker.enqueue(UploadScreenshotJob.new(app_screenshot_set, screenshot.path))
150
+ number_of_screenshots_per_set[app_screenshot_set] += 1
145
151
  end
146
152
 
147
- number_of_screenshots += 1
153
+ total_number_of_screenshots += 1
148
154
  end
149
155
 
150
156
  worker.start
@@ -154,7 +160,7 @@ module Deliver
154
160
  Helper.show_loading_indicator("Waiting for all the screenshots processed...")
155
161
  states = wait_for_complete(iterator)
156
162
  Helper.hide_loading_indicator
157
- retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, localizations, screenshots_per_language)
163
+ retry_upload_screenshots_if_needed(iterator, states, total_number_of_screenshots, tries, localizations, screenshots_per_language)
158
164
 
159
165
  UI.message("Successfully uploaded all screenshots")
160
166
  end
@@ -203,12 +209,21 @@ module Deliver
203
209
  # Check if local screenshots' checksum exist on App Store Connect
204
210
  checksum_to_app_screenshot = iterator.each_app_screenshot.map { |_, _, app_screenshot| [app_screenshot.source_file_checksum, app_screenshot] }.to_h
205
211
 
206
- missing_local_screenshots = iterator.each_local_screenshot(screenshots_per_language).select do |_, _, local_screenshot, index|
212
+ number_of_screenshots_per_set = {}
213
+ missing_local_screenshots = iterator.each_local_screenshot(screenshots_per_language).select do |_, app_screenshot_set, local_screenshot|
214
+ number_of_screenshots_per_set[app_screenshot_set] ||= (app_screenshot_set.app_screenshots || []).count
207
215
  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
216
+
217
+ if checksum_to_app_screenshot[checksum]
218
+ next(false)
219
+ else
220
+ is_missing = number_of_screenshots_per_set[app_screenshot_set] < 10 # if it's more than 10, it's skipped
221
+ number_of_screenshots_per_set[app_screenshot_set] += 1
222
+ next(is_missing)
223
+ end
209
224
  end
210
225
 
211
- missing_local_screenshots.each do |_, _, screenshot, _|
226
+ missing_local_screenshots.each do |_, _, screenshot|
212
227
  UI.error("#{screenshot.path} is missing on App Store Connect.")
213
228
  end
214
229
 
@@ -219,7 +234,7 @@ module Deliver
219
234
  iterator = AppScreenshotIterator.new(localizations)
220
235
 
221
236
  # Re-order screenshots within app_screenshot_set
222
- worker = QueueWorker.new(NUMBER_OF_THREADS) do |app_screenshot_set|
237
+ worker = QueueWorker.new do |app_screenshot_set|
223
238
  original_ids = app_screenshot_set.app_screenshots.map(&:id)
224
239
  sorted_ids = app_screenshot_set.app_screenshots.sort_by(&:file_name).map(&:id)
225
240
  if original_ids != sorted_ids