fastlane 2.155.3 → 2.157.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -78
  3. data/deliver/lib/deliver.rb +1 -0
  4. data/deliver/lib/deliver/app_screenshot_iterator.rb +95 -0
  5. data/deliver/lib/deliver/detect_values.rb +4 -1
  6. data/deliver/lib/deliver/languages.rb +7 -0
  7. data/deliver/lib/deliver/loader.rb +4 -5
  8. data/deliver/lib/deliver/queue_worker.rb +64 -0
  9. data/deliver/lib/deliver/runner.rb +7 -5
  10. data/deliver/lib/deliver/upload_screenshots.rb +143 -128
  11. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +120 -0
  12. data/fastlane/lib/fastlane/actions/commit_version_bump.rb +1 -1
  13. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -0
  14. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +17 -1
  15. data/fastlane/lib/fastlane/actions/set_changelog.rb +2 -2
  16. data/fastlane/lib/fastlane/actions/sonar.rb +5 -0
  17. data/fastlane/lib/fastlane/actions/spaceship_stats.rb +73 -0
  18. data/fastlane/lib/fastlane/actions/upload_to_testflight.rb +4 -0
  19. data/fastlane/lib/fastlane/version.rb +1 -1
  20. data/fastlane/swift/Deliverfile.swift +1 -1
  21. data/fastlane/swift/DeliverfileProtocol.swift +1 -1
  22. data/fastlane/swift/Fastlane.swift +67 -7
  23. data/fastlane/swift/Gymfile.swift +1 -1
  24. data/fastlane/swift/GymfileProtocol.swift +1 -1
  25. data/fastlane/swift/Matchfile.swift +1 -1
  26. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  27. data/fastlane/swift/Precheckfile.swift +1 -1
  28. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  29. data/fastlane/swift/Scanfile.swift +1 -1
  30. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  31. data/fastlane/swift/Screengrabfile.swift +1 -1
  32. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  33. data/fastlane/swift/Snapshotfile.swift +1 -1
  34. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  35. data/fastlane_core/lib/fastlane_core/command_executor.rb +1 -0
  36. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +71 -42
  37. data/fastlane_core/lib/fastlane_core/project.rb +1 -0
  38. data/gym/lib/gym/error_handler.rb +1 -1
  39. data/gym/lib/gym/generators/build_command_generator.rb +0 -1
  40. data/pilot/lib/pilot/build_manager.rb +18 -4
  41. data/pilot/lib/pilot/manager.rb +16 -5
  42. data/pilot/lib/pilot/options.rb +16 -0
  43. data/produce/lib/produce/itunes_connect.rb +2 -2
  44. data/scan/lib/scan/test_command_generator.rb +3 -1
  45. data/screengrab/lib/screengrab/runner.rb +36 -17
  46. data/sigh/lib/sigh/runner.rb +4 -4
  47. data/snapshot/lib/snapshot/test_command_generator_base.rb +3 -1
  48. data/spaceship/lib/spaceship.rb +4 -0
  49. data/spaceship/lib/spaceship/client.rb +2 -0
  50. data/spaceship/lib/spaceship/connect_api.rb +0 -15
  51. data/spaceship/lib/spaceship/connect_api/api_client.rb +270 -0
  52. data/spaceship/lib/spaceship/connect_api/client.rb +144 -213
  53. data/spaceship/lib/spaceship/connect_api/provisioning/client.rb +8 -17
  54. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +75 -64
  55. data/spaceship/lib/spaceship/connect_api/spaceship.rb +98 -0
  56. data/spaceship/lib/spaceship/connect_api/testflight/client.rb +8 -17
  57. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +288 -277
  58. data/spaceship/lib/spaceship/connect_api/token.rb +46 -5
  59. data/spaceship/lib/spaceship/connect_api/token_refresh_middleware.rb +24 -0
  60. data/spaceship/lib/spaceship/connect_api/tunes/client.rb +8 -17
  61. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +717 -706
  62. data/spaceship/lib/spaceship/connect_api/users/client.rb +8 -17
  63. data/spaceship/lib/spaceship/connect_api/users/users.rb +28 -17
  64. data/spaceship/lib/spaceship/stats_middleware.rb +65 -0
  65. metadata +26 -23
  66. data/match/lib/match/.options.rb.swp +0 -0
  67. data/match/lib/match/.runner.rb.swp +0 -0
  68. data/sigh/lib/sigh/.options.rb.swp +0 -0
  69. data/sigh/lib/sigh/.runner.rb.swp +0 -0
  70. data/spaceship/lib/spaceship/connect_api/models/.profile.rb.swp +0 -0
  71. data/spaceship/lib/spaceship/connect_api/provisioning/.provisioning.rb.swp +0 -0
