mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
♻️ Rework how output is handled
This commit is contained in:
@ -40,10 +40,9 @@ class Application {
|
|||||||
/** Checks if the app is installed. */
|
/** Checks if the app is installed. */
|
||||||
func isInstalled() -> Bool {
|
func isInstalled() -> Bool {
|
||||||
// If this script does not complain, the app exists!
|
// If this script does not complain, the app exists!
|
||||||
return Shell.user.execute(
|
return Shell.user.executeSynchronously(
|
||||||
"/usr/bin/open -Ra \"\(name)\"",
|
"/usr/bin/open -Ra \"\(name)\"",
|
||||||
requiresPath: false,
|
requiresPath: false
|
||||||
waitUntilExit: true
|
|
||||||
).task.terminationStatus == 0
|
).task.terminationStatus == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,9 +329,25 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
|
|
||||||
@objc func updateComposerDependencies() {
|
@objc func updateComposerDependencies() {
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
let output = Shell.user.execute(
|
let output = Shell.user.executeSynchronously(
|
||||||
"composer global update", requiresPath: true, waitUntilExit: true
|
"composer global update", requiresPath: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let task = Shell.user.createTask(for: "composer global update", requiresPath: true)
|
||||||
|
|
||||||
|
Shell.captureOutput(
|
||||||
|
task,
|
||||||
|
didReceiveStdOutData: { string in
|
||||||
|
print("\(string)")
|
||||||
|
},
|
||||||
|
didReceiveStdErrData: { string in
|
||||||
|
print("\(string)")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
task.launch()
|
||||||
|
task.waitUntilExit()
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if output.task.terminationStatus > 0 {
|
if output.task.terminationStatus > 0 {
|
||||||
// Error code means > 0
|
// Error code means > 0
|
||||||
|
@ -62,7 +62,7 @@ class Shell {
|
|||||||
_ command: String,
|
_ command: String,
|
||||||
requiresPath: Bool = false
|
requiresPath: Bool = false
|
||||||
) -> String {
|
) -> String {
|
||||||
let shellOutput = self.execute(command, requiresPath: requiresPath)
|
let shellOutput = self.executeSynchronously(command, requiresPath: requiresPath)
|
||||||
let hasError = (
|
let hasError = (
|
||||||
shellOutput.standardOutput == ""
|
shellOutput.standardOutput == ""
|
||||||
&& shellOutput.errorOutput.lengthOfBytes(using: .utf8) > 0
|
&& shellOutput.errorOutput.lengthOfBytes(using: .utf8) > 0
|
||||||
@ -77,35 +77,28 @@ class Shell {
|
|||||||
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
|
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
|
||||||
- Parameter waitUntilExit: Waits for the command to complete before returning the `ShellOutput`
|
- Parameter waitUntilExit: Waits for the command to complete before returning the `ShellOutput`
|
||||||
*/
|
*/
|
||||||
func execute(
|
func executeSynchronously(
|
||||||
_ command: String,
|
_ command: String,
|
||||||
requiresPath: Bool = false,
|
requiresPath: Bool = false
|
||||||
waitUntilExit: Bool = false
|
|
||||||
) -> ShellOutput {
|
) -> ShellOutput {
|
||||||
let task = Process()
|
|
||||||
let outputPipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
let errorPipe = Pipe()
|
let errorPipe = Pipe()
|
||||||
|
|
||||||
let tailoredCommand = requiresPath
|
let task = self.createTask(for: command, requiresPath: requiresPath)
|
||||||
? "export PATH=\(Paths.binPath):$PATH && \(command)"
|
|
||||||
: command
|
|
||||||
|
|
||||||
task.launchPath = self.shell
|
|
||||||
task.arguments = ["--login", "-c", tailoredCommand]
|
|
||||||
task.standardOutput = outputPipe
|
task.standardOutput = outputPipe
|
||||||
task.standardError = errorPipe
|
task.standardError = errorPipe
|
||||||
task.launch()
|
task.launch()
|
||||||
|
task.waitUntilExit()
|
||||||
if waitUntilExit {
|
|
||||||
task.waitUntilExit()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShellOutput(
|
return ShellOutput(
|
||||||
standardOutput: String(
|
standardOutput: String(
|
||||||
data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8
|
data: outputPipe.fileHandleForReading.readDataToEndOfFile(),
|
||||||
|
encoding: .utf8
|
||||||
)!,
|
)!,
|
||||||
errorOutput: String(
|
errorOutput: String(
|
||||||
data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8
|
data: errorPipe.fileHandleForReading.readDataToEndOfFile(),
|
||||||
|
encoding: .utf8
|
||||||
)!,
|
)!,
|
||||||
task: task
|
task: task
|
||||||
)
|
)
|
||||||
@ -118,6 +111,47 @@ class Shell {
|
|||||||
public static func fileExists(_ path: String) -> Bool {
|
public static func fileExists(_ path: String) -> Bool {
|
||||||
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
|
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a new process with the correct PATH and shell.
|
||||||
|
*/
|
||||||
|
func createTask(for command: String, requiresPath: Bool) -> Process {
|
||||||
|
let tailoredCommand = requiresPath
|
||||||
|
? "export PATH=\(Paths.binPath):$PATH && \(command)"
|
||||||
|
: command
|
||||||
|
|
||||||
|
let task = Process()
|
||||||
|
task.launchPath = self.shell
|
||||||
|
task.arguments = ["--login", "-c", tailoredCommand]
|
||||||
|
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
|
||||||
|
static func captureOutput(
|
||||||
|
_ task: Process,
|
||||||
|
didReceiveStdOutData: @escaping (String) -> Void,
|
||||||
|
didReceiveStdErrData: @escaping (String) -> Void
|
||||||
|
) {
|
||||||
|
let outputPipe = Pipe()
|
||||||
|
let errorPipe = Pipe()
|
||||||
|
|
||||||
|
task.standardOutput = outputPipe
|
||||||
|
task.standardError = errorPipe
|
||||||
|
|
||||||
|
outputPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
|
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: outputPipe.fileHandleForReading, queue: nil) { notification in
|
||||||
|
let outputString = String(data: outputPipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) ?? ""
|
||||||
|
didReceiveStdOutData(outputString)
|
||||||
|
outputPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
errorPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
|
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: errorPipe.fileHandleForReading, queue: nil) { notification in
|
||||||
|
let outputString = String(data: errorPipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) ?? ""
|
||||||
|
didReceiveStdErrData(outputString)
|
||||||
|
errorPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShellOutput {
|
class ShellOutput {
|
||||||
|
Reference in New Issue
Block a user