fastlane 2.73.0 → 2.74.0.beta.20180106010004

Sign up to get free protection for your applications and to get access to all the features.
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]