fastlane 2.202.0 → 2.212.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +98 -98
  3. data/cert/lib/cert/runner.rb +15 -7
  4. data/deliver/lib/deliver/app_screenshot.rb +18 -0
  5. data/deliver/lib/deliver/options.rb +6 -2
  6. data/deliver/lib/deliver/runner.rb +76 -29
  7. data/deliver/lib/deliver/upload_price_tier.rb +3 -1
  8. data/deliver/lib/deliver/upload_screenshots.rb +1 -1
  9. data/fastlane/lib/assets/AppfileTemplate +1 -1
  10. data/fastlane/lib/assets/AppfileTemplate.swift +1 -1
  11. data/fastlane/lib/fastlane/actions/badge.rb +1 -1
  12. data/fastlane/lib/fastlane/actions/changelog_from_git_commits.rb +1 -1
  13. data/fastlane/lib/fastlane/actions/danger.rb +14 -0
  14. data/fastlane/lib/fastlane/actions/docs/build_app.md +5 -5
  15. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +19 -2
  16. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +1 -1
  17. data/fastlane/lib/fastlane/actions/docs/run_tests.md +1 -1
  18. data/fastlane/lib/fastlane/actions/get_version_number.rb +1 -1
  19. data/fastlane/lib/fastlane/actions/git_commit.rb +4 -6
  20. data/fastlane/lib/fastlane/actions/import_certificate.rb +1 -1
  21. data/fastlane/lib/fastlane/actions/pod_lib_lint.rb +1 -1
  22. data/fastlane/lib/fastlane/actions/pod_push.rb +19 -1
  23. data/fastlane/lib/fastlane/actions/read_podspec.rb +1 -1
  24. data/fastlane/lib/fastlane/actions/run_tests.rb +11 -9
  25. data/fastlane/lib/fastlane/actions/setup_ci.rb +13 -4
  26. data/fastlane/lib/fastlane/actions/trainer.rb +2 -2
  27. data/fastlane/lib/fastlane/actions/update_code_signing_settings.rb +14 -4
  28. data/fastlane/lib/fastlane/actions/update_info_plist.rb +1 -1
  29. data/fastlane/lib/fastlane/actions/update_project_provisioning.rb +10 -1
  30. data/fastlane/lib/fastlane/actions/upload_symbols_to_sentry.rb +1 -1
  31. data/fastlane/lib/fastlane/actions/verify_build.rb +1 -1
  32. data/fastlane/lib/fastlane/actions/xcode_install.rb +5 -1
  33. data/fastlane/lib/fastlane/actions/xcode_select.rb +1 -1
  34. data/fastlane/lib/fastlane/actions/xcodebuild.rb +8 -2
  35. data/fastlane/lib/fastlane/actions/xcodes.rb +152 -0
  36. data/fastlane/lib/fastlane/actions/xcversion.rb +10 -15
  37. data/fastlane/lib/fastlane/cli_tools_distributor.rb +5 -0
  38. data/fastlane/lib/fastlane/commands_generator.rb +2 -1
  39. data/fastlane/lib/fastlane/fast_file.rb +18 -5
  40. data/fastlane/lib/fastlane/features.rb +3 -0
  41. data/fastlane/lib/fastlane/helper/xcodes_helper.rb +28 -0
  42. data/fastlane/lib/fastlane/helper/xcversion_helper.rb +0 -9
  43. data/fastlane/lib/fastlane/lane_manager.rb +1 -1
  44. data/fastlane/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +1 -1
  45. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +5 -1
  46. data/fastlane/lib/fastlane/setup/setup_ios.rb +1 -1
  47. data/fastlane/lib/fastlane/swift_fastlane_api_generator.rb +1 -1
  48. data/fastlane/lib/fastlane/swift_lane_manager.rb +11 -3
  49. data/fastlane/lib/fastlane/swift_runner_upgrader.rb +54 -1
  50. data/fastlane/lib/fastlane/tools.rb +16 -0
  51. data/fastlane/lib/fastlane/version.rb +1 -1
  52. data/fastlane/swift/Actions.swift +1 -1
  53. data/fastlane/swift/Appfile.swift +2 -2
  54. data/fastlane/swift/ArgumentProcessor.swift +1 -1
  55. data/fastlane/swift/Atomic.swift +150 -0
  56. data/fastlane/swift/ControlCommand.swift +1 -1
  57. data/fastlane/swift/Deliverfile.swift +2 -2
  58. data/fastlane/swift/DeliverfileProtocol.swift +8 -4
  59. data/fastlane/swift/Fastlane.swift +363 -184
  60. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj +30 -20
  61. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/xcshareddata/xcschemes/FastlaneRunner.xcscheme +1 -1
  62. data/fastlane/swift/Gymfile.swift +2 -2
  63. data/fastlane/swift/GymfileProtocol.swift +7 -3
  64. data/fastlane/swift/LaneFileProtocol.swift +2 -2
  65. data/fastlane/swift/MainProcess.swift +3 -3
  66. data/fastlane/swift/Matchfile.swift +2 -2
  67. data/fastlane/swift/MatchfileProtocol.swift +21 -5
  68. data/fastlane/swift/OptionalConfigValue.swift +1 -1
  69. data/fastlane/swift/Plugins.swift +1 -1
  70. data/fastlane/swift/Precheckfile.swift +2 -2
  71. data/fastlane/swift/PrecheckfileProtocol.swift +3 -3
  72. data/fastlane/swift/RubyCommand.swift +1 -1
  73. data/fastlane/swift/RubyCommandable.swift +1 -1
  74. data/fastlane/swift/Runner.swift +10 -2
  75. data/fastlane/swift/RunnerArgument.swift +1 -1
  76. data/fastlane/swift/Scanfile.swift +2 -2
  77. data/fastlane/swift/ScanfileProtocol.swift +11 -3
  78. data/fastlane/swift/Screengrabfile.swift +2 -2
  79. data/fastlane/swift/ScreengrabfileProtocol.swift +3 -3
  80. data/fastlane/swift/Snapshotfile.swift +2 -2
  81. data/fastlane/swift/SnapshotfileProtocol.swift +4 -4
  82. data/fastlane/swift/SocketClient.swift +9 -5
  83. data/fastlane/swift/SocketClientDelegateProtocol.swift +2 -2
  84. data/fastlane/swift/SocketResponse.swift +1 -1
  85. data/fastlane/swift/formatting/Brewfile.lock.json +42 -24
  86. data/fastlane/swift/main.swift +1 -1
  87. data/fastlane/swift/upgrade_manifest.json +1 -1
  88. data/fastlane_core/README.md +1 -0
  89. data/fastlane_core/lib/fastlane_core/cert_checker.rb +79 -17
  90. data/fastlane_core/lib/fastlane_core/device_manager.rb +5 -1
  91. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +335 -20
  92. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +1 -0
  93. data/fastlane_core/lib/fastlane_core/project.rb +19 -2
  94. data/fastlane_core/lib/fastlane_core/ui/implementations/shell.rb +4 -2
  95. data/frameit/lib/frameit/device.rb +1 -1
  96. data/frameit/lib/frameit/device_types.rb +9 -0
  97. data/frameit/lib/frameit/frame_downloader.rb +1 -1
  98. data/gym/lib/gym/generators/build_command_generator.rb +1 -0
  99. data/gym/lib/gym/options.rb +7 -0
  100. data/match/lib/match/change_password.rb +2 -0
  101. data/match/lib/match/commands_generator.rb +2 -1
  102. data/match/lib/match/encryption/openssl.rb +1 -1
  103. data/match/lib/match/encryption.rb +3 -0
  104. data/match/lib/match/generator.rb +1 -0
  105. data/match/lib/match/importer.rb +10 -1
  106. data/match/lib/match/migrate.rb +4 -3
  107. data/match/lib/match/module.rb +54 -2
  108. data/match/lib/match/nuke.rb +36 -47
  109. data/match/lib/match/options.rb +22 -1
  110. data/match/lib/match/runner.rb +25 -6
  111. data/match/lib/match/setup.rb +1 -1
  112. data/match/lib/match/spaceship_ensure.rb +4 -2
  113. data/match/lib/match/storage/gitlab/client.rb +102 -0
  114. data/match/lib/match/storage/gitlab/secure_file.rb +65 -0
  115. data/match/lib/match/storage/gitlab_secure_files.rb +182 -0
  116. data/match/lib/match/storage/google_cloud_storage.rb +7 -6
  117. data/match/lib/match/storage/s3_storage.rb +3 -3
  118. data/match/lib/match/storage.rb +4 -0
  119. data/match/lib/match/table_printer.rb +2 -1
  120. data/match/lib/match/utils.rb +15 -2
  121. data/pem/lib/pem/manager.rb +1 -1
  122. data/pilot/lib/pilot/build_manager.rb +33 -13
  123. data/pilot/lib/pilot/options.rb +6 -1
  124. data/scan/lib/scan/detect_values.rb +6 -0
  125. data/scan/lib/scan/options.rb +16 -1
  126. data/scan/lib/scan/runner.rb +33 -14
  127. data/scan/lib/scan/test_command_generator.rb +7 -1
  128. data/sigh/lib/sigh/download_all.rb +14 -2
  129. data/sigh/lib/sigh/module.rb +3 -1
  130. data/sigh/lib/sigh/options.rb +5 -0
  131. data/sigh/lib/sigh/runner.rb +12 -2
  132. data/snapshot/lib/assets/SnapshotHelper.swift +3 -3
  133. data/snapshot/lib/snapshot/options.rb +1 -1
  134. data/snapshot/lib/snapshot/reports_generator.rb +1 -0
  135. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +9 -2
  136. data/spaceship/lib/spaceship/client.rb +36 -25
  137. data/spaceship/lib/spaceship/connect_api/api_client.rb +10 -5
  138. data/spaceship/lib/spaceship/connect_api/models/actor.rb +26 -0
  139. data/spaceship/lib/spaceship/connect_api/models/app.rb +7 -5
  140. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +5 -0
  141. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +5 -0
  142. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +1 -1
  143. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +27 -10
  144. data/spaceship/lib/spaceship/connect_api/models/build_bundle.rb +9 -0
  145. data/spaceship/lib/spaceship/connect_api/models/build_bundle_file_sizes.rb +34 -0
  146. data/spaceship/lib/spaceship/connect_api/models/build_delivery.rb +2 -1
  147. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +1 -0
  148. data/spaceship/lib/spaceship/connect_api/models/device.rb +47 -4
  149. data/spaceship/lib/spaceship/connect_api/models/profile.rb +4 -0
  150. data/spaceship/lib/spaceship/connect_api/models/resolution_center_message.rb +29 -0
  151. data/spaceship/lib/spaceship/connect_api/models/resolution_center_thread.rb +67 -0
  152. data/spaceship/lib/spaceship/connect_api/models/review_rejection.rb +19 -0
  153. data/spaceship/lib/spaceship/connect_api/models/review_submission.rb +13 -0
  154. data/spaceship/lib/spaceship/connect_api/models/user.rb +5 -0
  155. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +19 -0
  156. data/spaceship/lib/spaceship/connect_api/response.rb +10 -6
  157. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +33 -2
  158. data/spaceship/lib/spaceship/connect_api/token.rb +5 -2
  159. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +41 -8
  160. data/spaceship/lib/spaceship/connect_api.rb +6 -0
  161. data/spaceship/lib/spaceship/errors.rb +34 -0
  162. data/spaceship/lib/spaceship/hashcash.rb +52 -0
  163. data/spaceship/lib/spaceship/portal/certificate.rb +4 -3
  164. data/spaceship/lib/spaceship/tunes/app_ratings.rb +6 -6
  165. data/spaceship/lib/spaceship/tunes/iap_families.rb +1 -1
  166. data/spaceship/lib/spaceship/tunes/tunes.rb +0 -1
  167. data/spaceship/lib/spaceship/tunes/tunes_client.rb +79 -21
  168. data/spaceship/lib/spaceship/two_step_or_factor_client.rb +11 -3
  169. data/spaceship/lib/spaceship.rb +1 -0
  170. data/supply/lib/supply/client.rb +2 -7
  171. data/trainer/lib/assets/junit.xml.erb +9 -1
  172. data/trainer/lib/trainer/junit_generator.rb +2 -2
  173. data/trainer/lib/trainer/options.rb +1 -1
  174. data/trainer/lib/trainer/test_parser.rb +25 -3
  175. metadata +36 -33
  176. data/deliver/lib/deliver/.runner.rb.swp +0 -0
  177. data/deliver/lib/deliver/.submit_for_review.rb.swp +0 -0
  178. data/fastlane/lib/.DS_Store +0 -0
  179. data/fastlane/lib/fastlane/.DS_Store +0 -0
  180. data/fastlane/lib/fastlane/actions/.DS_Store +0 -0
  181. data/spaceship/lib/spaceship/connect_api/models/.app.rb.swp +0 -0
  182. data/spaceship/lib/spaceship/connect_api/testflight/.testflight.rb.swp +0 -0
  183. data/spaceship/lib/spaceship/tunes/user_detail.rb +0 -15
  184. data/trainer/lib/.DS_Store +0 -0
