Class: Deliver::UploadScreenshots
- Inherits:
-
Object
- Object
- Deliver::UploadScreenshots
- Defined in:
- deliver/lib/deliver/upload_screenshots.rb
Overview
upload screenshots to App Store Connect
Defined Under Namespace
Classes: DeleteScreenshotSetJob, UploadScreenshotJob
Class Method Summary collapse
-
.available_languages ⇒ Object
helper method so Spaceship::Tunes.client.available_languages is easier to test.
-
.calculate_checksum(path) ⇒ Object
helper method to mock this step in tests.
Instance Method Summary collapse
- #collect_screenshots(options) ⇒ Object
- #delete_screenshots(localizations, screenshots_per_language, tries: 5) ⇒ Object
-
#retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, timeout_seconds, localizations, screenshots_per_language) ⇒ Object
Verify all screenshots states on App Store Connect are okay.
- #sort_screenshots(localizations) ⇒ Object
- #upload(options, screenshots) ⇒ Object
- #upload_screenshots(localizations, screenshots_per_language, timeout_seconds, tries: 5) ⇒ Object
-
#verify_local_screenshots_are_uploaded(iterator, screenshots_per_language) ⇒ Object
Return ‘true` if all the local screenshots are uploaded to App Store Connect.
-
#wait_for_complete(iterator, timeout_seconds) ⇒ Object
Verify all screenshots have been processed.
Class Method Details
.available_languages ⇒ Object
helper method so Spaceship::Tunes.client.available_languages is easier to test
266 267 268 269 270 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 266 def self.available_languages # 2020-08-24 - Available locales are not available as an endpoint in App Store Connect # Update with Spaceship::Tunes.client.available_languages.sort (as long as endpoint is available) Deliver::Languages::ALL_LANGUAGES end |
.calculate_checksum(path) ⇒ Object
helper method to mock this step in tests
273 274 275 276 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 273 def self.calculate_checksum(path) bytes = File.binread(path) Digest::MD5.hexdigest(bytes) end |
Instance Method Details
#collect_screenshots(options) ⇒ Object
260 261 262 263 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 260 def collect_screenshots() return [] if [:skip_screenshots] return Loader.load_app_screenshots([:screenshots_path], [:ignore_language_directory_validation]) end |
#delete_screenshots(localizations, screenshots_per_language, tries: 5) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 67 def delete_screenshots(localizations, screenshots_per_language, tries: 5) tries -= 1 worker = FastlaneCore::QueueWorker.new do |job| start_time = Time.now target = "#{job.localization.locale} #{job.app_screenshot_set.screenshot_display_type}" begin UI.verbose("Deleting '#{target}'") job.app_screenshot_set.delete! UI.("Deleted '#{target}' - (#{Time.now - start_time} secs)") rescue => error UI.error("Failed to delete screenshot #{target} - (#{Time.now - start_time} secs)") UI.error(error.) end end iterator = AppScreenshotIterator.new(localizations) iterator.each_app_screenshot_set do |localization, app_screenshot_set| # Only delete screenshots if trying to upload next unless screenshots_per_language.keys.include?(localization.locale) UI.verbose("Queued delete screenshot set job for #{localization.locale} #{app_screenshot_set.screenshot_display_type}") worker.enqueue(DeleteScreenshotSetJob.new(app_screenshot_set, localization)) end worker.start # Verify all screenshots have been deleted # Sometimes API requests will fail but screenshots will still be deleted count = iterator.each_app_screenshot_set.map { |_, app_screenshot_set| app_screenshot_set } .reduce(0) { |sum, app_screenshot_set| sum + app_screenshot_set.app_screenshots.size } UI.important("Number of screenshots not deleted: #{count}") if count > 0 if tries.zero? UI.user_error!("Failed verification of all screenshots deleted... #{count} screenshot(s) still exist") else UI.error("Failed to delete all screenshots... Tries remaining: #{tries}") delete_screenshots(localizations, screenshots_per_language, tries: tries) end else UI.("Successfully deleted all screenshots") end end |
#retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, timeout_seconds, localizations, screenshots_per_language) ⇒ Object
Verify all screenshots states on App Store Connect are okay
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 192 def retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, timeout_seconds, localizations, screenshots_per_language) is_failure = states.fetch("FAILED", 0) > 0 is_processing = states.fetch('UPLOAD_COMPLETE', 0) > 0 is_missing_screenshot = !screenshots_per_language.empty? && !verify_local_screenshots_are_uploaded(iterator, screenshots_per_language) return unless is_failure || is_missing_screenshot || is_processing if tries.zero? iterator.each_app_screenshot.select { |_, _, app_screenshot| app_screenshot.error? }.each do |localization, _, app_screenshot| UI.error("#{app_screenshot.file_name} for #{localization.locale} has error(s) - #{app_screenshot..join(', ')}") end incomplete_screenshot_count = states.reject { |k, v| k == 'COMPLETE' }.reduce(0) { |sum, (k, v)| sum + v } UI.user_error!("Failed verification of all screenshots uploaded... #{incomplete_screenshot_count} incomplete screenshot(s) still exist") else UI.error("Failed to upload all screenshots... Tries remaining: #{tries}") # Delete bad entries before retry iterator.each_app_screenshot do |_, _, app_screenshot| app_screenshot.delete! unless app_screenshot.complete? end upload_screenshots(localizations, screenshots_per_language, timeout_seconds, tries: tries) end end |
#sort_screenshots(localizations) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 240 def sort_screenshots(localizations) require 'naturally' iterator = AppScreenshotIterator.new(localizations) # Re-order screenshots within app_screenshot_set worker = FastlaneCore::QueueWorker.new do |app_screenshot_set| original_ids = app_screenshot_set.app_screenshots.map(&:id) sorted_ids = Naturally.sort(app_screenshot_set.app_screenshots, by: :file_name).map(&:id) if original_ids != sorted_ids app_screenshot_set.reorder_screenshots(app_screenshot_ids: sorted_ids) end end iterator.each_app_screenshot_set do |_, app_screenshot_set| worker.enqueue(app_screenshot_set) end worker.start end |
#upload(options, screenshots) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 16 def upload(, screenshots) return if [:skip_screenshots] return if [:edit_live] app = Deliver.cache[:app] platform = Spaceship::ConnectAPI::Platform.map([:platform]) version = app.get_edit_app_store_version(platform: platform) UI.user_error!("Could not find a version to edit for app '#{app.name}' for '#{platform}'") unless version UI.important("Will begin uploading snapshots for '#{version.version_string}' on App Store Connect") UI.("Starting with the upload of screenshots...") screenshots_per_language = screenshots.group_by(&:language) localizations = version.get_app_store_version_localizations if [:overwrite_screenshots] delete_screenshots(localizations, screenshots_per_language) end # Finding languages to enable languages = screenshots_per_language.keys locales_to_enable = languages - localizations.map(&:locale) if locales_to_enable.count > 0 lng_text = "language" lng_text += "s" if locales_to_enable.count != 1 Helper.show_loading_indicator("Activating #{lng_text} #{locales_to_enable.join(', ')}...") locales_to_enable.each do |locale| version.create_app_store_version_localization(attributes: { locale: locale }) end Helper.hide_loading_indicator # Refresh version localizations localizations = version.get_app_store_version_localizations end upload_screenshots(localizations, screenshots_per_language, [:screenshot_processing_timeout]) Helper.show_loading_indicator("Sorting screenshots uploaded...") sort_screenshots(localizations) Helper.hide_loading_indicator UI.success("Successfully uploaded screenshots to App Store Connect") end |
#upload_screenshots(localizations, screenshots_per_language, timeout_seconds, tries: 5) ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 112 def upload_screenshots(localizations, screenshots_per_language, timeout_seconds, tries: 5) tries -= 1 # Upload screenshots worker = FastlaneCore::QueueWorker.new do |job| begin UI.verbose("Uploading '#{job.path}'...") start_time = Time.now job.app_screenshot_set.upload_screenshot(path: job.path, wait_for_processing: false) UI.("Uploaded '#{job.path}'... (#{Time.now - start_time} secs)") rescue => error UI.error(error) end end # Each app_screenshot_set can have only 10 images number_of_screenshots_per_set = {} total_number_of_screenshots = 0 iterator = AppScreenshotIterator.new(localizations) iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot| # Initialize counter on each app screenshot set number_of_screenshots_per_set[app_screenshot_set] ||= (app_screenshot_set.app_screenshots || []).count if number_of_screenshots_per_set[app_screenshot_set] >= 10 UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})") next end checksum = UploadScreenshots.calculate_checksum(screenshot.path) duplicate = (app_screenshot_set.app_screenshots || []).any? { |s| s.source_file_checksum == checksum } # Enqueue uploading job if it's not duplicated otherwise screenshot will be skipped if duplicate UI.("Previous uploaded. Skipping '#{screenshot.path}'...") else UI.verbose("Queued upload screenshot job for #{localization.locale} #{app_screenshot_set.screenshot_display_type} #{screenshot.path}") worker.enqueue(UploadScreenshotJob.new(app_screenshot_set, screenshot.path)) number_of_screenshots_per_set[app_screenshot_set] += 1 end total_number_of_screenshots += 1 end worker.start UI.verbose('Uploading jobs are completed') Helper.show_loading_indicator("Waiting for all the screenshots to finish being processed...") states = wait_for_complete(iterator, timeout_seconds) Helper.hide_loading_indicator retry_upload_screenshots_if_needed(iterator, states, total_number_of_screenshots, tries, timeout_seconds, localizations, screenshots_per_language) UI.("Successfully uploaded all screenshots") end |
#verify_local_screenshots_are_uploaded(iterator, screenshots_per_language) ⇒ Object
Return ‘true` if all the local screenshots are uploaded to App Store Connect
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 215 def verify_local_screenshots_are_uploaded(iterator, screenshots_per_language) # Check if local screenshots' checksum exist on App Store Connect checksum_to_app_screenshot = iterator.each_app_screenshot.map { |_, _, app_screenshot| [app_screenshot.source_file_checksum, app_screenshot] }.to_h number_of_screenshots_per_set = {} missing_local_screenshots = iterator.each_local_screenshot(screenshots_per_language).select do |_, app_screenshot_set, local_screenshot| number_of_screenshots_per_set[app_screenshot_set] ||= (app_screenshot_set.app_screenshots || []).count checksum = UploadScreenshots.calculate_checksum(local_screenshot.path) if checksum_to_app_screenshot[checksum] next(false) else is_missing = number_of_screenshots_per_set[app_screenshot_set] < 10 # if it's more than 10, it's skipped number_of_screenshots_per_set[app_screenshot_set] += 1 next(is_missing) end end missing_local_screenshots.each do |_, _, screenshot| UI.error("#{screenshot.path} is missing on App Store Connect.") end missing_local_screenshots.empty? end |
#wait_for_complete(iterator, timeout_seconds) ⇒ Object
Verify all screenshots have been processed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'deliver/lib/deliver/upload_screenshots.rb', line 169 def wait_for_complete(iterator, timeout_seconds) start_time = Time.now loop do states = iterator.each_app_screenshot.map { |_, _, app_screenshot| app_screenshot }.each_with_object({}) do |app_screenshot, hash| state = app_screenshot.asset_delivery_state['state'] hash[state] ||= 0 hash[state] += 1 end is_processing = states.fetch('UPLOAD_COMPLETE', 0) > 0 return states unless is_processing if Time.now - start_time > timeout_seconds UI.important("Screenshot upload reached the timeout limit of #{timeout_seconds} seconds. We'll now retry uploading the screenshots that couldn't be uploaded in time.") return states end UI.verbose("There are still incomplete screenshots - #{states}") sleep(5) end end |