fastlane 2.177.0 → 2.181.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +83 -70
  4. data/cert/lib/cert/options.rb +1 -0
  5. data/cert/lib/cert/runner.rb +5 -1
  6. data/deliver/lib/deliver/download_screenshots.rb +1 -2
  7. data/deliver/lib/deliver/options.rb +1 -0
  8. data/deliver/lib/deliver/runner.rb +10 -2
  9. data/deliver/lib/deliver/setup.rb +0 -1
  10. data/deliver/lib/deliver/upload_metadata.rb +2 -1
  11. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +6 -1
  12. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +7 -4
  13. data/fastlane/lib/fastlane/actions/backup_file.rb +1 -1
  14. data/fastlane/lib/fastlane/actions/build_app.rb +4 -0
  15. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +4 -0
  16. data/fastlane/lib/fastlane/actions/clipboard.rb +3 -6
  17. data/fastlane/lib/fastlane/actions/commit_github_file.rb +11 -1
  18. data/fastlane/lib/fastlane/actions/create_xcframework.rb +5 -0
  19. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -1
  20. data/fastlane/lib/fastlane/actions/ensure_env_vars.rb +2 -6
  21. data/fastlane/lib/fastlane/actions/get_github_release.rb +11 -1
  22. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +4 -0
  23. data/fastlane/lib/fastlane/actions/get_version_number.rb +17 -10
  24. data/fastlane/lib/fastlane/actions/git_branch.rb +4 -10
  25. data/fastlane/lib/fastlane/actions/git_tag_exists.rb +4 -0
  26. data/fastlane/lib/fastlane/actions/github_api.rb +2 -1
  27. data/fastlane/lib/fastlane/actions/increment_build_number.rb +8 -1
  28. data/fastlane/lib/fastlane/actions/install_provisioning_profile.rb +4 -0
  29. data/fastlane/lib/fastlane/actions/jira.rb +61 -14
  30. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +1 -0
  31. data/fastlane/lib/fastlane/actions/match_nuke.rb +59 -0
  32. data/fastlane/lib/fastlane/actions/notarize.rb +98 -51
  33. data/fastlane/lib/fastlane/actions/register_device.rb +1 -1
  34. data/fastlane/lib/fastlane/actions/register_devices.rb +1 -1
  35. data/fastlane/lib/fastlane/actions/restore_file.rb +1 -1
  36. data/fastlane/lib/fastlane/actions/set_changelog.rb +1 -1
  37. data/fastlane/lib/fastlane/actions/slack.rb +148 -127
  38. data/fastlane/lib/fastlane/actions/sourcedocs.rb +164 -0
  39. data/fastlane/lib/fastlane/actions/spaceship_logs.rb +1 -1
  40. data/fastlane/lib/fastlane/actions/update_project_provisioning.rb +1 -2
  41. data/fastlane/lib/fastlane/erb_template_helper.rb +7 -1
  42. data/fastlane/lib/fastlane/fast_file.rb +9 -5
  43. data/fastlane/lib/fastlane/fastlane_require.rb +7 -1
  44. data/fastlane/lib/fastlane/helper/git_helper.rb +19 -7
  45. data/fastlane/lib/fastlane/lane_manager.rb +3 -2
  46. data/fastlane/lib/fastlane/plugins/plugin_fetcher.rb +1 -2
  47. data/fastlane/lib/fastlane/plugins/plugin_info_collector.rb +1 -2
  48. data/fastlane/lib/fastlane/plugins/plugin_manager.rb +1 -2
  49. data/fastlane/lib/fastlane/setup/setup.rb +23 -10
  50. data/fastlane/lib/fastlane/swift_fastlane_function.rb +4 -0
  51. data/fastlane/lib/fastlane/swift_runner_upgrader.rb +2 -0
  52. data/fastlane/lib/fastlane/version.rb +1 -1
  53. data/fastlane/swift/Deliverfile.swift +1 -1
  54. data/fastlane/swift/DeliverfileProtocol.swift +3 -3
  55. data/fastlane/swift/Fastlane.swift +550 -328
  56. data/fastlane/swift/Gymfile.swift +1 -1
  57. data/fastlane/swift/GymfileProtocol.swift +1 -1
  58. data/fastlane/swift/LaneFileProtocol.swift +9 -3
  59. data/fastlane/swift/Matchfile.swift +1 -1
  60. data/fastlane/swift/MatchfileProtocol.swift +1 -1
  61. data/fastlane/swift/Precheckfile.swift +1 -1
  62. data/fastlane/swift/PrecheckfileProtocol.swift +3 -3
  63. data/fastlane/swift/RubyCommand.swift +1 -1
  64. data/fastlane/swift/Scanfile.swift +1 -1
  65. data/fastlane/swift/ScanfileProtocol.swift +5 -1
  66. data/fastlane/swift/Screengrabfile.swift +1 -1
  67. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  68. data/fastlane/swift/Snapshotfile.swift +1 -1
  69. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  70. data/fastlane/swift/SocketClient.swift +2 -1
  71. data/fastlane/swift/SocketResponse.swift +4 -2
  72. data/fastlane/swift/formatting/Brewfile.lock.json +18 -16
  73. data/fastlane_core/lib/fastlane_core.rb +22 -21
  74. data/fastlane_core/lib/fastlane_core/build_watcher.rb +50 -9
  75. data/fastlane_core/lib/fastlane_core/clipboard.rb +20 -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/itunes_transporter.rb +10 -6
  79. data/fastlane_core/lib/fastlane_core/project.rb +3 -14
  80. data/fastlane_core/lib/fastlane_core/queue_worker.rb +2 -2
  81. data/fastlane_core/lib/fastlane_core/ui/implementations/shell.rb +12 -1
  82. data/fastlane_core/lib/fastlane_core/ui/interface.rb +1 -1
  83. data/gym/lib/gym/generators/package_command_generator.rb +4 -0
  84. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +13 -8
  85. data/gym/lib/gym/runner.rb +15 -4
  86. data/match/lib/match/change_password.rb +3 -3
  87. data/match/lib/match/encryption/interface.rb +1 -1
  88. data/match/lib/match/encryption/openssl.rb +2 -2
  89. data/match/lib/match/importer.rb +1 -1
  90. data/match/lib/match/migrate.rb +1 -1
  91. data/match/lib/match/module.rb +1 -0
  92. data/match/lib/match/nuke.rb +1 -1
  93. data/match/lib/match/runner.rb +1 -1
  94. data/match/lib/match/storage/google_cloud_storage.rb +1 -1
  95. data/match/lib/match/storage/s3_storage.rb +1 -1
  96. data/pilot/lib/pilot/build_manager.rb +9 -4
  97. data/pilot/lib/pilot/manager.rb +5 -1
  98. data/pilot/lib/pilot/options.rb +3 -2
  99. data/precheck/lib/precheck/options.rb +1 -0
  100. data/precheck/lib/precheck/runner.rb +5 -1
  101. data/scan/lib/scan/options.rb +10 -5
  102. data/scan/lib/scan/runner.rb +54 -1
  103. data/scan/lib/scan/test_command_generator.rb +10 -8
  104. data/screengrab/lib/screengrab/android_environment.rb +6 -4
  105. data/screengrab/lib/screengrab/runner.rb +1 -1
  106. data/sigh/lib/sigh/download_all.rb +1 -1
  107. data/sigh/lib/sigh/options.rb +1 -0
  108. data/sigh/lib/sigh/runner.rb +5 -1
  109. data/snapshot/lib/assets/SnapshotHelper.swift +1 -1
  110. data/snapshot/lib/snapshot/reports_generator.rb +4 -0
  111. data/spaceship/README.md +2 -12
  112. data/spaceship/lib/spaceship/commands_generator.rb +2 -1
  113. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +1 -1
  114. data/spaceship/lib/spaceship/connect_api/models/beta_group.rb +5 -0
  115. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
  116. data/spaceship/lib/spaceship/connect_api/token.rb +8 -2
  117. data/spaceship/lib/spaceship/spaceauth_runner.rb +19 -9
  118. data/spaceship/lib/spaceship/tunes/members.rb +1 -1
  119. data/supply/lib/supply/client.rb +3 -1
  120. data/supply/lib/supply/options.rb +2 -2
  121. data/supply/lib/supply/uploader.rb +1 -0
  122. metadata +28 -24
  123. data/gym/lib/gym/.runner.rb.swp +0 -0