@@ -28,10 +28,29 @@ module FastlaneCore
28
28
  OUTPUT_REGEX = />\s+(.+)/
29
29
  RETURN_VALUE_REGEX = />\sDBG-X:\sReturning\s+(\d+)/
30
30
 
31
+ # Matches a line in the iTMSTransporter provider table: "12 Initech Systems Inc LG89CQY559"
32
+ ITMS_PROVIDER_REGEX = /^\d+\s{2,}.+\s{2,}[^\s]+$/
33
+
31
34
  SKIP_ERRORS = ["ERROR: An exception has occurred: Scheduling automatic restart in 1 minute"]
32
35
 
33
36
  private_constant :ERROR_REGEX, :WARNING_REGEX, :OUTPUT_REGEX, :RETURN_VALUE_REGEX, :SKIP_ERRORS
34
37
 
38
+ def build_download_command(username, password, apple_id, destination = "/tmp", provider_short_name = "", jwt = nil)
39
+ not_implemented(__method__)
40
+ end
41
+
42
+ def build_provider_ids_command(username, password, jwt = nil, api_key = nil)
43
+ not_implemented(__method__)
44
+ end
45
+
46
+ def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil)
47
+ not_implemented(__method__)
48
+ end
49
+
50
+ def build_verify_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil)
51
+ not_implemented(__method__)
52
+ end
53
+
35
54
  def execute(command, hide_output)
