fastlane 2.240.1 → 2.241.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +57 -177
  3. data/bin/fastlane +4 -2
  4. data/credentials_manager/README.md +1 -1
  5. data/deliver/lib/deliver/app_screenshot.rb +0 -4
  6. data/deliver/lib/deliver/commands_generator.rb +1 -1
  7. data/deliver/lib/deliver/download_screenshots.rb +8 -1
  8. data/deliver/lib/deliver/sync_screenshots.rb +27 -28
  9. data/deliver/lib/deliver/upload_app_clip_default_experience_header_images.rb +3 -3
  10. data/deliver/lib/deliver/upload_metadata.rb +12 -16
  11. data/deliver/lib/deliver/upload_screenshots.rb +12 -14
  12. data/docs/README.md +25 -0
  13. data/fastlane/lib/assets/TeamTable.md.erb +28 -0
  14. data/fastlane/lib/fastlane/actions/appaloosa.rb +2 -1
  15. data/fastlane/lib/fastlane/actions/appium.rb +1 -1
  16. data/fastlane/lib/fastlane/actions/apteligent.rb +8 -1
  17. data/fastlane/lib/fastlane/actions/build_and_upload_to_appetize.rb +1 -1
  18. data/fastlane/lib/fastlane/actions/clean_build_artifacts.rb +3 -0
  19. data/fastlane/lib/fastlane/actions/danger.rb +5 -5
  20. data/fastlane/lib/fastlane/actions/docs/build_app.md +1 -2
  21. data/fastlane/lib/fastlane/actions/docs/create_app_online.md +1 -2
  22. data/fastlane/lib/fastlane/actions/docs/get_certificates.md +0 -2
  23. data/fastlane/lib/fastlane/actions/docs/get_push_certificate.md +1 -3
  24. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +13 -3
  25. data/fastlane/lib/fastlane/actions/ensure_no_debug_code.rb +2 -2
  26. data/fastlane/lib/fastlane/actions/get_managed_play_store_publishing_rights.rb +1 -0
  27. data/fastlane/lib/fastlane/actions/github_api.rb +1 -2
  28. data/fastlane/lib/fastlane/actions/hg_commit_version_bump.rb +1 -1
  29. data/fastlane/lib/fastlane/actions/increment_build_number.rb +1 -1
  30. data/fastlane/lib/fastlane/actions/increment_version_number.rb +1 -1
  31. data/fastlane/lib/fastlane/actions/install_xcode_plugin.rb +16 -6
  32. data/fastlane/lib/fastlane/actions/lcov.rb +9 -2
  33. data/fastlane/lib/fastlane/actions/nexus_upload.rb +16 -4
  34. data/fastlane/lib/fastlane/actions/notification.rb +3 -2
  35. data/fastlane/lib/fastlane/actions/oclint.rb +8 -8
  36. data/fastlane/lib/fastlane/actions/push_to_git_remote.rb +9 -4
  37. data/fastlane/lib/fastlane/actions/register_device.rb +1 -1
  38. data/fastlane/lib/fastlane/actions/say.rb +2 -4
  39. data/fastlane/lib/fastlane/actions/set_pod_key.rb +3 -3
  40. data/fastlane/lib/fastlane/actions/sonar.rb +16 -16
  41. data/fastlane/lib/fastlane/actions/sourcedocs.rb +8 -8
  42. data/fastlane/lib/fastlane/actions/splunkmint.rb +8 -1
  43. data/fastlane/lib/fastlane/actions/update_project_provisioning.rb +1 -1
  44. data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +5 -2
  45. data/fastlane/lib/fastlane/actions/upload_to_testflight.rb +5 -2
  46. data/fastlane/lib/fastlane/actions/verify_build.rb +1 -4
  47. data/fastlane/lib/fastlane/actions/verify_pod_keys.rb +3 -1
  48. data/fastlane/lib/fastlane/actions/xcode_server_get_assets.rb +20 -19
  49. data/fastlane/lib/fastlane/actions/xcodebuild.rb +5 -6
  50. data/fastlane/lib/fastlane/actions/zip.rb +2 -1
  51. data/fastlane/lib/fastlane/auto_complete.rb +1 -1
  52. data/fastlane/lib/fastlane/cli_tools_distributor.rb +14 -0
  53. data/fastlane/lib/fastlane/documentation/markdown_docs_generator.rb +23 -0
  54. data/fastlane/lib/fastlane/environment_printer.rb +12 -25
  55. data/fastlane/lib/fastlane/fast_file.rb +2 -1
  56. data/fastlane/lib/fastlane/fastlane_require.rb +5 -1
  57. data/fastlane/lib/fastlane/helper/sh_helper.rb +16 -6
  58. data/fastlane/lib/fastlane/new_action.rb +1 -1
  59. data/fastlane/lib/fastlane/plugins/plugin_fetcher.rb +1 -1
  60. data/fastlane/lib/fastlane/plugins/plugin_info_collector.rb +1 -1
  61. data/fastlane/lib/fastlane/plugins/plugin_manager.rb +36 -4
  62. data/fastlane/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +1 -1
  63. data/fastlane/lib/fastlane/plugins/template/.github/workflows/test.yml +1 -1
  64. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +3 -7
  65. data/fastlane/lib/fastlane/swift_lane_manager.rb +22 -4
  66. data/fastlane/lib/fastlane/version.rb +3 -2
  67. data/fastlane/swift/Deliverfile.swift +1 -1
  68. data/fastlane/swift/DeliverfileProtocol.swift +1 -1
  69. data/fastlane/swift/Fastlane.swift +55 -9
  70. data/fastlane/swift/Gymfile.swift +1 -1
  71. data/fastlane/swift/GymfileProtocol.swift +1 -1
  72. data/fastlane/swift/MainProcess.swift +190 -20
  73. data/fastlane/swift/Matchfile.swift +1 -1
  74. data/fastlane/swift/MatchfileProtocol.swift +10 -3
  75. data/fastlane/swift/Precheckfile.swift +1 -1
  76. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  77. data/fastlane/swift/Scanfile.swift +1 -1
  78. data/fastlane/swift/ScanfileProtocol.swift +8 -1
  79. data/fastlane/swift/Screengrabfile.swift +1 -1
  80. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  81. data/fastlane/swift/Snapshotfile.swift +1 -1
  82. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  83. data/fastlane/swift/formatting/Rakefile +1 -0
  84. data/fastlane/swift/main.swift +4 -1
  85. data/fastlane_core/README.md +1 -1
  86. data/fastlane_core/lib/fastlane_core/analytics/analytics_event_builder.rb +1 -17
  87. data/fastlane_core/lib/fastlane_core/configuration/configuration.rb +11 -8
  88. data/fastlane_core/lib/fastlane_core/device_manager.rb +2 -2
  89. data/fastlane_core/lib/fastlane_core/helper.rb +34 -5
  90. data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +0 -14
  91. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +24 -27
  92. data/fastlane_core/lib/fastlane_core/languages.rb +0 -1
  93. data/fastlane_core/lib/fastlane_core/print_table.rb +5 -2
  94. data/fastlane_core/lib/fastlane_core/provisioning_profile.rb +10 -19
  95. data/fastlane_core/lib/fastlane_core/secrets.rb +77 -0
  96. data/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb +3 -3
  97. data/fastlane_core/lib/fastlane_core/ui/github_issue_inspector_reporter.rb +1 -1
  98. data/fastlane_core/lib/fastlane_core/ui/implementations/shell.rb +2 -1
  99. data/fastlane_core/lib/fastlane_core.rb +1 -0
  100. data/frameit/lib/frameit/device.rb +1 -1
  101. data/frameit/lib/frameit/frame_downloader.rb +2 -0
  102. data/frameit/lib/frameit/runner.rb +3 -2
  103. data/frameit/lib/frameit/screenshot.rb +0 -1
  104. data/gym/lib/gym/generators/build_command_generator.rb +4 -4
  105. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +3 -3
  106. data/gym/lib/gym/runner.rb +1 -1
  107. data/internal/README.md +1 -1
  108. data/match/lib/match/generator.rb +1 -0
  109. data/match/lib/match/options.rb +13 -5
  110. data/match/lib/match/storage/git_storage.rb +43 -7
  111. data/match/lib/match/storage/gitlab/secure_file.rb +2 -1
  112. data/pem/lib/pem/module.rb +0 -4
  113. data/precheck/lib/precheck/rule_processor.rb +1 -0
  114. data/produce/lib/produce/developer_center.rb +49 -2
  115. data/produce/lib/produce/itunes_connect.rb +1 -1
  116. data/produce/lib/produce/module.rb +21 -0
  117. data/produce/lib/produce/options.rb +17 -0
  118. data/produce/lib/produce/service.rb +3 -5
  119. data/produce/lib/produce.rb +0 -1
  120. data/scan/lib/scan/options.rb +7 -0
  121. data/scan/lib/scan/runner.rb +1 -1
  122. data/scan/lib/scan/test_command_generator.rb +12 -9
  123. data/scan/lib/scan/test_result_parser.rb +1 -0
  124. data/scan/lib/scan/xcpretty_reporter_options_generator.rb +2 -2
  125. data/screengrab/lib/screengrab/page.html.erb +12 -7
  126. data/screengrab/lib/screengrab/reports_generator.rb +1 -1
  127. data/sigh/lib/assets/resign.sh +17 -0
  128. data/sigh/lib/sigh/local_manage.rb +1 -1
  129. data/sigh/lib/sigh/options.rb +5 -0
  130. data/sigh/lib/sigh/resign.rb +3 -3
  131. data/sigh/lib/sigh/runner.rb +6 -1
  132. data/snapshot/lib/snapshot/page.html.erb +17 -12
  133. data/snapshot/lib/snapshot/reports_generator.rb +1 -1
  134. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +1 -1
  135. data/snapshot/lib/snapshot.rb +0 -2
  136. data/spaceship/README.md +2 -3
  137. data/spaceship/lib/spaceship/base.rb +3 -1
  138. data/spaceship/lib/spaceship/client.rb +65 -47
  139. data/spaceship/lib/spaceship/connect_api/models/bundle_id.rb +13 -2
  140. data/spaceship/lib/spaceship/connect_api/models/bundle_id_capability.rb +6 -0
  141. data/spaceship/lib/spaceship/connect_api/models/profile.rb +7 -5
  142. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +25 -0
  143. data/spaceship/lib/spaceship/du/upload_file.rb +1 -1
  144. data/spaceship/lib/spaceship/errors.rb +7 -1
  145. data/spaceship/lib/spaceship/globals.rb +2 -2
  146. data/spaceship/lib/spaceship/portal/merchant.rb +127 -2
  147. data/spaceship/lib/spaceship/portal/portal_client.rb +58 -0
  148. data/spaceship/lib/spaceship/portal/ui/select_team.rb +0 -2
  149. data/spaceship/lib/spaceship/portal/website_push.rb +8 -2
  150. data/spaceship/lib/spaceship/test_flight/client.rb +0 -22
  151. data/spaceship/lib/spaceship/test_flight.rb +0 -1
  152. data/spaceship/lib/spaceship/two_step_or_factor_client.rb +11 -0
  153. data/supply/lib/supply/options.rb +1 -0
  154. data/supply/lib/supply.rb +0 -1
  155. metadata +54 -36
  156. data/produce/lib/produce/available_default_languages.rb +0 -62
  157. data/snapshot/lib/snapshot/screenshot_flatten.rb +0 -20
  158. data/snapshot/lib/snapshot/screenshot_rotate.rb +0 -36
  159. data/spaceship/lib/spaceship/test_flight/app_test_info.rb +0 -40
  160. data/supply/lib/supply/apk_listing.rb +0 -14
  161. /data/fastlane/lib/assets/{custom_action_template.rb → custom_action_template.rb.template} +0 -0
