fastlane 2.160.0 → 2.165.0

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -78
  3. data/cert/lib/cert/options.rb +28 -1
  4. data/cert/lib/cert/runner.rb +51 -34
  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 +5 -5
  8. data/deliver/lib/deliver/queue_worker.rb +14 -29
  9. data/deliver/lib/deliver/upload_metadata.rb +20 -5
  10. data/deliver/lib/deliver/upload_screenshots.rb +28 -13
  11. data/fastlane/lib/fastlane/actions/.download_dsyms.rb.swp +0 -0
  12. data/fastlane/lib/fastlane/actions/actions_helper.rb +20 -1
  13. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +39 -3
  14. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +15 -1
  15. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +1 -0
  16. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +2 -2
  17. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +2 -2
  18. data/fastlane/lib/fastlane/actions/docs/create_app_online.md +1 -1
  19. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +2 -2
  20. data/fastlane/lib/fastlane/actions/docs/run_tests.md +2 -2
  21. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +12 -3
  22. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -2
  23. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +1 -1
  24. data/fastlane/lib/fastlane/actions/download_dsyms.rb +1 -0
  25. data/fastlane/lib/fastlane/actions/ensure_git_status_clean.rb +13 -2
  26. data/fastlane/lib/fastlane/actions/get_certificates.rb +1 -0
  27. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +1 -0
  28. data/fastlane/lib/fastlane/actions/import_from_git.rb +9 -1
  29. data/fastlane/lib/fastlane/actions/is_ci.rb +1 -1
  30. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +15 -0
  31. data/fastlane/lib/fastlane/actions/register_device.rb +46 -5
  32. data/fastlane/lib/fastlane/actions/register_devices.rb +50 -16
  33. data/fastlane/lib/fastlane/actions/set_changelog.rb +31 -3
  34. data/fastlane/lib/fastlane/actions/sync_code_signing.rb +1 -0
  35. data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +3 -2
  36. data/fastlane/lib/fastlane/fast_file.rb +74 -23
  37. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -0
  38. data/fastlane/lib/fastlane/version.rb +1 -1
  39. data/fastlane/swift/Deliverfile.swift +1 -1
  40. data/fastlane/swift/DeliverfileProtocol.swift +4 -4
  41. data/fastlane/swift/Fastlane.swift +120 -27
  42. data/fastlane/swift/Gymfile.swift +1 -1
  43. data/fastlane/swift/GymfileProtocol.swift +1 -1
  44. data/fastlane/swift/LaneFileProtocol.swift +28 -36
  45. data/fastlane/swift/MainProcess.swift +1 -1
  46. data/fastlane/swift/Matchfile.swift +1 -1
  47. data/fastlane/swift/MatchfileProtocol.swift +21 -5
  48. data/fastlane/swift/Precheckfile.swift +1 -1
  49. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  50. data/fastlane/swift/Scanfile.swift +1 -1
  51. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  52. data/fastlane/swift/Screengrabfile.swift +1 -1
  53. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  54. data/fastlane/swift/Snapshotfile.swift +1 -1
  55. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  56. data/fastlane/swift/main.swift +1 -1
  57. data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +6 -7
  58. data/fastlane_core/lib/fastlane_core/device_manager.rb +8 -4
  59. data/fastlane_core/lib/fastlane_core/helper.rb +1 -1
  60. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +3 -3
  61. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +2 -2
  62. data/match/lib/match/generator.rb +6 -1
  63. data/match/lib/match/importer.rb +63 -18
  64. data/match/lib/match/migrate.rb +13 -2
  65. data/match/lib/match/nuke.rb +65 -22
  66. data/match/lib/match/options.rb +34 -3
  67. data/match/lib/match/runner.rb +38 -10
  68. data/match/lib/match/spaceship_ensure.rb +27 -21
  69. data/match/lib/match/storage/google_cloud_storage.rb +20 -3
  70. data/match/lib/match/storage/s3_storage.rb +19 -3
  71. data/scan/lib/scan/detect_values.rb +5 -8
  72. data/scan/lib/scan/runner.rb +2 -1
  73. data/sigh/lib/assets/resign.sh +1 -1
  74. data/sigh/lib/sigh/download_all.rb +16 -4
  75. data/sigh/lib/sigh/options.rb +21 -0
  76. data/sigh/lib/sigh/runner.rb +83 -41
  77. data/snapshot/lib/assets/SnapshotHelper.swift +4 -0
  78. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +2 -1
  79. data/spaceship/README.md +1 -1
  80. data/spaceship/lib/spaceship/client.rb +9 -4
  81. data/spaceship/lib/spaceship/connect_api.rb +27 -0
  82. data/spaceship/lib/spaceship/connect_api/api_client.rb +12 -3
  83. data/spaceship/lib/spaceship/connect_api/client.rb +20 -7
  84. data/spaceship/lib/spaceship/connect_api/models/app.rb +51 -0
  85. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +3 -1
  86. data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +2 -1
  87. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +42 -0
  88. data/spaceship/lib/spaceship/connect_api/models/custom_app_organization.rb +43 -0
  89. data/spaceship/lib/spaceship/connect_api/models/custom_app_user.rb +41 -0
  90. data/spaceship/lib/spaceship/connect_api/models/device.rb +5 -0
  91. data/spaceship/lib/spaceship/connect_api/models/profile.rb +7 -1
  92. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +59 -0
  93. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +45 -2
  94. data/spaceship/lib/spaceship/connect_api/spaceship.rb +7 -4
  95. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
  96. data/spaceship/lib/spaceship/connect_api/token.rb +6 -1
  97. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +75 -1
  98. data/spaceship/lib/spaceship/connect_api/users/users.rb +40 -0
  99. data/spaceship/lib/spaceship/helper/net_http_generic_request.rb +11 -5
  100. data/supply/lib/supply/uploader.rb +1 -1
  101. metadata +19 -15
