fastlane 2.149.1 → 2.150.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/deliver/lib/deliver.rb +0 -1
  3. data/deliver/lib/deliver/app_screenshot.rb +26 -25
  4. data/deliver/lib/deliver/download_screenshots.rb +46 -26
  5. data/deliver/lib/deliver/options.rb +6 -11
  6. data/deliver/lib/deliver/runner.rb +7 -21
  7. data/deliver/lib/deliver/setup.rb +5 -30
  8. data/deliver/lib/deliver/submit_for_review.rb +170 -86
  9. data/deliver/lib/deliver/upload_metadata.rb +355 -143
  10. data/deliver/lib/deliver/upload_price_tier.rb +22 -8
  11. data/deliver/lib/deliver/upload_screenshots.rb +140 -39
  12. data/{scan/lib/scan/.test_command_generator.rb.swp → fastlane/lib/fastlane/actions/.hockey.rb.swp} +0 -0
  13. data/fastlane/lib/fastlane/actions/.slack.rb.swp +0 -0
  14. data/{snapshot/lib/snapshot/.test_command_generator_base.rb.swp → fastlane/lib/fastlane/actions/.update_project_provisioning.rb.swp} +0 -0
  15. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +1 -1
  16. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +81 -96
  17. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +3 -2
  18. data/fastlane/lib/fastlane/actions/download_dsyms.rb +7 -1
  19. data/fastlane/lib/fastlane/actions/google_play_track_release_names.rb +74 -0
  20. data/fastlane/lib/fastlane/actions/set_changelog.rb +23 -20
  21. data/fastlane/lib/fastlane/actions/slack.rb +1 -1
  22. data/fastlane/lib/fastlane/actions/spm.rb +7 -0
  23. data/fastlane/lib/fastlane/actions/upload_symbols_to_crashlytics.rb +1 -32
  24. data/fastlane/lib/fastlane/lane.rb +3 -3
  25. data/fastlane/lib/fastlane/swift_fastlane_function.rb +8 -4
  26. data/fastlane/lib/fastlane/version.rb +1 -1
  27. data/fastlane/swift/ControlCommand.swift +1 -0
  28. data/fastlane/swift/Fastlane.swift +48 -12
  29. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.xcworkspace/xcuserdata/josh.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  30. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/xcshareddata/xcschemes/FastlaneRunner.xcscheme +3 -9
  31. data/fastlane/swift/LaneFileProtocol.swift +2 -5
  32. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  33. data/fastlane/swift/RubyCommand.swift +29 -6
  34. data/fastlane/swift/RubyCommandable.swift +1 -0
  35. data/fastlane/swift/Runner.swift +85 -13
  36. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  37. data/fastlane/swift/SnapshotfileProtocol.swift +3 -3
  38. data/fastlane/swift/SocketClient.swift +76 -45
  39. data/fastlane/swift/SocketClientDelegateProtocol.swift +1 -1
  40. data/fastlane/swift/SocketResponse.swift +1 -0
  41. data/fastlane_core/lib/fastlane_core/build_watcher.rb +4 -4
  42. data/fastlane_core/lib/fastlane_core/configuration/config_item.rb +1 -3
  43. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +89 -52
  44. data/fastlane_core/lib/fastlane_core/pkg_file_analyser.rb +7 -0
  45. data/frameit/lib/frameit/device_types.rb +100 -100
  46. data/pilot/lib/pilot/.manager.rb.swp +0 -0
  47. data/produce/lib/produce/itunes_connect.rb +59 -21
  48. data/produce/lib/produce/options.rb +3 -3
  49. data/sigh/lib/assets/resign.sh +7 -7
  50. data/snapshot/lib/assets/SnapshotHelper.swift +5 -5
  51. data/snapshot/lib/assets/SnapshotHelperXcode8.swift +3 -3
  52. data/snapshot/lib/snapshot/options.rb +0 -1
  53. data/snapshot/lib/snapshot/reports_generator.rb +8 -1
  54. data/spaceship/lib/spaceship/client.rb +4 -3
  55. data/spaceship/lib/spaceship/connect_api.rb +25 -2
  56. data/spaceship/lib/spaceship/connect_api/.DS_Store +0 -0
  57. data/spaceship/lib/spaceship/connect_api/client.rb +97 -31
  58. data/spaceship/lib/spaceship/connect_api/file_uploader.rb +98 -0
  59. data/spaceship/lib/spaceship/connect_api/model.rb +1 -1
  60. data/spaceship/lib/spaceship/connect_api/models/age_rating_declaration.rb +113 -0
  61. data/spaceship/lib/spaceship/connect_api/models/app.rb +135 -3
  62. data/spaceship/lib/spaceship/connect_api/models/app_category.rb +94 -0
  63. data/spaceship/lib/spaceship/connect_api/models/app_info.rb +67 -0
  64. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +38 -0
  65. data/spaceship/lib/spaceship/connect_api/models/app_preview.rb +129 -0
  66. data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +71 -0
  67. data/spaceship/lib/spaceship/connect_api/models/app_price.rb +22 -0
  68. data/spaceship/lib/spaceship/connect_api/models/app_price_tier.rb +12 -0
  69. data/spaceship/lib/spaceship/connect_api/models/app_review_attachment.rb +71 -0
  70. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +146 -0
  71. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +125 -0
  72. data/spaceship/lib/spaceship/connect_api/models/app_store_review_detail.rb +51 -0
  73. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +183 -0
  74. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +86 -0
  75. data/spaceship/lib/spaceship/connect_api/models/app_store_version_phased_release.rb +36 -0
  76. data/spaceship/lib/spaceship/connect_api/models/app_store_version_submission.rb +26 -0
  77. data/spaceship/lib/spaceship/connect_api/models/build.rb +4 -0
  78. data/spaceship/lib/spaceship/connect_api/models/idfa_declaration.rb +40 -0
  79. data/spaceship/lib/spaceship/connect_api/models/reset_ratings_request.rb +26 -0
  80. data/spaceship/lib/spaceship/connect_api/models/territory.rb +27 -0
  81. data/spaceship/lib/spaceship/connect_api/models/user.rb +2 -1
  82. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +10 -3
  83. data/spaceship/lib/spaceship/connect_api/tunes/client.rb +33 -0
  84. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +844 -0
  85. data/spaceship/lib/spaceship/connect_api/users/users.rb +13 -0
  86. data/spaceship/lib/spaceship/spaceauth_runner.rb +2 -2
  87. data/supply/lib/supply/client.rb +19 -0
  88. data/supply/lib/supply/reader.rb +16 -0
  89. metadata +34 -22
  90. data/deliver/lib/deliver/upload_assets.rb +0 -27
