fastlane 2.175.0 → 2.180.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +95 -82
  4. data/cert/lib/cert/options.rb +1 -0
  5. data/cert/lib/cert/runner.rb +5 -1
  6. data/deliver/lib/deliver/app_screenshot.rb +6 -2
  7. data/deliver/lib/deliver/download_screenshots.rb +1 -2
  8. data/deliver/lib/deliver/options.rb +1 -0
  9. data/deliver/lib/deliver/runner.rb +12 -4
  10. data/deliver/lib/deliver/setup.rb +0 -1
  11. data/deliver/lib/deliver/upload_metadata.rb +5 -4
  12. data/deliver/lib/deliver/upload_screenshots.rb +10 -10
  13. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +6 -1
  14. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +8 -5
  15. data/fastlane/lib/fastlane/actions/appaloosa.rb +7 -2
  16. data/fastlane/lib/fastlane/actions/backup_file.rb +1 -1
  17. data/fastlane/lib/fastlane/actions/build_app.rb +4 -0
  18. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +4 -0
  19. data/fastlane/lib/fastlane/actions/commit_github_file.rb +11 -1
  20. data/fastlane/lib/fastlane/actions/create_xcframework.rb +5 -0
  21. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -1
  22. data/fastlane/lib/fastlane/actions/get_github_release.rb +11 -1
  23. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +4 -0
  24. data/fastlane/lib/fastlane/actions/get_version_number.rb +17 -10
  25. data/fastlane/lib/fastlane/actions/git_branch.rb +4 -10
  26. data/fastlane/lib/fastlane/actions/git_tag_exists.rb +4 -0
  27. data/fastlane/lib/fastlane/actions/github_api.rb +2 -1
  28. data/fastlane/lib/fastlane/actions/increment_build_number.rb +8 -1
  29. data/fastlane/lib/fastlane/actions/install_provisioning_profile.rb +4 -0
  30. data/fastlane/lib/fastlane/actions/jazzy.rb +10 -1
  31. data/fastlane/lib/fastlane/actions/jira.rb +61 -14
  32. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +1 -0
  33. data/fastlane/lib/fastlane/actions/match_nuke.rb +59 -0
  34. data/fastlane/lib/fastlane/actions/notarize.rb +98 -51
  35. data/fastlane/lib/fastlane/actions/push_to_git_remote.rb +0 -1
  36. data/fastlane/lib/fastlane/actions/register_device.rb +1 -1
  37. data/fastlane/lib/fastlane/actions/register_devices.rb +1 -1
  38. data/fastlane/lib/fastlane/actions/restore_file.rb +1 -1
  39. data/fastlane/lib/fastlane/actions/set_changelog.rb +1 -1
  40. data/fastlane/lib/fastlane/actions/sourcedocs.rb +164 -0
  41. data/fastlane/lib/fastlane/actions/spaceship_logs.rb +1 -1
  42. data/fastlane/lib/fastlane/actions/update_project_provisioning.rb +1 -2
  43. data/fastlane/lib/fastlane/actions/upload_symbols_to_crashlytics.rb +4 -5
  44. data/fastlane/lib/fastlane/actions/upload_to_testflight.rb +5 -1
  45. data/fastlane/lib/fastlane/erb_template_helper.rb +7 -1
  46. data/fastlane/lib/fastlane/fast_file.rb +9 -5
  47. data/fastlane/lib/fastlane/helper/git_helper.rb +11 -7
  48. data/fastlane/lib/fastlane/plugins/plugin_fetcher.rb +1 -2
  49. data/fastlane/lib/fastlane/plugins/plugin_info_collector.rb +1 -2
  50. data/fastlane/lib/fastlane/plugins/plugin_manager.rb +1 -2
  51. data/fastlane/lib/fastlane/setup/setup.rb +23 -10
  52. data/fastlane/lib/fastlane/swift_fastlane_function.rb +4 -0
  53. data/fastlane/lib/fastlane/swift_runner_upgrader.rb +2 -0
  54. data/fastlane/lib/fastlane/version.rb +1 -1
  55. data/fastlane/swift/Deliverfile.swift +1 -1
  56. data/fastlane/swift/DeliverfileProtocol.swift +3 -3
  57. data/fastlane/swift/Fastlane.swift +574 -332
  58. data/fastlane/swift/Gymfile.swift +1 -1
  59. data/fastlane/swift/GymfileProtocol.swift +1 -1
  60. data/fastlane/swift/LaneFileProtocol.swift +9 -3
  61. data/fastlane/swift/Matchfile.swift +1 -1
  62. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  63. data/fastlane/swift/Precheckfile.swift +1 -1
  64. data/fastlane/swift/PrecheckfileProtocol.swift +3 -3
  65. data/fastlane/swift/RubyCommand.swift +1 -1
  66. data/fastlane/swift/Scanfile.swift +1 -1
  67. data/fastlane/swift/ScanfileProtocol.swift +13 -1
  68. data/fastlane/swift/Screengrabfile.swift +1 -1
  69. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  70. data/fastlane/swift/Snapshotfile.swift +1 -1
  71. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  72. data/fastlane/swift/SocketClient.swift +2 -1
  73. data/fastlane/swift/SocketResponse.swift +4 -2
  74. data/fastlane/swift/formatting/Brewfile.lock.json +12 -12
  75. data/fastlane_core/lib/fastlane_core.rb +1 -0
  76. data/fastlane_core/lib/fastlane_core/configuration/configuration.rb +5 -3
  77. data/fastlane_core/lib/fastlane_core/helper.rb +24 -1
  78. data/fastlane_core/lib/fastlane_core/ipa_upload_package_builder.rb +3 -2
  79. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +14 -8
  80. data/fastlane_core/lib/fastlane_core/pkg_upload_package_builder.rb +3 -2
  81. data/fastlane_core/lib/fastlane_core/project.rb +3 -14
  82. data/{deliver/lib/deliver → fastlane_core/lib/fastlane_core}/queue_worker.rb +4 -4
  83. data/fastlane_core/lib/fastlane_core/ui/implementations/shell.rb +12 -1
  84. data/fastlane_core/lib/fastlane_core/ui/interface.rb +1 -1
  85. data/gym/lib/gym/generators/.package_command_generator_xcode7.rb.swp +0 -0
  86. data/gym/lib/gym/generators/package_command_generator.rb +4 -0
  87. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +13 -8
  88. data/gym/lib/gym/runner.rb +11 -4
  89. data/match/lib/match/change_password.rb +3 -3
  90. data/match/lib/match/encryption/interface.rb +1 -1
  91. data/match/lib/match/encryption/openssl.rb +2 -2
  92. data/match/lib/match/importer.rb +1 -1
  93. data/match/lib/match/migrate.rb +1 -1
  94. data/match/lib/match/module.rb +1 -0
  95. data/match/lib/match/nuke.rb +1 -1
  96. data/match/lib/match/runner.rb +1 -1
  97. data/match/lib/match/storage/google_cloud_storage.rb +1 -1
  98. data/match/lib/match/storage/s3_storage.rb +1 -1
  99. data/pilot/lib/pilot/build_manager.rb +25 -8
  100. data/pilot/lib/pilot/manager.rb +5 -1
  101. data/pilot/lib/pilot/options.rb +4 -3
  102. data/precheck/lib/precheck/options.rb +1 -0
  103. data/precheck/lib/precheck/runner.rb +6 -2
  104. data/scan/lib/scan/detect_values.rb +4 -1
  105. data/scan/lib/scan/options.rb +20 -5
  106. data/scan/lib/scan/runner.rb +79 -1
  107. data/scan/lib/scan/test_command_generator.rb +8 -8
  108. data/screengrab/lib/screengrab/android_environment.rb +6 -4
  109. data/screengrab/lib/screengrab/runner.rb +2 -3
  110. data/sigh/lib/sigh/download_all.rb +1 -1
  111. data/sigh/lib/sigh/options.rb +1 -0
  112. data/sigh/lib/sigh/runner.rb +5 -1
  113. data/snapshot/lib/assets/SnapshotHelper.swift +1 -1
  114. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +2 -1
  115. data/spaceship/lib/spaceship/client.rb +18 -17
  116. data/spaceship/lib/spaceship/connect_api/api_client.rb +24 -6
  117. data/spaceship/lib/spaceship/connect_api/models/app.rb +1 -1
  118. data/spaceship/lib/spaceship/connect_api/models/app_preview_set.rb +5 -0
  119. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +1 -1
  120. data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +5 -0
  121. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +6 -0
  122. data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +5 -0
  123. data/spaceship/lib/spaceship/connect_api/models/build.rb +5 -0
  124. data/spaceship/lib/spaceship/connect_api/models/build_beta_detail.rb +4 -0
  125. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +13 -0
  126. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
  127. data/spaceship/lib/spaceship/connect_api/token.rb +8 -2
  128. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +15 -0
  129. data/spaceship/lib/spaceship/two_step_or_factor_client.rb +41 -28
  130. metadata +26 -21
