fastlane 2.160.0 → 2.165.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +78 -78
  3. data/cert/lib/cert/options.rb +28 -1
  4. data/cert/lib/cert/runner.rb +51 -34
  5. data/deliver/lib/deliver/app_screenshot_iterator.rb +4 -4
  6. data/deliver/lib/deliver/module.rb +2 -0
  7. data/deliver/lib/deliver/options.rb +5 -5
  8. data/deliver/lib/deliver/queue_worker.rb +14 -29
  9. data/deliver/lib/deliver/upload_metadata.rb +20 -5
  10. data/deliver/lib/deliver/upload_screenshots.rb +28 -13
  11. data/fastlane/lib/fastlane/actions/.download_dsyms.rb.swp +0 -0
  12. data/fastlane/lib/fastlane/actions/actions_helper.rb +20 -1
  13. data/fastlane/lib/fastlane/actions/app_store_build_number.rb +39 -3
  14. data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +15 -1
  15. data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +1 -0
  16. data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +2 -2
  17. data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +2 -2
  18. data/fastlane/lib/fastlane/actions/docs/create_app_online.md +1 -1
  19. data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +2 -2
  20. data/fastlane/lib/fastlane/actions/docs/run_tests.md +2 -2
  21. data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +12 -3
  22. data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -2
  23. data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +1 -1
  24. data/fastlane/lib/fastlane/actions/download_dsyms.rb +1 -0
  25. data/fastlane/lib/fastlane/actions/ensure_git_status_clean.rb +13 -2
  26. data/fastlane/lib/fastlane/actions/get_certificates.rb +1 -0
  27. data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +1 -0
  28. data/fastlane/lib/fastlane/actions/import_from_git.rb +9 -1
  29. data/fastlane/lib/fastlane/actions/is_ci.rb +1 -1
  30. data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +15 -0
  31. data/fastlane/lib/fastlane/actions/register_device.rb +46 -5
  32. data/fastlane/lib/fastlane/actions/register_devices.rb +50 -16
  33. data/fastlane/lib/fastlane/actions/set_changelog.rb +31 -3
  34. data/fastlane/lib/fastlane/actions/sync_code_signing.rb +1 -0
  35. data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +3 -2
  36. data/fastlane/lib/fastlane/fast_file.rb +74 -23
  37. data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -0
  38. data/fastlane/lib/fastlane/version.rb +1 -1
  39. data/fastlane/swift/Deliverfile.swift +1 -1
  40. data/fastlane/swift/DeliverfileProtocol.swift +4 -4
  41. data/fastlane/swift/Fastlane.swift +120 -27
  42. data/fastlane/swift/Gymfile.swift +1 -1
  43. data/fastlane/swift/GymfileProtocol.swift +1 -1
  44. data/fastlane/swift/LaneFileProtocol.swift +28 -36
  45. data/fastlane/swift/MainProcess.swift +1 -1
  46. data/fastlane/swift/Matchfile.swift +1 -1
  47. data/fastlane/swift/MatchfileProtocol.swift +21 -5
  48. data/fastlane/swift/Precheckfile.swift +1 -1
  49. data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
  50. data/fastlane/swift/Scanfile.swift +1 -1
  51. data/fastlane/swift/ScanfileProtocol.swift +1 -1
  52. data/fastlane/swift/Screengrabfile.swift +1 -1
  53. data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
  54. data/fastlane/swift/Snapshotfile.swift +1 -1
  55. data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
  56. data/fastlane/swift/main.swift +1 -1
  57. data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +6 -7
  58. data/fastlane_core/lib/fastlane_core/device_manager.rb +8 -4
  59. data/fastlane_core/lib/fastlane_core/helper.rb +1 -1
  60. data/fastlane_core/lib/fastlane_core/keychain_importer.rb +3 -3
  61. data/gym/lib/gym/generators/package_command_generator_xcode7.rb +2 -2
  62. data/match/lib/match/generator.rb +6 -1
  63. data/match/lib/match/importer.rb +63 -18
  64. data/match/lib/match/migrate.rb +13 -2
  65. data/match/lib/match/nuke.rb +65 -22
  66. data/match/lib/match/options.rb +34 -3
  67. data/match/lib/match/runner.rb +38 -10
  68. data/match/lib/match/spaceship_ensure.rb +27 -21
  69. data/match/lib/match/storage/google_cloud_storage.rb +20 -3
  70. data/match/lib/match/storage/s3_storage.rb +19 -3
  71. data/scan/lib/scan/detect_values.rb +5 -8
  72. data/scan/lib/scan/runner.rb +2 -1
  73. data/sigh/lib/assets/resign.sh +1 -1
  74. data/sigh/lib/sigh/download_all.rb +16 -4
  75. data/sigh/lib/sigh/options.rb +21 -0
  76. data/sigh/lib/sigh/runner.rb +83 -41
  77. data/snapshot/lib/assets/SnapshotHelper.swift +4 -0
  78. data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +2 -1
  79. data/spaceship/README.md +1 -1
  80. data/spaceship/lib/spaceship/client.rb +9 -4
  81. data/spaceship/lib/spaceship/connect_api.rb +27 -0
  82. data/spaceship/lib/spaceship/connect_api/api_client.rb +12 -3
  83. data/spaceship/lib/spaceship/connect_api/client.rb +20 -7
  84. data/spaceship/lib/spaceship/connect_api/models/app.rb +51 -0
  85. data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +3 -1
  86. data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +2 -1
  87. data/spaceship/lib/spaceship/connect_api/models/certificate.rb +42 -0
  88. data/spaceship/lib/spaceship/connect_api/models/custom_app_organization.rb +43 -0
  89. data/spaceship/lib/spaceship/connect_api/models/custom_app_user.rb +41 -0
  90. data/spaceship/lib/spaceship/connect_api/models/device.rb +5 -0
  91. data/spaceship/lib/spaceship/connect_api/models/profile.rb +7 -1
  92. data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +59 -0
  93. data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +45 -2
  94. data/spaceship/lib/spaceship/connect_api/spaceship.rb +7 -4
  95. data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
  96. data/spaceship/lib/spaceship/connect_api/token.rb +6 -1
  97. data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +75 -1
  98. data/spaceship/lib/spaceship/connect_api/users/users.rb +40 -0
  99. data/spaceship/lib/spaceship/helper/net_http_generic_request.rb +11 -5
  100. data/supply/lib/supply/uploader.rb +1 -1
  101. metadata +19 -15
