fastlane 2.152.0 → 2.155.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +76 -76
  3. data/deliver/lib/deliver/app_screenshot.rb +1 -1
  4. data/deliver/lib/deliver/commands_generator.rb +7 -4
  5. data/deliver/lib/deliver/detect_values.rb +9 -3
  6. data/deliver/lib/deliver/download_screenshots.rb +1 -3
  7. data/deliver/lib/deliver/html_generator.rb +8 -1
  8. data/deliver/lib/deliver/runner.rb +5 -10
  9. data/deliver/lib/deliver/setup.rb +92 -3
  10. data/deliver/lib/deliver/submit_for_review.rb +1 -3
  11. data/deliver/lib/deliver/upload_metadata.rb +46 -26
  12. data/deliver/lib/deliver/upload_price_tier.rb +1 -3
  13. data/deliver/lib/deliver/upload_screenshots.rb +75 -44
  14. data/{deliver/lib/deliver/.commands_generator.rb.swp → fastlane/lib/fastlane/.erb_template_helper.rb.swp} +0 -0
  15. data/{frameit/lib/frameit/.editor.rb.swp → fastlane/lib/fastlane/actions/.git_commit.rb.swp} +0 -0
  16. data/fastlane/lib/fastlane/actions/carthage.rb +7 -0
  17. data/fastlane/lib/fastlane/actions/create_keychain.rb +5 -1
  18. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +3 -1
  19. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +21 -2
  20. data/fastlane/lib/fastlane/actions/docs/upload_to_app_store.md.erb +4 -4
  21. data/fastlane/lib/fastlane/actions/download_dsyms.rb +4 -2
  22. data/fastlane/lib/fastlane/actions/erb.rb +10 -2
  23. data/fastlane/lib/fastlane/actions/git_pull.rb +13 -2
  24. data/fastlane/lib/fastlane/actions/sync_code_signing.rb +5 -0
  25. data/fastlane/lib/fastlane/runner.rb +3 -1
  26. data/fastlane/lib/fastlane/version.rb +1 -1
  27. data/fastlane/swift/Deliverfile.swift +1 -1
  28. data/fastlane/swift/DeliverfileProtocol.swift +1 -1
  29. data/fastlane/swift/Fastlane.swift +407 -189
  30. data/fastlane/swift/Gymfile.swift +1 -1
  31. data/fastlane/swift/GymfileProtocol.swift +1 -1
  32. data/fastlane/swift/Matchfile.swift +1 -1
  33. data/fastlane/swift/MatchfileProtocol.swift +10 -2
  34. data/fastlane/swift/Precheckfile.swift +1 -1
  35. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  36. data/fastlane/swift/Scanfile.swift +1 -1
  37. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  38. data/fastlane/swift/Screengrabfile.swift +1 -1
  39. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  40. data/fastlane/swift/Snapshotfile.swift +1 -1
  41. data/fastlane/swift/SnapshotfileProtocol.swift +9 -1
  42. data/fastlane_core/lib/fastlane_core/device_manager.rb +25 -6
  43. data/frameit/lib/frameit/editor.rb +11 -6
  44. data/gym/lib/gym/detect_values.rb +6 -3
  45. data/gym/lib/gym/module.rb +30 -0
  46. data/gym/lib/gym/runner.rb +23 -18
  47. data/match/lib/match/generator.rb +6 -0
  48. data/match/lib/match/options.rb +16 -4
  49. data/match/lib/match/runner.rb +13 -5
  50. data/match/lib/match/spaceship_ensure.rb +7 -9
  51. data/match/lib/match/storage/git_storage.rb +16 -2
  52. data/match/lib/match/storage/google_cloud_storage.rb +1 -1
  53. data/pilot/lib/pilot/build_manager.rb +9 -0
  54. data/pilot/lib/pilot/options.rb +1 -1
  55. data/scan/lib/scan/runner.rb +19 -6
  56. data/sigh/lib/sigh/.runner.rb.swp +0 -0
  57. data/sigh/lib/sigh/download_all.rb +42 -27
  58. data/sigh/lib/sigh/module.rb +26 -0
  59. data/sigh/lib/sigh/options.rb +2 -2
  60. data/sigh/lib/sigh/runner.rb +96 -35
  61. data/snapshot/lib/snapshot/options.rb +10 -0
  62. data/snapshot/lib/snapshot/simulator_launchers/launcher_configuration.rb +2 -0
  63. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +5 -0
  64. data/snapshot/lib/snapshot/test_command_generator.rb +3 -2
  65. data/snapshot/lib/snapshot/test_command_generator_xcode_8.rb +4 -1
  66. data/spaceship/lib/spaceship/connect_api.rb +1 -0
  67. data/spaceship/lib/spaceship/connect_api/client.rb +5 -3
  68. data/spaceship/lib/spaceship/connect_api/model.rb +15 -1
  69. data/spaceship/lib/spaceship/connect_api/models/.device.rb.swp +0 -0
  70. data/spaceship/lib/spaceship/connect_api/models/age_rating_declaration.rb +1 -0
  71. data/spaceship/lib/spaceship/connect_api/models/app.rb +61 -3
  72. data/spaceship/lib/spaceship/connect_api/models/app_info_localization.rb +1 -0
  73. data/spaceship/lib/spaceship/connect_api/models/app_preview.rb +1 -0
  74. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +44 -5
  75. data/spaceship/lib/spaceship/connect_api/models/app_store_review_detail.rb +1 -0
  76. data/spaceship/lib/spaceship/connect_api/models/app_store_version.rb +12 -0
  77. data/spaceship/lib/spaceship/connect_api/models/app_store_version_localization.rb +1 -0
  78. data/spaceship/lib/spaceship/connect_api/models/app_store_version_release_request.rb +12 -0
  79. data/spaceship/lib/spaceship/connect_api/models/build.rb +1 -0
  80. data/spaceship/lib/spaceship/connect_api/models/bundle_id.rb +17 -5
  81. data/spaceship/lib/spaceship/connect_api/models/bundle_id_capability.rb +41 -7
  82. data/spaceship/lib/spaceship/connect_api/models/idfa_declaration.rb +1 -0
  83. data/spaceship/lib/spaceship/connect_api/models/profile.rb +31 -1
  84. data/spaceship/lib/spaceship/connect_api/provisioning/client.rb +46 -4
  85. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +41 -0
  86. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +32 -1
  87. data/supply/lib/supply/client.rb +2 -1
  88. data/supply/lib/supply/options.rb +8 -1
  89. metadata +33 -58
  90. data/deliver/lib/deliver/.submit_for_review.rb.swp +0 -0
  91. data/deliver/lib/deliver/.upload_metadata.rb.swp +0 -0