@@ -52,6 +52,7 @@ module Precheck
52
52
  short_option: "-u",
53
53
  env_name: "PRECHECK_USERNAME",
54
54
  description: "Your Apple ID Username",
55
+ optional: true,
55
56
  default_value: user,
56
57
  default_value_dynamic: true),
57
58
  FastlaneCore::ConfigItem.new(key: :team_id,
@@ -26,12 +26,16 @@ module Precheck
26
26
  # There is also a check in Deliver::Runner for this.
27
27
  # Please remove check in Deliver when the API support IAPs.
28
28
  if Precheck.config[:include_in_app_purchases]
29
- UI.user_error!("Precheck cannot check In-app purchases with the App Store Connect API Key (yet). Exclude In-app purchases from precheck or use Apple ID login")
29
+ UI.user_error!("Precheck cannot check In-app purchases with the App Store Connect API Key (yet). Exclude In-app purchases from precheck, disable the precheck step in your build step, or use Apple ID login")
30
30
  end
31
31
 
32
32
  UI.message("Creating authorization token for App Store Connect API")
33
33
  Spaceship::ConnectAPI.token = api_token
34
34
  elsif Spaceship::Tunes.client.nil?
35
+ # Username is now optional since addition of App Store Connect API Key
36
+ # Force asking for username to prompt user if not already set
37
+ Precheck.config.fetch(:username, force_ask: true)
38
+
35
39
  # Team selection passed though FASTLANE_ITC_TEAM_ID and FASTLANE_ITC_TEAM_NAME environment variables
36
40
  # Prompts select team if multiple teams and none specified
37
41
  UI.message("Starting login with user '#{Precheck.config[:username]}'")
@@ -72,7 +76,7 @@ module Precheck
72
76
  end
73
77
 
74
78
  def api_token
75
- @api_token ||= Spaceship::ConnectAPI::Token.create(Precheck.config[:api_key]) if Precheck.config[:api_key]
79
+ @api_token ||= Spaceship::ConnectAPI::Token.create(**Precheck.config[:api_key]) if Precheck.config[:api_key]
76
80
  @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(Precheck.config[:api_key_path]) if Precheck.config[:api_key_path]
77
81
  return @api_token
78
82
  end
@@ -162,7 +162,10 @@ module Scan
162
162
  filter_simulators(simulators, :equal, version).tap(&potential_emptiness_error).select(&selector)
163
163
  end
164
164
  ).tap do |array|
