fastlane 2.73.0 → 2.74.0.beta.20180106010004

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/deliver/lib/assets/summary.html.erb +2 -1
  3. data/fastlane/lib/fastlane.rb +0 -2
  4. data/fastlane/lib/fastlane/commands_generator.rb +5 -2
  5. data/fastlane/lib/fastlane/lane_manager.rb +0 -45
  6. data/fastlane/lib/fastlane/lane_manager_base.rb +4 -4
  7. data/fastlane/lib/fastlane/runner.rb +4 -0
  8. data/fastlane/lib/fastlane/server/{command.rb → action_command.rb} +16 -25
  9. data/fastlane/lib/fastlane/server/action_command_return.rb +14 -0
  10. data/fastlane/lib/fastlane/server/command_executor.rb +0 -2
  11. data/fastlane/lib/fastlane/server/command_parser.rb +20 -0
  12. data/fastlane/lib/fastlane/server/control_command.rb +23 -0
  13. data/fastlane/lib/fastlane/server/json_return_value_processor.rb +71 -0
  14. data/fastlane/lib/fastlane/server/socket_server.rb +111 -112
  15. data/fastlane/lib/fastlane/server/socket_server_action_command_executor.rb +4 -3
  16. data/fastlane/lib/fastlane/swift_lane_manager.rb +17 -6
  17. data/fastlane/lib/fastlane/version.rb +1 -1
  18. data/fastlane/swift/ControlCommand.swift +71 -0
  19. data/fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj/project.pbxproj +8 -0
  20. data/fastlane/swift/LaneFileProtocol.swift +55 -27
  21. data/fastlane/swift/RubyCommand.swift +3 -6
  22. data/fastlane/swift/RubyCommandable.swift +38 -0
  23. data/fastlane/swift/Runner.swift +6 -3
  24. data/fastlane/swift/SocketClient.swift +43 -27
  25. data/fastlane/swift/SocketResponse.swift +7 -2
  26. data/fastlane/swift/main.swift +5 -3
  27. metadata +24 -24
  28. data/fastlane/lib/.DS_Store +0 -0
  29. data/fastlane/lib/assets/.DS_Store +0 -0
  30. data/fastlane/lib/fastlane/.DS_Store +0 -0
  31. data/fastlane/lib/fastlane/actions/.DS_Store +0 -0
  32. data/fastlane/lib/fastlane/actions/docs/.DS_Store +0 -0
  33. data/fastlane/lib/fastlane/setup/.DS_Store +0 -0
@@ -1,4 +1,5 @@
1
- require 'fastlane/server/command.rb'
1
+ require 'fastlane/server/action_command_return.rb'
2
+ require 'fastlane/server/command_parser.rb'
2
3
  require 'fastlane/server/command_executor.rb'
3
4
 
4
5
  module Fastlane
@@ -44,7 +45,7 @@ module Fastlane
44
45
  parameter_map: parameter_map
45
46
  )
46
47
 