@@ -42,9 +42,14 @@ module Match
42
42
  # while on Git we recommend using the git branch instead. As there is
43
43
  # no concept of branches in Google Cloud Storage (omg thanks), we use
44
44
  # the team id properly
45
- spaceship = SpaceshipEnsure.new(params[:username], params[:team_id], params[:team_name])
45
+ spaceship = SpaceshipEnsure.new(params[:username], params[:team_id], params[:team_name], api_token(params))
46
46
  team_id = spaceship.team_id
47
- UI.message("Detected team ID '#{team_id}' to use for Google Cloud Storage...")
47
+
48
+ if team_id.to_s.empty?
49
+ UI.user_error!("The `team_id` option is required. fastlane cannot automatically determine portal team id via the App Store Connect API (yet)")
50
+ else
51
+ UI.message("Detected team ID '#{team_id}' to use for Google Cloud Storage...")
52
+ end
48
53
 
49
54
  files_to_commit = []
50
55
  Dir.chdir(git_storage.working_directory) do
@@ -85,6 +90,12 @@ module Match
85
90
  UI.input("Please make sure to read the above and confirm with enter")
86
91
  end
87
92
 
93
+ def api_token(params)
94
+ @api_token ||= Spaceship::ConnectAPI::Token.create(params[:api_key]) if params[:api_key]
95
+ @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(params[:api_key_path]) if params[:api_key_path]
96
+ return @api_token
97
+ end
98
+
88
99
  def ensure_parameters_are_valid(params)
89
100
  if params[:readonly]
90
101
  UI.user_error!("`fastlane match migrate` doesn't work in `readonly` mode")
@@ -44,7 +44,7 @@ module Match
44
44
  s3_access_key: params[:s3_access_key].to_s,
45
45
  s3_secret_access_key: params[:s3_secret_access_key].to_s,
46
46
  s3_bucket: params[:s3_bucket].to_s,