@@ -15,7 +15,7 @@ module Frameit
15
15
  attr_reader :priority_config_key
16
16
 
17
17
  def initialize(id, formatted_name, priority, resolutions, density_ppi, default_color, platform = Platform::IOS, deliver_screen_id = nil, priority_config_key = nil)
18
- Raise("Priority mustn't be higher than #{REQUIRED_PRIORITY}") if priority > REQUIRED_PRIORITY
18
+ raise "Priority mustn't be higher than #{REQUIRED_PRIORITY}" if priority > REQUIRED_PRIORITY
19
19
  @id = id
20
20
  @deliver_screen_id = deliver_screen_id
21
21
  @formatted_name = formatted_name
@@ -20,6 +20,8 @@ module Frameit
20
20
 
21
21
  files = JSON.parse(download_file("files.json"))
22
22
  files.each_with_index do |current, index|
23
+ # files.json is downloaded: only accept plain file names, so nothing is written outside templates_path
24
+ UI.user_error!("Unexpected frame file name '#{current}'") if current.include?("/") || current.include?("\\") || current.start_with?(".")
23
25
  content = download_file(current, txt: "#{index + 1} of #{files.count} files")
24
26
  File.binwrite(File.join(templates_path, current), content)
25
27
  end
@@ -38,8 +38,9 @@ module Frameit
38
38
  if editor.should_skip?