47
- command_return = CommandReturn.new(
48
+ command_return = ActionCommandReturn.new(
48
49
  return_value: action_return,
49
50
  return_value_type: action_class_ref.return_type,
50
51
  closure_argument_value: closure_argument_value
@@ -88,7 +89,7 @@ module Fastlane
88
89
  action_return = Fastlane::FastFile.sh(command_param, log: log_param, error_callback: error_callback)
89
90
  end
90
91
 
91
- command_return = CommandReturn.new(
92
+ command_return = ActionCommandReturn.new(
92
93
  return_value: action_return,
93
94
  return_value_type: action_return_type,
94
95
  closure_argument_value: closure_argument_value
@@ -26,23 +26,34 @@ module Fastlane
26
26
  socket_thread = self.start_socket_thread
27
27
  sleep(0.250) while socket_thread[:ready].nil?
28
28
  # wait on socket_thread to be in ready state, then start the runner thread
29
- runner_thread = self.cruise_swift_lane_in_thread(lane, parameters)
29
+ self.cruise_swift_lane_in_thread(lane, parameters)
30
30
 
31
- runner_thread.join
32
31
  socket_thread.join
33
32
  rescue Exception => ex # rubocop:disable Lint/RescueException
33
+ e = ex
34
+ end
35
+ # If we have a thread exception, drop that in the exception
36
+ # won't ever have a situation where e is non-nil, and socket_thread[:exception] is also non-nil
37
+ e ||= socket_thread[:exception]
38
+
39
+ unless e.nil?
40
+ print_lane_context
41
+
34
42
  # We also catch Exception, since the implemented action might send a SystemExit signal
35
43
  # (or similar). We still want to catch that, since we want properly finish running fastlane
36
44
  # Tested with `xcake`, which throws a `Xcake::Informative` object
45
+ UI.error e.to_s if e.kind_of?(StandardError) # we don't want to print things like 'system exit'
46
+ end
37
47
 
38
- print_lane_context
39
- UI.error ex.to_s if ex.kind_of?(StandardError) # we don't want to print things like 'system exit'
40
- e = ex
48
+ skip_message = false
49
+ exit_reason = socket_thread[:exit_reason]
50
+ if exit_reason == :cancelled && e.nil?
51
+ skip_message = true
41
52
  end
42
53
 
43
54
  duration = ((Time.now - started) / 60.0).round
44
55
 
45
- finish_fastlane(nil, duration, e)
56
+ finish_fastlane(nil, duration, e, skip_message: skip_message)
46
57
  end
47
58
 
48
59
  def self.display_lanes
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
- VERSION = '2.73.0'.freeze
2
+ VERSION = '2.74.0.beta.20180106010004'.freeze
3
3
  DESCRIPTION = "The easiest way to automate beta deployments and releases for your iOS and Android apps".freeze
4
4
  MINIMUM_XCODE_RELEASE = "7.0".freeze
5
5
  RUBOCOP_REQUIREMENT = '0.49.1'.freeze
@@ -0,0 +1,71 @@
1
+ //
2
+ // ClientShutdownCommand.swift
3
+ // FastlaneRunner
4
+ //
5
+ // Created by Joshua Liebowitz on 1/3/18.
6
+ // Copyright © 2018 Joshua Liebowitz. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ struct ControlCommand: RubyCommandable {
12
+ static let commandKey = "command"
13
+ var type: CommandType { return .control }
14
+
15
+ enum ShutdownCommandType {
16
+ static let userMessageKey: String = "userMessage"
17
+
18
+ enum CancelReason {
19
+ static let reasonKey: String = "reason"
20
+ case clientError
21
+ case serverError
22
+
23
+ var reasonText: String {
24
+ switch self {
25
+ case .clientError:
26
+ return "clientError"
27
+ case .serverError:
28
+ return "serverError"
29
+ }
30
+ }
31
+ }
32
+
33
+ case done
34
+ case cancel(cancelReason: CancelReason)
35
+
36
+ var token: String {
37
+ switch self {
38
+ case .done:
39
+ return "done"
40
+ case .cancel:
41
+ return "cancelFastlaneRun"
42
+ }
43
+ }
44
+ }
45
+
46
+ let message: String?
47
+ let shutdownCommandType: ShutdownCommandType
48
+ var commandJson: String {
49
+ var jsonDictionary: [String: Any] = [ControlCommand.commandKey : self.shutdownCommandType.token]
50
+
51
+ if let message = message {
52
+ jsonDictionary[ShutdownCommandType.userMessageKey] = message
53
+ }
54
+ if case .cancel(let reason) = shutdownCommandType {
55
+ jsonDictionary[ShutdownCommandType.CancelReason.reasonKey] = reason.reasonText
56
+ }
57
+
58
+ let jsonData = try! JSONSerialization.data(withJSONObject: jsonDictionary, options: [])
59
+ let jsonString = String(data: jsonData, encoding: .utf8)!
60
+ return jsonString
61
+ }
62
+
63
+ init(commandType: ShutdownCommandType, message: String? = nil) {
64
+ self.shutdownCommandType = commandType
65
+ self.message = message
66
+ }
67
+ }
68
+
69
+ // Please don't remove the lines below
70
+ // They are used to detect outdated files
71
+ // FastlaneRunnerAPIVersion [0.9.2]
@@ -32,7 +32,9 @@
32
32
  D55B28C91F6C588300DC42C5 /* Snapshotfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B28C21F6C588300DC42C5 /* Snapshotfile.swift */; };
33
33
  D5A7C48F1F7C4DAF00A91DE6 /* Appfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A7C48D1F7C4DAF00A91DE6 /* Appfile.swift */; };
34
34
  D5A7C4901F7C4DAF00A91DE6 /* Fastfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A7C48E1F7C4DAF00A91DE6 /* Fastfile.swift */; };