@@ -4,10 +4,17 @@ require 'digest/md5'
4
4
  require_relative 'app_screenshot'
5
5
  require_relative 'module'
6
6
  require_relative 'loader'
7
+ require_relative 'queue_worker'
8
+ require_relative 'app_screenshot_iterator'
7
9
 
8
10
  module Deliver
9
11
  # upload screenshots to App Store Connect
10
12
  class UploadScreenshots
13
+ DeleteScreenshotJob = Struct.new(:app_screenshot, :localization, :app_screenshot_set)
14
+ UploadScreenshotJob = Struct.new(:app_screenshot_set, :path)
15
+
16
+ NUMBER_OF_THREADS = Helper.test? ? 1 : [ENV.fetch("DELIVER_NUMBER_OF_THREADS", 10).to_i, 10].min
17
+
11
18
  def upload(options, screenshots)
12
19
  return if options[:skip_screenshots]
13
20
  return if options[:edit_live]
@@ -50,57 +57,47 @@ module Deliver
50
57
  localizations = version.get_app_store_version_localizations
51
58
  end
52
59
 
53
- upload_screenshots(screenshots_per_language, localizations, options)
60
+ upload_screenshots(localizations, screenshots_per_language)
61
+
62
+ Helper.show_loading_indicator("Sorting screenshots uploaded...")
63
+ sort_screenshots(localizations)
64
+ Helper.hide_loading_indicator
65
+
66
+ UI.success("Successfully uploaded screenshots to App Store Connect")
54
67
  end
55
68
 
56
69
  def delete_screenshots(localizations, screenshots_per_language, tries: 5)
57
70
  tries -= 1
58
71
 
59
- # Get localizations on version
60
- localizations.each do |localization|
61
- # Only delete screenshots if trying to upload
62
- next unless screenshots_per_language.keys.include?(localization.locale)
63
-
64
- # Iterate over all screenshots for each set and delete
65
- screenshot_sets = localization.get_app_screenshot_sets
66
-
67
- # Multi threading delete on single localization
68
- threads = []
69
- errors = []
70
-
71
- screenshot_sets.each do |screenshot_set|
72
- UI.message("Removing all previously uploaded screenshots for '#{localization.locale}' '#{screenshot_set.screenshot_display_type}'...")
73
- screenshot_set.app_screenshots.each do |screenshot|
74
- UI.verbose("Deleting screenshot - #{localization.locale} #{screenshot_set.screenshot_display_type} #{screenshot.id}")
75
- threads << Thread.new do
76
- begin
77
- screenshot.delete!
78
- UI.verbose("Deleted screenshot - #{localization.locale} #{screenshot_set.screenshot_display_type} #{screenshot.id}")
79
- rescue => error
80
- UI.verbose("Failed to delete screenshot - #{localization.locale} #{screenshot_set.screenshot_display_type} #{screenshot.id}")
81
- errors << error
82
- end
83
- end
84
- end
72
+ worker = QueueWorker.new(NUMBER_OF_THREADS) do |job|
73
+ start_time = Time.now
74
+ target = "#{job.localization.locale} #{job.app_screenshot_set.screenshot_display_type} #{job.app_screenshot.id}"
75
+ begin
76
+ UI.verbose("Deleting '#{target}'")
77
+ job.app_screenshot.delete!
78
+ UI.message("Deleted '#{target}' - (#{Time.now - start_time} secs)")
79
+ rescue => error
80
+ UI.error("Failed to delete screenshot #{target} - (#{Time.now - start_time} secs)")
81
+ UI.error(error.message)
85
82
  end
83
+ end
86
84
 
87
- sleep(1) # Feels bad but sleeping a bit to let the threads catchup
88
-
89
- unless threads.empty?
90
- Helper.show_loading_indicator("Waiting for screenshots to be deleted for '#{localization.locale}'... (might be slow)") unless FastlaneCore::Globals.verbose?
91
- threads.each(&:join)
92
- Helper.hide_loading_indicator unless FastlaneCore::Globals.verbose?
93
- end
85
+ iterator = AppScreenshotIterator.new(localizations)
86
+ iterator.each_app_screenshot do |localization, app_screenshot_set, app_screenshot|
87
+ # Only delete screenshots if trying to upload
88
+ next unless screenshots_per_language.keys.include?(localization.locale)
94
89
 