@@ -40,6 +40,7 @@ module Match
40
40
  clone_branch_directly: params[:clone_branch_directly],
41
41
  git_basic_authorization: params[:git_basic_authorization],
42
42
  git_bearer_authorization: params[:git_bearer_authorization],
43
+ git_private_key: params[:git_private_key],
43
44
  type: params[:type].to_s,
44
45
  generate_apple_certs: params[:generate_apple_certs],
45
46
  platform: params[:platform].to_s,
@@ -206,20 +207,26 @@ module Match
206
207
  return File.basename(cert_path).gsub(".cer", "") # Certificate ID
207
208
  end
208
209
 
210
+ # rubocop:disable Metrics/PerceivedComplexity
209
211
  # @return [String] The UUID of the provisioning profile so we can verify it with the Apple Developer Portal
210
212
  def fetch_provisioning_profile(params: nil, certificate_id: nil, app_identifier: nil, working_directory: nil)
211
213
  prov_type = Match.profile_type_sym(params[:type])
212
214
 
213
215
  names = [Match::Generator.profile_type_name(prov_type), app_identifier]
214
- if params[:platform].to_s == :tvos.to_s
216
+ if params[:platform].to_s == :tvos.to_s || params[:platform].to_s == :catalyst.to_s
215
217
  names.push(params[:platform])
216
218
  end
217
219
 
218
220
  profile_name = names.join("_").gsub("*", '\*') # this is important, as it shouldn't be a wildcard
219
221
  base_dir = File.join(prefixed_working_directory, "profiles", prov_type.to_s)
220
222
 
221
- extension = params[:platform].to_s == :macos.to_s ? ".provisionprofile" : ".mobileprovision"
222
- profiles = Dir[File.join(base_dir, "#{profile_name}#{extension}")]
223
+ extension = ".mobileprovision"
224
+ if [:macos.to_s, :catalyst.to_s].include?(params[:platform].to_s)
225
+ extension = ".provisionprofile"
226
+ end
227
+
228
+ profile_file = "#{profile_name}#{extension}"
229
+ profiles = Dir[File.join(base_dir, profile_file)]
223
230
  if Helper.mac?
224
231
  keychain_path = FastlaneCore::Helper.keychain_path(params[:keychain_name]) unless params[:keychain_name].nil?
225
232
  end
@@ -241,7 +248,7 @@ module Match
241
248
 
242
249
  if profile.nil? || force
243
250
  if params[:readonly]
244
- UI.error("No matching provisioning profiles found for '#{profile_name}'")
251
+ UI.error("No matching provisioning profiles found for '#{profile_file}'")
245
252
  UI.error("A new one cannot be created because you enabled `readonly`")