@@ -2,40 +2,38 @@ require_relative 'module'
2
2
 
3
3
  module Deliver
4
4
  # upload description, rating, etc.
5
+ # rubocop:disable Metrics/ClassLength
5
6
  class UploadMetadata
6
7
  # All the localised values attached to the version
7
- LOCALISED_VERSION_VALUES = [:description, :keywords, :release_notes, :support_url, :marketing_url, :promotional_text]
8
+ LOCALISED_VERSION_VALUES = {
9
+ description: "description",
10
+ keywords: "keywords",
11
+ release_notes: "whatsNew",
12
+ support_url: "supportUrl",
13
+ marketing_url: "marketingUrl",
14
+ promotional_text: "promotionalText"
15
+ }
8
16
 
9
17
  # Everything attached to the version but not being localised
10
- NON_LOCALISED_VERSION_VALUES = [:copyright]
18
+ NON_LOCALISED_VERSION_VALUES = {
19
+ copyright: "copyright"
20
+ }
11
21
 
12
22
  # Localised app details values
13
- LOCALISED_APP_VALUES = [:name, :subtitle, :privacy_url, :apple_tv_privacy_policy]
23
+ LOCALISED_APP_VALUES = {
24
+ name: "name",
25
+ subtitle: "subtitle",
26
+ privacy_url: "privacyPolicyUrl",
27
+ apple_tv_privacy_policy: "privacyPolicyText"
28
+ }
14
29
 
15
30
  # Non localized app details values
16
31
  NON_LOCALISED_APP_VALUES = [:primary_category, :secondary_category,
17
32
  :primary_first_sub_category, :primary_second_sub_category,
18
33
  :secondary_first_sub_category, :secondary_second_sub_category]
19
34
 
20
- # Trade Representative Contact Information values
21
- TRADE_REPRESENTATIVE_CONTACT_INFORMATION_VALUES = {
22
- trade_representative_trade_name: :trade_name,
23
- trade_representative_first_name: :first_name,
24
- trade_representative_last_name: :last_name,
25
- trade_representative_address_line_1: :address_line1,
26
- trade_representative_address_line_2: :address_line2,
27
- trade_representative_address_line_3: :address_line3,
28
- trade_representative_city_name: :city_name,
29
- trade_representative_state: :state,
30
- trade_representative_country: :country,
31
- trade_representative_postal_code: :postal_code,
32
- trade_representative_phone_number: :phone_number,
33
- trade_representative_email: :email_address,
34
- trade_representative_is_displayed_on_app_store: :is_displayed_on_app_store
35
- }
36
-
37
35
  # Review information values