35
+ D5B8A5B31FFDC49E00536B24 /* ControlCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B8A5B21FFDC49D00536B24 /* ControlCommand.swift */; };
35
36
  D5BAFD121F7DAAFC0030B324 /* ArgumentProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BAFD111F7DAAFC0030B324 /* ArgumentProcessor.swift */; };
37
+ D5D1DE991FFEE8EA00502A00 /* RubyCommandable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D1DE981FFEE8E900502A00 /* RubyCommandable.swift */; };
36
38
  /* End PBXBuildFile section */
37
39
 
38
40
  /* Begin PBXFileReference section */
@@ -62,7 +64,9 @@
62
64
  D55B28C21F6C588300DC42C5 /* Snapshotfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Snapshotfile.swift; path = ../Snapshotfile.swift; sourceTree = "<group>"; };
63
65
  D5A7C48D1F7C4DAF00A91DE6 /* Appfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Appfile.swift; path = ../Appfile.swift; sourceTree = "<group>"; };
64
66
  D5A7C48E1F7C4DAF00A91DE6 /* Fastfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Fastfile.swift; path = ../Fastfile.swift; sourceTree = "<group>"; };
67
+ D5B8A5B21FFDC49D00536B24 /* ControlCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControlCommand.swift; path = ../ControlCommand.swift; sourceTree = "<group>"; };
65
68
  D5BAFD111F7DAAFC0030B324 /* ArgumentProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ArgumentProcessor.swift; path = ../ArgumentProcessor.swift; sourceTree = "<group>"; };
69
+ D5D1DE981FFEE8E900502A00 /* RubyCommandable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RubyCommandable.swift; path = ../RubyCommandable.swift; sourceTree = "<group>"; };
66
70
  /* End PBXFileReference section */
67
71
 
68
72
  /* Begin PBXFrameworksBuildPhase section */