95
- # Crash if any errors happen while deleting
96
- errors.each do |error|
97
- UI.error(error.message)
98
- end
90
+ UI.verbose("Queued delete sceeenshot job for #{localization.locale} #{app_screenshot_set.screenshot_display_type} #{app_screenshot.id}")
91
+ worker.enqueue(DeleteScreenshotJob.new(app_screenshot, localization, app_screenshot_set))
99
92
  end
100
93
 
94
+ worker.start
95
+
101
96
  # Verify all screenshots have been deleted
102
97
  # Sometimes API requests will fail but screenshots will still be deleted
103
- count = count_screenshots(localizations)
98
+ count = iterator.each_app_screenshot_set.map { |_, app_screenshot_set| app_screenshot_set }
99
+ .reduce(0) { |sum, app_screenshot_set| sum + app_screenshot_set.app_screenshots.size }
100
+
104
101
  UI.important("Number of screenshots not deleted: #{count}")
105
102
  if count > 0
106
103
  if tries.zero?
@@ -114,113 +111,127 @@ module Deliver
114
111
  end
115
112
  end
116
113
 
117
- def count_screenshots(localizations)
118
- count = 0
119
- localizations.each do |localization|
120
- screenshot_sets = localization.get_app_screenshot_sets
121
- screenshot_sets.each do |screenshot_set|
122
- count += screenshot_set.app_screenshots.size
114
+ def upload_screenshots(localizations, screenshots_per_language, tries: 5)
115
+ tries -= 1
116
+
117
+ # Upload screenshots
118
+ worker = QueueWorker.new(NUMBER_OF_THREADS) do |job|
119
+ begin
120
+ UI.verbose("Uploading '#{job.path}'...")
121
+ start_time = Time.now
122
+ job.app_screenshot_set.upload_screenshot(path: job.path, wait_for_processing: false)
123
+ UI.message("Uploaded '#{job.path}'... (#{Time.now - start_time} secs)")
124
+ rescue => error
125
+ UI.error(error)
123
126
  end
124
127
  end
125
128
 
126
- return count
127
- end
129
+ number_of_screenshots = 0
130
+ iterator = AppScreenshotIterator.new(localizations)
131
+ iterator.each_local_screenshot(screenshots_per_language) do |localization, app_screenshot_set, screenshot, index|
132
+ if index >= 10
133
+ UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})")
134
+ next
135
+ end
128
136
 
129
- def upload_screenshots(screenshots_per_language, localizations, options)
130
- # Check if should wait for processing
131
- # Default to waiting if submitting for review (since needed for submission)
132
- # Otherwise use enviroment variable
133
- if ENV["DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING"].nil?
134
- wait_for_processing = options[:submit_for_review]
135
- UI.verbose("Setting wait_for_processing from ':submit_for_review' option")
136
- else
137
- UI.verbose("Setting wait_for_processing from 'DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING' environment variable")
138
- wait_for_processing = !FastlaneCore::Env.truthy?("DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING")
139
- end
137
+ checksum = UploadScreenshots.calculate_checksum(screenshot.path)
138
+ duplicate = (app_screenshot_set.app_screenshots || []).any? { |s| s.source_file_checksum == checksum }
140
139
 
141
- if wait_for_processing
142
- UI.important("Will wait for screenshot image processing")
143
- UI.important("Set env DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING=true to skip waiting for screenshots to process")
144
- else
145
- UI.important("Skipping the wait for screenshot image processing (which may affect submission)")
146
- UI.important("Set env DELIVER_SKIP_WAIT_FOR_SCREENSHOT_PROCESSING=false to wait for screenshots to process")
140
+ # Enqueue uploading job if it's not duplicated otherwise screenshot will be skipped
141
+ if duplicate
142
+ UI.message("Previous uploaded. Skipping '#{screenshot.path}'...")
143
+ else
144
+ worker.enqueue(UploadScreenshotJob.new(app_screenshot_set, screenshot.path))
145
+ end
146
+
147
+ number_of_screenshots += 1
147
148
  end
148
149
 