39
39
  UI.message("Skipping framing of screenshot #{screenshot.path}. No title provided in your Framefile.json or title.strings.")
40
40
  else
41
- Helper.show_loading_indicator("Framing screenshot '#{full_path}'")
42
- editor.frame!
41
+ Helper.with_loading_indicator("Framing screenshot '#{full_path}'") do
42
+ editor.frame!
43
+ end
43
44
  end
44
45
  rescue => ex
45
46
  UI.error(ex.to_s)
@@ -48,7 +48,6 @@ module Frameit
48
48
  # Device name for a given screen size. Used to use the correct template
49
49
  def device_name
50
50
  @device.formatted_name
51
- # rubocop:enable Require/MissingRequireStatement
52
51
  end
53
52
 
54
53
  def default_color
@@ -34,11 +34,11 @@ module Gym
34
34
 
35
35
  options = []
36
36
  options += project_path_array
37
- options << "-sdk '#{config[:sdk]}'" if config[:sdk]
38
- options << "-toolchain '#{config[:toolchain]}'" if config[:toolchain]
39
- options << "-destination '#{config[:destination]}'" if config[:destination]
37
+ options << "-sdk #{config[:sdk].shellescape}" if config[:sdk]
38
+ options << "-toolchain #{config[:toolchain].shellescape}" if config[:toolchain]
39
+ options << "-destination #{config[:destination].shellescape}" if config[:destination]
40
40
  options << "-archivePath #{archive_path.shellescape}" unless config[:skip_archive]
41
- options << "-resultBundlePath '#{result_bundle_path}'" if config[:result_bundle]
41
+ options << "-resultBundlePath #{result_bundle_path.shellescape}" if config[:result_bundle]
42
42
  options << "-showBuildTimingSummary" if config[:build_timing_summary]
43
43
  if config[:use_system_scm] && !options.include?("-scmProvider system")
44
44
  options << "-scmProvider system"
@@ -33,10 +33,10 @@ module Gym
33
33
  config = Gym.config
34
34
 
35
35
  options = []
36
- options << "-exportOptionsPlist '#{config_path}'"
36
+ options << "-exportOptionsPlist #{config_path.shellescape}"
37
37
  options << "-archivePath #{BuildCommandGenerator.archive_path.shellescape}"
38
- options << "-exportPath '#{temporary_output_path}'"
39
- options << "-toolchain '#{config[:toolchain]}'" if config[:toolchain]
38
+ options << "-exportPath #{temporary_output_path.shellescape}"
39
+ options << "-toolchain #{config[:toolchain].shellescape}" if config[:toolchain]
40
40
  options << config[:export_xcargs] if config[:export_xcargs]
41
41
  options << config[:xcargs] if config[:xcargs]
42
42
 
@@ -223,7 +223,7 @@ module Gym
223
223
  UI.message("Compressing #{available_dsyms.count} dSYM(s)") unless Gym.config[:silent]
224
224
 
225
225
  output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip"))
226
- command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM"
226
+ command = "cd #{containing_directory.shellescape} && zip -r #{output_path.shellescape} *.dSYM"
227
227
  Helper.backticks(command, print: !Gym.config[:silent])
228
228
  puts("") # new line
229
229
 
data/internal/README.md CHANGED
@@ -8,4 +8,4 @@
8
8
 
9
9
  ------
10
10
 
11
- Internal scripts for Rubocop linting & Rake tasks.
11
+ Internal scripts for Rubocop linting, Rake tasks, and the docs checks and generation the Fastfile runs (`docs/`).
@@ -92,6 +92,7 @@ module Match
92
92
  fail_on_name_taken: params[:fail_on_name_taken],
93
93
  include_all_certificates: params[:include_all_certificates],
94
94
  include_mac_in_profiles: params[:include_mac_in_profiles],
95
+ offline_profile: params[:offline_profile],
95
96
  }
96
97
 
97
98
  values[:platform] = params[:platform]