36
55
  if Helper.test?
37
56
  yield(nil) if block_given?
@@ -100,8 +119,18 @@ module FastlaneCore
100
119
  @errors.map { |error| "[Transporter Error Output]: #{error}" }.join("\n").gsub!(/"/, "")
101
120
  end
102
121
 
122
+ def parse_provider_info(lines)
123
+ lines.map { |line| itms_provider_pair(line) }.compact.to_h
124
+ end
125
+
103
126
  private
104
127
 
128
+ def itms_provider_pair(line)
129
+ line = line.strip
130
+ return nil unless line =~ ITMS_PROVIDER_REGEX
131
+ line.split(/\s{2,}/).drop(1)
132
+ end
133
+
105
134
  def parse_line(line, hide_output)
106
135
  # Taken from https://github.com/sshaw/itunes_store_transporter/blob/master/lib/itunes/store/transporter/output_parser.rb
107
136
 
@@ -180,9 +209,166 @@ module FastlaneCore
180
209
  end
181
210
  end
182
211
 
212
+ # Generates commands and executes the altool.
213
+ class AltoolTransporterExecutor < TransporterExecutor
214
+ ERROR_REGEX = /\*\*\* Error:\s+(.+)/
215
+
216
+ private_constant :ERROR_REGEX
217
+
218
+ def execute(command, hide_output)
219
+ if Helper.test?
220
+ yield(nil) if block_given?
221
+ return command
222
+ end
223
+
224
+ @errors = []
225
+ @all_lines = []
226
+
227
+ if hide_output
228
+ # Show a one time message instead
229
+ UI.success("Waiting for App Store Connect transporter to be finished.")
230
+ UI.success("Application Loader progress... this might take a few minutes...")
231
+ end
232
+
233
+ begin
234
+ exit_status = FastlaneCore::FastlanePty.spawn(command) do |command_stdout, command_stdin, pid|
235
+ command_stdout.each do |line|
236
+ @all_lines << line
237
+ parse_line(line, hide_output) # this is where the parsing happens
238
+ end
239
+ end
240
+ rescue => ex
241
+ # FastlanePty adds exit_status on to StandardError so every error will have a status code
242
+ exit_status = ex.exit_status
243
+ @errors << ex.to_s
244
+ end
245
+
246
+ @errors << "The call to the altool completed with a non-zero exit status: #{exit_status}. This indicates a failure." unless exit_status.zero?
247
+
248
+ unless @errors.empty? || @all_lines.empty?
249
+ # Print the last lines that appear after the last error from the logs
250
+ # If error text is not detected, it will be 20 lines
251
+ # This is key for non-verbose mode
252
+
253
+ # The format of altool's result with error is like below
254
+ # > *** Error: Error uploading '...'.
255
+ # > *** Error: ...
256
+ # > {
257
+ # > NSLocalizedDescription = "...",
258
+ # > ...
259
+ # > }
260
+ # So this line tries to find the line which has "*** Error:" prefix from bottom of log
261
+ error_line_index = @all_lines.rindex { |line| ERROR_REGEX.match?(line) }
262
+
263
+ @all_lines[(error_line_index || -20)..-1].each do |line|
264
+ UI.important("[altool] #{line}")
265
+ end
266
+ UI.message("Application Loader output above ^")
267
+ @errors.each { |error| UI.error(error) }
268
+ end
269
+
270
+ yield(@all_lines) if block_given?
271
+ exit_status.zero?
272
+ end
273
+
274
+ def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil)
275
+ use_api_key = !api_key.nil?
276
+ [
277
+ ("API_PRIVATE_KEYS_DIR=#{api_key[:key_dir]}" if use_api_key),
278
+ "xcrun altool",
279
+ "--upload-app",
280
+ ("-u #{username.shellescape}" unless use_api_key),
281
+ ("-p #{password.shellescape}" unless use_api_key),
282
+ ("--apiKey #{api_key[:key_id]}" if use_api_key),
283
+ ("--apiIssuer #{api_key[:issuer_id]}" if use_api_key),
284
+ ("--asc-provider #{provider_short_name}" unless use_api_key || provider_short_name.to_s.empty?),
285
+ platform_option(platform),
286
+ file_upload_option(source),
287
+ additional_upload_parameters,
288
+ "-k 100000"
289
+ ].compact.join(' ')
290
+ end
291
+
292
+ def build_provider_ids_command(username, password, jwt = nil, api_key = nil)
293
+ use_api_key = !api_key.nil?
294
+ [
295
+ ("API_PRIVATE_KEYS_DIR=#{api_key[:key_dir]}" if use_api_key),
296
+ "xcrun altool",
297
+ "--list-providers",
298
+ ("-u #{username.shellescape}" unless use_api_key),
299
+ ("-p #{password.shellescape}" unless use_api_key),
300
+ ("--apiKey #{api_key[:key_id]}" if use_api_key),
301
+ ("--apiIssuer #{api_key[:issuer_id]}" if use_api_key),
302
+ "--output-format json"
303
+ ].compact.join(' ')
304
+ end
305
+
306
+ def build_download_command(username, password, apple_id, destination = "/tmp", provider_short_name = "", jwt = nil)
307
+ raise "This feature has not been implemented yet with altool for Xcode 14"
308
+ end
309
+
310
+ def build_verify_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil)
311
+ raise "This feature has not been implemented yet with altool for Xcode 14"
312
+ end
313
+
314
+ def additional_upload_parameters
315
+ env_deliver_additional_params = ENV["DELIVER_ALTOOL_ADDITIONAL_UPLOAD_PARAMETERS"]
316
+ return nil if env_deliver_additional_params.to_s.strip.empty?
317
+
318
+ env_deliver_additional_params.to_s.strip
319
+ end
320
+
321
+ def handle_error(password)
322
+ UI.error("Could not download/upload from App Store Connect!")
323
+ end
324
+
325
+ def displayable_errors
326
+ @errors.map { |error| "[Application Loader Error Output]: #{error}" }.join("\n")
327
+ end
328
+
329
+ def parse_provider_info(lines)
330
+ # This tries parsing the provider id from altool output to detect provider list
331
+ provider_info = {}
332
+ json_body = lines[-2] # altool outputs result in second line from last
333
+ return provider_info if json_body.nil?
334
+ providers = JSON.parse(json_body)["providers"]
335
+ return provider_info if providers.nil?
336
+ providers.each do |provider|
337
+ provider_info[provider["ProviderName"]] = provider["ProviderShortname"]
338
+ end
339
+ provider_info
340
+ end
341
+
342
+ private
343
+
344
+ def file_upload_option(source)
345
+ "-f #{source.shellescape}"
346
+ end
347
+
348
+ def platform_option(platform)
349
+ "-t #{platform == 'osx' ? 'macos' : platform}"
350
+ end
351
+
352
+ def parse_line(line, hide_output)
353
+ output_done = false
354
+
355
+ if line =~ ERROR_REGEX
356
+ @errors << $1
357
+ output_done = true
358
+ end
359
+
360
+ unless hide_output
361
+ # General logging for debug purposes
362
+ unless output_done
363
+ UI.verbose("[altool]: #{line}")
364
+ end
365
+ end
366
+ end
367
+ end
368
+
183
369
  # Generates commands and executes the iTMSTransporter through the shell script it provides by the same name