@@ -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
@@ -16,7 +16,7 @@ module Deliver
16
16
  env_name: "DELIVER_API_KEY_PATH",
17
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
18
  optional: true,
19
- conflicting_options: [:username],
19
+ conflicting_options: [:api_key],
20
20
  verify_block: proc do |value|
21
21
  UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
22
22
  end),
@@ -26,7 +26,7 @@ module Deliver
26
26
  type: Hash,
27
27
  optional: true,
28
28
  sensitive: true,
29
- conflicting_options: [:api_key_path, :username]),
29
+ conflicting_options: [:api_key_path]),
30
30
 
31
31
  FastlaneCore::ConfigItem.new(key: :username,
32
32
  short_option: "-u",
@@ -153,7 +153,7 @@ module Deliver
153
153
  FastlaneCore::ConfigItem.new(key: :force,
154
154
  short_option: "-f",
155
155
  env_name: "DELIVER_FORCE",
156
- description: "Skip the HTML report file verification",
156
+ description: "Skip verification of HTML preview file",
157
157
  is_string: false,
158
158
  default_value: false),
159
159
  FastlaneCore::ConfigItem.new(key: :overwrite_screenshots,
@@ -176,8 +176,8 @@ module Deliver
176
176
  FastlaneCore::ConfigItem.new(key: :automatic_release,
177
177
  env_name: "DELIVER_AUTOMATIC_RELEASE",
178
178
  description: "Should the app be automatically released once it's approved? (Can not be used together with `auto_release_date`)",
179
- is_string: false,
180
- default_value: false),
179
+ type: Boolean,
180
+ optional: true),
181
181
  FastlaneCore::ConfigItem.new(key: :auto_release_date,
182
182
  env_name: "DELIVER_AUTO_RELEASE_DATE",
183
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
@@ -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.
@@ -175,35 +176,48 @@ 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
223
  app_info = fetch_edit_app_info(app)
@@ -584,6 +598,7 @@ module Deliver
584
598
  def set_review_information(version, options)
585
599
  return unless options[:app_review_information]
586
600
  info = options[:app_review_information]
601
+ info = info.collect { |k, v| [k.to_sym, v] }.to_h
587
602
  UI.user_error!("`app_review_information` must be a hash", show_github_issues: true) unless info.kind_of?(Hash)
588
603
 
589
604
  attributes = {}
@@ -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
@@ -4,6 +4,25 @@ module Fastlane
4
4
  LANE_NAME = :LANE_NAME
5
5
  PLATFORM_NAME = :PLATFORM_NAME
6
6
  ENVIRONMENT = :ENVIRONMENT
7
+
8
+ # A slighly decorated hash that will store and fetch sensitive data
9
+ # but not display it while iterating keys and values
10
+ class LaneContextValues < Hash
11
+ def initialize
12
+ @sensitive_context = {}
13
+ end
14
+
15
+ def set_sensitive(key, value)
16
+ @sensitive_context[key] = value
17
+ end
18
+
19
+ def [](key)
20
+ if @sensitive_context.key?(key)
21
+ return @sensitive_context[key]
22
+ end
23
+ super
24
+ end
25
+ end
7
26
  end
8
27
 
9
28
  def self.reset_aliases
@@ -27,7 +46,7 @@ module Fastlane
27
46
 
28
47
  # The shared hash can be accessed by any action and contains information like the screenshots path or beta URL
29
48
  def self.lane_context
30
- @lane_context ||= {}
49
+ @lane_context ||= SharedValues::LaneContextValues.new
31
50
  end
32
51
 
33
52
  # Used in tests to get a clear lane before every test
@@ -27,9 +27,15 @@ module Fastlane
27
27
 
28
28
  def self.get_build_number(params)
29
29
  # Prompts select team if multiple teams and none specified
30
- UI.message("Login to App Store Connect (#{params[:username]})")
31
- Spaceship::ConnectAPI.login(params[:username], use_portal: false, use_tunes: true, tunes_team_id: params[:team_id], team_name: params[:team_name])
32
- UI.message("Login successful")
30
+ token = self.api_token(params)
31
+ if token
32
+ UI.message("Using App Store Connect API token...")
33
+ Spaceship::ConnectAPI.token = token
34
+ else
35
+ UI.message("Login to App Store Connect (#{params[:username]})")
36
+ Spaceship::ConnectAPI.login(params[:username], use_portal: false, use_tunes: true, tunes_team_id: params[:team_id], team_name: params[:team_name])
37
+ UI.message("Login successful")
38
+ end
33
39
 
34
40
  platform = Spaceship::ConnectAPI::Platform.map(params[:platform])
35
41
 
@@ -92,6 +98,13 @@ module Fastlane
92
98
  versions.map(&:to_s).sort_by { |v| Gem::Version.new(v) }
93
99
  end
94
100
 
101
+ def self.api_token(params)
102
+ params[:api_key] ||= Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
103
+ api_token ||= Spaceship::ConnectAPI::Token.create(params[:api_key]) if params[:api_key]
104
+ api_token ||= Spaceship::ConnectAPI::Token.from_json_file(params[:api_key_path]) if params[:api_key_path]
105
+ return api_token
106
+ end
107
+
95
108
  #####################################################
96
109
  # @!group Documentation
97
110
  #####################################################
@@ -104,6 +117,21 @@ module Fastlane
104
117
  user = CredentialsManager::AppfileConfig.try_fetch_value(:itunes_connect_id)
105
118
  user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
106
119
  [
120
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
121
+ env_name: "APPSTORE_BUILD_NUMBER_API_KEY_PATH",
122
+ 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)",
123
+ optional: true,
124
+ conflicting_options: [:api_key],
125
+ verify_block: proc do |value|
126
+ UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
127
+ end),
128
+ FastlaneCore::ConfigItem.new(key: :api_key,
129
+ env_name: "APPSTORE_BUILD_NUMBER_API_KEY",
130
+ 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)",
131
+ type: Hash,
132
+ optional: true,
133
+ sensitive: true,
134
+ conflicting_options: [:api_key_path]),
107
135
  FastlaneCore::ConfigItem.new(key: :initial_build_number,
108
136
  env_name: "INITIAL_BUILD_NUMBER",
109
137
  description: "sets the build number to given value if no build is in current train",
@@ -193,6 +221,14 @@ module Fastlane
193
221
  live: false,
194
222
  app_identifier: "app.identifier",
195
223
  version: "1.2.9"
224
+ )',
225
+ 'api_key = app_store_connect_api_key(
226
+ key_id: "MyKeyID12345",
227
+ issuer_id: "00000000-0000-0000-0000-000000000000",
228
+ key_filepath: "./AuthKey.p8"
229
+ )
230
+ build_num = app_store_build_number(
231
+ api_key: api_key
196
232
  )'