149
- # Upload screenshots
150
- indized = {} # per language and device type
150
+ worker.start
151
151
 
152
- screenshots_per_language.each do |language, screenshots_for_language|
153
- # Find localization to upload screenshots to
154
- localization = localizations.find do |l|
155
- l.locale == language
156
- end
152
+ UI.verbose('Uploading jobs are completed')
157
153
 
158
- unless localization
159
- UI.error("Couldn't find localization on version for #{language}")
160
- next
161
- end
154
+ Helper.show_loading_indicator("Waiting for all the screenshots processed...")
155
+ states = wait_for_complete(iterator)
156
+ Helper.hide_loading_indicator
157
+ retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, localizations, screenshots_per_language)
162
158
 
163
- indized[localization.locale] ||= {}
159
+ UI.message("Successfully uploaded all screenshots")
160
+ end
164
161
 
165
- # Create map to find screenshot set to add screenshot to
166
- app_screenshot_sets_map = {}
167
- app_screenshot_sets = localization.get_app_screenshot_sets
168
- app_screenshot_sets.each do |app_screenshot_set|
169
- app_screenshot_sets_map[app_screenshot_set.screenshot_display_type] = app_screenshot_set
162
+ # Verify all screenshots have been processed
163
+ def wait_for_complete(iterator)
164
+ loop do
165
+ states = iterator.each_app_screenshot.map { |_, _, app_screenshot| app_screenshot }.each_with_object({}) do |app_screenshot, hash|
166
+ state = app_screenshot.asset_delivery_state['state']
167
+ hash[state] ||= 0
168
+ hash[state] += 1
169
+ end
170
170
 
171
- # Set initial screnshot count
172
- indized[localization.locale][app_screenshot_set.screenshot_display_type] ||= {
173
- count: app_screenshot_set.app_screenshots.size,
174
- checksums: []
175
- }
171
+ is_processing = states.fetch('UPLOAD_COMPLETE', 0) > 0
172
+ return states unless is_processing
176
173
 
177
- checksums = app_screenshot_set.app_screenshots.map(&:source_file_checksum).uniq
178
- indized[localization.locale][app_screenshot_set.screenshot_display_type][:checksums] = checksums
179
- end
174
+ UI.verbose("There are still incomplete screenshots - #{states}")
175
+ sleep(5)
176
+ end
177
+ end
180
178
 
181
- UI.message("Uploading #{screenshots_for_language.length} screenshots for language #{language}")
182
- screenshots_for_language.each do |screenshot|
183
- display_type = screenshot.device_type
184
- set = app_screenshot_sets_map[display_type]
179
+ # Verify all screenshots states on App Store Connect are okay
180
+ def retry_upload_screenshots_if_needed(iterator, states, number_of_screenshots, tries, localizations, screenshots_per_language)
181
+ is_failure = states.fetch("FAILED", 0) > 0
182
+ is_missing_screenshot = !screenshots_per_language.empty? && !verify_local_screenshots_are_uploaded(iterator, screenshots_per_language)
183
+ return unless is_failure || is_missing_screenshot
185
184
 
186
- if display_type.nil?
187
- UI.error("Error... Screenshot size #{screenshot.screen_size} not valid for App Store Connect")
188
- next
189
- end
185
+ if tries.zero?
186
+ iterator.each_app_screenshot.select { |_, _, app_screenshot| app_screenshot.error? }.each do |localization, _, app_screenshot|
187
+ UI.error("#{app_screenshot.file_name} for #{localization.locale} has error(s) - #{app_screenshot.error_messages.join(', ')}")
188
+ end
189
+ incomplete_screenshot_count = states.reject { |k, v| k == 'COMPLETE' }.reduce(0) { |sum, (k, v)| sum + v }
190
+ UI.user_error!("Failed verification of all screenshots uploaded... #{incomplete_screenshot_count} incomplete screenshot(s) still exist")
191
+ else
192
+ UI.error("Failed to upload all screenshots... Tries remaining: #{tries}")
193
+ # Delete bad entries before retry
194
+ iterator.each_app_screenshot do |_, _, app_screenshot|
195
+ app_screenshot.delete! unless app_screenshot.complete?
196
+ end
197
+ upload_screenshots(localizations, screenshots_per_language, tries: tries)
198
+ end
199
+ end
190
200
 
