fastlane 2.160.0 → 2.165.0

Sign up to get free protection for your applications and to get access to all the features.
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",