165
- UI.error("Ignoring '#{device_string}', couldn’t find matching simulator") if array.empty?
165
+ if array.empty?
166
+ UI.test_failure!("No device found with name '#{device_string}'") if Scan.config[:ensure_devices_found]
167
+ UI.error("Ignoring '#{device_string}', couldn’t find matching simulator")
168
+ end
166
169
  end
167
170
  end
168
171
 
@@ -69,6 +69,11 @@ module Scan
69
69
  default_value: false,
70
70
  type: Boolean,
71
71
  optional: true),
72
+ FastlaneCore::ConfigItem.new(key: :ensure_devices_found,
73
+ description: "Should fail if devices not found",
74
+ default_value: false,
75
+ type: Boolean,
76
+ optional: true),
72
77
 
73
78
  # simulator management
74
79
  FastlaneCore::ConfigItem.new(key: :force_quit_simulator,
@@ -269,6 +274,11 @@ module Scan
269
274
  optional: true,
270
275
  is_string: false,
271
276
  default_value: false),
277
+ FastlaneCore::ConfigItem.new(key: :output_xctestrun,
278
+ type: Boolean,
279
+ env_name: "SCAN_OUTPUT_XCTESTRUN",
280
+ description: "Should provide additional copy of .xctestrun file (settings.xctestrun) and place in output path?",
281
+ default_value: false),
272
282
  FastlaneCore::ConfigItem.new(key: :result_bundle,
273
283
  short_option: "-z",
274
284
  env_name: "SCAN_RESULT_BUNDLE",
@@ -462,11 +472,16 @@ module Scan
462
472
  type: Boolean,
463
473
  default_value: false),