197
233
  ]
198
234
  end
@@ -1,3 +1,5 @@
1
+ require 'base64'
2
+
1
3
  module Fastlane
2
4
  module Actions
3
5
  module SharedValues
@@ -9,6 +11,7 @@ module Fastlane
9
11
  key_id = options[:key_id]
10
12
  issuer_id = options[:issuer_id]
11
13
  key_content = options[:key_content]
14
+ is_key_content_base64 = options[:is_key_content_base64]
12
15
  key_filepath = options[:key_filepath]
13
16
  duration = options[:duration]
14
17
  in_house = options[:in_house]
@@ -17,17 +20,22 @@ module Fastlane
17
20
  UI.user_error!(":key_content or :key_filepath is required")
18
21
  end
19
22
 
23
+ # New lines don't get read properly when coming from an ENV
24
+ # Replacing them literal version with a new line
25
+ key_content = key_content.gsub('\n', "\n") if key_content
26
+
20
27
  # This hash matches the named arguments on
21
28
  # the Spaceship::ConnectAPI::Token.create method
22
29
  key = {
23
30
  key_id: key_id,
24
31
  issuer_id: issuer_id,
25
32
  key: key_content || File.binread(key_filepath),
33
+ is_key_content_base64: is_key_content_base64,
26
34
  duration: duration,
27
35
  in_house: in_house
28
36
  }
29
37
 
30
- Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY] = key
38
+ Actions.lane_context.set_sensitive(SharedValues::APP_STORE_CONNECT_API_KEY, key)
31
39
 
32
40
  return key
33
41
  end
@@ -55,8 +63,14 @@ module Fastlane
55
63
  FastlaneCore::ConfigItem.new(key: :key_content,
56
64
  env_name: "APP_STORE_CONNECT_API_KEY_KEY",
57
65
  description: "The content of the key p8 file",
66
+ sensitive: true,
58
67
  optional: true,
59
68
  conflicting_options: [:filepath]),
69
+ FastlaneCore::ConfigItem.new(key: :is_key_content_base64,
70
+ env_name: "APP_STORE_CONNECT_API_KEY_IS_KEY_CONTENT_BASE64",
71
+ description: "Whether :key_content is Base64 encoded or not",
72
+ type: Boolean,
73
+ default_value: false),
60
74
  FastlaneCore::ConfigItem.new(key: :duration,
61
75
  env_name: "APP_STORE_CONNECT_API_KEY_DURATION",
62
76
  description: "The token session duration",