184
370
  class ShellScriptTransporterExecutor < TransporterExecutor
185
- def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil)
371
+ def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil)
186
372
  use_jwt = !jwt.to_s.empty?
187
373
  [
188
374
  '"' + Helper.transporter_path + '"',
@@ -212,7 +398,7 @@ module FastlaneCore
212
398
  ].compact.join(' ')
213
399
  end
214
400
 
215
- def build_provider_ids_command(username, password, jwt = nil)
401
+ def build_provider_ids_command(username, password, jwt = nil, api_key = nil)
216
402
  use_jwt = !jwt.to_s.empty?
217
403
  [
218
404
  '"' + Helper.transporter_path + '"',
@@ -223,6 +409,20 @@ module FastlaneCore
223
409
  ].compact.join(' ')
224
410
  end
225
411
 
412
+ def build_verify_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil)
413
+ use_jwt = !jwt.to_s.empty?
414
+ [
415
+ '"' + Helper.transporter_path + '"',
416
+ '-m verify',
417
+ ("-u #{username.shellescape}" unless use_jwt),
418
+ ("-p #{shell_escaped_password(password)}" unless use_jwt),
419
+ ("-jwt #{jwt}" if use_jwt),
420
+ "-f #{source.shellescape}",
421
+ ("-WONoPause true" if Helper.windows?), # Windows only: process instantly returns instead of waiting for key press
422
+ ("-itc_provider #{provider_short_name}" if jwt.nil? && !provider_short_name.to_s.empty?)
423
+ ].compact.join(' ')
424
+ end
425
+
226
426
  def handle_error(password)
227
427
  # rubocop:disable Style/CaseEquality
228
428
  # rubocop:disable Style/YodaCondition
@@ -264,7 +464,7 @@ module FastlaneCore
264
464
  # Generates commands and executes the iTMSTransporter by invoking its Java app directly, to avoid the crazy parameter
265
465
  # escaping problems in its accompanying shell script.
266
466
  class JavaTransporterExecutor < TransporterExecutor
267
- def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil)
467
+ def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil)
268
468
  use_jwt = !jwt.to_s.empty?
269
469
  if !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(11)