191
- unless set
192
- set = localization.create_app_screenshot_set(attributes: {
193
- screenshotDisplayType: display_type
194
- })
195
- app_screenshot_sets_map[display_type] = set
201
+ # Return `true` if all the local screenshots are uploaded to App Store Connect
202
+ def verify_local_screenshots_are_uploaded(iterator, screenshots_per_language)
203
+ # Check if local screenshots' checksum exist on App Store Connect
204
+ checksum_to_app_screenshot = iterator.each_app_screenshot.map { |_, _, app_screenshot| [app_screenshot.source_file_checksum, app_screenshot] }.to_h
196
205
 
197
- indized[localization.locale][set.screenshot_display_type] = {
198
- count: 0,
199
- checksums: []
200
- }
201
- end
206
+ missing_local_screenshots = iterator.each_local_screenshot(screenshots_per_language).select do |_, _, local_screenshot, index|
207
+ 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
209
+ end
202
210
 
203
- index = indized[localization.locale][set.screenshot_display_type][:count]
211
+ missing_local_screenshots.each do |_, _, screenshot, _|
212
+ UI.error("#{screenshot.path} is missing on App Store Connect.")
213
+ end
204
214
 
205
- if index >= 10
206
- UI.error("Too many screenshots found for device '#{screenshot.device_type}' in '#{screenshot.language}', skipping this one (#{screenshot.path})")
207
- next
208
- end
215
+ missing_local_screenshots.empty?
216
+ end
209
217
 
210
- bytes = File.binread(screenshot.path)
211
- checksum = Digest::MD5.hexdigest(bytes)
212
- duplicate = indized[localization.locale][set.screenshot_display_type][:checksums].include?(checksum)
218
+ def sort_screenshots(localizations)
219
+ iterator = AppScreenshotIterator.new(localizations)
213
220
 
214
- if duplicate
215
- UI.message("Previous uploaded. Skipping '#{screenshot.path}'...")
216
- else
217
- indized[localization.locale][set.screenshot_display_type][:count] += 1
218
- UI.message("Uploading '#{screenshot.path}'...")
219
- set.upload_screenshot(path: screenshot.path, wait_for_processing: wait_for_processing)
220
- end
221
+ # Re-order screenshots within app_screenshot_set
222
+ worker = QueueWorker.new(NUMBER_OF_THREADS) do |app_screenshot_set|
223
+ original_ids = app_screenshot_set.app_screenshots.map(&:id)
224
+ sorted_ids = app_screenshot_set.app_screenshots.sort_by(&:file_name).map(&:id)
225
+ if original_ids != sorted_ids
226
+ app_screenshot_set.reorder_screenshots(app_screenshot_ids: sorted_ids)
221
227
  end
222
228
  end
223
- UI.success("Successfully uploaded screenshots to App Store Connect")
229
+
230
+ iterator.each_app_screenshot_set do |_, app_screenshot_set|
231
+ worker.enqueue(app_screenshot_set)
232
+ end
233
+
234
+ worker.start
224
235
  end
225
236
 
226
237
  def collect_screenshots(options)
@@ -293,11 +304,15 @@ module Deliver
293
304
 
294
305
  # helper method so Spaceship::Tunes.client.available_languages is easier to test
295
306
  def self.available_languages
296
- if Helper.test?
297
- FastlaneCore::Languages::ALL_LANGUAGES
298
- else
299
- Spaceship::Tunes.client.available_languages
300
- end
307
+ # 2020-08-24 - Available locales are not available as an endpoint in App Store Connect
308
+ # Update with Spaceship::Tunes.client.available_languages.sort (as long as endpoint is avilable)
309
+ Deliver::Languages::ALL_LANGUAGES
310
+ end
311
+
312
+ # helper method to mock this step in tests
313
+ def self.calculate_checksum(path)
314
+ bytes = File.binread(path)
315
+ Digest::MD5.hexdigest(bytes)
301
316
  end
302
317
  end
303
318
  end