246
253
  if Dir.exist?(base_dir) # folder for `prov_type` does not exist on first match use for that type
247
254
  all_profiles = Dir.entries(base_dir).reject { |f| f.start_with?(".") }
@@ -302,6 +309,7 @@ module Match
302
309
 
303
310
  return uuid
304
311
  end
312
+ # rubocop:enable Metrics/PerceivedComplexity
305
313
 
306
314
  def device_count_different?(profile: nil, keychain_path: nil, platform: nil)
307
315
  return false unless profile
@@ -319,7 +327,7 @@ module Match
319
327
  Spaceship.device.all_ios_profile_devices.count
320
328
  when :tvos
321
329
  Spaceship.device.all_apple_tvs.count
322
- when :mac
330
+ when :mac, :catalyst
323
331
  Spaceship.device.all_macs.count
324
332
  else
325
333
  Spaceship.device.all.count
@@ -46,6 +46,10 @@ module Match
46
46
  end
47
47
 
48
48
  def certificates_exists(username: nil, certificate_ids: [], platform: nil)
49
+ if platform == :catalyst.to_s
50
+ platform = :macos.to_s
51
+ end
52
+
49
53
  Spaceship.certificate.all(mac: platform == "macos").each do |cert|
50
54
  certificate_ids.delete(cert.id)
51
55
  end
@@ -61,18 +65,12 @@ module Match
61
65
  end
62
66
 
63
67
  def profile_exists(username: nil, uuid: nil, platform: nil)
64
- is_mac = platform == "macos"
65
- found = Spaceship.provisioning_profile.all(mac: is_mac).find do |profile|
68
+ # App Store Connect API does not allow filter of profile by platform or uuid (as of 2020-07-30)
69
+ # Need to fetch all profiles and search for uuid on client side
70
+ found = Spaceship::ConnectAPI::Profile.all.find do |profile|
66
71
  profile.uuid == uuid
67
72
  end
68
73
 
69
- # Look for iOS after looking for macOS (needed for Catalyst apps)
70
- if !found && is_mac
71
- found = Spaceship.provisioning_profile.all(mac: false).find do |profile|
72
- profile.uuid == uuid
73
- end
74
- end
75
-
76
74
  unless found
77
75
  UI.error("Provisioning profile '#{uuid}' is not available on the Developer Portal for the user #{username}, fixing this now for you 🔨")
78
76
  return false
@@ -19,6 +19,7 @@ module Match
19
19
  attr_accessor :platform
20
20
  attr_accessor :git_basic_authorization
21
21
  attr_accessor :git_bearer_authorization
22
+ attr_accessor :git_private_key
22
23
 
23
24
  def self.configure(params)
24
25
  return self.new(
@@ -32,7 +33,8 @@ module Match
32
33
  git_user_email: params[:git_user_email],
33
34
  clone_branch_directly: params[:clone_branch_directly],
34
35
  git_basic_authorization: params[:git_basic_authorization],
35
- git_bearer_authorization: params[:git_bearer_authorization]
36
+ git_bearer_authorization: params[:git_bearer_authorization],
37
+ git_private_key: params[:git_private_key]
36
38
  )
37
39
  end
38
40
 
@@ -46,7 +48,8 @@ module Match
46
48
  git_user_email: nil,
47
49
  clone_branch_directly: false,
48
50
  git_basic_authorization: nil,
49
- git_bearer_authorization: nil)
51
+ git_bearer_authorization: nil,
52
+ git_private_key: nil)
50
53
  self.git_url = git_url
51
54
  self.shallow_clone = shallow_clone
52
55
  self.skip_docs = skip_docs
@@ -56,6 +59,7 @@ module Match
56
59
  self.clone_branch_directly = clone_branch_directly
57
60
  self.git_basic_authorization = git_basic_authorization
58
61
  self.git_bearer_authorization = git_bearer_authorization
62
+ self.git_private_key = git_private_key
59
63
 
60
64
  self.type = type if type
61
65
  self.platform = platform if platform
@@ -85,6 +89,16 @@ module Match
85
89
  command += " -b #{self.branch.shellescape} --single-branch"
86
90
  end
87
91
 
92
+ unless self.git_private_key.nil?
93
+ if File.file?(self.git_private_key)
94
+ ssh_add = File.expand_path(self.git_private_key).shellescape.to_s
95
+ else
96
+ UI.message("Private key file does not exist, will continue by using it as a raw key.")
97
+ ssh_add = "- <<< \"#{self.git_private_key}\""
98
+ end
99
+ command = "ssh-agent bash -c 'ssh-add #{ssh_add}; #{command}'"
100
+ end
101
+
88
102
  UI.message("Cloning remote git repo...")