270
470
  [
@@ -304,6 +504,42 @@ module FastlaneCore
304
504
  end
305
505
  end
306
506
 
507
+ def build_verify_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil)
508
+ use_jwt = !jwt.to_s.empty?
509
+ if !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(11)
510
+ [
511
+ ("ITMS_TRANSPORTER_PASSWORD=#{password.shellescape}" unless use_jwt),
512
+ 'xcrun iTMSTransporter',
513
+ '-m verify',
514
+ ("-u #{username.shellescape}" unless use_jwt),
515
+ ("-p @env:ITMS_TRANSPORTER_PASSWORD" unless use_jwt),
516
+ ("-jwt #{jwt}" if use_jwt),
517
+ "-f #{source.shellescape}",
518
+ ("-itc_provider #{provider_short_name}" if jwt.nil? && !provider_short_name.to_s.empty?),
519
+ '2>&1' # cause stderr to be written to stdout
520
+ ].compact.join(' ') # compact gets rid of the possibly nil ENV value
521
+ else
522
+ [
523
+ Helper.transporter_java_executable_path.shellescape,
524
+ "-Djava.ext.dirs=#{Helper.transporter_java_ext_dir.shellescape}",
525
+ '-XX:NewSize=2m',
526
+ '-Xms32m',
527
+ '-Xmx1024m',
528
+ '-Xms1024m',
529
+ '-Djava.awt.headless=true',
530
+ '-Dsun.net.http.retryPost=false',
531
+ java_code_option,
532
+ '-m verify',
533
+ ("-u #{username.shellescape}" unless use_jwt),
534
+ ("-p #{password.shellescape}" unless use_jwt),
535
+ ("-jwt #{jwt}" if use_jwt),
536
+ "-f #{source.shellescape}",
537
+ ("-itc_provider #{provider_short_name}" if jwt.nil? && !provider_short_name.to_s.empty?),
538
+ '2>&1' # cause stderr to be written to stdout
539
+ ].compact.join(' ') # compact gets rid of the possibly nil ENV value
540
+ end
541
+ end
542
+
307
543
  def build_download_command(username, password, apple_id, destination = "/tmp", provider_short_name = "", jwt = nil)
308
544
  use_jwt = !jwt.to_s.empty?
309
545
  if !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(11)
@@ -342,7 +578,7 @@ module FastlaneCore
342
578
  end
343
579
  end
344
580
 
345
- def build_provider_ids_command(username, password, jwt = nil)
581
+ def build_provider_ids_command(username, password, jwt = nil, api_key = nil)
346
582
  use_jwt = !jwt.to_s.empty?
347
583
  if !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(11)
348
584
  [
@@ -401,8 +637,6 @@ module FastlaneCore
401
637
  end
402
638
 
403
639
  class ItunesTransporter
404
- # Matches a line in the provider table: "12 Initech Systems Inc LG89CQY559"
405
- PROVIDER_REGEX = /^\d+\s{2,}.+\s{2,}[^\s]+$/
406
640
  TWO_STEP_HOST_PREFIX = "deliver.appspecific"
407
641
 
408
642
  # This will be called from the Deliverfile, and disables the logging of the transporter output
@@ -426,7 +660,7 @@ module FastlaneCore
426
660
  # see: https://github.com/fastlane/fastlane/issues/1524#issuecomment-196370628
427
661
  # for more information about how to use the iTMSTransporter to list your provider
428
662
  # short names
429
- def initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil)
663
+ def initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil, upload: false, api_key: nil)
430
664
  # Xcode 6.x doesn't have the same iTMSTransporter Java setup as later Xcode versions, so
431
665
  # we can't default to using the newer direct Java invocation strategy for those versions.
432
666
  use_shell_script ||= Helper.is_mac? && Helper.xcode_version.start_with?('6.')
@@ -439,8 +673,16 @@ module FastlaneCore
439
673
  end
440
674
 
441
675
  @jwt = jwt
676
+ @api_key = api_key
677
+
678
+ if should_use_altool?(upload, use_shell_script)
679
+ UI.verbose("Using altool as transporter.")
680
+ @transporter_executor = AltoolTransporterExecutor.new
681
+ else
682
+ UI.verbose("Using iTMSTransporter as transporter.")
683
+ @transporter_executor = use_shell_script ? ShellScriptTransporterExecutor.new : JavaTransporterExecutor.new
684
+ end
442
685
 
443
- @transporter_executor = use_shell_script ? ShellScriptTransporterExecutor.new : JavaTransporterExecutor.new
444
686
  @provider_short_name = provider_short_name
445
687
  end
446
688
 
@@ -489,7 +731,7 @@ module FastlaneCore
489
731
  # @return (Bool) True if everything worked fine
490
732
  # @raise [Deliver::TransporterTransferError] when something went wrong
491
733
  # when transferring
492
- def upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil)
734
+ def upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil, platform: nil)
493
735
  raise "app_id and dir are required or package_path or asset_path is required" if (app_id.nil? || dir.nil?) && package_path.nil? && asset_path.nil?
494
736
 
495
737
  # Transport can upload .ipa, .dmg, and .pkg files directly with -assetFile
@@ -519,14 +761,25 @@ module FastlaneCore
519
761
  password_placeholder = @jwt.nil? ? 'YourPassword' : nil
520
762
  jwt_placeholder = @jwt.nil? ? nil : 'YourJWT'
521
763
 
522
- command = @transporter_executor.build_upload_command(@user, @password, actual_dir, @provider_short_name, @jwt)
523
- UI.verbose(@transporter_executor.build_upload_command(@user, password_placeholder, actual_dir, @provider_short_name, jwt_placeholder))
764
+ # Handle AppStore Connect API
765
+ use_api_key = !@api_key.nil?
766
+ api_key_placeholder = use_api_key ? { key_id: "YourKeyID", issuer_id: "YourIssuerID", key_dir: "YourTmpP8KeyDir" } : nil
767
+
768
+ api_key = nil
769
+ api_key = api_key_with_p8_file_path(@api_key) if use_api_key
770
+
771
+ command = @transporter_executor.build_upload_command(@user, @password, actual_dir, @provider_short_name, @jwt, platform, api_key)
772
+ UI.verbose(@transporter_executor.build_upload_command(@user, password_placeholder, actual_dir, @provider_short_name, jwt_placeholder, platform, api_key_placeholder))
524
773
 
