fastlane 2.160.0 → 2.165.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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