89
103
  if self.branch && !self.clone_branch_directly
90
104
  UI.message("If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.")
@@ -213,7 +213,7 @@ module Match
213
213
 
214
214
  return DEFAULT_KEYS_FILE_NAME if File.exist?(DEFAULT_KEYS_FILE_NAME)
215
215
 
216
- fastlane_folder_gc_keys_path = File.join(FastlaneCore::FastlaneFolder.path, DEFAULT_KEYS_FILE_NAME)
216
+ fastlane_folder_gc_keys_path = File.join(FastlaneCore::FastlaneFolder.path || Dir.pwd, DEFAULT_KEYS_FILE_NAME)
217
217
  return fastlane_folder_gc_keys_path if File.exist?(fastlane_folder_gc_keys_path)
218
218
 
219
219
  if google_cloud_project_id.to_s.length > 0
@@ -281,8 +281,17 @@ module Pilot
281
281
  changelog
282
282
  end
283
283
 
284
+ def self.strip_less_than_sign(changelog)
285
+ if changelog && changelog.include?("<")
286
+ changelog.delete!("<")
287
+ UI.important("Less than signs (<) have been removed from the changelog, since they're not allowed by Apple.")
288
+ end
289
+ changelog
290
+ end
291
+
284
292
  def self.sanitize_changelog(changelog)
285
293
  changelog = strip_emoji(changelog)
294
+ changelog = strip_less_than_sign(changelog)
286
295
  truncate_changelog(changelog)
287
296
  end
288
297
 
@@ -122,7 +122,7 @@ module Pilot
122
122
  short_option: "-w",
123
123
  optional: true,
124
124
  env_name: "PILOT_CHANGELOG",