525
774
  begin
526
775
  result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?)
527
776
  rescue TransporterRequiresApplicationSpecificPasswordError => ex
528
777
  handle_two_step_failure(ex)
529
778
  return upload(app_id, dir, package_path: package_path, asset_path: asset_path)
779
+ ensure
780
+ if use_api_key
781
+ FileUtils.rm_rf(api_key[:key_dir]) unless api_key.nil?
782
+ end
530
783
  end
531
784
 
532
785
  if result
@@ -540,6 +793,46 @@ module FastlaneCore
540
793
  return result
541
794
  end
542
795
 
796
+ # Verifies the given binary with App Store Connect
797
+ # @param app_id [Integer] The unique App ID
798
+ # @param dir [String] the path in which the package file is located
799
+ # @param package_path [String] the path to the package file (used instead of app_id and dir)
800
+ # @return (Bool) True if everything worked fine
801
+ # @raise [Deliver::TransporterTransferError] when something went wrong
802
+ # when transferring
803
+ def verify(app_id = nil, dir = nil, package_path: nil)
804
+ raise "Either a combination of app id and directory or a package_path are required" if (app_id.nil? || dir.nil?) && package_path.nil?
805
+
806
+ actual_dir = if package_path
807
+ package_path
808
+ else
809
+ File.join(dir, "#{app_id}.itmsp")
810
+ end
811
+
812
+ password_placeholder = @jwt.nil? ? 'YourPassword' : nil
813
+ jwt_placeholder = @jwt.nil? ? nil : 'YourJWT'
814
+
815
+ command = @transporter_executor.build_verify_command(@user, @password, actual_dir, @provider_short_name, @jwt)
816
+ UI.verbose(@transporter_executor.build_verify_command(@user, password_placeholder, actual_dir, @provider_short_name, jwt_placeholder))
817
+
818
+ begin
819
+ result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?)
820
+ rescue TransporterRequiresApplicationSpecificPasswordError => ex
821
+ handle_two_step_failure(ex)
822
+ return verify(app_id, dir, package_path: package_path)
823
+ end
824
+
825
+ if result
826
+ UI.header("Successfully verified package on App Store Connect")
827
+
828
+ FileUtils.rm_rf(actual_dir) unless Helper.test? # we don't need the package any more, since the upload was successful
829
+ else
830
+ handle_error(@password)
831
+ end
832
+
833
+ return result
834
+ end
835
+
543
836
  def displayable_errors
544
837
  @transporter_executor.displayable_errors
545
838
  end
@@ -548,8 +841,15 @@ module FastlaneCore
548
841
  password_placeholder = @jwt.nil? ? 'YourPassword' : nil
549
842
  jwt_placeholder = @jwt.nil? ? nil : 'YourJWT'
550
843
 
551
- command = @transporter_executor.build_provider_ids_command(@user, @password, @jwt)
552
- UI.verbose(@transporter_executor.build_provider_ids_command(@user, password_placeholder, jwt_placeholder))
844
+ # Handle AppStore Connect API
845
+ use_api_key = !@api_key.nil?
846
+ api_key_placeholder = use_api_key ? { key_id: "YourKeyID", issuer_id: "YourIssuerID", key_dir: "YourTmpP8KeyDir" } : nil
847
+
848
+ api_key = nil
849
+ api_key = api_key_with_p8_file_path(@api_key) if use_api_key
850
+
851
+ command = @transporter_executor.build_provider_ids_command(@user, @password, @jwt, api_key)
852
+ UI.verbose(@transporter_executor.build_provider_ids_command(@user, password_placeholder, jwt_placeholder, api_key_placeholder))
553
853
 
554
854
  lines = []
555
855
  begin
@@ -558,15 +858,36 @@ module FastlaneCore
558
858
  rescue TransporterRequiresApplicationSpecificPasswordError => ex
559
859
  handle_two_step_failure(ex)
560
860
  return provider_ids
861
+ ensure
862
+ if use_api_key
863
+ FileUtils.rm_rf(api_key[:key_dir]) unless api_key.nil?
864
+ end
561
865
  end
562
866
 
563
- lines.map { |line| provider_pair(line) }.compact.to_h
867
+ @transporter_executor.parse_provider_info(lines)
564
868
  end
565
869
 
566
870
  private
567
871
 
568
872
  TWO_FACTOR_ENV_VARIABLE = "FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"
569
873
 
874
+ # Create .p8 file from api_key and provide api key info which contains .p8 file path
875
+ def api_key_with_p8_file_path(original_api_key)
876
+ api_key = original_api_key.dup
877
+ api_key[:key_dir] = Dir.mktmpdir("deliver-")
878
+ # Specified p8 needs to be generated to call altool
879
+ File.open(File.join(api_key[:key_dir], "AuthKey_#{api_key[:key_id]}.p8"), "wb") do |p8|
880
+ p8.write(api_key[:key])
881
+ end
882
+ api_key
883
+ end
884
+
885
+ # Returns whether altool should be used or ItunesTransporter should be used
886
+ def should_use_altool?(upload, use_shell_script)
887
+ # Xcode 14 no longer supports iTMSTransporter. Use altool instead
888
+ !use_shell_script && upload && !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(14)
889
+ end
890
+
570
891
  # Returns the password to be used with the transporter
571
892
  def load_password_for_transporter
572
893
  # 3 different sources for the password
@@ -624,11 +945,5 @@ module FastlaneCore
624
945
  def handle_error(password)
625
946
  @transporter_executor.handle_error(password)
626
947
  end
627
-
628
- def provider_pair(line)
629
- line = line.strip
630
- return nil unless line =~ PROVIDER_REGEX
631
- line.split(/\s{2,}/).drop(1)
632
- end
633
948
  end
