fastlane 2.117.0.beta.20190228200006 → 2.117.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,4 +18,4 @@ class Gymfile: GymfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.116.1
21
+ // Generated with fastlane 2.117.0
@@ -18,4 +18,4 @@ class Matchfile: MatchfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.116.1
21
+ // Generated with fastlane 2.117.0
@@ -18,4 +18,4 @@ class Precheckfile: PrecheckfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.116.1
21
+ // Generated with fastlane 2.117.0
@@ -18,4 +18,4 @@ class Scanfile: ScanfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.116.1
21
+ // Generated with fastlane 2.117.0
@@ -18,4 +18,4 @@ class Screengrabfile: ScreengrabfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.116.1
21
+ // Generated with fastlane 2.117.0
@@ -18,4 +18,4 @@ class Snapshotfile: SnapshotfileProtocol {
18
18
 
19
19
 
20
20
 
21
- // Generated with fastlane 2.116.1
21
+ // Generated with fastlane 2.117.0
@@ -72,28 +72,13 @@ module Pilot
72
72
  UI.user_error!("No build to distribute!")
73
73
  end
74
74
 
75
- if should_update_app_test_information?(options)
76
- app_test_info = Spaceship::TestFlight::AppTestInfo.find(app_id: build.app_id)
77
- app_test_info.test_info.feedback_email = options[:beta_app_feedback_email] if options[:beta_app_feedback_email]
78
- app_test_info.test_info.description = options[:beta_app_description] if options[:beta_app_description]
79
- begin
80
- app_test_info.save_for_app!(app_id: build.app_id)
81
- UI.success("Successfully set the beta_app_feedback_email and/or beta_app_description")
82
- rescue => ex
83
- UI.user_error!("Could not set beta_app_feedback_email and/or beta_app_description: #{ex}")
84
- end
85
- end
86
-
87
- if should_update_build_information?(options)
88
- begin
89
- build.update_build_information!(whats_new: options[:changelog])
90
- UI.success("Successfully set the changelog for build")
91
- rescue => ex
92
- UI.user_error!("Could not set changelog: #{ex}")
93
- end
94
- end
95
-
96
- build.auto_notify_enabled = config[:notify_external_testers]
75
+ # Update beta app meta info
76
+ # 1. Demo account required
77
+ # 2. App info
78
+ # 3. Localized app info
79
+ # 4. Localized build info
80
+ # 5. Auto notify enabled with config[:notify_external_testers]
81
+ update_beta_app_meta(options, build)
97
82
 
98
83
  return if config[:skip_submission]
99
84
  if options[:reject_build_waiting_for_review]
@@ -129,6 +114,44 @@ module Pilot
129
114
  ))
130
115
  end
131
116
 
117
+ def update_beta_app_meta(options, build)
118
+ # Setting account required wth AppStore Connect API
119
+ update_review_detail(build.app_id, { demo_account_required: options[:demo_account_required] })
120
+
121
+ if should_update_beta_app_review_info(options)
122
+ update_review_detail(build.app_id, options[:beta_app_review_info])
123
+ end
124
+
125
+ if should_update_localized_app_information?(options)
126
+ update_localized_app_review(build.app_id, options[:localized_app_info])
127
+ elsif should_update_app_test_information?(options)
128
+ default_info = {}
129
+ default_info[:feedback_email] = options[:beta_app_feedback_email] if options[:beta_app_feedback_email]
130
+ default_info[:description] = options[:beta_app_description] if options[:beta_app_description]
131
+ begin
132
+ update_localized_app_review(build.app_id, {}, default_info: default_info)
133
+ UI.success("Successfully set the beta_app_feedback_email and/or beta_app_description")
134
+ rescue => ex
135
+ UI.user_error!("Could not set beta_app_feedback_email and/or beta_app_description: #{ex}")
136
+ end
137
+ end
138
+
139
+ if should_update_localized_build_information?(options)
140
+ update_localized_build_review(build, options[:localized_build_info])
141
+ elsif should_update_build_information?(options)
142
+ begin
143
+ update_localized_build_review(build, {}, default_info: { whats_new: options[:changelog] })
144
+ UI.success("Successfully set the changelog for build")
145
+ rescue => ex
146
+ UI.user_error!("Could not set changelog: #{ex}")
147
+ end
148
+ end
149
+
150
+ update_build_beta_details(build, {
151
+ auto_notify_enabled: options[:notify_external_testers]
152
+ })
153
+ end
154
+
132
155
  def self.truncate_changelog(changelog)
133
156
  max_changelog_length = 4000