125
- description: "Provide the 'What to Test' text when uploading a new build. `skip_waiting_for_build_processing: false` is required to set the changelog"),
125
+ description: "Provide the 'What to Test' text when uploading a new build"),
126
126
  FastlaneCore::ConfigItem.new(key: :skip_submission,
127
127
  short_option: "-s",
128
128
  env_name: "PILOT_SKIP_SUBMISSION",
@@ -39,12 +39,7 @@ module Scan
39
39
  end
40
40
  end
41
41
 
42
- # We call this method, to be sure that all other simulators are killed
43
- # And a correct one is freshly launched. Switching between multiple simulator
44
- # in case the user specified multiple targets works with no issues
45
- # This way it's okay to just call it for the first simulator we're using for
46
- # the first test run
47
- FastlaneCore::Simulator.launch(Scan.devices.first) if Scan.devices && Scan.config[:prelaunch_simulator]
42
+ prelaunch_simulators
48
43
 
49
44
  if Scan.config[:reinstall_app]
50
45
  app_identifier = Scan.config[:app_identifier]
@@ -165,6 +160,24 @@ module Scan
165
160
  File.read(Scan.cache[:temp_junit_report])
166
161
  end
167
162
 
163
+ def prelaunch_simulators
164
+ return unless Scan.devices.to_a.size > 0 # no devices selected, no sims to launch
165
+
166
+ # Return early unless the user wants to prelaunch simulators. Or if the user wants simulator logs
167
+ # then we must prelaunch simulators because Xcode's headless
168
+ # mode launches and shutsdown the simulators before we can collect the logs.
169
+ return unless Scan.config[:prelaunch_simulator] || Scan.config[:include_simulator_logs]
170
+
171
+ devices_to_shutdown = []
172
+ Scan.devices.each do |device|
173
+ devices_to_shutdown << device if device.state == "Shutdown"
174
+ device.boot
175
+ end
176
+ at_exit do
177
+ devices_to_shutdown.each(&:shutdown)
178
+ end
179
+ end
180
+
168
181
  def copy_simulator_logs
169
182
  return unless Scan.config[:include_simulator_logs]
170
183
 
@@ -1,5 +1,7 @@
1
1
  require 'spaceship'
2
2
 
3
+ require 'base64'
4
+
3
5
  require_relative 'manager'
4
6
  require_relative 'module'
5
7
 
@@ -12,35 +14,43 @@ module Sigh
12
14
  Spaceship.select_team
13
15
  UI.message("Successfully logged in")
14
16
 
17
+ if download_xcode_profiles
18
+ UI.deprecated("The App Store Connect API does not support querying for Xcode managed profiles: --download_code_profiles is deprecated")
19
+ end
20
+
15
21
  case Sigh.config[:platform].to_s
16
22
  when 'ios'
17
- download_profiles(Spaceship.provisioning_profile.all(xcode: download_xcode_profiles))
18
- xcode_profiles_downloaded?(xcode: download_xcode_profiles, supported: true)
23
+ profile_types = [
24
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_STORE,
25
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE,
26
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC,
27
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT
28
+ ]
19
29
  when 'macos'
20
- download_profiles(Spaceship.provisioning_profile.all(mac: true, xcode: download_xcode_profiles))
21
- xcode_profiles_downloaded?(xcode: download_xcode_profiles, supported: true)
30
+ profile_types = [
31
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE,
32
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT,
33
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT
34
+ ]
35
+ when 'catalyst'
36
+ profile_types = [
37
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE,
38
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT,
39
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
40
+ ]
22
41
  when 'tvos'
23
- download_profiles(Spaceship.provisioning_profile.all_tvos)
24
- xcode_profiles_downloaded?(xcode: download_xcode_profiles, supported: false)
42
+ profile_types = [
43
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE,
44
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE,
45
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC,
46
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT
47
+ ]
25
48
  end
26
- end
27
-
28
- # @param xcode [Bool] Whether or not the user passed the download_xcode_profiles flag
29
- # @param supported [Bool] Whether or not this platform supports downloading xcode profiles at all
30
- def xcode_profiles_downloaded?(xcode: false, supported: false)
31
- if supported
32
- if xcode
33
- UI.message("This run also included all Xcode managed provisioning profiles, as you used the `--download_xcode_profiles` flag")
34
- elsif !xcode
35
- UI.message("All Xcode managed provisioning profiles were ignored on this, to include them use the `--download_xcode_profiles` flag")
36
- end
37
49
 
38
- elsif !supported
39
- if xcode
40
- UI.important("Downloading Xcode managed profiles is not supported for platform #{Sigh.config[:platform]}")
41
- return
42
- end
43
- end
50
+ # Filtering on 'profileType' seems to be undocumented as of 2020-07-30
51
+ # but works on both web session and official API
52
+ profiles = Spaceship::ConnectAPI::Profile.all(filter: { profileType: profile_types.join(",") }, includes: "bundleId")
53
+ download_profiles(profiles)
44
54
  end
45
55
 
46
56
  # @param profiles [Array] Array of all the provisioning profiles we want to download
@@ -57,18 +67,22 @@ module Sigh
57
67
  end
58
68
  end
59
69
 
70
+ def pretty_type(profile_type)
71
+ return Sigh.profile_pretty_type(profile_type)
72
+ end
73
+
60
74
  # @param profile [ProvisioningProfile] A profile we plan to download and store
61
75
  def download_profile(profile)
62
76
  FileUtils.mkdir_p(Sigh.config[:output_path])
63
77
 
64
- type_name = profile.class.pretty_type
65
- profile_name = "#{type_name}_#{profile.uuid}_#{profile.app.bundle_id}"
78
+ type_name = pretty_type(profile.profile_type)
79
+ profile_name = "#{type_name}_#{profile.uuid}_#{profile.bundle_id.identifier}"
66
80
 
67
81
  if Sigh.config[:platform].to_s == 'tvos'
68
82
  profile_name += "_tvos"
69
83
  end
70
84
 
71
- if Sigh.config[:platform].to_s == 'macos'
85
+ if ['macos', 'catalyst'].include?(Sigh.config[:platform].to_s)
72
86
  profile_name += '.provisionprofile'
73
87
  else
74
88
  profile_name += '.mobileprovision'
@@ -76,7 +90,8 @@ module Sigh
76
90
 
77
91
  output_path = File.join(Sigh.config[:output_path], profile_name)
78
92
  File.open(output_path, "wb") do |f|
79
- f.write(profile.download)
93
+ content = Base64.decode64(profile.profile_content)
94
+ f.write(content)
80
95
  end
81
96
 
82
97
  Manager.install_profile(output_path) unless Sigh.config[:skip_install]
@@ -5,6 +5,32 @@ module Sigh
5
5
  # Use this to just setup the configuration attribute and set it later somewhere else
6
6
  class << self
7
7
  attr_accessor :config
8
+
9
+ def profile_pretty_type(profile_type)
10
+ require 'spaceship'
11
+
12
+ case profile_type
13
+ when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT,
14
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT,
15
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT,
16
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT
17
+ "Development"
18
+ when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_STORE,
19
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE,
20
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE,
21
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE
22
+ "AppStore"
23
+ when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC,
24
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC
25
+ "AdHoc"
26
+ when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE,
27
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE
28
+ "InHouse"
29
+ when Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT,
30
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
31
+ "Direct"
32
+ end
33
+ end
8
34
  end
9
35
 
10
36
  Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
@@ -131,12 +131,12 @@ module Sigh
131
131
  FastlaneCore::ConfigItem.new(key: :platform,
132
132
  short_option: '-p',
133
133
  env_name: "SIGH_PLATFORM",
134
- description: "Set the provisioning profile's platform (i.e. ios, tvos)",
134
+ description: "Set the provisioning profile's platform (i.e. ios, tvos, macos, catalyst)",
135
135
  is_string: false,
136
136
  default_value: "ios",
137
137
  verify_block: proc do |value|
138
138
  value = value.to_s
139
- pt = %w(macos tvos ios)
139
+ pt = %w(macos tvos ios catalyst)
140
140
  UI.user_error!("Unsupported platform, must be: #{pt}") unless pt.include?(value)
141
141
  end),
142
142
  FastlaneCore::ConfigItem.new(key: :readonly,
@@ -1,5 +1,7 @@
1
1
  require 'spaceship'
2
2
 
3
+ require 'base64'
4
+
3
5
  require 'fastlane_core/print_table'
4
6
  require 'fastlane_core/cert_checker'
5
7
  require_relative 'module'
@@ -42,7 +44,7 @@ module Sigh
42
44
 
43
45
  UI.user_error!("Something went wrong fetching the latest profile") unless profile
44
46
 
45
- if profile_type == Spaceship.provisioning_profile.in_house
47
+ if [Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE, Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE].include?(profile_type)
46
48
  ENV["SIGH_PROFILE_ENTERPRISE"] = "1"
47
49
  else
48
50
  ENV.delete("SIGH_PROFILE_ENTERPRISE")
@@ -55,11 +57,26 @@ module Sigh
55
57
  def profile_type
56
58
  return @profile_type if @profile_type
57
59
 
58
- @profile_type = Spaceship.provisioning_profile.app_store
59
- @profile_type = Spaceship.provisioning_profile.in_house if Spaceship.client.in_house?
60
- @profile_type = Spaceship.provisioning_profile.ad_hoc if Sigh.config[:adhoc]
61
- @profile_type = Spaceship.provisioning_profile.direct if Sigh.config[:developer_id]
62
- @profile_type = Spaceship.provisioning_profile.development if Sigh.config[:development]
60
+ case Sigh.config[:platform]
61
+ when "ios"
62
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_STORE
63
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE if Spaceship.client.in_house?
64
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC if Sigh.config[:adhoc]
65
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT if Sigh.config[:development]
66
+ when "tvos"
67
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE
68
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE if Spaceship.client.in_house?
69
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC if Sigh.config[:adhoc]
70
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT if Sigh.config[:development]
71
+ when "macos"
72
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE
73
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT if Sigh.config[:development]
74
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT if Sigh.config[:developer_id]
75
+ when "catalyst"
76
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE
77
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT if Sigh.config[:development]
78
+ @profile_type = Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT if Sigh.config[:developer_id]
79
+ end
63
80
 
64
81
  @profile_type
65
82
  end
@@ -67,9 +84,13 @@ module Sigh
67
84
  # Fetches a profile matching the user's search requirements
68
85
  def fetch_profiles
69
86
  UI.message("Fetching profiles...")
70
- results = profile_type.find_by_bundle_id(bundle_id: Sigh.config[:app_identifier],
71
- mac: Sigh.config[:platform].to_s == 'macos',
72
- sub_platform: Sigh.config[:platform].to_s == 'tvos' ? 'tvOS' : nil)
87
+
88
+ # Filtering on 'profileType' seems to be undocumented as of 2020-07-30
89
+ # but works on both web session and official API
90
+ results = Spaceship::ConnectAPI::Profile.all(filter: { profileType: profile_type }, includes: "bundleId,certificates").select do |profile|
91
+ profile.bundle_id.identifier == Sigh.config[:app_identifier]
92
+ end
93
+
73
94
  results = results.find_all do |current_profile|
74
95
  if current_profile.valid? || Sigh.config[:force]
75
96
  true
@@ -93,7 +114,7 @@ module Sigh
93
114
  # "member" and not an a "admin"
94
115
  raw_certs = current_profile.certificates.map do |cert|
95
116
  begin
96
- raw_cert = cert.download_raw
117
+ raw_cert = Base64.decode64(cert.certificate_content)
97
118
  rescue => error
98
119
  UI.important("Cannot download cert #{cert.id} - #{error.message}")
99
120
  raw_cert = nil
@@ -114,31 +135,46 @@ module Sigh
114
135
  UI.message("Certificate for Provisioning Profile '#{current_profile.name}' not available locally: #{current_cert[:cert].id}, skipping this one...")
115
136
  end
116
137
  end
117
- installed && current_profile.certificate_valid?
138
+
139
+ # Don't need to check if certificate is valid because it comes with the
140
+ # profile in the response
141
+ installed
118
142
  end
119
143
  end
120
144
 
145
+ def profile_type_pretty_type
146
+ return Sigh.profile_pretty_type(profile_type)
147
+ end
148
+
121
149
  # Create a new profile and return it
122
150
  def create_profile!
123
- cert = certificate_to_use
124
- bundle_id = Sigh.config[:app_identifier]
125
- name = Sigh.config[:provisioning_name] || [bundle_id, profile_type.pretty_type].join(' ')
151
+ app_identifier = Sigh.config[:app_identifier]
152
+ name = Sigh.config[:provisioning_name] || [app_identifier, profile_type_pretty_type].join(' ')
126
153
 
127
154
  unless Sigh.config[:skip_fetch_profiles]
128
- if Spaceship.provisioning_profile.all(mac: Sigh.config[:platform].to_s == 'macos').find { |p| p.name == name }
155
+ profile = Spaceship::ConnectAPI::Profile.all.find { |p| p.name == name }
156
+ if profile
129
157
  UI.user_error!("The name '#{name}' is already taken, and fail_on_name_taken is true") if Sigh.config[:fail_on_name_taken]
130
158
  UI.error("The name '#{name}' is already taken, using another one.")
131
159
  name += " #{Time.now.to_i}"
132
160
  end
133
161
  end
134
162
 
163
+ bundle_id = Spaceship::ConnectAPI::BundleId.find(app_identifier)
164
+ unless bundle_id
165
+ UI.user_error!("Could not find App with App Identifier '#{Sigh.config[:app_identifier]}'")
166
+ end
167
+
135
168
  UI.important("Creating new provisioning profile for '#{Sigh.config[:app_identifier]}' with name '#{name}' for '#{Sigh.config[:platform]}' platform")
136
- profile = profile_type.create!(name: name,
137
- bundle_id: bundle_id,
138
- certificate: cert,
139
- mac: Sigh.config[:platform].to_s == 'macos',
140
- sub_platform: Sigh.config[:platform].to_s == 'tvos' ? 'tvOS' : nil,
141
- template_name: Sigh.config[:template_name])
169
+
170
+ profile = Spaceship::ConnectAPI::Profile.create(
171
+ name: name,
172
+ profile_type: profile_type,
173
+ bundle_id_id: bundle_id.id,
174
+ certificate_ids: certificates_to_use.map(&:id),
175
+ device_ids: devices_to_use.map(&:id)
176
+ )
177
+
142
178
  profile
143
179
  end
144
180
 
@@ -155,16 +191,16 @@ module Sigh
155
191
  def certificates_for_profile_and_platform
156
192
  case Sigh.config[:platform].to_s
157
193
  when 'ios', 'tvos'
158
- if profile_type == Spaceship.provisioning_profile.Development
194
+ if profile_type == Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT
159
195
  certificates = Spaceship.certificate.development.all +
160
196
  Spaceship.certificate.apple_development.all
161
- elsif profile_type == Spaceship.provisioning_profile.InHouse
197
+ elsif profile_type == Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE
162
198
  # Enterprise accounts don't have access to Apple Distribution certificates
163
199
  certificates = Spaceship.certificate.in_house.all
164
200
  # handles case where the desired certificate type is adhoc but the account is an enterprise account
165
201
  # the apple dev portal api has a weird quirk in it where if you query for distribution certificates
166
202
  # for enterprise accounts, you get nothing back even if they exist.
167
- elsif profile_type == Spaceship.provisioning_profile.AdHoc && Spaceship.client && Spaceship.client.in_house?
203
+ elsif (profile_type == Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC) && Spaceship.client && Spaceship.client.in_house?
168
204
  # Enterprise accounts don't have access to Apple Distribution certificates
169
205
  certificates = Spaceship.certificate.in_house.all
170
206
  else
@@ -173,14 +209,14 @@ module Sigh
173
209
  Spaceship.certificate.apple_distribution.all
174
210
  end
175
211
 
176
- when 'macos'
177
- if profile_type == Spaceship.provisioning_profile.Development
212
+ when 'macos', 'catalyst'
213
+ if profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT
178
214
  certificates = Spaceship.certificate.mac_development.all +
179
215
  Spaceship.certificate.apple_development.all
180
- elsif profile_type == Spaceship.provisioning_profile.AppStore
216
+ elsif profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE
181
217
  certificates = Spaceship.certificate.mac_app_distribution.all +
182
218
  Spaceship.certificate.apple_distribution.all
183
- elsif profile_type == Spaceship.provisioning_profile.Direct
219
+ elsif profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT || profile_type == Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
184
220
  certificates = Spaceship.certificate.developer_id_application.all
185
221
  else
186
222
  certificates = Spaceship.certificate.mac_app_distribution.all +
@@ -191,8 +227,27 @@ module Sigh
191
227
  certificates
192
228
  end
193
229
 
230
+ def devices_to_use
231
+ device_class = case Sigh.config[:platform].to_s
232
+ when 'ios'
233
+ [
234
+ Spaceship::ConnectAPI::Device::DeviceClass::APPLE_WATCH,
235
+ Spaceship::ConnectAPI::Device::DeviceClass::IPAD,
236
+ Spaceship::ConnectAPI::Device::DeviceClass::IPHONE,
237
+ Spaceship::ConnectAPI::Device::DeviceClass::IPOD
238
+ ].join(",")
239
+ when 'tvos'
240
+ Spaceship::ConnectAPI::Device::DeviceClass::APPLE_TV
241
+ when 'macos', 'catalyst'
242
+ Spaceship::ConnectAPI::Device::DeviceClass::MAC
243
+ end
244
+
245
+ filter = { deviceClass: device_class }
246
+ return Spaceship::ConnectAPI::Device.all(filter: filter)
247
+ end
248
+
194
249
  # Certificate to use based on the current distribution mode
195
- def certificate_to_use
250
+ def certificates_to_use
196
251
  certificates = certificates_for_profile_and_platform
197
252
 
198
253
  # Filter them
@@ -235,26 +290,28 @@ module Sigh
235
290
  filters << "Owner Name: '#{Sigh.config[:cert_owner_name]}' " if Sigh.config[:cert_owner_name]
236
291
  filters << "Certificate ID: '#{Sigh.config[:cert_id]}' " if Sigh.config[:cert_id]
237
292
  UI.important("No certificates for filter: #{filters}") if filters.length > 0
238
- message = "Could not find a matching code signing identity for type '#{profile_type.to_s.split(':').last}'. "
293
+ message = "Could not find a matching code signing identity for type '#{profile_type_pretty_type}'. "
239
294
  message += "It is recommended to use match to manage code signing for you, more information on https://codesigning.guide. "
240
295
  message += "If you don't want to do so, you can also use cert to generate a new one: https://fastlane.tools/cert"
241
296
  UI.user_error!(message)
242
297
  end
243
298
 
244
299
  return certificates if Sigh.config[:development] # development profiles support multiple certificates
245
- return certificates.first
300
+ return [certificates.first]
246
301
  end
247
302
 
248
303
  # Downloads and stores the provisioning profile
249
304
  def download_profile(profile)
250
305
  UI.important("Downloading provisioning profile...")
251
- profile_name ||= "#{profile_type.pretty_type}_#{Sigh.config[:app_identifier]}"
306
+ profile_name ||= "#{profile_type_pretty_type}_#{Sigh.config[:app_identifier]}"
252
307
 
253
308
  if Sigh.config[:platform].to_s == 'tvos'
254
309
  profile_name += "_tvos"
310
+ elsif Sigh.config[:platform].to_s == 'catalyst'
311
+ profile_name += "_catalyst"
255
312
  end
256
313
 
257
- if Sigh.config[:platform].to_s == 'macos'
314
+ if ['macos', 'catalyst'].include?(Sigh.config[:platform].to_s)
258
315
  profile_name += '.provisionprofile'
259
316
  else
260
317
  profile_name += '.mobileprovision'
@@ -263,7 +320,8 @@ module Sigh
263
320
  tmp_path = Dir.mktmpdir("profile_download")
264
321
  output_path = File.join(tmp_path, profile_name)
265
322
  File.open(output_path, "wb") do |f|
266
- f.write(profile.download)
323
+ content = Base64.decode64(profile.profile_content)
324
+ f.write(content)
267
325
  end
268
326
 
269
327
  UI.success("Successfully downloaded provisioning profile...")
@@ -272,7 +330,10 @@ module Sigh
272
330
 
273
331
  # Makes sure the current App ID exists. If not, it will show an appropriate error message
274
332
  def ensure_app_exists!
275
- return if Spaceship::App.find(Sigh.config[:app_identifier], mac: Sigh.config[:platform].to_s == 'macos')
333
+ # Only ensuring by app identifier
334
+ # We used to ensure by platform (IOS and MAC_OS) but now apps are
335
+ # always UNIVERSAL as of 2020-07-30
336
+ return if Spaceship::ConnectAPI::BundleId.find(Sigh.config[:app_identifier])
276
337
  print_produce_command(Sigh.config)
277
338
  UI.user_error!("Could not find App with App Identifier '#{Sigh.config[:app_identifier]}'")
278
339
  end