634
949
  end
@@ -14,6 +14,7 @@ module FastlaneCore
14
14
  command << " -T /usr/bin/codesign" # to not be asked for permission when running a tool like `gym` (before Sierra)
15
15
  command << " -T /usr/bin/security"
16
16
  command << " -T /usr/bin/productbuild" # to not be asked for permission when using an installer cert for macOS
17
+ command << " -T /usr/bin/productsign" # to not be asked for permission when using an installer cert for macOS
17
18
  command << " 1> /dev/null" unless output
18
19
 
19
20
  sensitive_command = command.gsub(password_part, " -P ********")
@@ -356,7 +356,7 @@ module FastlaneCore
356
356
  # This xcodebuild bug is fixed in Xcode 8.3 so 'clean' it's not necessary anymore
357
357
  # See: https://github.com/fastlane/fastlane/pull/5626
358
358
  if FastlaneCore::Helper.xcode_at_least?('8.3')
359
- command = "xcodebuild -showBuildSettings #{xcodebuild_parameters.join(' ')}"
359
+ command = "xcodebuild -showBuildSettings #{xcodebuild_parameters.join(' ')}#{xcodebuild_destination_parameter}"
360
360
  else
361
361
  command = "xcodebuild clean -showBuildSettings #{xcodebuild_parameters.join(' ')}"
362
362
  end
@@ -365,10 +365,27 @@ module FastlaneCore
365
365
 
366
366
  def build_xcodebuild_resolvepackagedependencies_command
367
367
  return nil if options[:skip_package_dependencies_resolution]
368
- command = "xcodebuild -resolvePackageDependencies #{xcodebuild_parameters.join(' ')}"
368
+ command = "xcodebuild -resolvePackageDependencies #{xcodebuild_parameters.join(' ')}#{xcodebuild_destination_parameter}"
369
369
  command
370
370
  end
371
371
 
372
+ def xcodebuild_destination_parameter
373
+ # Xcode13+ xcodebuild command 'without destination parameter' generates annoying warnings
374
+ # See: https://github.com/fastlane/fastlane/issues/19579
375
+ destination_parameter = ""
376
+ xcode_at_least_13 = FastlaneCore::Helper.xcode_at_least?("13")
377
+ if xcode_at_least_13 && options[:destination]
378
+ begin
379
+ destination_parameter = " " + "-destination #{options[:destination].shellescape}"
380
+ rescue => ex
381
+ # xcodebuild command can continue without destination parameter, so
382
+ # we really don't care about this exception if something goes wrong with shellescape
383
+ UI.important("Failed to set destination parameter for xcodebuild command: #{ex}")
384
+ end
385
+ end
386
+ destination_parameter
387
+ end
388
+
372
389
  # Get the build settings for our project
373
390
  # e.g. to properly get the DerivedData folder
374
391
  # @param [String] The key of which we want the value for (e.g. "PRODUCT_NAME")
@@ -86,11 +86,13 @@ module FastlaneCore
86
86
 
87
87
  def header(message)
88
88
  format = format_string
89
- if message.length + 8 < TTY::Screen.width - format.length
89
+ # clamp to zero to prevent negative argument error below
90
+ available_width = [0, TTY::Screen.width - format.length].max
91
+ if message.length + 8 < available_width
90
92
  message = "--- #{message} ---"
91
93
  i = message.length
92
94
  else
93
- i = TTY::Screen.width - format.length
95
+ i = available_width
94
96
  end
95
97
  success("-" * i)
96
98
  success(message)
@@ -51,7 +51,7 @@ module Frameit
51
51
  found_device = nil
52
52
  filename_device = nil
53
53
  filename = Pathname.new(path).basename.to_s
54
- Devices.constants.each do |c|
54
+ Devices.constants.sort_by(&:length).reverse_each do |c|
55
55
  device = Devices.const_get(c)
56
56
  next unless device.resolutions.include?(size)
57
57
  # assign to filename_device if the filename contains the formatted name / id and its priority is higher than the current filename_device
@@ -47,6 +47,10 @@ module Frameit
47
47
  PURPLE ||= "Purple"
48
48
  GRAPHITE ||= "Graphite"
49
49
  PACIFIC_BLUE ||= "Pacific Blue"
50
+ MIDNIGHT ||= "Midnight"
51
+ STARLIGHT ||= "Starlight"
52
+ SIERRA ||= "Sierra"
53
+ SORTA_SAGE ||= "Sorta Sage"
50
54
 
51
55
  def self.all_colors
52
56
  Color.constants.map { |c| Color.const_get(c).upcase.gsub(' ', '_') }
@@ -82,6 +86,7 @@ module Frameit
82
86
  # Google Pixel 4's priority should be higher than Samsung Galaxy S10+ (priority 8):
83
87
  GOOGLE_PIXEL_4 ||= Frameit::Device.new("google-pixel-4", "Google Pixel 4", 9, [[1080, 2280], [2280, 1080]], 444, Color::JUST_BLACK, Platform::ANDROID)
84
88
  GOOGLE_PIXEL_4_XL ||= Frameit::Device.new("google-pixel-4-xl", "Google Pixel 4 XL", 9, [[1440, 3040], [3040, 1440]], 537, Color::JUST_BLACK, Platform::ANDROID)
89
+ GOOGLE_PIXEL_5 ||= Frameit::Device.new("google-pixel-5", "Google Pixel 5", 10, [[1080, 2340], [2340, 1080]], 432, Color::JUST_BLACK, Platform::ANDROID)
85
90
  HTC_ONE_A9 ||= Frameit::Device.new("htc-one-a9", "HTC One A9", 6, [[1080, 1920], [1920, 1080]], 441, Color::BLACK, Platform::ANDROID)
86
91
  HTC_ONE_M8 ||= Frameit::Device.new("htc-one-m8", "HTC One M8", 3, [[1080, 1920], [1920, 1080]], 441, Color::BLACK, Platform::ANDROID)