@@ -31,6 +31,7 @@ module Pilot
31
31
  short_option: "-u",
32
32
  env_name: "PILOT_USERNAME",
33
33
  description: "Your Apple ID Username",
34
+ optional: true,
34
35
  default_value: user,
35
36
  default_value_dynamic: true),
36
37
  FastlaneCore::ConfigItem.new(key: :app_identifier,
@@ -182,8 +183,8 @@ module Pilot
182
183
  FastlaneCore::ConfigItem.new(key: :notify_external_testers,
183
184
  is_string: false,
184
185
  env_name: "PILOT_NOTIFY_EXTERNAL_TESTERS",
185
- description: "Should notify external testers?",
186
- default_value: true),
186
+ description: "Should notify external testers? (Not setting a value will use App Store Connect's default which is to notify)",
187
+ optional: true),
187
188
  FastlaneCore::ConfigItem.new(key: :app_version,
188
189
  env_name: "PILOT_APP_VERSION",
189
190
  description: "The version number of the application build to distribute. If the version number is not specified, then the most recent build uploaded to TestFlight will be distributed. If specified, the most recent build for the version number will be distributed",
@@ -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,
@@ -32,6 +32,10 @@ module Precheck
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
@@ -472,11 +472,16 @@ module Scan
472
472
  type: Boolean,
473
473
  default_value: false),