47
- team_id: params[:team_id] || Spaceship.client.team_id
47
+ team_id: params[:team_id] || Spaceship::ConnectAPI.client.portal_team_id
48
48
  })
49
49
  self.storage.download
50
50
 
@@ -97,10 +97,14 @@ module Match
97
97
  end
98
98
 
99
99
  def spaceship_login
100
- Spaceship.login(params[:username])
101
- Spaceship.select_team(team_id: params[:team_id], team_name: params[:team_name])
100
+ if api_token
101
+ UI.message("Creating authorization token for App Store Connect API")
102
+ Spaceship::ConnectAPI.token = api_token
103
+ else
104
+ Spaceship::ConnectAPI.login(params[:username], use_portal: true, use_tunes: false, portal_team_id: params[:team_id], team_name: params[:team_name])
105
+ end
102
106
 
103
- if Spaceship.client.in_house? && (type == "distribution" || type == "enterprise")
107
+ if Spaceship::ConnectAPI.client.in_house? && (type == "distribution" || type == "enterprise")
104
108
  UI.error("---")
105
109
  UI.error("⚠️ Warning: This seems to be an Enterprise account!")
106
110
  UI.error("By nuking your account's distribution, all your apps deployed via ad-hoc will stop working!") if type == "distribution"
@@ -111,6 +115,12 @@ module Match
111
115
  end
112
116
  end
113
117
 
118
+ def api_token
119
+ @api_token ||= Spaceship::ConnectAPI::Token.create(params[:api_key]) if params[:api_key]
120
+ @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(params[:api_key_path]) if params[:api_key_path]
121
+ return @api_token
122
+ end
123
+
114
124
  # Collect all the certs/profiles
115
125
  def prepare_list
116
126
  UI.message("Fetching certificates and profiles...")
@@ -125,8 +135,10 @@ module Match
125
135
  # Get all iOS and macOS profile
126
136
  self.profiles = []
127
137
  prov_types.each do |prov_type|
128
- self.profiles += profile_type(prov_type).all(mac: false)
129
- self.profiles += profile_type(prov_type).all(mac: true)
138
+ types = profile_types(prov_type)
139
+ # Filtering on 'profileType' seems to be undocumented as of 2020-07-30
140
+ # but works on both web session and official API
141
+ self.profiles += Spaceship::ConnectAPI::Profile.all(filter: { profileType: types.join(",") })
130
142
  end
131
143
 
132
144
  # Gets the main and additional cert types
@@ -138,7 +150,7 @@ module Match
138
150
  self.certs = []
139
151
  self.certs += cert_types.map do |ct|
140
152
  certificate_type(ct).flat_map do |cert|
141
- cert.all(mac: false) + cert.all(mac: true)
153
+ Spaceship::ConnectAPI::Certificate.all(filter: { certificateType: cert })
142
154
  end
143
155
  end.flatten
144
156
 
@@ -165,7 +177,7 @@ module Match
165
177
  puts("")
166
178
  if self.certs.count > 0
167
179
  rows = self.certs.collect do |cert|
168
- cert_expiration = cert.expires.nil? ? "Unknown" : cert.expires.strftime("%Y-%m-%d")
180
+ cert_expiration = cert.expiration_date.nil? ? "Unknown" : Time.parse(cert.expiration_date).strftime("%Y-%m-%d")
169
181
  [cert.name, cert.id, cert.class.to_s.split("::").last, cert_expiration]
170
182
  end