@@ -107,7 +111,9 @@
107
111
  B3BA65B11F5A325E00B34850 /* Networking */ = {
108
112
  isa = PBXGroup;
109
113
  children = (
114
+ D5B8A5B21FFDC49D00536B24 /* ControlCommand.swift */,
110
115
  B3BA65A01F5A269100B34850 /* RubyCommand.swift */,
116
+ D5D1DE981FFEE8E900502A00 /* RubyCommandable.swift */,
111
117
  B3BA65A11F5A269100B34850 /* Runner.swift */,
112
118
  B3BA65A21F5A269100B34850 /* SocketClient.swift */,
113
119
  B3BA65A31F5A269100B34850 /* SocketClientDelegateProtocol.swift */,
@@ -225,6 +231,7 @@
225
231
  buildActionMask = 2147483647;
226
232
  files = (
227
233
  B3BA65A91F5A269100B34850 /* RubyCommand.swift in Sources */,
234
+ D5D1DE991FFEE8EA00502A00 /* RubyCommandable.swift in Sources */,
228
235
  D55B28C41F6C588300DC42C5 /* Gymfile.swift in Sources */,
229
236
  B302067D1F5E3E9000DE6EBD /* MatchfileProtocol.swift in Sources */,
230
237
  B3BA65AC1F5A269100B34850 /* SocketClientDelegateProtocol.swift in Sources */,
@@ -243,6 +250,7 @@
243
250
  B30206811F5E3E9000DE6EBD /* DeliverfileProtocol.swift in Sources */,
244
251
  B3BA65AA1F5A269100B34850 /* Runner.swift in Sources */,
245
252
  B3BA65AF1F5A2D5C00B34850 /* RunnerArgument.swift in Sources */,
253
+ D5B8A5B31FFDC49E00536B24 /* ControlCommand.swift in Sources */,
246
254
  B302067E1F5E3E9000DE6EBD /* PrecheckfileProtocol.swift in Sources */,
247
255
  B3BA65AD1F5A269100B34850 /* SocketResponse.swift in Sources */,
248
256
  B3BA65A81F5A269100B34850 /* main.swift in Sources */,
@@ -10,8 +10,8 @@ import Foundation
10
10
 
11
11
  public protocol LaneFileProtocol: class {
12
12
  var fastlaneVersion: String { get }
13
- static func runLane(named: String, parameters: [String : String])
14
-
13
+ static func runLane(named: String, parameters: [String : String]) -> Bool
14
+
15
15
  func recordLaneDescriptions()
16
16
  func beforeAll()
17
17
  func afterAll(currentLane: String)
@@ -29,38 +29,54 @@ public extension LaneFileProtocol {
29
29
  @objcMembers
30
30
  public class LaneFile: NSObject, LaneFileProtocol {
31
31
  private(set) static var fastfileInstance: Fastfile?
32
-
32
+
33
33
  // Called before any lane is executed.
34
34
  private func setupAllTheThings() {
35
- // Step 1, add lange descriptions
36
- (self as! Fastfile).recordLaneDescriptions()
37
-
38
- // Step 2, run beforeAll() function
39
35
  LaneFile.fastfileInstance!.beforeAll()
40
36
  }
41
-
42
- public static var lanes: [String : String] {
43
- var laneToMethodName: [String : String] = [:]
37
+
38
+ private static func trimLaneFromName(laneName: String) -> String {
39
+ return String(laneName.prefix(laneName.count - 4))
40
+ }
41
+
42
+ private static func trimLaneWithOptionsFromName(laneName: String) -> String {
43
+ return String(laneName.prefix(laneName.count - 12))
44
+ }
45
+
46
+ private static var laneFunctionNames: [String] {
47
+ var lanes: [String] = []
44
48
  var methodCount: UInt32 = 0
45
49
  let methodList = class_copyMethodList(self, &methodCount)
46
50
  for i in 0..<Int(methodCount) {
47
51
  let selName = sel_getName(method_getName(methodList![i]))
48
52
  let name = String(cString: selName)
53
+ let lowercasedName = name.lowercased()
54
+ if lowercasedName.hasSuffix("lane") || lowercasedName.hasSuffix("lanewithoptions:") {
55
+ lanes.append(name)
56
+ }
57
+ }
58
+ return lanes
59
+ }
60
+
61
+ public static var lanes: [String : String] {
62
+ var laneToMethodName: [String : String] = [:]
63
+ self.laneFunctionNames.forEach { name in
49
64
  let lowercasedName = name.lowercased()
50
65
  if lowercasedName.hasSuffix("lane") {
51
66
  laneToMethodName[lowercasedName] = name
52
- let lowercasedNameNoLane = String(lowercasedName.prefix(lowercasedName.count - 4))
67
+ let lowercasedNameNoLane = trimLaneFromName(laneName: lowercasedName)
53
68
  laneToMethodName[lowercasedNameNoLane] = name
54
69
  } else if lowercasedName.hasSuffix("lanewithoptions:") {
55
- let lowercasedNameNoOptions = String(lowercasedName.prefix(lowercasedName.count - 12))
70
+ let lowercasedNameNoOptions = trimLaneWithOptionsFromName(laneName: lowercasedName)
56
71
  laneToMethodName[lowercasedNameNoOptions] = name
57
- let lowercasedNameNoLane = String(lowercasedNameNoOptions.prefix(lowercasedNameNoOptions.count - 4))
72
+ let lowercasedNameNoLane = trimLaneFromName(laneName: lowercasedNameNoOptions)
58
73
  laneToMethodName[lowercasedNameNoLane] = name
59
74
  }
60
75
  }
76
+
61
77
  return laneToMethodName
62
78
  }
63
-
79
+
64
80
  public static func loadFastfile() {
65
81
  if self.fastfileInstance == nil {
66
82
  let fastfileType: AnyObject.Type = NSClassFromString(self.className())!
@@ -69,38 +85,50 @@ public class LaneFile: NSObject, LaneFileProtocol {
69
85
  self.fastfileInstance = currentFastfileInstance
70
86
  }
71
87
  }
72
-
73
- public static func runLane(named: String, parameters: [String : String]) {
88
+
89
+ public static func runLane(named: String, parameters: [String : String]) -> Bool {
74
90
  log(message: "Running lane: \(named)")
75
91
  self.loadFastfile()
76
-
92
+
77
93
  guard let fastfileInstance: Fastfile = self.fastfileInstance else {
78
94
  let message = "Unable to instantiate class named: \(self.className())"
79
95
  log(message: message)
80
96
  fatalError(message)
81
97
  }
82
-
83
- // call all methods that need to be called before we start calling lanes
84
- fastfileInstance.setupAllTheThings()
85
-
98
+
86
99
  let currentLanes = self.lanes
87
100
  let lowerCasedLaneRequested = named.lowercased()
88
-
101
+
89
102
  guard let laneMethod = currentLanes[lowerCasedLaneRequested] else {
90
- let message = "unable to find lane named: \(named)"
103
+ let laneNames = self.laneFunctionNames.map { laneFuctionName in
104
+ if laneFuctionName.hasSuffix("lanewithoptions:") {
105
+ return trimLaneWithOptionsFromName(laneName: laneFuctionName)
106
+ } else {
107
+ return trimLaneFromName(laneName: laneFuctionName)
108
+ }
109
+ }.joined(separator: ", ")
110
+
111
+ let message = "[!] Could not find lane '\(named)'. Available lanes: \(laneNames)"
91
112
  log(message: message)
92
- fatalError(message)
113
+
114
+ let shutdownCommand = ControlCommand(commandType: .cancel(cancelReason: .clientError), message: message)
115
+ _ = runner.executeCommand(shutdownCommand)
116
+ return false
93
117
  }
94
-
118
+
119
+ // call all methods that need to be called before we start calling lanes
120
+ fastfileInstance.setupAllTheThings()
121
+
95
122
  // We need to catch all possible errors here and display a nice message
96
123
  _ = fastfileInstance.perform(NSSelectorFromString(laneMethod), with: parameters)
97
-
124
+
98
125
  // only call on success
99
126
  fastfileInstance.afterAll(currentLane: named)
100
127
  log(message: "Done running lane: \(named) 🚀")
128
+ return true
101
129
  }
102
130
  }
103
131
 
104
132
  // Please don't remove the lines below
105
133
  // They are used to detect outdated files
106
- // FastlaneRunnerAPIVersion [0.9.1]
134
+ // FastlaneRunnerAPIVersion [0.9.2]
@@ -8,11 +8,9 @@
8
8
 
9
9
  import Foundation
10
10
 
11
- protocol RubyCommandable {
12
- var json: String { get }
13
- }
14
-
15
11
  struct RubyCommand: RubyCommandable {
12
+ var type: CommandType { return .action }
13
+
16
14
  struct Argument {
17
15
  enum ArgType {
18
16
  case stringClosure
@@ -41,7 +39,6 @@ struct RubyCommand: RubyCommandable {
41
39
 
42
40
  var json: String {
43
41
  get {
44
-
45
42
  if let someValue = value {
46
43
  let typeJson: String
47
44
  if let type = type {
@@ -102,7 +99,7 @@ struct RubyCommand: RubyCommandable {
102
99
  callbackClosure(callbackArg)
103
100
  }
104
101
 
105
- var json: String {
102
+ var commandJson: String {
106
103
  let argsArrayJson = self.args
107
104
  .map { $0.json }
108
105
  .filter { $0 != "" }
@@ -0,0 +1,38 @@
1
+ //
2
+ // RubyCommandable.swift
3
+ // FastlaneRunner
4
+ //
5
+ // Created by Joshua Liebowitz on 1/4/18.
6
+ // Copyright © 2018 Joshua Liebowitz. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ enum CommandType {
12
+ case action
13
+ case control
14
+
15
+ var token: String {
16
+ switch self {
17
+ case .action:
18
+ return "action"
19
+ case .control:
20
+ return "control"
21
+ }
22
+ }
23
+ }
24
+
25
+ protocol RubyCommandable {
26
+ var type: CommandType { get }
27
+ var commandJson: String { get }
28
+ }
29
+
30
+ extension RubyCommandable {
31
+ var json: String {
32
+ return "{\"commandType\" : \"\(self.type.token)\", \"command\" : \(self.commandJson)}"
33
+ }
34
+ }
35
+
36
+ // Please don't remove the lines below
37
+ // They are used to detect outdated files
38
+ // FastlaneRunnerAPIVersion [0.9.2]
@@ -27,7 +27,7 @@ class Runner {
27
27
  fileprivate var returnValue: String? // lol, so safe
28
28
  fileprivate var currentlyExecutingCommand: RubyCommandable? = nil
29
29
  fileprivate var shouldLeaveDispatchGroupDuringDisconnect = false
30
-
30
+
31
31
  func executeCommand(_ command: RubyCommandable) -> String {
32
32
  self.dispatchGroup.enter()
33
33
  currentlyExecutingCommand = command
@@ -115,7 +115,10 @@ extension Runner : SocketClientDelegateProtocol {
115
115
  }
116
116
  }
117
117
  self.dispatchGroup.leave()
118
-
118
+ case .clientInitiatedCancelAcknowledged:
119
+ verbose(message: "server acknowledged a cancel request")
120
+ self.dispatchGroup.leave()
121
+
119
122
  case .alreadyClosedSockets, .connectionFailure, .malformedRequest, .malformedResponse, .serverError:
120
123
  log(message: "error encountered while executing command:\n\(serverResponse)")
121
124
  self.dispatchGroup.leave()
@@ -187,4 +190,4 @@ func verbose(message: String) {
187
190
 
188
191
  // Please don't remove the lines below
189
192
  // They are used to detect outdated files
190
- // FastlaneRunnerAPIVersion [0.9.1]
193
+ // FastlaneRunnerAPIVersion [0.9.2]
@@ -13,6 +13,7 @@ public enum SocketClientResponse: Error {
13
13
  case malformedRequest
14
14
  case malformedResponse
15
15
  case serverError
16
+ case clientInitiatedCancelAcknowledged
16
17
  case commandTimeout(seconds: Int)
17
18
  case connectionFailure
18
19
  case success(returnedObject: String?, closureArgumentValue: String?)
@@ -27,7 +28,8 @@ class SocketClient: NSObject {
27
28
 
28
29
  static let connectTimeoutSeconds = 2
29
30
  static let defaultCommandTimeoutSeconds = 3_600 // Hopefully 1 hr is enough ¯\_(ツ)_/¯
30
- static let doneToken = "done"
31
+ static let doneToken = "done" // TODO: remove these
32
+ static let cancelToken = "cancelFastlaneRun"
31
33
 
32
34
  fileprivate var inputStream: InputStream!
33
35
  fileprivate var outputStream: OutputStream!
@@ -106,7 +108,7 @@ class SocketClient: NSObject {
106
108
  }
107
109
 
108
110
  public func sendComplete() {
109
- sendAbort()
111
+ closeSession(sendAbort: true)
110
112
  }
111
113
 
112
114
  private func testDispatchTimeoutResult(_ timeoutResult: DispatchTimeoutResult, failureMessage: String, timeToWait: DispatchTimeInterval) -> Bool {
@@ -130,42 +132,50 @@ class SocketClient: NSObject {
130
132
  private func stopOutputSession() {
131
133
  outputStream.close()
132
134
  }
133
-
134
- private func send(string: String) {
135
- guard !self.cleaningUpAfterDone else {
136
- // This will happen after we abort if there are commands waiting to be executed
137
- // Need to check state of SocketClient in command runner to make sure we can accept `send`
138
- socketDelegate?.commandExecuted(serverResponse: .alreadyClosedSockets)
139
- return
140
- }
141
-
142
- if string == SocketClient.doneToken {
143
- self.cleaningUpAfterDone = true
144
- }
145
-
146
- self.dispatchGroup.enter()
135
+
136
+ private func sendThroughQueue(string: String) {
147
137
  streamQueue.async {
148
138
  let data = string.data(using: .utf8)!
149
139
  _ = data.withUnsafeBytes { self.outputStream.write($0, maxLength: data.count) }
150
140
  }
141
+ }
151
142
 
152
- let timeoutSeconds = self.cleaningUpAfterDone ? 1 : self.commandTimeoutSeconds
143
+ private func privateSend(string: String) {
144
+ self.dispatchGroup.enter()
145
+ sendThroughQueue(string: string)
153
146
 
147
+ let timeoutSeconds = self.cleaningUpAfterDone ? 1 : self.commandTimeoutSeconds
154
148
  let timeToWait = DispatchTimeInterval.seconds(timeoutSeconds)
155
149
  let commandTimeout = DispatchTime.now() + timeToWait
156
150
  let timeoutResult = self.dispatchGroup.wait(timeout: commandTimeout)
157
151
 
158
152
  _ = testDispatchTimeoutResult(timeoutResult, failureMessage: "Ruby process didn't return after: \(SocketClient.connectTimeoutSeconds) seconds", timeToWait: timeToWait)
159
153
  }
160
-
161
- func sendAbort() {
154
+
155
+ private func send(string: String) {
156
+ guard !self.cleaningUpAfterDone else {
157
+ // This will happen after we abort if there are commands waiting to be executed
158
+ // Need to check state of SocketClient in command runner to make sure we can accept `send`
159
+ socketDelegate?.commandExecuted(serverResponse: .alreadyClosedSockets)
160
+ return
161
+ }
162
+
163
+ if string == SocketClient.doneToken {
164
+ self.cleaningUpAfterDone = true
165
+ }
166
+
167
+ privateSend(string: string)
168
+ }
169
+
170
+ func closeSession(sendAbort: Bool = true) {
162
171
  self.socketStatus = .closed
163
-
172
+
164
173
  stopInputSession()
165
-
166
- // and error occured, let's try to send the "done" message
167
- send(string: SocketClient.doneToken)
168
-
174
+
175
+ if sendAbort {
176
+ send(rubyCommand: ControlCommand(commandType: .done))
177
+ }
178
+
169
179
  stopOutputSession()
170
180
  self.socketDelegate?.connectionsClosed()
171
181
  }
@@ -188,7 +198,7 @@ extension SocketClient: StreamDelegate {
188
198
 
189
199
  case Stream.Event.errorOccurred:
190
200
  verbose(message: "input stream error occurred")
191
- sendAbort()
201
+ closeSession(sendAbort: true)
192
202
 
193
203
  case Stream.Event.hasBytesAvailable:
194
204
  read()
@@ -246,7 +256,8 @@ extension SocketClient: StreamDelegate {
246
256
 
247
257
  func handleFailure(message: [String]) {
248
258
  log(message: "Encountered a problem: \(message.joined(separator:"\n"))")
249
- sendAbort()
259
+ let shutdownCommand = ControlCommand(commandType: .cancel(cancelReason: .serverError))
260
+ self.send(rubyCommand: shutdownCommand)
250
261
  }
251
262
 
252
263
  func processResponse(string: String) {
@@ -261,6 +272,10 @@ extension SocketClient: StreamDelegate {
261
272
  let socketResponse = SocketResponse(payload: responseString)
262
273
  verbose(message: "response is: \(responseString)")
263
274
  switch socketResponse.responseType {
275
+ case .clientInitiatedCancel:
276
+ self.socketDelegate?.commandExecuted(serverResponse: .clientInitiatedCancelAcknowledged)
277
+ self.closeSession(sendAbort: false)
278
+
264
279
  case .failure(let failureInformation):
265
280
  self.socketDelegate?.commandExecuted(serverResponse: .serverError)
266
281
  self.handleFailure(message: failureInformation)
@@ -274,10 +289,11 @@ extension SocketClient: StreamDelegate {
274
289
  // cool, ready for next command
275
290
  break
276
291
  }
292
+
277
293
  self.dispatchGroup.leave() // should now pull the next piece of work
278
294
  }
279
295
  }
280
296
 
281
297
  // Please don't remove the lines below
282
298
  // They are used to detect outdated files
283
- // FastlaneRunnerAPIVersion [0.9.1]
299
+ // FastlaneRunnerAPIVersion [0.9.2]