134
157
  if changelog && changelog.length > max_changelog_length
@@ -163,6 +186,10 @@ module Pilot
163
186
  return row
164
187
  end
165
188
 
189
+ def should_update_beta_app_review_info(options)
190
+ !options[:beta_app_review_info].nil?
191
+ end
192
+
166
193
  def should_update_build_information?(options)
167
194
  options[:changelog].to_s.length > 0
168
195
  end
@@ -171,6 +198,14 @@ module Pilot
171
198
  options[:beta_app_description].to_s.length > 0 || options[:beta_app_feedback_email].to_s.length > 0
172
199
  end
173
200
 
201
+ def should_update_localized_app_information?(options)
202
+ !options[:localized_app_info].nil?
203
+ end
204
+
205
+ def should_update_localized_build_information?(options)
206
+ !options[:localized_build_info].nil?
207
+ end
208
+
174
209
  def distribute_build(uploaded_build, options)
175
210
  UI.message("Distributing new build to testers: #{uploaded_build.train_version} - #{uploaded_build.build_version}")
176
211
 
@@ -178,7 +213,6 @@ module Pilot
178
213
  uploaded_build.export_compliance.encryption_updated = false
179
214
 
180
215
  if options[:groups] || options[:distribute_external]
181
- uploaded_build.beta_review_info.demo_account_required = options[:demo_account_required] # this needs to be set for iTC to continue
182
216
  begin
183
217
  uploaded_build.submit_for_testflight_review!
184
218
  rescue => ex
@@ -216,5 +250,134 @@ module Pilot
216
250
 
217
251
  true
218
252
  end