@@ -160,14 +160,14 @@ module Match
160
160
  FastlaneCore::ConfigItem.new(key: :git_basic_authorization,
161
161
  env_name: "MATCH_GIT_BASIC_AUTHORIZATION",
162
162
  sensitive: true,
163
- description: "Use a basic authorization header to access the git repo (e.g.: access via HTTPS, GitHub Actions, etc), usually a string in Base64",
163
+ description: "Use a basic authorization header to access the git repo (e.g.: access via HTTPS, GitHub Actions, etc), usually a string in Base64. Needs git 2.31 or later",
164
164
  conflicting_options: [:git_bearer_authorization, :git_private_key],
165
165
  optional: true,
166
166
  default_value: nil),
167
167
  FastlaneCore::ConfigItem.new(key: :git_bearer_authorization,
168
168
  env_name: "MATCH_GIT_BEARER_AUTHORIZATION",
169
169
  sensitive: true,
170
- description: "Use a bearer authorization header to access the git repo (e.g.: access to an Azure DevOps repository), usually a string in Base64",
170
+ description: "Use a bearer authorization header to access the git repo (e.g.: access to an Azure DevOps repository), usually a string in Base64. Needs git 2.31 or later",
171
171
  conflicting_options: [:git_basic_authorization, :git_private_key],
172
172
  optional: true,
173
173
  default_value: nil),
@@ -209,7 +209,8 @@ module Match
209
209
  FastlaneCore::ConfigItem.new(key: :s3_access_key,
210
210
  env_name: "MATCH_S3_ACCESS_KEY",
211
211
  description: "S3 access key",
212
- optional: true),
212
+ optional: true,
213
+ sensitive: true),
213
214
  FastlaneCore::ConfigItem.new(key: :s3_secret_access_key,
214
215
  env_name: "MATCH_S3_SECRET_ACCESS_KEY",
215
216
  description: "S3 secret access key",
@@ -247,11 +248,13 @@ module Match
247
248
  FastlaneCore::ConfigItem.new(key: :job_token,
248
249
  env_name: "CI_JOB_TOKEN",
249
250
  description: "GitLab CI_JOB_TOKEN",
250
- optional: true),
251
+ optional: true,
252
+ sensitive: true),
251
253
  FastlaneCore::ConfigItem.new(key: :private_token,
252
254
  env_name: "PRIVATE_TOKEN",
253
255
  description: "GitLab Access Token",
254
- optional: true),
256
+ optional: true,
257
+ sensitive: true),
255
258
 
256
259
  # Keychain
257
260
  FastlaneCore::ConfigItem.new(key: :keychain_name,
@@ -374,6 +377,11 @@ module Match
374
377
  description: "Force encryption to use legacy cbc algorithm for backwards compatibility with older match versions",
375
378
  type: Boolean,
376
379
  default_value: false),
380
+ FastlaneCore::ConfigItem.new(key: :offline_profile,
381
+ env_name: "MATCH_OFFLINE_PROFILE",
382
+ description: "Enable profile with 'Offline Support' (7 day validity). Requires Apple ID login, not supported with App Store Connect API key authentication",
383
+ type: Boolean,
384
+ default_value: false),
377
385
 
378
386
  # other