171
183
  puts(Terminal::Table.new({
@@ -178,11 +190,11 @@ module Match
178
190
 
179
191
  if self.profiles.count > 0
180
192
  rows = self.profiles.collect do |p|
181
- status = p.status == 'Active' ? p.status.green : p.status.red
193
+ status = p.valid? ? p.profile_state.green : p.profile_state.red
182
194
 
183
195
  # Expires is sometimes nil
184
- expires = p.expires ? p.expires.strftime("%Y-%m-%d") : nil
185
- [p.name, p.id, status, p.type, expires]
196
+ expires = p.expiration_date ? Time.parse(p.expiration_date).strftime("%Y-%m-%d") : nil
197
+ [p.name, p.id, status, p.profile_type, expires]
186
198
  end
187
199
  puts(Terminal::Table.new({
188
200
  title: "Provisioning Profiles that are going to be revoked".green,
@@ -227,7 +239,7 @@ module Match
227
239
  self.certs.each do |cert|
228
240
  UI.message("Revoking certificate '#{cert.name}' (#{cert.id})...")
229
241
  begin
230
- cert.revoke!
242
+ cert.delete!
231
243
  rescue => ex
232
244
  UI.message(ex.to_s)
233
245
  end
@@ -274,31 +286,62 @@ module Match
274
286
  def certificate_type(type)
275
287
  case type.to_sym
276
288
  when :mac_installer_distribution
277
- return [Spaceship.certificate.mac_installer_distribution]
289
+ return [
290
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_INSTALLER_DISTRIBUTION
291
+ ]
278
292
  when :distribution
279
- return [Spaceship.certificate.production, Spaceship.certificate.apple_distribution]
293
+ return [
294
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_APP_DISTRIBUTION,
295
+ Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DISTRIBUTION,
296
+ Spaceship::ConnectAPI::Certificate::CertificateType::DISTRIBUTION
297
+ ]
280
298
  when :development
281
- return [Spaceship.certificate.development, Spaceship.certificate.apple_development]
299
+ return [
300
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_APP_DEVELOPMENT,
301
+ Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DEVELOPMENT,
302
+ Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPMENT
303
+ ]
282
304
  when :enterprise
283
- return [Spaceship.certificate.in_house]
305
+ return [
306
+ Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DISTRIBUTION
307
+ ]
284
308
  else
285
309
  raise "Unknown type '#{type}'"
286
310
  end
287
311
  end
288
312
 
289
313
  # The kind of provisioning profile we're interested in
290
- def profile_type(prov_type)
314
+ def profile_types(prov_type)
291
315
  case prov_type.to_sym
292
316
  when :appstore
293
- return Spaceship.provisioning_profile.app_store
317
+ return [
318
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_STORE,
319
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE,
320
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE,
321
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE
322
+ ]
294
323
  when :development
295
- return Spaceship.provisioning_profile.development
324
+ return [
325
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT,
326
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT,
327
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT,
328
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT
329
+ ]
296
330
  when :enterprise
297
- return Spaceship.provisioning_profile.in_house
331
+ return [
332
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE,
333
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE
334
+ ]
298
335
  when :adhoc
299
- return Spaceship.provisioning_profile.ad_hoc
336
+ return [
337
+ Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC,
338
+ Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC
339
+ ]
300
340
  when :developer_id
301
- return Spaceship.provisioning_profile.direct
341
+ return [
342
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT,
343
+ Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
344
+ ]
302
345
  else
303
346
  raise "Unknown provisioning type '#{prov_type}'"
304
347
  end
@@ -62,20 +62,39 @@ module Match
62
62
  type: Boolean,
63
63
  default_value: false),
64
64
 
65
- # app
66
65
  FastlaneCore::ConfigItem.new(key: :app_identifier,
67
66
  short_option: "-a",
68
67
  env_name: "MATCH_APP_IDENTIFIER",
69
- description: "The bundle identifier(s) of your app (comma-separated)",
68
+ description: "The bundle identifier(s) of your app (comma-separated string or array of strings)",
70
69
  type: Array, # we actually allow String and Array here
71
70
  skip_type_validation: true,
72
71
  code_gen_sensitive: true,
73
72
  default_value: CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier),
74
73
  default_value_dynamic: true),
74
+
75
+ # App Store Connect API
76
+ FastlaneCore::ConfigItem.new(key: :api_key_path,
77
+ env_name: "SIGH_API_KEY_PATH",
78
+ 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)",
79
+ optional: true,
80
+ conflicting_options: [:api_key],
81
+ verify_block: proc do |value|
82
+ UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
83
+ end),
84
+ FastlaneCore::ConfigItem.new(key: :api_key,
85
+ env_name: "SIGH_API_KEY",
86
+ description: "Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#use-return-value-and-pass-in-as-an-option)",
87
+ type: Hash,
88
+ optional: true,
89
+ sensitive: true,
90
+ conflicting_options: [:api_key_path]),
91
+
92
+ # Apple ID
75
93
  FastlaneCore::ConfigItem.new(key: :username,
76
94
  short_option: "-u",
77
95
  env_name: "MATCH_USERNAME",
78
96
  description: "Your Apple ID Username",
97
+ optional: true,
79
98
  default_value: user,
80
99
  default_value_dynamic: true),
81
100
  FastlaneCore::ConfigItem.new(key: :team_id,
@@ -147,7 +166,7 @@ module Match
147
166
  FastlaneCore::ConfigItem.new(key: :git_bearer_authorization,
148
167
  env_name: "MATCH_GIT_BEARER_AUTHORIZATION",
149
168
  sensitive: true,
150
- description: "Use a bearer authorization header to access the git repo (e.g.: access to an Azure Devops repository), usually a string in Base64",
169
+ description: "Use a bearer authorization header to access the git repo (e.g.: access to an Azure DevOps repository), usually a string in Base64",
151
170
  conflicting_options: [:git_basic_authorization, :git_private_key],
152
171
  optional: true,
153
172
  default_value: nil),
@@ -264,10 +283,22 @@ module Match
264
283
  optional: true,
265
284
  type: Boolean,
266
285
  default_value: false),
286
+ FastlaneCore::ConfigItem.new(key: :skip_certificate_matching,
287
+ env_name: "MATCH_SKIP_CERTIFICATE_MATCHING",
288
+ description: "Set to true if there is no access to Apple developer portal but there are certificates, keys and profiles provided. Only works with match import action",
289
+ optional: true,
290
+ type: Boolean,
291
+ default_value: false),
267
292
  FastlaneCore::ConfigItem.new(key: :output_path,
268
293
  env_name: "MATCH_OUTPUT_PATH",
269
294
  description: "Path in which to export certificates, key and profile",
270
295
  optional: true),
296
+ FastlaneCore::ConfigItem.new(key: :skip_set_partition_list,
297
+ short_option: "-P",
298
+ env_name: "MATCH_SKIP_SET_PARTITION_LIST",
299
+ description: "Skips setting the partition list (which can sometimes take a long time). Setting the partition list is usually needed to prevent Xcode from prompting to allow a cert to be used for signing",
300
+ type: Boolean,
301
+ default_value: false),
271
302
 
272
303
  # other
273
304
  FastlaneCore::ConfigItem.new(key: :verbose,
@@ -55,7 +55,9 @@ module Match
55
55
  readonly: params[:readonly],
56
56
  username: params[:readonly] ? nil : params[:username], # only pass username if not readonly
57
57
  team_id: params[:team_id],
58
- team_name: params[:team_name]
58
+ team_name: params[:team_name],
59
+ api_key_path: params[:api_key_path],
60
+ api_key: params[:api_key]
59
61
  })
60
62
  storage.download
61
63
 
@@ -67,7 +69,7 @@ module Match
67
69
  encryption.decrypt_files if encryption
68
70
 
69
71
  unless params[:readonly]
70
- self.spaceship = SpaceshipEnsure.new(params[:username], params[:team_id], params[:team_name])
72
+ self.spaceship = SpaceshipEnsure.new(params[:username], params[:team_id], params[:team_name], api_token(params))
71
73
  if params[:type] == "enterprise" && !Spaceship.client.in_house?
72
74
  UI.user_error!("You defined the profile type 'enterprise', but your Apple account doesn't support In-House profiles")
73
75
  end
@@ -100,7 +102,7 @@ module Match
100
102
  end
101
103
 
102
104
  cert_ids << cert_id
103
- spaceship.certificates_exists(username: params[:username], certificate_ids: cert_ids, platform: params[:platform]) if spaceship
105
+ spaceship.certificates_exists(username: params[:username], certificate_ids: cert_ids) if spaceship
104
106
 
105
107
  # Provisioning Profiles
106
108
  unless params[:skip_provisioning_profiles]
@@ -136,6 +138,12 @@ module Match
136
138
  end
137
139
  # rubocop:enable Metrics/PerceivedComplexity
138
140
 
141
+ def api_token(params)
142
+ @api_token ||= Spaceship::ConnectAPI::Token.create(params[:api_key]) if params[:api_key]
143
+ @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(params[:api_key_path]) if params[:api_key_path]
144
+ return @api_token
145
+ end
146
+
139
147
  # Used when creating a new certificate or profile
140
148
  def prefixed_working_directory
141
149
  return self.storage.prefixed_working_directory
@@ -316,23 +324,43 @@ module Match
316
324
 
317
325
  parsed = FastlaneCore::ProvisioningProfile.parse(profile, keychain_path)
318
326
  uuid = parsed["UUID"]
319
- portal_profile = Spaceship.provisioning_profile.all.detect { |i| i.uuid == uuid }
327
+
328
+ all_profiles = Spaceship::ConnectAPI::Profile.all(includes: "devices")
329
+ portal_profile = all_profiles.detect { |i| i.uuid == uuid }
320
330
 
321
331
  if portal_profile
322
- profile_device_count = portal_profile.devices.count
332
+ profile_device_count = portal_profile.fetch_all_devices.count
323
333
 
324
- portal_device_count =
334
+ device_classes =
325
335
  case platform
326
336
  when :ios
327
- Spaceship.device.all_ios_profile_devices.count
337
+ [
338
+ Spaceship::ConnectAPI::Device::DeviceClass::IPAD,
339
+ Spaceship::ConnectAPI::Device::DeviceClass::IPHONE,
340
+ Spaceship::ConnectAPI::Device::DeviceClass::IPOD,
341
+ Spaceship::ConnectAPI::Device::DeviceClass::APPLE_WATCH
342
+ ]
328
343
  when :tvos
329
- Spaceship.device.all_apple_tvs.count
344
+ [
345
+ Spaceship::ConnectAPI::Device::DeviceClass::APPLE_TV
346
+ ]
330
347
  when :mac, :catalyst
331
- Spaceship.device.all_macs.count
348
+ [
349
+ Spaceship::ConnectAPI::Device::DeviceClass::MAC
350
+ ]
332
351
  else
333
- Spaceship.device.all.count
352
+ []
334
353
  end
335
354
 
355
+ devices = Spaceship::ConnectAPI::Device.all
356
+ unless device_classes.empty?
357
+ devices = devices.select do |device|
358
+ device_classes.include?(device.device_class)
359
+ end
360
+ end
361
+
362
+ portal_device_count = devices.size
363
+
336
364
  return portal_device_count != profile_device_count
337
365
  end
338
366
  return false
@@ -4,28 +4,38 @@ require_relative 'module'
4
4
  module Match
5
5
  # Ensures the certificate and profiles are also available on App Store Connect
6
6
  class SpaceshipEnsure
7
- def initialize(user, team_id, team_name)
8
- # We'll try to manually fetch the password
9
- # to tell the user that a password is optional
10
- require 'credentials_manager/account_manager'
7
+ attr_accessor :team_id
11
8
 
12
- keychain_entry = CredentialsManager::AccountManager.new(user: user)
9
+ def initialize(user, team_id, team_name, api_token)
10
+ UI.message("Verifying that the certificate and profile are still valid on the Dev Portal...")
13
11
 
14
- if keychain_entry.password(ask_if_missing: false).to_s.length == 0
15
- UI.important("You can also run `fastlane match` in readonly mode to not require any access to the")
16
- UI.important("Developer Portal. This way you only share the keys and credentials")
17
- UI.command("fastlane match --readonly")
18
- UI.important("More information https://docs.fastlane.tools/actions/match/#access-control")
19
- end
12
+ if api_token
13
+ UI.message("Creating authorization token for App Store Connect API")
14
+ Spaceship::ConnectAPI.token = api_token
15
+ self.team_id = team_id
16
+ else
17
+ # We'll try to manually fetch the password
18
+ # to tell the user that a password is optional
19
+ require 'credentials_manager/account_manager'
20
20
 
21
- # Prompts select team if multiple teams and none specified
22
- UI.message("Verifying that the certificate and profile are still valid on the Dev Portal...")
23
- Spaceship::ConnectAPI.login(user, use_portal: true, use_tunes: false, portal_team_id: team_id, team_name: team_name)
21
+ keychain_entry = CredentialsManager::AccountManager.new(user: user)
22
+
23
+ if keychain_entry.password(ask_if_missing: false).to_s.length == 0
24
+ UI.important("You can also run `fastlane match` in readonly mode to not require any access to the")
25
+ UI.important("Developer Portal. This way you only share the keys and credentials")
26
+ UI.command("fastlane match --readonly")
27
+ UI.important("More information https://docs.fastlane.tools/actions/match/#access-control")
28
+ end
29
+
30
+ # Prompts select team if multiple teams and none specified
31
+ Spaceship::ConnectAPI.login(user, use_portal: true, use_tunes: false, portal_team_id: team_id, team_name: team_name)
32
+ self.team_id = Spaceship::ConnectAPI.client.portal_team_id
33
+ end
24
34
  end
25
35
 
26
36
  # The team ID of the currently logged in team
27
37
  def team_id
28
- return Spaceship::ConnectAPI.client.portal_team_id
38
+ return @team_id
29
39
  end
30
40
 
31
41
  def bundle_identifier_exists(username: nil, app_identifier: nil, platform: nil)
@@ -45,12 +55,8 @@ module Match
45
55
  UI.user_error!("Couldn't find bundle identifier '#{app_identifier}' for the user '#{username}'")
46
56
  end
47
57
 
48
- def certificates_exists(username: nil, certificate_ids: [], platform: nil)
49
- if platform == :catalyst.to_s
50
- platform = :macos.to_s
51
- end
52
-
53
- Spaceship.certificate.all(mac: platform == "macos").each do |cert|
58
+ def certificates_exists(username: nil, certificate_ids: [])
59
+ Spaceship::ConnectAPI::Certificate.all.each do |cert|
54
60
  certificate_ids.delete(cert.id)
55
61
  end
56
62
  return if certificate_ids.empty?
@@ -23,6 +23,8 @@ module Match
23
23
  attr_reader :username
24
24
  attr_reader :team_id
25
25
  attr_reader :team_name
26
+ attr_reader :api_key_path
27
+ attr_reader :api_key
26
28
 
27
29
  # Managed values
28
30
  attr_accessor :gc_storage
@@ -44,7 +46,9 @@ module Match
44
46
  readonly: params[:readonly],
45
47
  username: params[:username],
46
48
  team_id: params[:team_id],
47
- team_name: params[:team_name]
49
+ team_name: params[:team_name],
50
+ api_key_path: params[:api_key_path],
51
+ api_key: params[:api_key]
48
52
  )
49
53
  end
50
54
 
@@ -56,7 +60,9 @@ module Match
56
60
  readonly: nil,
57
61
  username: nil,
58
62
  team_id: nil,
59
- team_name: nil)
63
+ team_name: nil,
64
+ api_key_path: nil,
65
+ api_key: nil)
60
66
  @type = type if type
