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
@@ -17,4 +17,4 @@ public class Gymfile: GymfileProtocol {
17
17
  // during the `init` process, and you won't see this message
18
18
  }
19
19
 
20
- // Generated with fastlane 2.160.0
20
+ // Generated with fastlane 2.165.0
@@ -184,4 +184,4 @@ public extension GymfileProtocol {
184
184
 
185
185
  // Please don't remove the lines below
186
186
  // They are used to detect outdated files
187
- // FastlaneRunnerAPIVersion [0.9.46]
187
+ // FastlaneRunnerAPIVersion [0.9.51]
@@ -12,31 +12,26 @@ import Foundation
12
12
 
13
13
  public protocol LaneFileProtocol: class {
14
14
  var fastlaneVersion: String { get }
15
- static func runLane(from fastfile: LaneFile?, named: String, parameters: [String: String]) -> Bool
15
+ static func runLane(from fastfile: LaneFile?, named lane: String, with parameters: [String: String]) -> Bool
16
16
 
17
17
  func recordLaneDescriptions()
18
- func beforeAll()
19
- func afterAll(currentLane: String)
18
+ func beforeAll(with lane: String)
19
+ func afterAll(with lane: String)
20
20
  func onError(currentLane: String, errorInfo: String)
21
21
  }
22
22
 
23
23
  public extension LaneFileProtocol {
24
- var fastlaneVersion: String { return "" } // default "" because that means any is fine
25
- func beforeAll() {} // no op by default
26
- func afterAll(currentLane _: String) {} // no op by default
27
- func onError(currentLane _: String, errorInfo _: String) {} // no op by default
28
- func recordLaneDescriptions() {} // no op by default
24
+ var fastlaneVersion: String { return "" } // Defaults to "" because that means any is fine
25
+ func beforeAll(with lane: String) {} // No-op by default
26
+ func afterAll(with lane: String) {} // No-op by default
27
+ func onError(currentLane _: String, errorInfo _: String) {} // No-op by default
28
+ func recordLaneDescriptions() {} // No-op by default
29
29
  }
30
30
 
31
31
  @objcMembers
32
32
  open class LaneFile: NSObject, LaneFileProtocol {
33
33
  private(set) static var fastfileInstance: LaneFile?
34
34
 
35
- // Called before any lane is executed.
36
- private func setupAllTheThings() {
37
- LaneFile.fastfileInstance!.beforeAll()
38
- }
39
-
40
35
  private static func trimLaneFromName(laneName: String) -> String {
41
36
  return String(laneName.prefix(laneName.count - 4))
42
37
  }
@@ -51,8 +46,8 @@ open class LaneFile: NSObject, LaneFileProtocol {
51
46
  #if !SWIFT_PACKAGE
52
47
  let methodList = class_copyMethodList(self, &methodCount)
53
48
  #else
54
- // In SPM we're calling this functions out of the scope of the normal binary that it
55
- // is being built, so self in this scope would be the SPM executable instead of the Fastfile
49
+ // In SPM we're calling this functions out of the scope of the normal binary that it's
50
+ // being built, so *self* in this scope would be the SPM executable instead of the Fastfile
56
51
  // that we'd normally expect.
57
52
  let methodList = class_copyMethodList(type(of: fastfileInstance!), &methodCount)
58
53
  #endif
@@ -95,32 +90,28 @@ open class LaneFile: NSObject, LaneFileProtocol {
95
90
  }
96
91
  }
97
92
 
98
- public static func runLane(from fastfile: LaneFile?, named: String, parameters: [String: String]) -> Bool {
99
- log(message: "Running lane: \(named)")
93
+ public static func runLane(from fastfile: LaneFile?, named lane: String, with parameters: [String: String]) -> Bool {
94
+ log(message: "Running lane: \(lane)")
100
95
  #if !SWIFT_PACKAGE
101
- // In SPM we do not load the Fastfile class from its `className()`, because we're in another
102
- // in the executable's scope that loads the library, so in that case `className()` won't be the
103
- // expected Fastfile and so, we do not dynamically load it as we do without SPM.
96
+ // When not in SPM environment, we load the Fastfile from its `className()`.
104
97
  loadFastfile()
105
- #endif
106
-
107
- #if !SWIFT_PACKAGE
108
- guard let fastfileInstance: LaneFile = self.fastfileInstance else {
98
+ guard let fastfileInstance = self.fastfileInstance as? Fastfile else {
109
99
  let message = "Unable to instantiate class named: \(className())"
110
100
  log(message: message)
111
101
  fatalError(message)
112
102
  }
113
103
  #else
114
- // We load the fastfile as a Lanefile in a static way, by parameter, because the Fastlane library
115
- // cannot know nothing about the caller (in this case, the executable).
116
- guard let fastfileInstance: LaneFile = fastfile else {
104
+ // When in SPM environment, we can't load the Fastfile from its `className()` because the executable is in
105
+ // another scope, so `className()` won't be the expected Fastfile. Instead, we load the Fastfile as a Lanefile
106
+ // in a static way, by parameter.
107
+ guard let fastfileInstance = fastfile else {
117
108
  log(message: "Found nil instance of fastfile")
118
109
  preconditionFailure()
119
110
  }
111
+ self.fastfileInstance = fastfileInstance
120
112
  #endif
121
- self.fastfileInstance = fastfile!
122
113
  let currentLanes = lanes
123
- let lowerCasedLaneRequested = named.lowercased()
114
+ let lowerCasedLaneRequested = lane.lowercased()
124
115
 
125
116
  guard let laneMethod = currentLanes[lowerCasedLaneRequested] else {
126
117
  let laneNames = laneFunctionNames.map { laneFuctionName in
@@ -131,7 +122,7 @@ open class LaneFile: NSObject, LaneFileProtocol {
131
122
  }
132
123
  }.joined(separator: ", ")
133
124
 
134
- let message = "[!] Could not find lane '\(named)'. Available lanes: \(laneNames)"
125
+ let message = "[!] Could not find lane '\(lane)'. Available lanes: \(laneNames)"
135
126
  log(message: message)
136
127
 
137
128
  let shutdownCommand = ControlCommand(commandType: .cancel(cancelReason: .clientError), message: message)
@@ -139,15 +130,16 @@ open class LaneFile: NSObject, LaneFileProtocol {
139
130
  return false
140
131
  }
141
132
 
142
- // call all methods that need to be called before we start calling lanes
143
- fastfileInstance.setupAllTheThings()
133
+ // Call all methods that need to be called before we start calling lanes.
134
+ fastfileInstance.beforeAll(with: lane)
144
135
 
145
- // We need to catch all possible errors here and display a nice message
136
+ // We need to catch all possible errors here and display a nice message.
146
137
  _ = fastfileInstance.perform(NSSelectorFromString(laneMethod), with: parameters)
147
138
 
148
- // only call on success
149
- fastfileInstance.afterAll(currentLane: named)
150
- log(message: "Done running lane: \(named) 🚀")
139
+ // Call only on success.
140
+ fastfileInstance.afterAll(with: lane)
141
+
142
+ log(message: "Done running lane: \(lane) 🚀")
151
143
  return true
152
144
  }
153
145
  }
@@ -28,7 +28,7 @@ class MainProcess {
28
28
  @objc func connectToFastlaneAndRunLane(_ fastfile: LaneFile?) {
29
29
  runner.startSocketThread(port: argumentProcessor.port)
30
30
 
31
- let completedRun = Fastfile.runLane(from: fastfile, named: argumentProcessor.currentLane, parameters: argumentProcessor.laneParameters())
31
+ let completedRun = Fastfile.runLane(from: fastfile, named: argumentProcessor.currentLane, with: argumentProcessor.laneParameters())
32
32
  if completedRun {
33
33
  runner.disconnectFromFastlaneProcess()
34
34
  }
@@ -17,4 +17,4 @@ public class Matchfile: MatchfileProtocol {
17
17
  // during the `init` process, and you won't see this message
18
18
  }
19
19
 
20
- // Generated with fastlane 2.160.0
20
+ // Generated with fastlane 2.165.0
@@ -17,11 +17,17 @@ public protocol MatchfileProtocol: class {
17
17
  /// Skip syncing provisioning profiles
18
18
  var skipProvisioningProfiles: Bool { get }
19
19
 
20
- /// The bundle identifier(s) of your app (comma-separated)
20
+ /// The bundle identifier(s) of your app (comma-separated string or array of strings)
21
21
  var appIdentifier: [String] { get }
22
22
 
23
+ /// Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)
24
+ var apiKeyPath: String? { get }
25
+
26
+ /// Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#use-return-value-and-pass-in-as-an-option)
27
+ var apiKey: [String: Any]? { get }
28
+
23
29
  /// Your Apple ID Username
24
- var username: String { get }
30
+ var username: String? { get }
25
31
 
26
32
  /// The ID of your Developer Portal team if you're in multiple teams
27
33
  var teamId: String? { get }
@@ -53,7 +59,7 @@ public protocol MatchfileProtocol: class {
53
59
  /// Use a basic authorization header to access the git repo (e.g.: access via HTTPS, GitHub Actions, etc), usually a string in Base64
54
60
  var gitBasicAuthorization: String? { get }
55
61
 
56
- /// Use a bearer authorization header to access the git repo (e.g.: access to an Azure Devops repository), usually a string in Base64
62
+ /// Use a bearer authorization header to access the git repo (e.g.: access to an Azure DevOps repository), usually a string in Base64
57
63
  var gitBearerAuthorization: String? { get }
58
64
 
59
65
  /// Use a private key to access the git repo (e.g.: access to GitHub repository via Deploy keys), usually a id_rsa named file or the contents hereof
@@ -116,9 +122,15 @@ public protocol MatchfileProtocol: class {
116
122
  /// Should the command fail if it was about to create a duplicate of an existing provisioning profile. It can happen due to issues on Apple Developer Portal, when profile to be recreated was not properly deleted first
117
123
  var failOnNameTaken: Bool { get }
118
124
 
125
+ /// 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
126
+ var skipCertificateMatching: Bool { get }
127
+
119
128
  /// Path in which to export certificates, key and profile
120
129
  var outputPath: String? { get }
121
130
 
131
+ /// 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
132
+ var skipSetPartitionList: Bool { get }
133
+
122
134
  /// Print out extra information and all commands
123
135
  var verbose: Bool { get }
124
136
  }
@@ -130,7 +142,9 @@ public extension MatchfileProtocol {
130
142
  var generateAppleCerts: Bool { return true }
131
143
  var skipProvisioningProfiles: Bool { return false }
132
144
  var appIdentifier: [String] { return [] }
133
- var username: String { return "" }
145
+ var apiKeyPath: String? { return nil }
146
+ var apiKey: [String: Any]? { return nil }
147
+ var username: String? { return nil }
134
148
  var teamId: String? { return nil }
135
149
  var teamName: String? { return nil }
136
150
  var storageMode: String { return "git" }
@@ -162,10 +176,12 @@ public extension MatchfileProtocol {
162
176
  var templateName: String? { return nil }
163
177
  var profileName: String? { return nil }
164
178
  var failOnNameTaken: Bool { return false }
179
+ var skipCertificateMatching: Bool { return false }
165
180
  var outputPath: String? { return nil }
181
+ var skipSetPartitionList: Bool { return false }
166
182
  var verbose: Bool { return false }
167
183
  }
168
184
 
169
185
  // Please don't remove the lines below
170
186
  // They are used to detect outdated files
171
- // FastlaneRunnerAPIVersion [0.9.40]
187
+ // FastlaneRunnerAPIVersion [0.9.45]
@@ -17,4 +17,4 @@ public class Precheckfile: PrecheckfileProtocol {
17
17
  // during the `init` process, and you won't see this message
18
18
  }
19
19
 
20
- // Generated with fastlane 2.160.0
20
+ // Generated with fastlane 2.165.0
@@ -48,4 +48,4 @@ public extension PrecheckfileProtocol {
48
48
 
49
49
  // Please don't remove the lines below
50
50
  // They are used to detect outdated files
51
- // FastlaneRunnerAPIVersion [0.9.39]
51
+ // FastlaneRunnerAPIVersion [0.9.44]
@@ -17,4 +17,4 @@ public class Scanfile: ScanfileProtocol {
17
17
  // during the `init` process, and you won't see this message
18
18
  }
19
19
 
20
- // Generated with fastlane 2.160.0
20
+ // Generated with fastlane 2.165.0
@@ -260,4 +260,4 @@ public extension ScanfileProtocol {
260
260
 
261
261
  // Please don't remove the lines below
262
262
  // They are used to detect outdated files
263
- // FastlaneRunnerAPIVersion [0.9.51]
263
+ // FastlaneRunnerAPIVersion [0.9.56]
@@ -17,4 +17,4 @@ public class Screengrabfile: ScreengrabfileProtocol {
17
17
  // during the `init` process, and you won't see this message
18
18
  }
19
19
 
20
- // Generated with fastlane 2.160.0
20
+ // Generated with fastlane 2.165.0
@@ -96,4 +96,4 @@ public extension ScreengrabfileProtocol {
96
96
 
97
97
  // Please don't remove the lines below
98
98
  // They are used to detect outdated files
99
- // FastlaneRunnerAPIVersion [0.9.41]
99
+ // FastlaneRunnerAPIVersion [0.9.46]
@@ -17,4 +17,4 @@ public class Snapshotfile: SnapshotfileProtocol {
17
17
  // during the `init` process, and you won't see this message
18
18
  }
19
19
 
20
- // Generated with fastlane 2.160.0
20
+ // Generated with fastlane 2.165.0
@@ -184,4 +184,4 @@ public extension SnapshotfileProtocol {
184
184
 
185
185
  // Please don't remove the lines below
186
186
  // They are used to detect outdated files
187
- // FastlaneRunnerAPIVersion [0.9.35]
187
+ // FastlaneRunnerAPIVersion [0.9.40]
@@ -20,7 +20,7 @@ class MainProcess {
20
20
  @objc func connectToFastlaneAndRunLane() {
21
21
  runner.startSocketThread(port: argumentProcessor.port)
22
22
 
23
- let completedRun = Fastfile.runLane(from: nil, named: argumentProcessor.currentLane, parameters: argumentProcessor.laneParameters())
23
+ let completedRun = Fastfile.runLane(from: nil, named: argumentProcessor.currentLane, with: argumentProcessor.laneParameters())
24
24
  if completedRun {
25
25
  runner.disconnectFromFastlaneProcess()
26
26
  }
@@ -19,7 +19,7 @@ module FastlaneCore
19
19
  end
20
20
 
21
21
  def action_launched(launch_context: nil)
22
- unless did_show_message?
22
+ if should_show_message?
23
23
  show_message
24
24
  end
25
25
 
@@ -52,16 +52,15 @@ module FastlaneCore
52
52
  UI.message("You can disable this by adding `opt_out_usage` at the top of your Fastfile")
53
53
  end
54
54
 
55
- def did_show_message?
56
- file_name = ".did_show_opt_info"
55
+ def should_show_message?
56
+ return false if FastlaneCore::Env.truthy?("FASTLANE_OPT_OUT_USAGE")
57
57
 
58
+ file_name = ".did_show_opt_info"
58
59
  new_path = File.join(FastlaneCore.fastlane_user_dir, file_name)
59
- did_show = File.exist?(new_path)
60
-
61
- return did_show if did_show
60
+ return false if File.exist?(new_path)
62
61
 
63
62
  File.write(new_path, '1')
64
- false
63
+ true
65
64
  end
66
65
 
67
66
  def finalize_session
@@ -301,7 +301,7 @@ module FastlaneCore
301
301
  Helper.backticks("open -a #{simulator_path} --args -CurrentDeviceUDID #{device.udid}", print: FastlaneCore::Globals.verbose?)
302
302
  end
303
303
 
304
- def copy_logs(device, log_identity, logs_destination_dir)
304
+ def copy_logs(device, log_identity, logs_destination_dir, log_collection_start_time)
305
305
  logs_destination_dir = File.expand_path(logs_destination_dir)
306
306
  os_version = FastlaneCore::CommandExecutor.execute(command: 'sw_vers -productVersion', print_all: false, print_command: false)
307
307
 
@@ -310,7 +310,7 @@ module FastlaneCore
310
310
 
311
311
  are_logarchives_supported = device_supports_logarchives && host_computer_supports_logarchives
312
312
  if are_logarchives_supported
313
- copy_logarchive(device, log_identity, logs_destination_dir)
313
+ copy_logarchive(device, log_identity, logs_destination_dir, log_collection_start_time)
314
314
  else
315
315
  copy_logfile(device, log_identity, logs_destination_dir)
316
316
  end
@@ -340,13 +340,17 @@ module FastlaneCore
340
340
  UI.success("Copying file '#{logfile_src}' to '#{logfile_dst}'...")
341
341
  end
342
342
 
343
- def copy_logarchive(device, log_identity, logs_destination_dir)
343
+ def copy_logarchive(device, log_identity, logs_destination_dir, log_collection_start_time)
344
344
  require 'shellwords'
345
345
 
346
346
  logarchive_dst = File.join(logs_destination_dir, "system_logs-#{log_identity}.logarchive")
347
347
  FileUtils.rm_rf(logarchive_dst)
348
348
  FileUtils.mkdir_p(File.expand_path("..", logarchive_dst))
349
- command = "xcrun simctl spawn #{device.udid} log collect --output #{logarchive_dst.shellescape} 2>/dev/null"
349
+
350
+ logs_collection_start = log_collection_start_time.strftime('%Y-%m-%d %H:%M:%S')
351
+ command = "xcrun simctl spawn #{device.udid} log collect "
352
+ command << "--start '#{logs_collection_start}' "
353
+ command << "--output #{logarchive_dst.shellescape} 2>/dev/null"
350
354
  FastlaneCore::CommandExecutor.execute(command: command, print_all: false, print_command: true)
351
355
  end
352
356
  end
@@ -66,7 +66,7 @@ module FastlaneCore
66
66
 
67
67
  # @return true if it is enabled to execute external commands
68
68
  def self.sh_enabled?
69
- !self.test?
69
+ !self.test? || ENV["FORCE_SH_DURING_TESTS"]
70
70
  end
71
71
 
72
72
  # @return [boolean] true if building in a known CI environment
@@ -4,7 +4,7 @@ require 'security'
4
4
 
5
5
  module FastlaneCore
6
6
  class KeychainImporter
7
- def self.import_file(path, keychain_path, keychain_password: nil, certificate_password: "", output: FastlaneCore::Globals.verbose?)
7
+ def self.import_file(path, keychain_path, keychain_password: nil, certificate_password: "", skip_set_partition_list: false, output: FastlaneCore::Globals.verbose?)
8
8
  UI.user_error!("Could not find file '#{path}'") unless File.exist?(path)
9
9
 
10
10
  command = "security import #{path.shellescape} -k '#{keychain_path.shellescape}'"
@@ -19,7 +19,7 @@ module FastlaneCore
19
19
  UI.command_output(stdout.read.to_s) if output
20
20
 
21
21
  # Set partition list only if success since it can be a time consuming process if a lot of keys are installed
22
- if thrd.value.success?
22
+ if thrd.value.success? && !skip_set_partition_list
23
23
  keychain_password ||= resolve_keychain_password(keychain_path)
24
24
  set_partition_list(path, keychain_path, keychain_password: keychain_password, output: output)
25
25
  else
@@ -39,7 +39,7 @@ module FastlaneCore
39
39
  # See https://openradar.appspot.com/28524119
40
40
  if Helper.backticks('security -h | grep set-key-partition-list', print: false).length > 0
41
41
  command = "security set-key-partition-list"
42
- command << " -S apple-tool:,apple:"
42
+ command << " -S apple-tool:,apple:,codesign:"
43
43
  command << " -s" # This is a needed in Catalina to prevent "security: SecKeychainItemCopyAccess: A missing value was detected."
44
44
  command << " -k #{keychain_password.to_s.shellescape}"
45
45
  command << " #{keychain_path.shellescape}"
@@ -66,14 +66,14 @@ module Gym
66
66
  if path
67
67
  # Try to find IPA file in the output directory, used when app thinning was not set
68
68
  Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
69
- FileUtils.mv(path, Gym.cache[:ipa_path]) unless File.expand_path(path).casecmp(File.expand_path(Gym.cache[:ipa_path]).downcase).zero?
69
+ FileUtils.mv(path, Gym.cache[:ipa_path]) unless File.expand_path(path).casecmp?(File.expand_path(Gym.cache[:ipa_path]).downcase)
70
70
  elsif Dir.exist?(apps_path)
71
71
  # Try to find "generic" IPA file inside "Apps" folder, used when app thinning was set
72
72
  files = Dir[File.join(apps_path, "*.ipa")]
73
73
  # Generic IPA file doesn't have suffix so its name is the shortest
74
74
  path = files.min_by(&:length)
75
75
  Gym.cache[:ipa_path] = File.join(temporary_output_path, "#{Gym.config[:output_name]}.ipa")
76
- FileUtils.cp(path, Gym.cache[:ipa_path]) unless File.expand_path(path).casecmp(File.expand_path(Gym.cache[:ipa_path]).downcase).zero?
76
+ FileUtils.cp(path, Gym.cache[:ipa_path]) unless File.expand_path(path).casecmp?(File.expand_path(Gym.cache[:ipa_path]).downcase)
77
77
  else
78
78
  ErrorHandler.handle_empty_archive unless path
79
79
  end
@@ -26,11 +26,14 @@ module Match
26
26
  generate_apple_certs: params[:generate_apple_certs],
27
27
  output_path: output_path,
28
28
  force: true, # we don't need a certificate without its private key, we only care about a new certificate
29
+ api_key_path: params[:api_key_path],
30
+ api_key: params[:api_key],
29
31
  username: params[:username],
30
32
  team_id: params[:team_id],
31
33
  team_name: params[:team_name],
32
34
  keychain_path: FastlaneCore::Helper.keychain_path(params[:keychain_name]),
33
- keychain_password: params[:keychain_password]
35
+ keychain_password: params[:keychain_password],
36
+ skip_set_partition_list: params[:skip_set_partition_list]
34
37
  })
35
38
 
36
39
  Cert.config = arguments
@@ -80,6 +83,8 @@ module Match
80
83
  cert_id: certificate_id,
81
84
  provisioning_name: profile_name,
82
85
  ignore_profiles_with_different_name: true,
86
+ api_key_path: params[:api_key_path],
87
+ api_key: params[:api_key],
83
88
  team_id: params[:team_id],
84
89
  team_name: params[:team_name],
85
90
  template_name: params[:template_name],
@@ -28,10 +28,17 @@ module Match
28
28
  google_cloud_bucket_name: params[:google_cloud_bucket_name].to_s,
29
29
  google_cloud_keys_file: params[:google_cloud_keys_file].to_s,
30
30
  google_cloud_project_id: params[:google_cloud_project_id].to_s,
31
+ s3_bucket: params[:s3_bucket],
32
+ s3_region: params[:s3_region],
33
+ s3_access_key: params[:s3_access_key],
34
+ s3_secret_access_key: params[:s3_secret_access_key],
35
+ s3_object_prefix: params[:s3_object_prefix],
31
36
  readonly: params[:readonly],
32
37
  username: params[:username],
33
38
  team_id: params[:team_id],
34
- team_name: params[:team_name]
39
+ team_name: params[:team_name],
40
+ api_key_path: params[:api_key_path],
41
+ api_key: params[:api_key]
35
42
  })
36
43
  storage.download
37
44
 
@@ -48,11 +55,25 @@ module Match
48
55
 
49
56
  case cert_type
50
57
  when :development
51
- certificate_type = Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DEVELOPMENT + "," + Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPMENT
58
+ certificate_type = [
59
+ Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DEVELOPMENT,
60
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_APP_DEVELOPMENT,
61
+ Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPMENT
62
+ ].join(',')
52
63
  when :distribution, :enterprise
53
- certificate_type = Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DISTRIBUTION + "," + Spaceship::ConnectAPI::Certificate::CertificateType::DISTRIBUTION
64
+ certificate_type = [
65
+ Spaceship::ConnectAPI::Certificate::CertificateType::IOS_DISTRIBUTION,
66
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_APP_DISTRIBUTION,
67
+ Spaceship::ConnectAPI::Certificate::CertificateType::DISTRIBUTION
68
+ ].join(',')
54
69
  when :developer_id_application
55
- certificate_type = Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPER_ID_APPLICATION
70
+ certificate_type = [
71
+ Spaceship::ConnectAPI::Certificate::CertificateType::DEVELOPER_ID_APPLICATION
72
+ ].join(',')
73
+ when :mac_installer_distribution
74
+ certificate_type = [
75
+ Spaceship::ConnectAPI::Certificate::CertificateType::MAC_INSTALLER_DISTRIBUTION
76
+ ].join(',')
56
77
  else
57
78
  UI.user_error!("Cert type '#{cert_type}' is not supported")
58
79
  end
@@ -61,23 +82,41 @@ module Match
61
82
  output_dir_certs = File.join(storage.prefixed_working_directory, "certs", cert_type.to_s)
62
83
  output_dir_profiles = File.join(storage.prefixed_working_directory, "profiles", prov_type.to_s)
63
84
 
64
- # Need to get the cert id by comparing base64 encoded cert content with certificate content from the API responses
65
- Spaceship::Portal.login(params[:username])
66
- Spaceship::Portal.select_team(team_id: params[:team_id], team_name: params[:team_name])
67
- certs = Spaceship::ConnectAPI::Certificate.all(filter: { certificateType: certificate_type })
85
+ should_skip_certificate_matching = params[:skip_certificate_matching]
86
+ # In case there is no access to Apple Developer portal but we have the certificates, keys and profiles
87
+ if should_skip_certificate_matching
88
+ cert_name = File.basename(cert_path, ".*")
89
+ p12_name = File.basename(p12_path, ".*")
68
90
 
69
- # Base64 encode contents to find match from API to find a cert ID
70
- cert_contents_base_64 = Base64.strict_encode64(File.binread(cert_path))
71
- matching_cert = certs.find do |cert|
72
- cert.certificate_content == cert_contents_base_64
73
- end
91
+ # Make dir if doesn't exist
92
+ FileUtils.mkdir_p(output_dir_certs)
93
+ dest_cert_path = File.join(output_dir_certs, "#{cert_name}.cer")
94
+ dest_p12_path = File.join(output_dir_certs, "#{p12_name}.p12")
95
+ else
96
+ # Need to get the cert id by comparing base64 encoded cert content with certificate content from the API responses
97
+ token = api_token(params)
98
+ if token
99
+ UI.message("Creating authorization token for App Store Connect API")
100
+ Spaceship::ConnectAPI.token = token
101
+ else
102
+ UI.message("Login to App Store Connect (#{params[:username]})")
103
+ Spaceship::ConnectAPI.login(params[:username], use_portal: true, use_tunes: false, portal_team_id: params[:team_id], team_name: params[:team_name])
104
+ end
105
+ certs = Spaceship::ConnectAPI::Certificate.all(filter: { certificateType: certificate_type })
106
+
107
+ # Base64 encode contents to find match from API to find a cert ID
108
+ cert_contents_base_64 = Base64.strict_encode64(File.binread(cert_path))
109
+ matching_cert = certs.find do |cert|
110
+ cert.certificate_content == cert_contents_base_64
111
+ end
74
112
 
75
- UI.user_error!("This certificate cannot be imported - the certificate contents did not match with any available on the Developer Portal") if matching_cert.nil?
113
+ UI.user_error!("This certificate cannot be imported - the certificate contents did not match with any available on the Developer Portal") if matching_cert.nil?
76
114
 
77
- # Make dir if doesn't exist
78
- FileUtils.mkdir_p(output_dir_certs)
79
- dest_cert_path = File.join(output_dir_certs, "#{matching_cert.id}.cer")
80
- dest_p12_path = File.join(output_dir_certs, "#{matching_cert.id}.p12")
115
+ # Make dir if doesn't exist
116
+ FileUtils.mkdir_p(output_dir_certs)
117
+ dest_cert_path = File.join(output_dir_certs, "#{matching_cert.id}.cer")
118
+ dest_p12_path = File.join(output_dir_certs, "#{matching_cert.id}.p12")
119
+ end
81
120
 
82
121
  files_to_commit = [dest_cert_path, dest_p12_path]
83
122
 
@@ -108,5 +147,11 @@ module Match
108
147
  UI.user_error!("#{file_description} does not exist at path: #{file_path}") unless !file_path.nil? || optional
109
148
  file_path
110
149
  end
150
+
151
+ def api_token(params)
152
+ @api_token ||= Spaceship::ConnectAPI::Token.create(params[:api_key]) if params[:api_key]
153
+ @api_token ||= Spaceship::ConnectAPI::Token.from_json_file(params[:api_key_path]) if params[:api_key_path]
154
+ return @api_token
155
+ end
111
156
  end
112
157
  end