87
92
  HUAWEI_P8 ||= Frameit::Device.new("huawei-p8", "Huawei P8", 5, [[1080, 1920], [1920, 1080]], 424, Color::BLACK, Platform::ANDROID)
@@ -124,6 +129,10 @@ module Frameit
124
129
  IPHONE_12_PRO ||= Frameit::Device.new("iphone-12-pro", "Apple iPhone 12 Pro", 10, [[1170, 2532], [2532, 1170]], 460, Color::SPACE_GRAY, Platform::IOS)
125
130
  IPHONE_12_PRO_MAX ||= Frameit::Device.new("iphone12-pro-max", "Apple iPhone 12 Pro Max", 10, [[1284, 2778], [2778, 1284]], 458, Color::GRAPHITE, Platform::IOS)
126
131
  IPHONE_12_MINI ||= Frameit::Device.new("iphone-12-mini", "Apple iPhone 12 Mini", 10, [[1125, 2436], [2436, 1125]], 476, Color::BLACK, Platform::IOS)
132
+ IPHONE_13 ||= Frameit::Device.new("iphone-13", "Apple iPhone 13", 11, [[1170, 2532], [2532, 1170]], 460, Color::MIDNIGHT, Platform::IOS)
133
+ IPHONE_13_PRO ||= Frameit::Device.new("iphone-13-pro", "Apple iPhone 13 Pro", 11, [[1170, 2532], [2532, 1170]], 460, Color::GRAPHITE, Platform::IOS)
134
+ IPHONE_13_PRO_MAX ||= Frameit::Device.new("iphone13-pro-max", "Apple iPhone 13 Pro Max", 11, [[1284, 2778], [2778, 1284]], 458, Color::GRAPHITE, Platform::IOS)
135
+ IPHONE_13_MINI ||= Frameit::Device.new("iphone-13-mini", "Apple iPhone 13 Mini", 11, [[1080, 2340], [2340, 1080]], 476, Color::MIDNIGHT, Platform::IOS)
127
136
  IPAD_10_2 ||= Frameit::Device.new("ipad-10-2", "Apple iPad 10.2", 1, [[1620, 2160], [2160, 1620]], 264, Color::SPACE_GRAY, Platform::IOS)
128
137
  IPAD_AIR_2 ||= Frameit::Device.new("ipad-air-2", "Apple iPad Air 2", 1, [[1536, 2048], [2048, 1536]], 264, Color::SPACE_GRAY, Platform::IOS, Deliver::AppScreenshot::ScreenSize::IOS_IPAD)
129
138
  IPAD_AIR_2019 ||= Frameit::Device.new("ipad-air-2019", "Apple iPad Air (2019)", 2, [[1668, 2224], [2224, 1668]], 265, Color::SPACE_GRAY, Platform::IOS)
@@ -53,7 +53,7 @@ module Frameit
53
53
 
54
54
  def print_disclaimer
55
55
  UI.header("Device frames disclaimer")
56
- UI.important("All used device frames are available via Facebook Design: http://facebook.design/devices")
56
+ UI.important("All used device frames are available via Facebook Design: https://design.facebook.com/toolsandresources/devices/")
57
57
  UI.message("----------------------------------------")
58
58
  UI.message("While Facebook has redrawn and shares these assets for the benefit")
59
59
  UI.message("of the design community, Facebook does not own any of the underlying")
@@ -39,6 +39,7 @@ module Gym
39
39
  options << "-destination '#{config[:destination]}'" if config[:destination]
40
40
  options << "-archivePath #{archive_path.shellescape}" unless config[:skip_archive]
41
41
  options << "-resultBundlePath '#{result_bundle_path}'" if config[:result_bundle]
42
+ options << "-showBuildTimingSummary" if config[:build_timing_summary]
42
43
  if config[:use_system_scm] && !options.include?("-scmProvider system")
43
44
  options << "-scmProvider system"
44
45
  end
@@ -239,6 +239,13 @@ module Gym
239
239
  default_value: Fastlane::Helper::XcodebuildFormatterHelper.xcbeautify_installed? ? 'xcbeautify' : 'xcpretty',
240
240
  default_value_dynamic: true),
241
241
 
242
+ FastlaneCore::ConfigItem.new(key: :build_timing_summary,
243
+ env_name: "GYM_BUILD_TIMING_SUMMARY",
244
+ description: "Create a build timing summary",
245
+ type: Boolean,
246
+ default_value: false,
247
+ optional: true),
248
+
242
249
  # xcpretty
243
250
  FastlaneCore::ConfigItem.new(key: :disable_xcpretty,
244
251
  env_name: "DISABLE_XCPRETTY",
@@ -42,6 +42,8 @@ module Match
42
42
  message = "[fastlane] Changed passphrase"
43
43
  files_to_commit = encryption.encrypt_files(password: new_password)
44
44
  storage.save_changes!(files_to_commit: files_to_commit, custom_message: message)
45
+ ensure
46
+ storage.clear_changes if storage
45
47
  end
46
48
 
47
49
  def self.ensure_ui_interactive
@@ -155,7 +155,8 @@ module Match
155
155
  FastlaneCore::CommanderGenerator.new.generate(Match::Options.available_options, command: c)
156
156
 
157
157
  c.action do |args, options|
158
- Match::Migrate.new.migrate(args, options)
158
+ params = FastlaneCore::Configuration.create(Match::Options.available_options, options.__hash__)
159
+ Match::Migrate.new.migrate(params)
159
160
  end
160
161
  end
161
162
 
@@ -135,7 +135,7 @@ module Match
135
135
  UI.crash!("Error encrypting '#{path}'")
136
136
  end
137
137
 
138
- # The encryption parameters in this implementations reflect the old behaviour which depended on the users' local OpenSSL version
138
+ # The encryption parameters in this implementations reflect the old behavior which depended on the users' local OpenSSL version
139
139
  # 1.0.x OpenSSL and earlier versions use MD5, 1.1.0c and newer uses SHA256, we try both before giving an error
140
140
  def decrypt_specific_file(path: nil, password: nil, hash_algorithm: "MD5")
141
141
  stored_data = Base64.decode64(File.read(path))