@@ -0,0 +1,120 @@
1
+ module Fastlane
2
+ module Actions
3
+ module SharedValues
4
+ APP_STORE_CONNECT_API_KEY = :APP_STORE_CONNECT_API_KEY
5
+ end
6
+
7
+ class AppStoreConnectApiKeyAction < Action
8
+ def self.run(options)
9
+ key_id = options[:key_id]
10
+ issuer_id = options[:issuer_id]
11
+ key_content = options[:key_content]
12
+ key_filepath = options[:key_filepath]
13
+ duration = options[:duration]
14
+ in_house = options[:in_house]
15
+
16
+ if key_content.nil? && key_filepath.nil?
17
+ UI.user_error!(":key_content or :key_filepath is required")
18
+ end
19
+
20
+ # This hash matches the named arguments on
21
+ # the Spaceship::ConnectAPI::Token.create method
22
+ key = {
23
+ key_id: key_id,
24
+ issuer_id: issuer_id,
25
+ key: key_content || File.binread(key_filepath),
26
+ duration: duration,
27
+ in_house: in_house
28
+ }
29
+
30
+ Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] = key
31
+
32
+ return key
33
+ end
34
+
35
+ def self.description
36
+ "Load the App Store Connect API token to use in other fastlane tools and actions"
37
+ end
38
+
39
+ def self.available_options
40
+ [
41
+ FastlaneCore::ConfigItem.new(key: :key_id,
42
+ env_name: "APP_STORE_CONNECT_API_KEY_KEY_ID",
43
+ description: "The key ID"),
44
+ FastlaneCore::ConfigItem.new(key: :issuer_id,
45
+ env_name: "APP_STORE_CONNECT_API_KEY_ISSUER_ID",
46
+ description: "The issuer ID"),
47
+ FastlaneCore::ConfigItem.new(key: :key_filepath,
48
+ env_name: "APP_STORE_CONNECT_API_KEY_KEY_FILEPATH",
49
+ description: "The path to the key p8 file",
50
+ optional: true,
51
+ conflicting_options: [:key_content],
52
+ verify_block: proc do |value|
53
+ UI.user_error!("Couldn't find key p8 file at path '#{value}'") unless File.exist?(value)
54
+ end),
55
+ FastlaneCore::ConfigItem.new(key: :key_content,
56
+ env_name: "APP_STORE_CONNECT_API_KEY_KEY",
57
+ description: "The content of the key p8 file",
58
+ optional: true,
59
+ conflicting_options: [:filepath]),
60
+ FastlaneCore::ConfigItem.new(key: :duration,
61
+ env_name: "APP_STORE_CONNECT_API_KEY_DURATION",
62
+ description: "The token session duration",
63
+ optional: true,
64
+ type: Integer),
65
+ FastlaneCore::ConfigItem.new(key: :in_house,
66
+ env_name: "APP_STORE_CONNECT_API_KEY_IN_HOUSE",
67
+ description: "Is App Store or Enterprise (in house) team? App Store Connect API cannot not determine this on its own (yet)",
68
+ optional: true,
69
+ type: Boolean)
70
+ ]
71
+ end
72
+
73
+ def self.output
74
+ [
75
+ ['APP_STORE_CONNECT_API_KEY', 'The App Store Connect API key information used for authorization requests. This hash can be passed directly into the :api_key options on other tools or into Spaceship::ConnectAPI::Token.create method']
76
+ ]
77
+ end
78
+
79
+ def self.author
80
+ ["joshdholtz"]
81
+ end
82
+
83
+ def self.is_supported?(platform)
84
+ true
85
+ end
86
+
87
+ def self.details
88
+ [
89
+ "Load the App Store Connect API token to use in other fastlane tools and actions"
90
+ ].join("\n")
91
+ end
92
+
93
+ def self.example_code
94
+ [
95
+ 'app_store_connect_api_key(
96
+ key_id: "D83848D23",
97
+ issuer_id: "227b0bbf-ada8-458c-9d62-3d8022b7d07f",
98
+ key_filepath: "D83848D23.p8"
99
+ )',
100
+ 'app_store_connect_api_key(
101
+ key_id: "D83848D23",
102
+ issuer_id: "227b0bbf-ada8-458c-9d62-3d8022b7d07f",
103
+ key_filepath: "D83848D23.p8",
104
+ duration: 200,
105
+ in_house: true
106
+ )',
107
+ 'app_store_connect_api_key(
108
+ key_id: "D83848D23",
109
+ issuer_id: "227b0bbf-ada8-458c-9d62-3d8022b7d07f",
110
+ key_content: "-----BEGIN EC PRIVATE KEY-----\nfewfawefawfe\n-----END EC PRIVATE KEY-----"
111
+ )'
112
+ ]
113
+ end
114
+
115
+ def self.category
116
+ :app_store_connect
117
+ end
118
+ end
119
+ end
120
+ end