253
+
254
+ def update_review_detail(app_id, info)
255
+ info = info.collect { |k, v| [k.to_sym, v] }.to_h
256
+
257
+ attributes = {}
258
+ attributes[:contactEmail] = info[:contact_email] if info.key?(:contact_email)
259
+ attributes[:contactFirstName] = info[:contact_first_name] if info.key?(:contact_first_name)
260
+ attributes[:contactLastName] = info[:contact_last_name] if info.key?(:contact_last_name)
261
+ attributes[:contactPhone] = info[:contact_phone] if info.key?(:contact_phone)
262
+ attributes[:demoAccountName] = info[:demo_account_name] if info.key?(:demo_account_name)
263
+ attributes[:demoAccountPassword] = info[:demo_account_password] if info.key?(:demo_account_password)
264
+ attributes[:demoAccountRequired] = info[:demo_account_required] if info.key?(:demo_account_required)
265
+ attributes[:notes] = info[:notes] if info.key?(:notes)
266
+
267
+ client = Spaceship::ConnectAPI::Base.client
268
+ client.patch_beta_app_review_detail(app_id: app_id, attributes: attributes)
269
+ end
270
+
271
+ def update_localized_app_review(app_id, info_by_lang, default_info: nil)
272
+ info_by_lang = info_by_lang.collect { |k, v| [k.to_sym, v] }.to_h
273
+
274
+ if default_info
275
+ info_by_lang.delete(:default)
276
+ else
277
+ default_info = info_by_lang.delete(:default)
278
+ end
279
+
280
+ # Initialize hash of lang codes
281
+ langs_localization_ids = {}
282
+
283
+ # Validate locales exist
284
+ client = Spaceship::ConnectAPI::Base.client
285
+ localizations = client.get_beta_app_localizations(filter: { app: app_id })
286
+ localizations.each do |localization|
287
+ localization_id = localization["id"]
288
+ attributes = localization["attributes"]
289
+ locale = attributes["locale"]
290
+
291
+ langs_localization_ids[locale.to_sym] = localization_id
292
+ end
293
+
294
+ # Create or update localized app review info
295
+ langs_localization_ids.each do |lang_code, localization_id|
296
+ info = info_by_lang[lang_code]
297
+
298
+ info = default_info unless info
299
+ update_localized_app_review_for_lang(app_id, localization_id, lang_code, info) if info
300
+ end
301
+ end
302
+
303
+ def update_localized_app_review_for_lang(app_id, localization_id, locale, info)
304
+ attributes = {}
305
+ attributes[:feedbackEmail] = info[:feedback_email] if info.key?(:feedback_email)
306
+ attributes[:marketingUrl] = info[:marketing_url] if info.key?(:marketing_url)
307
+ attributes[:privacyPolicyUrl] = info[:privacy_policy_url] if info.key?(:privacy_policy_url)
308
+ attributes[:tvOsPrivacyPolicy] = info[:tv_os_privacy_policy_url] if info.key?(:tv_os_privacy_policy_url)
309
+ attributes[:description] = info[:description] if info.key?(:description)
310
+
311
+ client = Spaceship::ConnectAPI::Base.client
312
+ if localization_id
313
+ client.patch_beta_app_localizations(localization_id: localization_id, attributes: attributes)
314
+ else
315
+ attributes[:locale] = locale if locale
316
+ client.post_beta_app_localizations(app_id: app_id, attributes: attributes)
317
+ end
318
+ end
319
+
320
+ def update_localized_build_review(build, info_by_lang, default_info: nil)
321
+ resp = Spaceship::ConnectAPI::Base.client.get_builds(filter: { expired: false, processingState: "PROCESSING,VALID", version: build.build_version })
322
+ build_id = resp.first["id"]
323
+
324
+ info_by_lang = info_by_lang.collect { |k, v| [k.to_sym, v] }.to_h
325
+
326
+ if default_info
327
+ info_by_lang.delete(:default)
328
+ else
329
+ default_info = info_by_lang.delete(:default)
330
+ end
331
+
332
+ # Initialize hash of lang codes
333
+ langs_localization_ids = {}
334
+
335
+ # Validate locales exist
336
+ client = Spaceship::ConnectAPI::Base.client
337
+ localizations = client.get_beta_build_localizations(filter: { build: build_id })
338
+ localizations.each do |localization|
339
+ localization_id = localization["id"]
340
+ attributes = localization["attributes"]
341
+ locale = attributes["locale"]
342
+
343
+ langs_localization_ids[locale.to_sym] = localization_id
344
+ end
345
+
346
+ # Create or update localized app review info
347
+ langs_localization_ids.each do |lang_code, localization_id|
348
+ info = info_by_lang[lang_code]
349
+
350
+ info = default_info unless info
351
+ update_localized_build_review_for_lang(build_id, localization_id, lang_code, info) if info
352
+ end
353
+ end
354
+
355
+ def update_localized_build_review_for_lang(build_id, localization_id, locale, info)
356
+ attributes = {}
357
+ attributes[:whatsNew] = info[:whats_new] if info.key?(:whats_new)
358
+
359
+ client = Spaceship::ConnectAPI::Base.client
360
+ if localization_id
361
+ client.patch_beta_build_localizations(localization_id: localization_id, attributes: attributes)
362
+ else
363
+ attributes[:locale] = locale if locale
364
+ client.post_beta_build_localizations(build_id: build_id, attributes: attributes)
365
+ end
366
+ end
367
+
368
+ def update_build_beta_details(build, info)
369
+ client = Spaceship::ConnectAPI::Base.client
370
+
371
+ resp = client.get_builds(filter: { expired: false, processingState: "PROCESSING,VALID", version: build.build_version })
372
+ build_id = resp.first["id"]
373
+
374
+ resp = client.get_build_beta_details(filter: { build: build_id })
375
+ build_beta_details_id = resp.first["id"]
376
+
377
+ attributes = {}
378
+ attributes[:autoNotifyEnabled] = info[:auto_notify_enabled] if info.key?(:auto_notify_enabled)
379
+
380
+ client.patch_build_beta_details(build_beta_details_id: build_beta_details_id, attributes: attributes)
381
+ end
219
382
  end
220
383
  end
@@ -21,7 +21,7 @@ module Pilot
21
21
 
22
22
  UI.message("Login to App Store Connect (#{config[:username]})")
23
23
  Spaceship::Tunes.login(config[:username])
24
- Spaceship::Tunes.select_team
24
+ Spaceship::Tunes.select_team(team_id: config[:team_id], team_name: config[:team_name])
25
25
  UI.message("Login successful")
26
26
  end
27
27
 
@@ -1,8 +1,10 @@
1
1
  require 'fastlane_core/helper'
2
+ require 'fastlane/boolean'
2
3
 
3
4
  module Pilot
4
5
  Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
5
6
  UI = FastlaneCore::UI
7
+ Boolean = Fastlane::Boolean
6
8
  ROOT = Pathname.new(File.expand_path('../../..', __FILE__))
7
9
 
8
10
  DESCRIPTION = "The best way to manage your TestFlight testers and builds from your terminal"
@@ -10,6 +10,7 @@ module Pilot
10
10
  user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
11
11
 