38
- REVIEW_INFORMATION_VALUES = {
36
+ REVIEW_INFORMATION_VALUES_LEGACY = {
39
37
  review_first_name: :first_name,
40
38
  review_last_name: :last_name,
41
39
  review_phone_number: :phone_number,
@@ -44,6 +42,15 @@ module Deliver
44
42
  review_demo_password: :demo_password,
45
43
  review_notes: :notes
46
44
  }
45
+ REVIEW_INFORMATION_VALUES = {
46
+ first_name: "contactFirstName",
47
+ last_name: "contactLastName",
48
+ phone_number: "contactPhone",
49
+ email_address: "contactEmail",
50
+ demo_user: "demoAccountName",
51
+ demo_password: "demoAccountPassword",
52
+ notes: "notes"
53
+ }
47
54
 
48
55
  # Localized app details values, that are editable in live state
49
56
  LOCALISED_LIVE_VALUES = [:description, :release_notes, :support_url, :marketing_url, :promotional_text, :privacy_url]
@@ -66,35 +73,46 @@ module Deliver
66
73
  # Make sure to call `load_from_filesystem` before calling upload
67
74
  def upload(options)
68
75
  return if options[:skip_metadata]
69
- # it is not possible to create new languages, because
70
- # :keywords is not write-able on published versions
71
- # therefore skip it.
72
- verify_available_languages!(options) unless options[:edit_live]
73
76
 
74
- app = options[:app]
77
+ legacy_app = options[:app]
78
+ app_id = legacy_app.apple_id
79
+ app = Spaceship::ConnectAPI::App.get(app_id: app_id)
80
+
81
+ platform = Spaceship::ConnectAPI::Platform.map(options[:platform])
82
+
83
+ app_store_version_localizations = verify_available_version_languages!(options, app) unless options[:edit_live]
84
+ app_info_localizations = verify_available_info_languages!(options, app) unless options[:edit_live]
75
85
 
76
- details = app.details
77
86
  if options[:edit_live]
78
87
  # not all values are editable when using live_version
79
- v = app.live_version(platform: options[:platform])
88
+ version = app.get_live_app_store_version(platform: platform)
80
89
  localised_options = LOCALISED_LIVE_VALUES
81
90
  non_localised_options = NON_LOCALISED_LIVE_VALUES
82
91
 
83
92
  if v.nil?
84
93
  UI.message("Couldn't find live version, editing the current version on App Store Connect instead")
85
- v = app.edit_version(platform: options[:platform])
94
+ version = app.get_edit_app_store_version(platform: platform)
86
95
  # we don't want to update the localised_options and non_localised_options
87
96
  # as we also check for `options[:edit_live]` at other areas in the code
88
97
  # by not touching those 2 variables, deliver is more consistent with what the option says
89
98
  # in the documentation
99
+ else
100
+ UI.message("Found live version")
90
101
  end
91
102
  else
92
- v = app.edit_version(platform: options[:platform])
93
- localised_options = (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES)
94
- non_localised_options = (NON_LOCALISED_VERSION_VALUES + NON_LOCALISED_APP_VALUES)
103
+ version = app.get_edit_app_store_version(platform: platform)
104
+ localised_options = (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys)
105
+ non_localised_options = NON_LOCALISED_VERSION_VALUES.keys
95
106
  end
96
107
 
97
- individual = options[:individual_metadata_items] || []
108
+ # Needed for to filter out release notes from being sent up
109
+ is_first_version = app.get_live_app_store_version(platform: platform).nil?
110
+
111
+ UI.important("Will begin uploading metadata for '#{version.version_string}' on App Store Connect")
112
+
113
+ localized_version_attributes_by_locale = {}
114
+ localized_info_attributes_by_locale = {}
115
+
98
116
  localised_options.each do |key|
99
117
  current = options[key]
100
118
  next unless current
@@ -104,70 +122,195 @@ module Deliver
104
122
  next
105
123
  end
106
124
 
125
+ if key == :release_notes && is_first_version
126
+ UI.error("Skipping 'release_notes'... this is the first version of the app")
127
+ next
128
+ end
129
+
107
130
  current.each do |language, value|
108
131
  next unless value.to_s.length > 0
109
132
  strip_value = value.to_s.strip
110
- if individual.include?(key.to_s)
111
- upload_individual_item(app, v, language, key, strip_value)
112
- else
113
- v.send(key)[language] = strip_value if LOCALISED_VERSION_VALUES.include?(key)
114
- details.send(key)[language] = strip_value if LOCALISED_APP_VALUES.include?(key)
133
+
134
+ if LOCALISED_VERSION_VALUES.include?(key) && !strip_value.empty?
135
+ attribute_name = LOCALISED_VERSION_VALUES[key]
136
+
137
+ localized_version_attributes_by_locale[language] ||= {}
138
+ localized_version_attributes_by_locale[language][attribute_name] = strip_value
115
139
  end
140
+
141
+ next unless LOCALISED_APP_VALUES.include?(key) && !strip_value.empty?
142
+ attribute_name = LOCALISED_APP_VALUES[key]
143
+
144
+ localized_info_attributes_by_locale[language] ||= {}
145
+ localized_info_attributes_by_locale[language][attribute_name] = strip_value
116
146
  end
117
147
  end
118
148
 
149
+ non_localized_version_attributes = {}
119
150
  non_localised_options.each do |key|
120
- current = options[key].to_s.strip
121
- next unless current.to_s.length > 0
122
- v.send("#{key}=", current) if NON_LOCALISED_VERSION_VALUES.include?(key)
123
- details.send("#{key}=", current) if NON_LOCALISED_APP_VALUES.include?(key)
151
+ strip_value = options[key].to_s.strip
152
+ next unless strip_value.to_s.length > 0
153
+
154
+ if NON_LOCALISED_VERSION_VALUES.include?(key) && !strip_value.empty?
155
+ attribute_name = NON_LOCALISED_VERSION_VALUES[key]
156
+ non_localized_version_attributes[attribute_name] = strip_value
157
+ end
124
158
  end
125
159
 
126
- v.release_on_approval = options[:automatic_release]
127
- v.auto_release_date = options[:auto_release_date] unless options[:auto_release_date].nil?
128
- v.toggle_phased_release(enabled: !!options[:phased_release]) unless options[:phased_release].nil?
160
+ release_type = if options[:auto_release_date]
161
+ non_localized_version_attributes['earliestReleaseDate'] = options[:auto_release_date]
162
+ Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::SCHEDULED
163
+ elsif options[:automatic_release]
164
+ Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::AFTER_APPROVAL
165
+ else
166
+ Spaceship::ConnectAPI::AppStoreVersion::ReleaseType::MANUAL
167
+ end
168
+ non_localized_version_attributes['releaseType'] = release_type
169
+
170
+ # Update app store version localizations
171
+ app_store_version_localizations.each do |app_store_version_localization|
172
+ attributes = localized_version_attributes_by_locale[app_store_version_localization.locale]
173
+ if attributes
174
+ UI.message("Uploading metadata to App Store Connect for localized version '#{app_store_version_localization.locale}'")
175
+ app_store_version_localization.update(attributes: attributes)
176
+ end
177
+ end
129
178
 
130
- set_trade_representative_contact_information(v, options)
131
- set_review_information(v, options)
132
- set_app_rating(v, options)
133
- v.ratings_reset = options[:reset_ratings] unless options[:reset_ratings].nil?
179
+ # Update app info localizations
180
+ app_info_localizations.each do |app_info_localization|
181
+ attributes = localized_info_attributes_by_locale[app_info_localization.locale]
182
+ if attributes
183
+ UI.message("Uploading metadata to App Store Connect for localized info '#{app_info_localization.locale}'")
184
+ app_info_localization.update(attributes: attributes)
185
+ end
186
+ end
134
187
 
135
- set_review_attachment_file(v, options)
188
+ # Update app store version
189
+ UI.message("Uploading metadata to App Store Connect for version")
190
+ version.update(attributes: non_localized_version_attributes)
136
191
 
137
- Helper.show_loading_indicator("Uploading metadata to App Store Connect")
138
- v.save!
139
- Helper.hide_loading_indicator
140
- begin
141
- details.save!
142
- UI.success("Successfully uploaded set of metadata to App Store Connect")
143
- rescue Spaceship::TunesClient::ITunesConnectError => e
144
- # This makes sure that we log invalid app names as user errors
145
- # If another string needs to be checked here we should
146
- # figure out a more generic way to handle these cases.
147
- if e.message.include?('App Name cannot be longer than 50 characters') || e.message.include?('The app name you entered is already being used')
148
- UI.error("Error in app name. Try using 'individual_metadata_items' to identify the problem language.")
149
- UI.user_error!(e.message)
150
- else
151
- raise e
192
+ # Update categories
193
+ app_info = app.fetch_edit_app_info
194
+ if app_info
195
+ category_id_map = {}
196
+
197
+ primary_category = options[:primary_category].to_s.strip
198
+ secondary_category = options[:secondary_category].to_s.strip
199
+ primary_first_sub_category = options[:primary_first_sub_category].to_s.strip
200
+ primary_second_sub_category = options[:primary_second_sub_category].to_s.strip
201
+ secondary_first_sub_category = options[:secondary_first_sub_category].to_s.strip
202
+ secondary_second_sub_category = options[:secondary_second_sub_category].to_s.strip
203
+
204
+ mapped_values = {}
205
+
206
+ # Only update primary and secondar category if explicitly set
207
+ unless primary_category.empty?
208
+ mapped = Spaceship::ConnectAPI::AppCategory.map_category_from_itc(
209
+ primary_category
210
+ )
211
+
212
+ mapped_values[primary_category] = mapped
213
+ category_id_map[:primary_category_id] = mapped
214
+ end
215
+ unless secondary_category.empty?
216
+ mapped = Spaceship::ConnectAPI::AppCategory.map_category_from_itc(
217
+ secondary_category
218
+ )
219
+
220
+ mapped_values[secondary_category] = mapped
221
+ category_id_map[:secondary_category_id] = mapped
152
222
  end
223
+
224
+ # Only set if primary category is going to be set
225
+ unless primary_category.empty?
226
+ mapped = Spaceship::ConnectAPI::AppCategory.map_subcategory_from_itc(
227
+ primary_first_sub_category
228
+ )
229
+
230
+ mapped_values[primary_first_sub_category] = mapped
231
+ category_id_map[:primary_subcategory_one_id] = mapped
232
+ end
233
+ unless primary_category.empty?
234
+ mapped = Spaceship::ConnectAPI::AppCategory.map_subcategory_from_itc(
235
+ primary_second_sub_category
236
+ )
237
+
238
+ mapped_values[primary_second_sub_category] = mapped
239
+ category_id_map[:primary_subcategory_two_id] = mapped
240
+ end
241
+
242
+ # Only set if secondary category is going to be set
243
+ unless secondary_category.empty?
244
+ mapped = Spaceship::ConnectAPI::AppCategory.map_subcategory_from_itc(
245
+ secondary_first_sub_category
246
+ )
247
+
248
+ mapped_values[secondary_first_sub_category] = mapped
249
+ category_id_map[:secondary_subcategory_one_id] = mapped
250
+ end
251
+ unless secondary_category.empty?
252
+ mapped = Spaceship::ConnectAPI::AppCategory.map_subcategory_from_itc(
253
+ secondary_second_sub_category
254
+ )
255
+
256
+ mapped_values[secondary_second_sub_category] = mapped
257
+ category_id_map[:secondary_subcategory_two_id] = mapped
258
+ end
259
+
260
+ # Print deprecation warnings if category was mapped
261
+ has_mapped_values = false
262
+ mapped_values.each do |k, v|
263
+ next if k.nil? || v.nil?
264
+ next if k == v
265
+ has_mapped_values = true
266
+ UI.deprecated("Category '#{k}' from iTunesConnect as been deprecated. Please replace with '#{v}'")
267
+ end
268
+ UI.deprecated("You can find more info at https://docs.fastlane.tools/actions/deliver/#reference") if has_mapped_values
269
+
270
+ app_info.update_categories(category_id_map: category_id_map)
153
271
  end
154
- end
155
272
 
156
- # Uploads metadata individually by language to help identify exactly which items have issues
157
- def upload_individual_item(app, version, language, key, value)
158
- details = app.details
159
- version.send(key)[language] = value if LOCALISED_VERSION_VALUES.include?(key)
160
- details.send(key)[language] = value if LOCALISED_APP_VALUES.include?(key)
161
- Helper.show_loading_indicator("Uploading #{language} #{key} to App Store Connect")
162
- version.save!
163
- Helper.hide_loading_indicator
164
- begin
165
- details.save!
166
- UI.success("Successfully uploaded #{language} #{key} to App Store Connect")
167
- rescue Spaceship::TunesClient::ITunesConnectError => e
168
- UI.error("Error in #{language} #{key}: \n#{value}")
169
- UI.error(e.message) # Don't use user_error to allow all values to get checked
273
+ # Update phased release
274
+ unless options[:phased_release].nil?
275
+ phased_release = begin
276
+ version.fetch_app_store_version_phased_release
277
+ rescue
278
+ nil
279
+ end # returns no data error so need to rescue
280
+ if !!options[:phased_release]
281
+ unless phased_release
282
+ UI.message("Creating phased release on App Store Connect")
283
+ version.create_app_store_version_phased_release(attributes: {
284
+ phasedReleaseState: Spaceship::ConnectAPI::AppStoreVersionPhasedRelease::PhasedReleaseState::INACTIVE
285
+ })
286
+ end
287
+ elsif phased_release
288
+ UI.message("Removing phased release on App Store Connect")
289
+ phased_release.delete!
290
+ end
170
291
  end
292
+
293
+ # Update rating reset
294
+ unless options[:reset_ratings].nil?
295
+ reset_rating_request = begin
296
+ version.fetch_reset_ratings_request
297
+ rescue
298
+ nil
299
+ end # returns no data error so need to rescue
300
+ if !!options[:reset_ratings]
301
+ unless reset_rating_request
302
+ UI.message("Creating reset ratings request on App Store Connect")
303
+ version.create_reset_ratings_request
304
+ end
305
+ elsif reset_rating_request
306
+ UI.message("Removing reset ratings request on App Store Connect")
307
+ reset_rating_request.delete!
308
+ end
309
+ end
310
+
311
+ set_review_information(version, options)
312
+ set_review_attachment_file(version, options)
313
+ set_app_rating(version, options)
171
314
  end
172
315
 
173
316
  # rubocop:enable Metrics/PerceivedComplexity
@@ -181,7 +324,7 @@ module Deliver
181
324
  enabled_languages = detect_languages(options)
182
325
 
183
326
  # Get all languages used in existing settings
184
- (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
327
+ (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys).each do |key|
185
328
  current = options[key]
186
329
  next unless current && current.kind_of?(Hash)
187
330
  current.each do |language, value|
@@ -200,7 +343,7 @@ module Deliver
200
343
  return unless enabled_languages.include?("default")
201
344
  UI.message("Detected languages: " + enabled_languages.to_s)
202
345
 
203
- (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
346
+ (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys).each do |key|
204
347
  current = options[key]
205
348
  next unless current && current.kind_of?(Hash)
206
349
 
@@ -222,7 +365,7 @@ module Deliver
222
365
  enabled_languages = options[:languages] || []
223
366
 
224
367
  # Get all languages used in existing settings
225
- (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
368
+ (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys).each do |key|
226
369
  current = options[key]
227
370
  next unless current && current.kind_of?(Hash)
228
371
  current.each do |language, value|
@@ -245,41 +388,73 @@ module Deliver
245
388
  .uniq
246
389
  end
247
390
 
248
- # Makes sure all languages we need are actually created
249
- def verify_available_languages!(options)
250
- return if options[:skip_metadata]
391
+ # Finding languages to enable
392
+ def verify_available_info_languages!(options, app)
393
+ app_info = app.fetch_edit_app_info
251
394
 
252
- # Collect all languages we need
253
- # We only care about languages from user provided values
254
- # as the other languages are on iTC already anyway
255
- v = options[:app].edit_version(platform: options[:platform])
256
- UI.user_error!("Could not find a version to edit for app '#{options[:app].name}', the app metadata is read-only currently") unless v
395
+ unless app_info
396
+ UI.user_error!("Cannot update languages - could not find an editable info")
397
+ return
398
+ end
257
399
 
258
- enabled_languages = options[:languages] || []
259
- LOCALISED_VERSION_VALUES.each do |key|
260
- current = options[key]
261
- next unless current && current.kind_of?(Hash)
262
- current.each do |language, value|
263
- language = language.to_s
264
- enabled_languages << language unless enabled_languages.include?(language)
400
+ localizations = app_info.get_app_info_localizations
401
+
402
+ languages = (options[:languages] || []).reject { |lang| lang == "default" }
403
+ locales_to_enable = languages - localizations.map(&:locale)
404
+
405
+ if locales_to_enable.count > 0
406
+ lng_text = "language"
407
+ lng_text += "s" if locales_to_enable.count != 1
408
+ Helper.show_loading_indicator("Activating info #{lng_text} #{locales_to_enable.join(', ')}...")
409
+
410
+ locales_to_enable.each do |locale|
411
+ app_info.create_app_info_localization(attributes: {
412
+ locale: locale
413
+ })
265
414
  end
415
+
416
+ Helper.hide_loading_indicator
417
+
418
+ # Refresh version localizations
419
+ localizations = app_info.get_app_info_localizations
420
+ end
421
+
422
+ return localizations
423
+ end
424
+
425
+ # Finding languages to enable
426
+ def verify_available_version_languages!(options, app)
427
+ platform = Spaceship::ConnectAPI::Platform.map(options[:platform])
428
+ version = app.get_edit_app_store_version(platform: platform)
429
+
430
+ unless version
431
+ UI.user_error!("Cannot update languages - could not find an editable version for '#{platform}'")
432
+ return
266
433
  end
267
434
 
268
- # Reject "default" language from getting enabled
269
- # because "default" is not an iTC language
270
- enabled_languages = enabled_languages.reject do |lang|
271
- lang == "default"
272
- end.uniq
435
+ localizations = version.get_app_store_version_localizations
273
436
 
274
- if enabled_languages.count > 0
275
- v.create_languages(enabled_languages)
437
+ languages = (options[:languages] || []).reject { |lang| lang == "default" }
438
+ locales_to_enable = languages - localizations.map(&:locale)
439
+
440
+ if locales_to_enable.count > 0
276
441
  lng_text = "language"
277
- lng_text += "s" if enabled_languages.count != 1
278
- Helper.show_loading_indicator("Activating #{lng_text} #{enabled_languages.join(', ')}...")
279
- v.save!
442
+ lng_text += "s" if locales_to_enable.count != 1
443
+ Helper.show_loading_indicator("Activating version #{lng_text} #{locales_to_enable.join(', ')}...")
444
+
445
+ locales_to_enable.each do |locale|
446
+ version.create_app_store_version_localization(attributes: {
447
+ locale: locale
448
+ })
449
+ end
450
+
280
451
  Helper.hide_loading_indicator
452
+
453
+ # Refresh version localizations
454
+ localizations = version.get_app_store_version_localizations
281
455
  end
282
- true
456
+
457
+ return localizations
283
458
  end
284
459
 
285
460
  # Loads the metadata files and stores them into the options object
@@ -290,7 +465,7 @@ module Deliver
290
465
  ignore_validation = options[:ignore_language_directory_validation]
291
466
  Loader.language_folders(options[:metadata_path], ignore_validation).each do |lang_folder|
292
467
  language = File.basename(lang_folder)
293
- (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
468
+ (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys).each do |key|
294
469
  path = File.join(lang_folder, "#{key}.txt")
295
470
  next unless File.exist?(path)
296
471
 
@@ -301,7 +476,7 @@ module Deliver
301
476
  end
302
477
 
303
478
  # Load non localised data
304
- (NON_LOCALISED_VERSION_VALUES + NON_LOCALISED_APP_VALUES).each do |key|
479
+ (NON_LOCALISED_VERSION_VALUES.keys + NON_LOCALISED_APP_VALUES).each do |key|
305
480
  path = File.join(options[:metadata_path], "#{key}.txt")
306
481
  next unless File.exist?(path)
307
482
 
@@ -309,20 +484,9 @@ module Deliver
309
484
  options[key] ||= File.read(path)
310
485
  end
311
486
 
312
- # Load trade representative contact information
313
- options[:trade_representative_contact_information] ||= {}
314
- TRADE_REPRESENTATIVE_CONTACT_INFORMATION_VALUES.values.each do |option_name|
315
- path = File.join(options[:metadata_path], TRADE_REPRESENTATIVE_CONTACT_INFORMATION_DIR, "#{option_name}.txt")
316
- next unless File.exist?(path)
317
- next if options[:trade_representative_contact_information][option_name].to_s.length > 0
318
-
319
- UI.message("Loading '#{path}'...")
320
- options[:trade_representative_contact_information][option_name] ||= File.read(path)
321
- end
322
-
323
487
  # Load review information
324
488
  options[:app_review_information] ||= {}
325
- REVIEW_INFORMATION_VALUES.values.each do |option_name|
489
+ REVIEW_INFORMATION_VALUES.keys.each do |option_name|
326
490
  path = File.join(options[:metadata_path], REVIEW_INFORMATION_DIR, "#{option_name}.txt")
327
491
  next unless File.exist?(path)
328
492
  next if options[:app_review_information][option_name].to_s.length > 0
@@ -336,7 +500,7 @@ module Deliver
336
500
 
337
501
  # Normalizes languages keys from symbols to strings
338
502
  def normalize_language_keys(options)
339
- (LOCALISED_VERSION_VALUES + LOCALISED_APP_VALUES).each do |key|
503
+ (LOCALISED_VERSION_VALUES.keys + LOCALISED_APP_VALUES.keys).each do |key|
340
504
  current = options[key]
341
505
  next unless current && current.kind_of?(Hash)
342
506
 
@@ -348,33 +512,55 @@ module Deliver
348
512
  options
349
513
  end
350
514
 
351
- def set_trade_representative_contact_information(v, options)
352
- return unless options[:trade_representative_contact_information]
353
- info = options[:trade_representative_contact_information]
354
- UI.user_error!("`trade_representative_contact_information` must be a hash", show_github_issues: true) unless info.kind_of?(Hash)
355
-
356
- TRADE_REPRESENTATIVE_CONTACT_INFORMATION_VALUES.each do |key, option_name|
357
- v.send("#{key}=", info[option_name].to_s.chomp) if info[option_name]
358
- end
359
- end
360
-
361
- def set_review_information(v, options)
515
+ def set_review_information(version, options)
362
516
  return unless options[:app_review_information]
363
517
  info = options[:app_review_information]
364
518
  UI.user_error!("`app_review_information` must be a hash", show_github_issues: true) unless info.kind_of?(Hash)
365
519
 
366
- REVIEW_INFORMATION_VALUES.each do |key, option_name|
367
- v.send("#{key}=", info[option_name].to_s.chomp) if info[option_name]
520
+ attributes = {}
521
+ REVIEW_INFORMATION_VALUES.each do |key, attribute_name|
522
+ strip_value = info[key].to_s.strip
523
+ attributes[attribute_name] = strip_value unless strip_value.empty?
524
+ end
525
+
526
+ if !attributes["demoAccountName"].to_s.empty? && !attributes["demoAccountPassword"].to_s.empty?
527
+ attributes["demoAccountRequired"] = true
528
+ else
529
+ attributes["demoAccountRequired"] = false
530
+ end
531
+
532
+ UI.message("Uploading app review information to App Store Connect")
533
+ app_store_review_detail = begin
534
+ version.fetch_app_store_review_detail
535
+ rescue
536
+ nil
537
+ end # errors if doesn't exist
538
+ if app_store_review_detail
539
+ app_store_review_detail.update(attributes: attributes)
540
+ else
541
+ version.create_app_store_review_detail(attributes: attributes)
368
542
  end
369
- v.review_user_needed = (v.review_demo_user.to_s.chomp + v.review_demo_password.to_s.chomp).length > 0
370
543
  end
371
544
 
372
- def set_review_attachment_file(v, options)
373
- return unless options[:app_review_attachment_file]
374
- v.upload_review_attachment!(options[:app_review_attachment_file])
545
+ def set_review_attachment_file(version, options)
546
+ app_store_review_detail = version.fetch_app_store_review_detail
547
+ app_review_attachments = app_store_review_detail.fetch_app_review_attachments
548
+
549
+ if options[:app_review_attachment_file]
550
+ app_review_attachments.each do |app_review_attachment|
551
+ UI.message("Removing previous review attachment file from App Store Connect")
552
+ app_review_attachment.delete!
553
+ end
554
+
555
+ UI.message("Uploading review attachment file to App Store Connect")
556
+ app_store_review_detail.upload_attachment(path: options[:app_review_attachment_file])
557
+ else
558
+ app_review_attachments.each(&:delete!)
559
+ UI.message("Removing review attachment file to App Store Connect") unless app_review_attachments.empty?
560
+ end
375
561
  end
376
562
 
377
- def set_app_rating(v, options)
563
+ def set_app_rating(version, options)
378
564
  return unless options[:app_rating_config_path]
379
565
 
380
566
  require 'json'
@@ -385,7 +571,33 @@ module Deliver
385
571
  UI.user_error!("Error parsing JSON file at path '#{options[:app_rating_config_path]}'")
386
572
  end
387
573
  UI.message("Setting the app's age rating...")
388
- v.update_rating(json)
574
+
575
+ # Maping from legacy ITC values to App Store Connect Values
576
+ mapped_values = {}
577
+ attributes = {}
578
+ json.each do |k, v|
579
+ new_key = Spaceship::ConnectAPI::AgeRatingDeclaration.map_key_from_itc(k)
580
+ new_value = Spaceship::ConnectAPI::AgeRatingDeclaration.map_value_from_itc(new_key, v)
581
+
582
+ mapped_values[k] = new_key
583
+ mapped_values[v] = new_value
584
+
585
+ attributes[new_key] = new_value
586
+ end
587
+
588
+ # Print deprecation warnings if category was mapped
589
+ has_mapped_values = false
590
+ mapped_values.each do |k, v|
591
+ next if k.nil? || v.nil?
592
+ next if k == v
593
+ has_mapped_values = true
594
+ UI.deprecated("Age rating '#{k}' from iTunesConnect as been deprecated. Please replace with '#{v}'")
595
+ end
596
+ UI.deprecated("You can find more info at https://docs.fastlane.tools/actions/deliver/#reference") if has_mapped_values
597
+
598
+ age_rating_delcaration = version.fetch_age_rating_declaration
599
+ age_rating_delcaration.update(attributes: attributes)
389
600
  end
390
601
  end
602
+ # rubocop:enable Metrics/ClassLength
391
603
  end