464
474
  FastlaneCore::ConfigItem.new(key: :use_system_scm,
465
- env_name: "SCAN_USE_SYSTEM_SCM",
466
- description: "Lets xcodebuild use system's scm configuration",
467
- optional: true,
468
- type: Boolean,
469
- default_value: false)
475
+ env_name: "SCAN_USE_SYSTEM_SCM",
476
+ description: "Lets xcodebuild use system's scm configuration",
477
+ optional: true,
478
+ type: Boolean,
479
+ default_value: false),
480
+ FastlaneCore::ConfigItem.new(key: :number_of_retries,
481
+ env_name: 'SCAN_NUMBER_OF_RETRIES',
482
+ description: "The number of times a test can fail before scan should stop retrying",
483
+ type: Integer,
484
+ default_value: 0)
470
485
 
471
486
  ]
472
487
  end
@@ -51,7 +51,12 @@ module Scan
51
51
  end
52
52
  end
53
53
 
54
+ execute(retries: Scan.config[:number_of_retries])
55
+ end
56
+
57
+ def execute(retries: 0)
54
58
  command = @test_command_generator.generate
59
+
55
60
  prefix_hash = [
56
61
  {
57
62
  prefix: "Running Tests: ",
@@ -71,7 +76,12 @@ module Scan
71
76
  error: proc do |error_output|
72
77
  begin
73
78
  exit_status = $?.exitstatus
74
- ErrorHandler.handle_build_error(error_output, @test_command_generator.xcodebuild_log_path)
79
+ if retries > 0
80
+ # If there are retries remaining, run the tests again
81
+ return retry_execute(retries: retries, error_output: error_output)
82
+ else
83
+ ErrorHandler.handle_build_error(error_output, @test_command_generator.xcodebuild_log_path)
84
+ end
75
85
  rescue => ex
76
86
  SlackPoster.new.run({
77
87
  build_errors: 1
@@ -79,9 +89,50 @@ module Scan
79
89
  raise ex
80
90
  end
81
91
  end)
92
+
82
93
  exit_status
83
94
  end
84
95
 
96
+ def retry_execute(retries:, error_output: "")
97
+ tests = retryable_tests(error_output)
98
+
99
+ if tests.empty?
100
+ UI.crash!("Failed to find failed tests to retry (could not parse error output)")
101
+ end
102
+
103
+ Scan.config[:only_testing] = tests
104
+ UI.important("Retrying tests: #{Scan.config[:only_testing].join(', ')}")
105
+
106
+ retries -= 1
107
+ UI.important("Number of retries remaining: #{retries}")
108
+
109
+ return execute(retries: retries)
110
+ end
111
+
112
+ def retryable_tests(input)
113
+ input = Helper.strip_ansi_colors(input)
114
+
115
+ retryable_tests = []
116
+
117
+ failing_tests = input.split("Failing tests:\n").fetch(1, [])
118
+ .split("\n\n").first
119
+
120
+ suites = failing_tests.split(/(?=\n\s+[\w\s]+:\n)/)
121
+
122
+ suites.each do |suite|
123
+ suite_name = suite.match(/\s*([\w\s]+):/).captures.first
124
+
125
+ test_cases = suite.split(":\n").fetch(1, []).split("\n").each
126
+ .select { |line| line.match?(/^\s+/) }
127
+ .map { |line| line.strip.gsub(".", "/").gsub("()", "") }
128
+ .map { |line| suite_name + "/" + line }
129
+
130
+ retryable_tests += test_cases
131
+ end
132
+
133
+ return retryable_tests.uniq
134
+ end
135
+
85
136
  def handle_results(tests_exit_status)
86
137
  if Scan.config[:disable_xcpretty]
87
138
  unless tests_exit_status == 0
@@ -110,6 +161,7 @@ module Scan
110
161
 
111
162
  copy_simulator_logs
112
163
  zip_build_products
164
+ copy_xctestrun
113
165
 
114
166
  if result[:failures] > 0
115
167
  open_report
@@ -150,6 +202,32 @@ module Scan
150
202
  UI.message("Successfully zipped build products: #{output_path}")
151
203
  end
152
204
 
205
+ def copy_xctestrun
206
+ return unless Scan.config[:output_xctestrun]
207
+
208
+ # Gets :derived_data_path/Build/Products directory for coping .xctestrun file
209
+ derived_data_path = Scan.config[:derived_data_path]
210
+ path = File.join(derived_data_path, "Build", "Products")
211
+
212
+ # Gets absolute path of output directory
213
+ output_directory = File.absolute_path(Scan.config[:output_directory])
214
+ output_path = File.join(output_directory, "settings.xctestrun")
215
+
216
+ # Caching path for action to put into lane_context
217
+ Scan.cache[:output_xctestrun] = output_path
218
+
219
+ # Copy .xctestrun file and moves it to output directory
220
+ UI.message("Copying .xctestrun file")
221
+ xctestrun_file = Dir.glob("#{path}/*.xctestrun").first
222
+
223
+ if xctestrun_file
224
+ FileUtils.cp(xctestrun_file, output_path)
225
+ UI.message("Successfully copied xctestrun file: #{output_path}")
226
+ else
227
+ UI.user_error!("Could not find .xctextrun file to copy")
228
+ end
229
+ end
230
+
153
231
  def test_results
154
232
  temp_junit_report = Scan.cache[:temp_junit_report]
155
233
  return File.read(temp_junit_report) if temp_junit_report && File.file?(temp_junit_report)
@@ -162,16 +162,16 @@ module Scan
162
162
  Scan.cache[:build_path]
163
163
  end
164
164
 
165
+ # The path to the result bundle
165
166
  def result_bundle_path
166
- unless Scan.cache[:result_bundle_path]
167
- ext = FastlaneCore::Helper.xcode_version.to_i >= 11 ? '.xcresult' : '.test_result'
168
- path = File.join(Scan.config[:output_directory], Scan.config[:scheme]) + ext
169
- if File.directory?(path)
170
- FileUtils.remove_dir(path)
171
- end
172
- Scan.cache[:result_bundle_path] = path
167
+ ext = FastlaneCore::Helper.xcode_version.to_i >= 11 ? '.xcresult' : '.test_result'
168
+ path = File.join(Scan.config[:output_directory], Scan.config[:scheme]) + ext
169
+ if File.directory?(path)
170
+ FileUtils.remove_dir(path)
173
171
  end
174
- return Scan.cache[:result_bundle_path]
172
+ Scan.cache[:result_bundle_path] = path
173
+
174
+ return path
175
175
  end
176
176
  end
177
177
  end
@@ -34,22 +34,22 @@ module Screengrab
34
34
  def find_platform_tools(android_home)
35
35
  return nil unless android_home
36
36
 
37
- platform_tools_path = File.join(android_home, 'platform-tools')
37
+ platform_tools_path = Helper.localize_file_path(File.join(android_home, 'platform-tools'))
38
38
  File.directory?(platform_tools_path) ? platform_tools_path : nil
39
39
  end
40
40
 
41
41
  def find_build_tools(android_home, build_tools_version)
42
42
  return nil unless android_home
43
43
 
44
- build_tools_dir = File.join(android_home, 'build-tools')
44
+ build_tools_dir = Helper.localize_file_path(File.join(android_home, 'build-tools'))
45
45
 
46
46
  return nil unless build_tools_dir && File.directory?(build_tools_dir)
47
47
 
48
- return File.join(build_tools_dir, build_tools_version) if build_tools_version
48
+ return Helper.localize_file_path(File.join(build_tools_dir, build_tools_version)) if build_tools_version
49
49
 
50
50
  version = select_build_tools_version(build_tools_dir)
51
51
 
52
- return version ? File.join(build_tools_dir, version) : nil
52
+ return version ? Helper.localize_file_path(File.join(build_tools_dir, version)) : nil
53
53
  end
54
54
 
55
55
  def select_build_tools_version(build_tools_dir)
@@ -74,6 +74,7 @@ module Screengrab
74
74
  return FastlaneCore::CommandExecutor.which('adb') unless platform_tools_path
75
75
 
76
76
  adb_path = Helper.get_executable_path(File.join(platform_tools_path, 'adb'))
77
+ adb_path = Helper.localize_file_path(adb_path)
77
78
  return executable_command?(adb_path) ? adb_path : nil
78
79
  end
79
80
 
@@ -81,6 +82,7 @@ module Screengrab
81
82
  return FastlaneCore::CommandExecutor.which('aapt') unless build_tools_path
82
83
 
83
84
  aapt_path = Helper.get_executable_path(File.join(build_tools_path, 'aapt'))
85
+ aapt_path = Helper.localize_file_path(aapt_path)
84
86
  return executable_command?(aapt_path) ? aapt_path : nil
85
87
  end
86
88
 
@@ -308,7 +308,7 @@ module Screengrab
308
308
  if out =~ /Permission denied/
309
309
  dir = File.dirname(path)
310
310
  base = File.basename(path)
311
- run_adb_command("-s #{device_serial} shell run-as #{@config[:app_package_name]} 'tar -cC #{dir} #{base}' | tar -xvC #{tempdir}",
311
+ run_adb_command("-s #{device_serial} shell run-as #{@config[:app_package_name]} \"tar -cC #{dir} #{base}\" | tar -xv -f- -C #{tempdir}",
312
312
  print_all: false,
313
313
  print_command: true)
314
314
  end
@@ -392,13 +392,12 @@ module Screengrab
392
392
  end
393
393
 
394
394
  def run_adb_command(command, print_all: false, print_command: false, raise_errors: true)
395
- adb_path = @android_env.adb_path.chomp("adb")
396
395
  adb_host = @config[:adb_host]
397
396
  host = adb_host.nil? ? '' : "-H #{adb_host} "
398
397
  output = ''
399
398
  begin
400
399
  errout = nil
401
- cmdout = @executor.execute(command: adb_path + "adb " + host + command,
400
+ cmdout = @executor.execute(command: @android_env.adb_path + " " + host + command,
402
401
  print_all: print_all,
403
402
  print_command: print_command,
404
403
  error: raise_errors ? nil : proc { |out, status| errout = out }) || ''
@@ -60,7 +60,7 @@ module Sigh
60
60
  end
61
61
 
62
62
  def api_token
63
- api_token ||= Spaceship::ConnectAPI::Token.create(Sigh.config[:api_key]) if Sigh.config[:api_key]
63
+ api_token ||= Spaceship::ConnectAPI::Token.create(**Sigh.config[:api_key]) if Sigh.config[:api_key]
64
64
  api_token ||= Spaceship::ConnectAPI::Token.from_json_file(Sigh.config[:api_key_path]) if Sigh.config[:api_key_path]
65
65
  return api_token
66
66
  end
@@ -77,6 +77,7 @@ module Sigh
77
77
  short_option: "-u",
78
78
  env_name: "SIGH_USERNAME",
79
79
  description: "Your Apple ID Username",
80
+ optional: true,
80
81
  default_value: user,
81
82
  default_value_dynamic: true),
82
83
  FastlaneCore::ConfigItem.new(key: :team_id,
@@ -21,6 +21,10 @@ module Sigh
21
21
  UI.message("Creating authorization token for App Store Connect API")
22
22
  Spaceship::ConnectAPI.token = api_token
23
23
  else
24
+ # Username is now optional since addition of App Store Connect API Key
25
+ # Force asking for username to prompt user if not already set
26
+ Sigh.config.fetch(:username, force_ask: true)
27
+
24
28
  # Team selection passed though FASTLANE_ITC_TEAM_ID and FASTLANE_ITC_TEAM_NAME environment variables
25
29
  # Prompts select team if multiple teams and none specified
26
30
  UI.message("Starting login with user '#{Sigh.config[:username]}'")
@@ -60,7 +64,7 @@ module Sigh
60
64
  end
61
65
 
62
66
  def api_token
63
- @api_token ||= Spaceship::ConnectAPI::Token.create(Sigh.config[:api_key]) if Sigh.config[:api_key]
67
+ @api_token ||= Spaceship::ConnectAPI::Token.create(**Sigh.config[:api_key]) if Sigh.config[:api_key]
64
68
  @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(Sigh.config[:api_key_path]) if Sigh.config[:api_key_path]
65
69
  return @api_token
66
70
  end
@@ -302,4 +302,4 @@ private extension CGFloat {
302
302
 
303
303
  // Please don't remove the lines below
304
304
  // They are used to detect outdated configuration files
305
- // SnapshotHelperVersion [1.24]
305
+ // SnapshotHelperVersion [1.25]
@@ -69,7 +69,8 @@ module Snapshot
69
69
  unless launcher_config.dark_mode.nil?
70
70
  interface_style(type, launcher_config.dark_mode)
71
71
  end
72
- elsif launcher_config.reinstall_app
72
+ end
73
+ if launcher_config.reinstall_app && !launcher_config.erase_simulator
73
74
  # no need to reinstall if device has been erased
74
75
  uninstall_app(type)
75
76
  end
@@ -134,7 +134,7 @@ module Spaceship
134
134
  if teams.count > 1
135
135
  puts("The current user is in #{teams.count} teams. Pass a team ID or call `select_team` to choose a team. Using the first one for now.")
136
136
  end
137
- @current_team_id ||= teams[0]['contentProvider']['contentProviderId']
137
+ @current_team_id ||= user_details_data['sessionToken']['contentProviderId']
138
138
  end
139
139
 
140
140
  # Set a new team ID which will be used from now on
@@ -171,6 +171,9 @@ module Spaceship
171
171
 
172
172
  handle_itc_response(response.body)
173
173
 
174
+ # clear user_details_data cache, as session switch will have changed sessionToken attribute
175
+ @_cached_user_details = nil
176
+
174
177
  @current_team_id = team_id
175
178
  end
176
179
 
@@ -880,10 +883,7 @@ module Spaceship
880
883
  response = @client.send(method, url_or_path, params, headers, &block)
881
884
  log_response(method, url_or_path, response, headers, &block)
882
885
 
883
- resp_hash = response.to_hash
884
- if resp_hash[:status] == 401
885
- handle_401(response)
886
- end
886
+ handle_error(response)
887
887
 
888
888
  if response.body.to_s.include?("<title>302 Found</title>")
889
889
  raise AppleTimeoutError.new, "Apple 302 detected - this might be temporary server error, check https://developer.apple.com/system-status/ to see if there is a known downtime"
@@ -893,22 +893,23 @@ module Spaceship
893
893
  raise BadGatewayError.new, "Apple 502 detected - this might be temporary server error, try again later"
894
894
  end
895
895
 
896
- if resp_hash[:status] == 403
897
- msg = "Access forbidden"
898
- logger.warn(msg)
899
- raise AccessForbiddenError.new, msg
900
- elsif resp_hash[:status] == 429
901
- raise TooManyRequestsError, resp_hash
902
- end
903
-
904
896
  return response
905
897
  end
906
898
  end
907
899
 
908
- def handle_401(response)
909
- msg = "Auth lost"
910
- logger.warn(msg)
911
- raise UnauthorizedAccessError.new, "Unauthorized Access"
900
+ def handle_error(response)
901
+ case response.status
902
+ when 401
903
+ msg = "Auth lost"
904
+ logger.warn(msg)
905
+ raise UnauthorizedAccessError.new, "Unauthorized Access"
906
+ when 403
907
+ msg = "Access forbidden"
908
+ logger.warn(msg)
909
+ raise AccessForbiddenError.new, msg
910
+ when 429
911
+ raise TooManyRequestsError, response.to_hash
912
+ end
912
913
  end
913
914
 
914
915
  def send_request_auto_paginate(method, url_or_path, params, headers, &block)
@@ -150,6 +150,12 @@ module Spaceship
150
150
 
151
151
  protected
152
152
 
153
+ class TimeoutRetryError < StandardError
154
+ def initialize(msg)
155
+ super
156
+ end
157
+ end
158
+
153
159
  def with_asc_retry(tries = 5, &_block)
154
160
  tries = 1 if Object.const_defined?("SpecHelper")
155
161
 
@@ -159,7 +165,7 @@ module Spaceship
159
165
 
160
166
  if [500, 504].include?(status)
161
167
  msg = "Timeout received! Retrying after 3 seconds (remaining: #{tries})..."
162
- raise msg
168
+ raise TimeoutRetryError, msg
163
169
  end
164
170
 
165
171
  return response
@@ -167,7 +173,7 @@ module Spaceship
167
173
  # Catch unathorized access and re-raising
168
174
  # There is no need to try again
169
175
  raise error
170
- rescue => error
176
+ rescue TimeoutRetryError => error
171
177
  tries -= 1
172
178
  puts(error) if Spaceship::Globals.verbose?
173
179
  if tries.zero?
@@ -190,7 +196,7 @@ module Spaceship
190
196
 
191
197
  raise UnexpectedResponse, response.body['error'] if response.body['error']
192
198
 
193
- raise UnexpectedResponse, handle_errors(response) if response.body['errors']
199
+ raise UnexpectedResponse, format_errors(response) if response.body['errors']
194
200
 
195
201
  raise UnexpectedResponse, "Temporary App Store Connect error: #{response.body}" if response.body['statusCode'] == 'ERROR'
196
202
 
@@ -199,11 +205,23 @@ module Spaceship
199
205
  return Spaceship::ConnectAPI::Response.new(body: response.body, status: response.status, headers: response.headers, client: self)
200
206
  end
201
207
 
202
- def handle_401(response)
203
- raise UnauthorizedAccessError, handle_errors(response) if response && (response.body || {})['errors']
208
+ # Overridden from Spaceship::Client
209
+ def handle_error(response)
210
+ case response.status.to_i
211
+ when 401
212
+ raise UnauthorizedAccessError, format_errors(response) if response && (response.body || {})['errors']
213
+ when 403
214
+ error = (response.body['errors'] || []).first || {}
215
+ error_code = error['code']
216
+ if error_code == "FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED"
217
+ raise ProgramLicenseAgreementUpdated, format_errors(response) if response && (response.body || {})['errors']
218
+ else
219
+ raise AccessForbiddenError, format_errors(response) if response && (response.body || {})['errors']
220
+ end
221
+ end
204
222
  end
205
223
 
206
- def handle_errors(response)
224
+ def format_errors(response)
207
225
  # Example error format
208
226
  # {
209
227
  # "errors":[