474
474
  FastlaneCore::ConfigItem.new(key: :use_system_scm,
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)
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)
480
485
 
481
486
  ]
482
487
  end
@@ -51,7 +51,14 @@ 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)
58
+ Scan.cache[:retry_attempt] = Scan.config[:number_of_retries] - retries
59
+
54
60
  command = @test_command_generator.generate
61
+
55
62
  prefix_hash = [
56
63
  {
57
64
  prefix: "Running Tests: ",
@@ -71,7 +78,12 @@ module Scan
71
78
  error: proc do |error_output|
72
79
  begin
73
80
  exit_status = $?.exitstatus
74
- ErrorHandler.handle_build_error(error_output, @test_command_generator.xcodebuild_log_path)
81
+ if retries > 0
82
+ # If there are retries remaining, run the tests again
83
+ return retry_execute(retries: retries, error_output: error_output)
84
+ else
85
+ ErrorHandler.handle_build_error(error_output, @test_command_generator.xcodebuild_log_path)
86
+ end
75
87
  rescue => ex
76
88
  SlackPoster.new.run({
77
89
  build_errors: 1
@@ -79,9 +91,50 @@ module Scan
79
91
  raise ex
80
92
  end
81
93
  end)
94
+
82
95
  exit_status
83
96
  end
84
97
 
98
+ def retry_execute(retries:, error_output: "")
99
+ tests = retryable_tests(error_output)
100
+
101
+ if tests.empty?
102
+ UI.crash!("Failed to find failed tests to retry (could not parse error output)")
103
+ end
104
+
105
+ Scan.config[:only_testing] = tests
106
+ UI.important("Retrying tests: #{Scan.config[:only_testing].join(', ')}")
107
+
108
+ retries -= 1
109
+ UI.important("Number of retries remaining: #{retries}")
110
+
111
+ return execute(retries: retries)
112
+ end
113
+
114
+ def retryable_tests(input)
115
+ input = Helper.strip_ansi_colors(input)
116
+
117
+ retryable_tests = []
118
+
119
+ failing_tests = input.split("Failing tests:\n").fetch(1, [])
120
+ .split("\n\n").first
121
+
122
+ suites = failing_tests.split(/(?=\n\s+[\w\s]+:\n)/)
123
+
124
+ suites.each do |suite|
125
+ suite_name = suite.match(/\s*([\w\s]+):/).captures.first
126
+
127
+ test_cases = suite.split(":\n").fetch(1, []).split("\n").each
128
+ .select { |line| line.match?(/^\s+/) }
129
+ .map { |line| line.strip.gsub(".", "/").gsub("()", "") }
130
+ .map { |line| suite_name + "/" + line }
131
+
132
+ retryable_tests += test_cases
133
+ end
134
+
135
+ return retryable_tests.uniq
136
+ end
137
+
85
138
  def handle_results(tests_exit_status)
86
139
  if Scan.config[:disable_xcpretty]
87
140
  unless tests_exit_status == 0
@@ -162,16 +162,18 @@ 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
+ retry_count = Scan.cache[:retry_attempt] || 0
168
+ attempt = retry_count > 0 ? "-#{retry_count}" : ""
169
+ ext = FastlaneCore::Helper.xcode_version.to_i >= 11 ? '.xcresult' : '.test_result'
170
+ path = File.join(Scan.config[:output_directory], Scan.config[:scheme]) + attempt + ext
171
+ if File.directory?(path)
172
+ FileUtils.remove_dir(path)
173
173
  end
174
- return Scan.cache[:result_bundle_path]
174
+ Scan.cache[:result_bundle_path] = path
175
+
176
+ return path
175
177
  end
176
178
  end
177
179
  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
@@ -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]
@@ -84,6 +84,10 @@ module Snapshot
84
84
  {
85
85
  # snapshot in Xcode 9 saves screenshots with the SIMULATOR_DEVICE_NAME
86
86
  # which includes spaces
87
+ 'iPhone 12 Pro Max' => "iPhone 12 Pro Max",
88
+ 'iPhone 12 Pro' => "iPhone 12 Pro",
89
+ 'iPhone 12 mini' => "iPhone 12 mini",
90
+ 'iPhone 12' => "iPhone 12",
87
91
  'iPhone 11 Pro Max' => "iPhone 11 Pro Max",
88
92
  'iPhone 11 Pro' => "iPhone 11 Pro",
89
93
  'iPhone 11' => "iPhone 11",
data/spaceship/README.md CHANGED
@@ -111,21 +111,11 @@ When your Apple account has 2 factor verification enabled, you'll automatically
111
111
 
112
112
  #### Web sessions
113
113
 
114
- To generate a web session for your CI machine, use
115
-
116
- ```sh
117
- fastlane spaceauth -u user@example.org
118
- ```
119
-
120
- This will authenticate you and provide a string that can be transferred to your CI system. Copy everything from `---\n` to your CI server and provide it as environment variable named `FASTLANE_SESSION`. For example:
121
-
122
- ```
123
- export FASTLANE_SESSION='---\n- !ruby/object:HTTP::Cookie\n name: DES5c148586dfd451e55afbaaa5f62418f91\n value: HSARMTKNSRVTWFla1+yO4gVPowH17VaaaxPFnUdMUegQZxqy1Ie1c2v6bM1vSOzIbuOmrl/FNenlScsd/NbF7/Lw4cpnL15jsyg0TOJwP32tC/NguPiyOaaaU+jrj4tf4uKdIywVaaaFSRVT\n domain: idmsa.apple.com\n for_domain: true\n path: "/"\n secure: true\n httponly: true\n expires: 2016-04-27 23:55:56.000000000 Z\n max_age: \n created_at: 2016-03-28 16:55:57.032086000 -07:00\n accessed_at: 2016-03-28 19:11:17.828141000 -07:00\n'
124
- ```
114
+ See [Continuous Integration > Authenticating with Apple services > Method 2: Two-step or two-factor authentication > Storing a manually verified session using spaceauth](https://docs.fastlane.tools/best-practices/continuous-integration/#storing-a-manually-verified-session-using-spaceauth)
125
115
 
126
116
  #### Transporter
127
117
 
128
- See [Continuous Integration > Authentication with Apple services > Application specific passwords](https://docs.fastlane.tools/best-practices/continuous-integration/#application-specific-passwords)
118
+ See [Getting Started > iOS > Authentication > Method 3: Application-specific passwords](https://docs.fastlane.tools/getting-started/ios/authentication/#method-3-application-specific-passwords)
129
119
 
130
120
  ## _spaceship_ in use
131
121
 
@@ -39,9 +39,10 @@ module Spaceship
39
39
  command :spaceauth do |c|
40
40
  c.syntax = 'fastlane spaceship spaceauth'
41
41
  c.description = 'Authentication helper for spaceship/fastlane to work with Apple 2-Step/2FA'
42
+ c.option('--copy_to_clipboard', 'Whether the session string should be copied to clipboard. For more info see https://docs.fastlane.tools/best-practices/continuous-integration/#storing-a-manually-verified-session-using-spaceauth`')
42
43
 
43
44
  c.action do |args, options|
44
- Spaceship::SpaceauthRunner.new(username: options.user).run
45
+ Spaceship::SpaceauthRunner.new(username: options.user, copy_to_clipboard: options.copy_to_clipboard).run
45
46
  end
46
47
  end
47
48
 
@@ -112,7 +112,7 @@ module Spaceship
112
112
  timeout_minutes = (ENV["SPACESHIP_SCREENSHOT_UPLOAD_TIMEOUT"] || 20).to_i
113
113
 
114
114
  loop do
115
- # This error handling needs to be revised since any error occured can reach here.
115
+ # This error handling needs to be revised since any error occurred can reach here.
116
116
  # It should handle errors based on what status code is.
117
117
  puts("Waiting for screenshots to appear before uploading. This is unlikely to be recovered unless it's 503 error. error=\"#{error}\"")
118
118
  sleep(30)
@@ -38,6 +38,11 @@ module Spaceship
38
38
  return client.post_bulk_beta_tester_assignments(beta_group_id: id, beta_testers: beta_testers)
39
39
  end
40
40
 
41
+ def add_beta_testers(client: nil, beta_tester_ids:)
42
+ client ||= Spaceship::ConnectAPI
43
+ return client.add_beta_tester_to_group(beta_group_id: id, beta_tester_ids: beta_tester_ids)
44
+ end
45
+
41
46
  def update(client: nil, attributes: nil)
42
47
  return if attributes.empty?
43
48
 
@@ -275,6 +275,19 @@ module Spaceship
275
275
  test_flight_request_client.post("bulkBetaTesterAssignments", body)
276
276
  end
277
277
 
278
+ def add_beta_tester_to_group(beta_group_id: nil, beta_tester_ids: nil)
279
+ beta_tester_ids || []
280
+ body = {
281
+ data: beta_tester_ids.map do |id|
282
+ {
283
+ type: "betaTesters",
284
+ id: id
285
+ }
286
+ end
287
+ }
288
+ test_flight_request_client.post("betaGroups/#{beta_group_id}/relationships/betaTesters", body)
289
+ end
290
+
278
291
  def delete_beta_tester_from_apps(beta_tester_id: nil, app_ids: [])
279
292
  body = {
280
293
  data: app_ids.map do |id|
@@ -37,7 +37,7 @@ module Spaceship
37
37
  raise "App Store Connect API key JSON is missing field(s): #{missing_keys.join(', ')}"
38
38
  end
39
39
 
40
- self.create(json)
40
+ self.create(**json)
41
41
  end
42
42
 
43
43
  def self.create(key_id: nil, issuer_id: nil, filepath: nil, key: nil, is_key_content_base64: false, duration: nil, in_house: nil, **)
@@ -60,14 +60,16 @@ module Spaceship
60
60
  key_id: key_id,
61
61
  issuer_id: issuer_id,
62
62
  key: OpenSSL::PKey::EC.new(key),
63
+ key_raw: key,
63
64
  duration: duration,
64
65
  in_house: in_house
65
66
  )
66
67
  end
67
68
 
68
- def initialize(key_id: nil, issuer_id: nil, key: nil, duration: nil, in_house: nil)
69
+ def initialize(key_id: nil, issuer_id: nil, key: nil, key_raw: nil, duration: nil, in_house: nil)
69
70
  @key_id = key_id
70
71
  @key = key
72
+ @key_raw = key_raw
71
73
  @issuer_id = issuer_id
72
74
  @duration = duration
73
75
  @in_house = in_house
@@ -97,6 +99,10 @@ module Spaceship
97
99
  def expired?
98
100
  @expiration < Time.now
99
101
  end
102
+
103
+ def write_key_to_file(path)
104
+ File.open(path, 'w') { |f| f.write(@key_raw) }
105
+ end
100
106
  end
101
107
  end
102
108
  end
@@ -1,15 +1,17 @@
1
1
  require 'colored'
2
2
  require 'credentials_manager/appfile_config'
3
3
  require 'yaml'
4
+ require 'fastlane_core'
4
5
 
5
6
  require_relative 'tunes/tunes_client'
6
7
 
7
8
  module Spaceship
8
9
  class SpaceauthRunner
9
- def initialize(username: nil)
10
+ def initialize(username: nil, copy_to_clipboard: nil)
10
11
  @username = username
11
12
  @username ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
12
13
  @username ||= ask("Username: ")
14
+ @copy_to_clipboard = copy_to_clipboard
13
15
  end
14
16
 
15
17
  def run
@@ -22,7 +24,7 @@ module Spaceship
22
24
  puts("Could not login to App Store Connect".red)
23
25
  puts("Please check your credentials and try again.".yellow)
24
26
  puts("This could be an issue with App Store Connect,".yellow)
25
- puts("Please try unsetting the FASTLANE_SESSION environment variable".yellow)
27
+ puts("Please try unsetting the FASTLANE_SESSION environment variable by calling 'unset FASTLANE_SESSION'".yellow)
26
28
  puts("(if it is set) and re-run `fastlane spaceauth`".yellow)
27
29
  puts("")
28
30
  puts("Exception type: #{ex.class}")
@@ -49,22 +51,30 @@ module Spaceship
49
51
  cookie.name.start_with?("myacinfo") || cookie.name == "dqsid" || cookie.name.start_with?("DES")
50
52
  end
51
53
 
52
- yaml = cookies.to_yaml.gsub("\n", "\\n")
54
+ @yaml = cookies.to_yaml.gsub("\n", "\\n")
53
55
 
54
56
  puts("---")
55
57
  puts("")
56
58
  puts("Pass the following via the FASTLANE_SESSION environment variable:")
57
- puts(yaml.cyan.underline)
59
+ puts(@yaml.cyan.underline)
58
60
  puts("")
59
61
  puts("")
60
62
  puts("Example:")
61
- puts("export FASTLANE_SESSION='#{yaml}'".cyan.underline)
63
+ puts("export FASTLANE_SESSION='#{@yaml}'".cyan.underline)
62
64
 
63
- if mac? && Spaceship::Client::UserInterface.interactive? && agree("🙄 Should fastlane copy the cookie into your clipboard, so you can easily paste it? (y/n)", true)
64
- require 'open3'
65
- Open3.popen3('pbcopy') { |input, _, _| input << yaml }
66
- puts("Successfully copied text into your clipboard 🎨".green)
65
+ if @copy_to_clipboard == false
66
+ puts("Skipped asking to copy the session string into your clipboard ⏭️".green)
67
+ elsif @copy_to_clipboard || (mac? && Spaceship::Client::UserInterface.interactive? && agree("🙄 Should fastlane copy the cookie into your clipboard, so you can easily paste it? (y/n)", true))
68
+ FastlaneCore::Clipboard.copy(content: @yaml)
69
+ puts("Successfully copied the session string into your clipboard 🎨".green)
67
70
  end
71
+
72
+ return self
73
+ end
74
+
75
+ def session_string
76
+ FastlaneCore::UI.user_error!("`#{__method__}` method called before calling `run` in `SpaceauthRunner`") unless @yaml
77
+ @yaml
68
78
  end
69
79
 
70
80
  def mac?