fastlane 2.160.0 → 2.165.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +78 -78
- data/cert/lib/cert/options.rb +28 -1
- data/cert/lib/cert/runner.rb +51 -34
- data/deliver/lib/deliver/app_screenshot_iterator.rb +4 -4
- data/deliver/lib/deliver/module.rb +2 -0
- data/deliver/lib/deliver/options.rb +5 -5
- data/deliver/lib/deliver/queue_worker.rb +14 -29
- data/deliver/lib/deliver/upload_metadata.rb +20 -5
- data/deliver/lib/deliver/upload_screenshots.rb +28 -13
- data/fastlane/lib/fastlane/actions/.download_dsyms.rb.swp +0 -0
- data/fastlane/lib/fastlane/actions/actions_helper.rb +20 -1
- data/fastlane/lib/fastlane/actions/app_store_build_number.rb +39 -3
- data/fastlane/lib/fastlane/actions/app_store_connect_api_key.rb +15 -1
- data/fastlane/lib/fastlane/actions/check_app_store_metadata.rb +1 -0
- data/fastlane/lib/fastlane/actions/docs/capture_android_screenshots.md +2 -2
- data/fastlane/lib/fastlane/actions/docs/capture_ios_screenshots.md +2 -2
- data/fastlane/lib/fastlane/actions/docs/create_app_online.md +1 -1
- data/fastlane/lib/fastlane/actions/docs/frame_screenshots.md +2 -2
- data/fastlane/lib/fastlane/actions/docs/run_tests.md +2 -2
- data/fastlane/lib/fastlane/actions/docs/sync_code_signing.md +12 -3
- data/fastlane/lib/fastlane/actions/docs/upload_to_play_store.md +2 -2
- data/fastlane/lib/fastlane/actions/docs/upload_to_testflight.md +1 -1
- data/fastlane/lib/fastlane/actions/download_dsyms.rb +1 -0
- data/fastlane/lib/fastlane/actions/ensure_git_status_clean.rb +13 -2
- data/fastlane/lib/fastlane/actions/get_certificates.rb +1 -0
- data/fastlane/lib/fastlane/actions/get_provisioning_profile.rb +1 -0
- data/fastlane/lib/fastlane/actions/import_from_git.rb +9 -1
- data/fastlane/lib/fastlane/actions/is_ci.rb +1 -1
- data/fastlane/lib/fastlane/actions/latest_testflight_build_number.rb +15 -0
- data/fastlane/lib/fastlane/actions/register_device.rb +46 -5
- data/fastlane/lib/fastlane/actions/register_devices.rb +50 -16
- data/fastlane/lib/fastlane/actions/set_changelog.rb +31 -3
- data/fastlane/lib/fastlane/actions/sync_code_signing.rb +1 -0
- data/fastlane/lib/fastlane/actions/upload_to_app_store.rb +3 -2
- data/fastlane/lib/fastlane/fast_file.rb +74 -23
- data/fastlane/lib/fastlane/plugins/template/.rubocop.yml +1 -0
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +4 -4
- data/fastlane/swift/Fastlane.swift +120 -27
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +1 -1
- data/fastlane/swift/LaneFileProtocol.swift +28 -36
- data/fastlane/swift/MainProcess.swift +1 -1
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +21 -5
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/ScanfileProtocol.swift +1 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
- data/fastlane/swift/main.swift +1 -1
- data/fastlane_core/lib/fastlane_core/analytics/analytics_session.rb +6 -7
- data/fastlane_core/lib/fastlane_core/device_manager.rb +8 -4
- data/fastlane_core/lib/fastlane_core/helper.rb +1 -1
- data/fastlane_core/lib/fastlane_core/keychain_importer.rb +3 -3
- data/gym/lib/gym/generators/package_command_generator_xcode7.rb +2 -2
- data/match/lib/match/generator.rb +6 -1
- data/match/lib/match/importer.rb +63 -18
- data/match/lib/match/migrate.rb +13 -2
- data/match/lib/match/nuke.rb +65 -22
- data/match/lib/match/options.rb +34 -3
- data/match/lib/match/runner.rb +38 -10
- data/match/lib/match/spaceship_ensure.rb +27 -21
- data/match/lib/match/storage/google_cloud_storage.rb +20 -3
- data/match/lib/match/storage/s3_storage.rb +19 -3
- data/scan/lib/scan/detect_values.rb +5 -8
- data/scan/lib/scan/runner.rb +2 -1
- data/sigh/lib/assets/resign.sh +1 -1
- data/sigh/lib/sigh/download_all.rb +16 -4
- data/sigh/lib/sigh/options.rb +21 -0
- data/sigh/lib/sigh/runner.rb +83 -41
- data/snapshot/lib/assets/SnapshotHelper.swift +4 -0
- data/snapshot/lib/snapshot/simulator_launchers/simulator_launcher_base.rb +2 -1
- data/spaceship/README.md +1 -1
- data/spaceship/lib/spaceship/client.rb +9 -4
- data/spaceship/lib/spaceship/connect_api.rb +27 -0
- data/spaceship/lib/spaceship/connect_api/api_client.rb +12 -3
- data/spaceship/lib/spaceship/connect_api/client.rb +20 -7
- data/spaceship/lib/spaceship/connect_api/models/app.rb +51 -0
- data/spaceship/lib/spaceship/connect_api/models/app_screenshot.rb +3 -1
- data/spaceship/lib/spaceship/connect_api/models/beta_tester.rb +2 -1
- data/spaceship/lib/spaceship/connect_api/models/certificate.rb +42 -0
- data/spaceship/lib/spaceship/connect_api/models/custom_app_organization.rb +43 -0
- data/spaceship/lib/spaceship/connect_api/models/custom_app_user.rb +41 -0
- data/spaceship/lib/spaceship/connect_api/models/device.rb +5 -0
- data/spaceship/lib/spaceship/connect_api/models/profile.rb +7 -1
- data/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +59 -0
- data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +45 -2
- data/spaceship/lib/spaceship/connect_api/spaceship.rb +7 -4
- data/spaceship/lib/spaceship/connect_api/testflight/testflight.rb +13 -0
- data/spaceship/lib/spaceship/connect_api/token.rb +6 -1
- data/spaceship/lib/spaceship/connect_api/tunes/tunes.rb +75 -1
- data/spaceship/lib/spaceship/connect_api/users/users.rb +40 -0
- data/spaceship/lib/spaceship/helper/net_http_generic_request.rb +11 -5
- data/supply/lib/supply/uploader.rb +1 -1
- metadata +19 -15
@@ -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(
|
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 "" } //
|
25
|
-
func beforeAll() {} //
|
26
|
-
func afterAll(
|
27
|
-
func onError(currentLane _: String, errorInfo _: String) {} //
|
28
|
-
func recordLaneDescriptions() {} //
|
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
|
-
//
|
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: \(
|
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
|
-
//
|
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
|
-
|
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
|
-
//
|
115
|
-
//
|
116
|
-
|
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 =
|
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 '\(
|
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
|
-
//
|
143
|
-
fastfileInstance.
|
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
|
149
|
-
fastfileInstance.afterAll(
|
150
|
-
|
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,
|
31
|
+
let completedRun = Fastfile.runLane(from: fastfile, named: argumentProcessor.currentLane, with: argumentProcessor.laneParameters())
|
32
32
|
if completedRun {
|
33
33
|
runner.disconnectFromFastlaneProcess()
|
34
34
|
}
|
@@ -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
|
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
|
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.
|
187
|
+
// FastlaneRunnerAPIVersion [0.9.45]
|
data/fastlane/swift/main.swift
CHANGED
@@ -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,
|
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
|
-
|
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
|
56
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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)
|
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)
|
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],
|
data/match/lib/match/importer.rb
CHANGED
@@ -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 =
|
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 =
|
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 =
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|