61
67
  @platform = platform if platform
62
68
  @google_cloud_project_id = google_cloud_project_id if google_cloud_project_id
@@ -67,6 +73,9 @@ module Match
67
73
  @team_id = team_id
68
74
  @team_name = team_name
69
75
 
76
+ @api_key_path = api_key_path
77
+ @api_key = api_key
78
+
70
79
  @google_cloud_keys_file = ensure_keys_file_exists(google_cloud_keys_file, google_cloud_project_id)
71
80
 
72
81
  if self.google_cloud_keys_file.to_s.length > 0
@@ -106,11 +115,19 @@ module Match
106
115
  # see `prefixed_working_directory` comments for more details
107
116
  return self.team_id
108
117
  else
109
- spaceship = SpaceshipEnsure.new(self.username, self.team_id, self.team_name)
118
+ UI.user_error!("The `team_id` option is required. fastlane cannot automatically determine portal team id via the App Store Connect API (yet)") if self.team_id.to_s.empty?
119
+
120
+ spaceship = SpaceshipEnsure.new(self.username, self.team_id, self.team_name, self.api_token)
110
121
  return spaceship.team_id
111
122
  end
112
123
  end
113
124
 
125
+ def api_token
126
+ api_token ||= Spaceship::ConnectAPI::Token.create(self.api_key) if self.api_key
127
+ api_token ||= Spaceship::ConnectAPI::Token.from_json_file(self.api_key_path) if self.api_key_path
128
+ return api_token
129
+ end
130
+
114
131
  def prefixed_working_directory
115
132
  # We fall back to "*", which means certificates and profiles
116
133
  # from all teams that use this bucket would be installed. This is not ideal, but