379
387
  FastlaneCore::ConfigItem.new(key: :verbose,
@@ -1,4 +1,5 @@
1
1
  require 'fastlane_core/command_executor'
2
+ require 'open3'
2
3
 
3
4
  require_relative '../module'
4
5
  require_relative './interface'
@@ -7,6 +8,9 @@ module Match
7
8
  module Storage
8
9
  # Store the code signing identities in a git repo
9
10
  class GitStorage < Interface
11
+ DEFAULT_BRANCH = "main"
12
+ PRIVATE_KEY_ENV = "MATCH_SSH_ADD_PRIVATE_KEY"
13
+
10
14
  # User provided values
11
15
  attr_accessor :git_url
12
16
  attr_accessor :shallow_clone
@@ -85,11 +89,6 @@ module Match
85
89
  self.working_directory = Dir.mktmpdir
86
90
 
87
91
  command = "git clone #{self.git_url.shellescape} #{self.working_directory.shellescape}"
88
- # HTTP headers are supposed to be case-insensitive but
89
- # Bitbucket requires `Authorization: Basic` and `Authorization Bearer` to work
90
- # https://github.com/fastlane/fastlane/pull/15928
91
- command << " -c http.extraheader='Authorization: Basic #{self.git_basic_authorization}'" unless self.git_basic_authorization.nil?
92
- command << " -c http.extraheader='Authorization: Bearer #{self.git_bearer_authorization}'" unless self.git_bearer_authorization.nil?
93
92
 
94
93
  if self.shallow_clone
95
94
  command << " --depth 1"
@@ -165,8 +164,10 @@ module Match
165
164
  def generate_matchfile_content
166
165
  UI.important("Please create a new, private git repository to store the certificates and profiles there")
167
166
  url = UI.input("URL of the Git Repo: ")
167
+ branch = UI.input("Branch of the Git Repo (leave blank to use '#{DEFAULT_BRANCH}'): ").to_s.strip
168
+ branch = DEFAULT_BRANCH if branch.empty?
168
169
 
169
- return "git_url(\"#{url}\")"
170
+ return "git_url(\"#{url}\")\ngit_branch(\"#{branch}\")"
170
171
  end
171
172
 
172
173
  def list_files(file_name: "", file_ext: "")
@@ -178,7 +179,8 @@ module Match
178
179
  ssh_add = File.expand_path(self.git_private_key).shellescape.to_s
179
180
  else
180
181
  UI.message("Private key file does not exist, will continue by using it as a raw key.")
181
- ssh_add = "- <<< \"#{self.git_private_key}\""
182
+ # From the environment (git_env_values), so the key is not on bash's command line
183
+ ssh_add = "- <<< \"$#{PRIVATE_KEY_ENV}\""
182
184
  end
183
185
  return "ssh-agent bash -c 'ssh-add #{ssh_add}; #{command}'"
184
186
  end
@@ -264,9 +266,43 @@ module Match
264
266
  def git_env_values
265
267
  env_values = { 'GIT_TERMINAL_PROMPT' => '0' }
266
268
  env_values['GIT_SSH_COMMAND'] = non_interactive_git_ssh_command
269
+ env_values[PRIVATE_KEY_ENV] = self.git_private_key unless self.git_private_key.nil?
270
+ env_values.merge(authorization_env_values)
271
+ end
272
+
273
+ # Through git's environment (git 2.31+), so the header is neither on the command line nor stored in .git/config
274
+ def authorization_env_values
275
+ headers = []
276
+ # HTTP headers are supposed to be case-insensitive but
277
+ # Bitbucket requires `Authorization: Basic` and `Authorization Bearer` to work
278
+ # https://github.com/fastlane/fastlane/pull/15928
279
+ headers << "Authorization: Basic #{self.git_basic_authorization}" unless self.git_basic_authorization.nil?
280
+ headers << "Authorization: Bearer #{self.git_bearer_authorization}" unless self.git_bearer_authorization.nil?
281
+ return {} if headers.empty?
282
+
283
+ version = git_version
284
+ UI.user_error!("git_basic_authorization and git_bearer_authorization need git 2.31 or later, found git #{version}") if version && Gem::Version.new(version) < Gem::Version.new('2.31')
285
+
286
+ # Keep pairs the user already passes this way
287
+ first = ENV['GIT_CONFIG_COUNT'].to_i
288
+ env_values = { 'GIT_CONFIG_COUNT' => (first + headers.count).to_s }
289
+ headers.each_with_index do |header, i|
290
+ env_values["GIT_CONFIG_KEY_#{first + i}"] = 'http.extraheader'
291
+ env_values["GIT_CONFIG_VALUE_#{first + i}"] = header
292
+ end
267
293
  env_values
268
294
  end
269
295
 
296
+ # nil when git cannot be run: cloning reports that
297
+ def git_version
298
+ @git_version ||= begin
299
+ output, status = Open3.capture2('git', '--version')
300
+ output[/\d+\.\d+(\.\d+)?/] if status.success?
301
+ rescue Errno::ENOENT
302
+ nil
303
+ end
304
+ end
305
+
270
306
  def non_interactive_git_ssh_command
271
307
  ssh_command = ENV['GIT_SSH_COMMAND']
272
308
 
@@ -1,4 +1,5 @@
1
1
  require 'open-uri'
2
+ require 'ostruct'
2
3
 
3
4
  require_relative '../../module'
4
5
 
@@ -39,7 +40,7 @@ module Match
39
40
 
40
41
  create_subfolders(working_directory)
41
42
  File.open(destination_file, "wb") do |saved_file|
42
- URI.open(url, "rb", { @client.authentication_key => @client.authentication_value }) do |data|
43
+ url.open("rb", { @client.authentication_key => @client.authentication_value }) do |data|
43
44
  saved_file.write(data.read)
44
45
  end
45
46
 
@@ -6,10 +6,6 @@ module PEM
6
6
  attr_accessor :config
7
7
  end
8
8
 
9
- tmp_dir = Dir.tmpdir
10
- TMP_FOLDER = "#{tmp_dir}/fastlane/PEM/"
11
- FileUtils.mkdir_p(TMP_FOLDER)
12
-
13
9
  ENV['FASTLANE_TEAM_ID'] ||= ENV["PEM_TEAM_ID"]
14
10
  ENV['DELIVER_USER'] ||= ENV["PEM_USERNAME"]
15
11
 
@@ -1,3 +1,4 @@
1
+ require 'ostruct'
1
2
  require 'spaceship/tunes/language_item'
2
3
  require 'spaceship/tunes/iap_list'
3
4
  require 'fastlane/markdown_table_formatter'
@@ -1,5 +1,7 @@
1
+ require 'ostruct'
1
2
  require 'spaceship'
2
3
  require_relative 'module'
4
+ require_relative 'service'
3
5
 
4
6
  module Produce
5
7
  class DeveloperCenter
@@ -86,6 +88,8 @@ module Produce
86
88
  end
87
89
 
88
90
  def create_new_app
91
+ return create_new_app_via_connect_api if using_connect_api?
92
+
89
93
  ENV["CREATED_NEW_APP_ID"] = Time.now.to_i.to_s
90
94
  if app_exists?
91
95
  UI.success("[DevCenter] App '#{Produce.config[:app_identifier]}' already exists, nothing to do on the Dev Center")
@@ -118,6 +122,31 @@ module Produce
118
122
  return true
119
123
  end
120
124
 
125
+ # App Store Connect API key path — used instead of Spaceship.login.
126
+ def create_new_app_via_connect_api
127
+ config_enabled_services = Produce.config[:enable_services] || Produce.config[:enabled_features]
128
+
129
+ existing = Spaceship::ConnectAPI::BundleId.find(app_identifier)
130
+ if existing
131
+ UI.success("[DevCenter] App '#{Produce.config[:app_identifier]}' already exists, nothing to do on the Dev Center")
132
+ return true
133
+ end
134
+
135
+ app_name = Produce.config[:app_name]
136
+ UI.message("Creating new app '#{app_name}' on the Apple Dev Center")
137
+
138
+ connect_api_platform = Spaceship::ConnectAPI::BundleIdPlatform.map(platform)
139
+ bundle_id = Spaceship::ConnectAPI::BundleId.create(name: app_name, platform: connect_api_platform, identifier: app_identifier)
140
+
141
+ UI.crash!("Something went wrong when creating the new app - it's not listed in the apps list") unless bundle_id
142
+
143
+ UI.message("Created app #{bundle_id.id}")
144
+ enable_inline_services(config_enabled_services) if config_enabled_services && !config_enabled_services.empty?
145
+ UI.success("Finished creating new app '#{app_name}' on the Dev Center")
146
+
147
+ return true
148
+ end
149
+
121
150
  def enable_services
122
151
  app_service = Spaceship.app_service
123
152
  enabled_clean_options = {}
@@ -161,6 +190,17 @@ module Produce
161
190
 
162
191
  private
163
192
 
193
+ # Delegates to Service#update instead of duplicating its ~40-capability mapping.
194
+ # Skips any capability explicitly marked "off" — meaningless on a brand-new app.
195
+ def enable_inline_services(config_enabled_services)
196
+ to_enable = config_enabled_services.reject { |_, v| v == SERVICE_OFF }
197
+ return if to_enable.empty?
198
+
199
+ options = OpenStruct.new(to_enable)
200
+ options.define_singleton_method(:__hash__) { to_enable }
201
+ Produce::Service.new.update(true, options)
202
+ end
203
+
164
204
  def platform
165
205
  # This was added to support creation of multiple platforms
166
206
  # Produce::ItunesConnect can take an array of platforms to create for App Store Connect
@@ -174,9 +214,16 @@ module Produce
174
214
  Spaceship.app.find(app_identifier, mac: platform == "osx") != nil
175
215
  end
176
216
 
217
+ def using_connect_api?
218
+ !connect_api_token.nil?
219
+ end
220
+
221
+ def connect_api_token
222
+ Spaceship::ConnectAPI::Token.from(hash: Produce.config[:api_key], filepath: Produce.config[:api_key_path])
223
+ end
224
+
177
225
  def login
178
- Spaceship.login(Produce.config[:username], nil)
179
- Spaceship.select_team
226
+ Produce.authenticate_connect_api!
180
227
  end
181
228
  end
182
229
  end
@@ -38,7 +38,7 @@ module Produce
38
38
  end
39
39
 
40
40
  # Produce.config[:itc_users]
41
- application = Spaceship::ConnectAPI::App.create(
41
+ Spaceship::ConnectAPI::App.create(
42
42
  name: Produce.config[:app_name],
43
43
  version_string: Produce.config[:app_version] || "1.0",
44
44
  sku: Produce.config[:sku].to_s,
@@ -1,4 +1,5 @@
1
1
  require 'fastlane_core/helper'
2
+ require 'spaceship'
2
3
 
3
4
  module Produce
4
5
  class << self
@@ -11,4 +12,24 @@ module Produce
11
12
 
12
13
  ENV['FASTLANE_TEAM_ID'] ||= ENV["PRODUCE_TEAM_ID"]
13
14
  ENV['DELIVER_USER'] ||= ENV["PRODUCE_USERNAME"]
15
+
16
+ # Shared by DeveloperCenter#login and Service#bundle_id. Prefers a configured API key, falls
17
+ # back to a token already set by another lane (e.g. app_store_connect_api_key), then falls
18
+ # back to username-based session login.
19
+ def self.authenticate_connect_api!
20
+ if (token = Spaceship::ConnectAPI::Token.from(hash: Produce.config[:api_key], filepath: Produce.config[:api_key_path]))
21
+ UI.message("Authenticating with App Store Connect API Key")
22
+
23
+ # `current_team_id:` avoids a live, session-based team_id lookup elsewhere in
24
+ # ConnectAPI's provisioning client, which has no session to use here.
25
+ Spaceship::ConnectAPI.client = Spaceship::ConnectAPI::Client.new(token: token, current_team_id: Produce.config[:team_id])
26
+ elsif !Spaceship::ConnectAPI.token.nil?
27
+ UI.message("Using existing authorization token for App Store Connect API")
28
+ else
29
+ UI.message("Starting login with user '#{Produce.config[:username]}'")
30
+ Spaceship.login(Produce.config[:username], nil)
31
+ Spaceship.select_team
32
+ UI.message("Successfully logged in")
33
+ end
34
+ end
14
35
  end
@@ -17,6 +17,23 @@ module Produce
17
17
  code_gen_sensitive: true,
18
18
  default_value: user,
19
19
  default_value_dynamic: true),
20
+
21
+ # App Store Connect API
22
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
23
+ env_names: ["PRODUCE_API_KEY_PATH", "APP_STORE_CONNECT_API_KEY_PATH"],
24
+ description: "Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)",
25
+ optional: true,
26
+ conflicting_options: [:api_key],
27
+ verify_block: proc do |value|
28
+ UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
29
+ end),
30
+ FastlaneCore::ConfigItem.new(key: :api_key,
31
+ env_names: ["PRODUCE_API_KEY", "APP_STORE_CONNECT_API_KEY"],
32
+ description: "Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-hash-option)",
33
+ type: Hash,
34
+ optional: true,
35
+ sensitive: true,
36
+ conflicting_options: [:api_key_path]),
20
37
  FastlaneCore::ConfigItem.new(key: :app_identifier,
21
38
  env_name: "PRODUCE_APP_IDENTIFIER",
22
39
  short_option: "-a",
@@ -88,7 +88,6 @@ module Produce
88
88
  end
89
89
 
90
90
  # rubocop:disable Metrics/PerceivedComplexity
91
- # rubocop:disable Require/MissingRequireStatement
92
91
  def update(on, options)
93
92
  updated = valid_services_for(options).count
94
93
 
@@ -420,10 +419,9 @@ module Produce
420
419
 
421
420
  def bundle_id
422
421
  return @bundle_id if @bundle_id
423
- UI.message("Starting login with user '#{Produce.config[:username]}'")
424
- Spaceship.login(Produce.config[:username], nil)
425
- Spaceship.select_team
426
- UI.message("Successfully logged in")
422
+
423
+ Produce.authenticate_connect_api!
424
+
427
425
  @bundle_id ||= Spaceship::ConnectAPI::BundleId.find(Produce.config[:app_identifier].to_s)
428
426
  end
429
427
  end
@@ -2,5 +2,4 @@ require_relative 'produce/manager'
2
2
  require_relative 'produce/options'
3
3
  require_relative 'produce/developer_center'
4
4
  require_relative 'produce/itunes_connect'
5
- require_relative 'produce/available_default_languages'
6
5
  require_relative 'produce/module'
@@ -207,6 +207,13 @@ module Scan
207
207
  conflict_block: proc do |value|
208
208
  UI.user_error!("You can't use 'thread_sanitizer' and 'address_sanitizer' options in one run")
209
209
  end),
210
+ FastlaneCore::ConfigItem.new(key: :collect_test_diagnostics,
211
+ env_name: "SCAN_COLLECT_TEST_DIAGNOSTICS",
212
+ description: "Whether verbose and long-running diagnostics (like sysdiagnoses or log archives) are collected when testing. Valid values are: on-failure or never. If not specified, the value in the test plan is used. Equivalent to -collect-test-diagnostics (Xcode 14 and up)",
213
+ optional: true,
214
+ verify_block: proc do |value|
215
+ UI.user_error!("Invalid collect_test_diagnostics #{value}") unless ['on-failure', 'never'].include?(value)
216
+ end),
210
217
 
211
218
  # output
212
219
  FastlaneCore::ConfigItem.new(key: :open_report,
@@ -327,7 +327,7 @@ module Scan
327
327
 
328
328
  def open_report
329
329
  if !Helper.ci? && Scan.cache[:open_html_report_path]
330
- `open --hide '#{Scan.cache[:open_html_report_path]}'`
330
+ system("open", "--hide", Scan.cache[:open_html_report_path])
331
331
  end
332
332
  end
333
333
 
@@ -44,9 +44,9 @@ module Scan
44
44
 
45
45
  options = []
46
46
  options += project_path_array unless config[:xctestrun]
47
- options << "-sdk '#{config[:sdk]}'" if config[:sdk]
47
+ options << "-sdk #{config[:sdk].shellescape}" if config[:sdk]
48
48
  options << destination if destination # generated in `detect_values`
49
- options << "-toolchain '#{config[:toolchain]}'" if config[:toolchain]
49
+ options << "-toolchain #{config[:toolchain].to_s.shellescape}" if config[:toolchain]
50
50
  if config[:derived_data_path] && !options.include?("-derivedDataPath #{config[:derived_data_path].shellescape}")
51
51
  options << "-derivedDataPath #{config[:derived_data_path].shellescape}"
52
52
  end
@@ -68,6 +68,9 @@ module Scan
68
68
  options << "-enableCodeCoverage #{config[:code_coverage] ? 'YES' : 'NO'}" unless config[:code_coverage].nil?
69
69
  options << "-enableAddressSanitizer #{config[:address_sanitizer] ? 'YES' : 'NO'}" unless config[:address_sanitizer].nil?
70
70
  options << "-enableThreadSanitizer #{config[:thread_sanitizer] ? 'YES' : 'NO'}" unless config[:thread_sanitizer].nil?
71
+ if config[:collect_test_diagnostics] && FastlaneCore::Helper.xcode_at_least?(14)
72
+ options << "-collect-test-diagnostics #{config[:collect_test_diagnostics]}"
73
+ end
71
74
  if FastlaneCore::Helper.xcode_at_least?(11)
72
75
  if config[:cloned_source_packages_path] && !options.include?("-clonedSourcePackagesDirPath #{config[:cloned_source_packages_path].shellescape}")
73
76
  options << "-clonedSourcePackagesDirPath #{config[:cloned_source_packages_path].shellescape}"
@@ -77,14 +80,14 @@ module Scan
77
80
  options << "-packageCachePath #{config[:package_cache_path].shellescape}"
78
81
  end
79
82
 
80
- options << "-testPlan '#{config[:testplan]}'" if config[:testplan]
83
+ options << "-testPlan #{config[:testplan].shellescape}" if config[:testplan]
81
84
 
82
85
  # detect_values will ensure that these values are present as Arrays if
83
86
  # they are present at all
84
- options += config[:only_test_configurations].map { |name| "-only-test-configuration '#{name}'" } if config[:only_test_configurations]
85
- options += config[:skip_test_configurations].map { |name| "-skip-test-configuration '#{name}'" } if config[:skip_test_configurations]
87
+ options += config[:only_test_configurations].map { |name| "-only-test-configuration #{name.shellescape}" } if config[:only_test_configurations]
88
+ options += config[:skip_test_configurations].map { |name| "-skip-test-configuration #{name.shellescape}" } if config[:skip_test_configurations]
86
89
  end
87
- options << "-xctestrun '#{config[:xctestrun]}'" if config[:xctestrun]
90
+ options << "-xctestrun #{config[:xctestrun].shellescape}" if config[:xctestrun]
88
91
  options << config[:xcargs] if config[:xcargs]
89
92
 
90
93
  # Number of retries does not equal xcodebuild's -test-iterations number
@@ -127,7 +130,7 @@ module Scan
127
130
  end
128
131
 
129
132
  def pipe
130
- pipe = ["| tee '#{xcodebuild_log_path}'"]
133
+ pipe = ["| tee #{xcodebuild_log_path.shellescape}"]
131
134
 
132
135
  # disable_xcpretty is now deprecated and directs to use output_style of raw
133
136
  if Scan.config[:disable_xcpretty] || Scan.config[:output_style] == 'raw'
@@ -182,7 +185,7 @@ module Scan
182
185
  formatter = []
183
186
  if (custom_formatter = Scan.config[:xcpretty_formatter] || Scan.config[:formatter])
184
187
  if custom_formatter.end_with?(".rb")
185
- formatter << "-f '#{custom_formatter}'"
188
+ formatter << "-f #{custom_formatter.shellescape}"
186
189
  else
187
190
  formatter << "-f `#{custom_formatter}`"
188
191
  end
@@ -234,7 +237,7 @@ module Scan
234
237
  # Generate destination parameters
235
238
  def destination
236
239
  unless Scan.cache[:destination]
237
- Scan.cache[:destination] = [*Scan.config[:destination]].map { |dst| "-destination '#{dst}'" }.join(' ')
240
+ Scan.cache[:destination] = [*Scan.config[:destination]].map { |dst| "-destination #{dst.shellescape}" }.join(' ')
238
241
  end
239
242
  Scan.cache[:destination]
240
243
  end
@@ -3,6 +3,7 @@ require_relative 'module'
3
3
  module Scan
4
4
  class TestResultParser
5
5
  def parse_result(output)
6
+ UI.deprecated("Scan::TestResultParser is deprecated and will be removed in a future version of fastlane; scan no longer uses it. Read the test counts from the result bundle with Trainer::TestParser, or from the <testsuites> attributes of the JUnit report.")
6
7
  unless output
7
8
  return {
8
9
  tests: 0,
@@ -49,7 +49,7 @@ module Scan
49
49
  type = raw_type.strip
50
50
  output_path = File.join(File.expand_path(@output_directory), determine_output_file_name(type))
51
51
  reporter << "--report #{type}"
52
- reporter << "--output '#{output_path}'"
52
+ reporter << "--output #{output_path.shellescape}"
53
53
 
54
54
  if type == "html" && @open_report
55
55
  Scan.cache[:open_html_report_path] = output_path
@@ -62,7 +62,7 @@ module Scan
62
62
  @temp_junit_report = Tempfile.new("junit_report")
63
63
  Scan.cache[:temp_junit_report] = @temp_junit_report.path
64
64
  reporter << "--report junit"
65
- reporter << "--output '#{Scan.cache[:temp_junit_report]}'"
65
+ reporter << "--output #{Scan.cache[:temp_junit_report].shellescape}"
66
66
  return reporter
67
67
  end
68
68
 
@@ -65,18 +65,18 @@
65
65
  </style>
66
66
  </head>
67
67
  <body><% image_counter = 0 %><% @data.each do |language, content| %>
68
- <h1 id="<%= language %>" class="language"><%= language %></h1>
68
+ <h1 id="<%= ERB::Util.h(language) %>" class="language"><%= ERB::Util.h(language) %></h1>
69
69
  <hr>
70
70
  <table><% content.each do |device_name, screens| %>
71
71
  <tr>
72
72
  <th colspan="<%= screens.count %>">
73
- <a id="<%= language %>-<%= device_name %>" class="deviceName" href="#<%= language %>-<%= device_name %>"><%= device_name %></a>
73
+ <a id="<%= ERB::Util.h(language) %>-<%= ERB::Util.h(device_name) %>" class="deviceName" href="#<%= ERB::Util.h(language) %>-<%= ERB::Util.h(device_name) %>"><%= ERB::Util.h(device_name) %></a>
74
74
  </th>
75
75
  </tr>
76
76
  <tr><% screens.each do |screen_path| %><% next if screen_path.include?"_framed.png" %>
77
77
  <td><% image_counter += 1 %>
78
- <a href="<%= screen_path %>" target="_blank" class="screenshotLink">
79
- <img class="screenshot" src="<%= screen_path %>" style="width: 100%;" alt="<%= language %> <%= device_name %>" data-counter="<%= image_counter %>">
78
+ <a href="<%= ERB::Util.h(screen_path) %>" target="_blank" class="screenshotLink">
79
+ <img class="screenshot" src="<%= ERB::Util.h(screen_path) %>" style="width: 100%;" alt="<%= ERB::Util.h(language) %> <%= ERB::Util.h(device_name) %>" data-counter="<%= image_counter %>">
80
80
  </a>
81
81
  </td><% end %>
82
82
  </tr><% end %>
@@ -128,9 +128,14 @@
128
128
  imageDisplay.alt = img.alt;
129
129
  imageDisplay.dataset.counter = img.dataset.counter;
130
130
 
131
- imageInfo.innerHTML = '<h3>'+img.alt+'</h3>';
132
- imageInfo.innerHTML += decodeURI(img.src.split("/").pop());
133
- imageInfo.innerHTML += '<br />'+tmpImg.height+'&times;'+tmpImg.width+'px';
131
+ // textContent, not innerHTML: file names must not be interpreted as HTML
132
+ var imageTitle = document.createElement('h3');
133
+ imageTitle.textContent = img.alt;
134
+ imageInfo.textContent = '';
135
+ imageInfo.appendChild(imageTitle);
136
+ imageInfo.appendChild(document.createTextNode(decodeURI(img.src.split("/").pop())));
137
+ imageInfo.appendChild(document.createElement('br'));
138
+ imageInfo.appendChild(document.createTextNode(tmpImg.height+'\u00d7'+tmpImg.width+'px'));
134
139
 
135
140
  overlay.style.display = "block";
136
141
  });
@@ -30,7 +30,7 @@ module Screengrab
30
30
 
31
31
  export_path = File.expand_path(export_path)
32
32
  UI.success("Successfully created HTML file with an overview of all the screenshots: '#{export_path}'")
33
- system("open '#{export_path}'") unless Screengrab.config[:skip_open_summary]
33
+ system("open", export_path) unless Screengrab.config[:skip_open_summary]
34
34
  end
35
35
  end
36
36
  end