12
12
  [
13
+ # app upload info
13
14
  FastlaneCore::ConfigItem.new(key: :username,
14
15
  short_option: "-u",
15
16
  env_name: "PILOT_USERNAME",
@@ -34,6 +35,14 @@ module Pilot
34
35
  verify_block: proc do |value|
35
36
  UI.user_error!("The platform can only be ios, appletvos, or osx") unless ['ios', 'appletvos', 'osx'].include?(value)
36
37
  end),
38
+ FastlaneCore::ConfigItem.new(key: :apple_id,
39
+ short_option: "-p",
40
+ env_name: "PILOT_APPLE_ID",
41
+ description: "The unique App ID provided by App Store Connect",
42
+ optional: true,
43
+ code_gen_sensitive: true,
44
+ default_value: ENV["TESTFLIGHT_APPLE_ID"],
45
+ default_value_dynamic: true),
37
46
  FastlaneCore::ConfigItem.new(key: :ipa,
38
47
  short_option: "-i",
39
48
  optional: true,
@@ -47,11 +56,35 @@ module Pilot
47
56
  UI.user_error!("Could not find ipa file at path '#{value}'") unless File.exist?(value)
48
57
  UI.user_error!("'#{value}' doesn't seem to be an ipa file") unless value.end_with?(".ipa")
49
58
  end),
50
- FastlaneCore::ConfigItem.new(key: :changelog,
51
- short_option: "-w",
59
+
60
+ # app review info
61
+ FastlaneCore::ConfigItem.new(key: :demo_account_required,
62
+ type: Boolean,
63
+ env_name: "DEMO_ACCOUNT_REQUIRED",
64
+ description: "Do you need a demo account when Apple does review?",
65
+ default_value: false),
66
+ FastlaneCore::ConfigItem.new(key: :beta_app_review_info,
67
+ type: Hash,
68
+ env_name: "PILOT_BETA_APP_REVIEW_INFO",
69
+ description: "Beta app review information for contact info and demo account",
52
70
  optional: true,
53
- env_name: "PILOT_CHANGELOG",
54
- description: "Provide the 'What to Test' text when uploading a new build. `skip_waiting_for_build_processing: false` is required to set the changelog"),
71
+ verify_block: proc do |values|
72
+ valid_keys = %w(contact_email contact_first_name contact_last_name contact_phone demo_account_required demo_account_name demo_account_password notes)
73
+ values.keys.each { |value| UI.user_error!("Invalid key '#{value}'") unless valid_keys.include?(value.to_s) }
74
+ end),
75
+
76
+ # app detail
77
+ FastlaneCore::ConfigItem.new(key: :localized_app_info,
78
+ type: Hash,
79
+ env_name: "PILOT_LOCALIZED_APP_INFO",
80
+ description: "Localized beta app test info for description, feedback email, marketing url, and privacy policy",
81
+ optional: true,
82
+ verify_block: proc do |lang_values|
83
+ valid_keys = %w(feedback_email marketing_url privacy_policy_url tv_os_privacy_policy_url description)
84
+ lang_values.values.each do |values|
85
+ values.keys.each { |value| UI.user_error!("Invalid key '#{value}'") unless valid_keys.include?(value.to_s) }
86
+ end
87
+ end),
55
88
  FastlaneCore::ConfigItem.new(key: :beta_app_description,
56
89
  short_option: "-d",
57
90
  optional: true,
@@ -62,6 +95,24 @@ module Pilot
62
95
  optional: true,
63
96
  env_name: "PILOT_BETA_APP_FEEDBACK",
64
97
  description: "Provide the beta app email when uploading a new build"),
98
+
99
+ # build review info
100
+ FastlaneCore::ConfigItem.new(key: :localized_build_info,
101
+ type: Hash,
102
+ env_name: "PILOT_LOCALIZED_BUILD_INFO",
103
+ description: "Localized beta app test info for what's new",
104
+ optional: true,
105
+ verify_block: proc do |lang_values|
106
+ valid_keys = %w(whats_new)
107
+ lang_values.values.each do |values|
108
+ values.keys.each { |value| UI.user_error!("Invalid key '#{value}'") unless valid_keys.include?(value.to_s) }
109
+ end
110
+ end),
111
+ FastlaneCore::ConfigItem.new(key: :changelog,
112
+ short_option: "-w",
113
+ optional: true,
114
+ env_name: "PILOT_CHANGELOG",
115
+ description: "Provide the 'What to Test' text when uploading a new build. `skip_waiting_for_build_processing: false` is required to set the changelog"),
65
116
  FastlaneCore::ConfigItem.new(key: :skip_submission,
66
117
  short_option: "-s",
67
118
  env_name: "PILOT_SKIP_SUBMISSION",
@@ -81,14 +132,8 @@ module Pilot
81
132
  description: "Update build info immediately after validation. This is deprecated and will be removed in a future release. App Store Connect no longer supports setting build info until after build processing has completed, which is when build info is updated by default",
82
133
  is_string: false,
83
134
  default_value: false),
84
- FastlaneCore::ConfigItem.new(key: :apple_id,
85
- short_option: "-p",
86
- env_name: "PILOT_APPLE_ID",
87
- description: "The unique App ID provided by App Store Connect",
88
- optional: true,
89
- code_gen_sensitive: true,
90
- default_value: ENV["TESTFLIGHT_APPLE_ID"],
91
- default_value_dynamic: true),
135
+
136
+ # distribution
92
137
  FastlaneCore::ConfigItem.new(key: :distribute_external,
93
138
  is_string: false,
94
139
  env_name: "PILOT_DISTRIBUTE_EXTERNAL",
@@ -99,11 +144,8 @@ module Pilot
99
144
  env_name: "PILOT_NOTIFY_EXTERNAL_TESTERS",
100
145
  description: "Should notify external testers?",
101
146
  default_value: true),
102
- FastlaneCore::ConfigItem.new(key: :demo_account_required,
103
- is_string: false,
104
- env_name: "DEMO_ACCOUNT_REQUIRED",
105
- description: "Do you need a demo account when Apple does review?",
106
- default_value: false),
147
+
148
+ # testers
107
149
  FastlaneCore::ConfigItem.new(key: :first_name,
108
150
  short_option: "-f",
109
151
  env_name: "PILOT_TESTER_FIRST_NAME",
@@ -128,15 +170,17 @@ module Pilot
128
170
  description: "Path to a CSV file of testers",
129
171
  default_value: "./testers.csv",
130
172
  optional: true),
131
- FastlaneCore::ConfigItem.new(key: :wait_processing_interval,
132
- short_option: "-k",
133
- env_name: "PILOT_WAIT_PROCESSING_INTERVAL",
134
- description: "Interval in seconds to wait for App Store Connect processing",
135
- default_value: 30,
136
- type: Integer,
173
+ FastlaneCore::ConfigItem.new(key: :groups,
174
+ short_option: "-g",
175
+ env_name: "PILOT_GROUPS",
176
+ description: "Associate tester to one group or more by group name / group id. E.g. `-g \"Team 1\",\"Team 2\"`",
177
+ optional: true,
178
+ type: Array,
137
179
  verify_block: proc do |value|
138
- UI.user_error!("Please enter a valid positive number of seconds") unless value.to_i > 0
180
+ UI.user_error!("Could not evaluate array from '#{value}'") unless value.kind_of?(Array)
139
181
  end),
182
+
183
+ # app store connect teams
140
184
  FastlaneCore::ConfigItem.new(key: :team_id,
141
185
  short_option: "-q",
142
186
  env_name: "PILOT_TEAM_ID",
@@ -177,14 +221,16 @@ module Pilot
177
221
  description: "The provider short name to be used with the iTMSTransporter to identify your team. To get provider short name run `pathToXcode.app/Contents/Applications/Application\\ Loader.app/Contents/itms/bin/iTMSTransporter -m provider -u 'USERNAME' -p 'PASSWORD' -account_type itunes_connect -v off`. The short names of providers should be listed in the second column",
178
222
  optional: true),
179
223
  # rubocop:enable Metrics/LineLength
180
- FastlaneCore::ConfigItem.new(key: :groups,
181
- short_option: "-g",
182
- env_name: "PILOT_GROUPS",
183
- description: "Associate tester to one group or more by group name / group id. E.g. `-g \"Team 1\",\"Team 2\"`",
184
- optional: true,
185
- type: Array,
224
+
225
+ # waiting and uploaded build
226
+ FastlaneCore::ConfigItem.new(key: :wait_processing_interval,
227
+ short_option: "-k",
228
+ env_name: "PILOT_WAIT_PROCESSING_INTERVAL",
229
+ description: "Interval in seconds to wait for App Store Connect processing",
230
+ default_value: 30,
231
+ type: Integer,
186
232
  verify_block: proc do |value|
187
- UI.user_error!("Could not evaluate array from '#{value}'") unless value.kind_of?(Array)
233
+ UI.user_error!("Please enter a valid positive number of seconds") unless value.to_i > 0
188
234
  end),
189
235
  FastlaneCore::ConfigItem.new(key: :wait_for_uploaded_build,
190
236
  env_name: "PILOT_WAIT_FOR_UPLOADED_BUILD",