From 692d3c143fc7f96a3363a5a22bba9c35358691d8 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 23 Feb 2022 13:39:49 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20Composer=20wind?= =?UTF-8?q?ow=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 66 ++++++--- phpmon-tests/{ => Commands}/CommandTest.swift | 0 .../{ => Parsers}/BrewJsonParserTest.swift | 0 .../{ => Parsers}/ExtensionParserTest.swift | 0 .../{ => Parsers}/ValetConfigParserTest.swift | 0 .../PhpVersionDetectionTest.swift | 0 .../{ => Versions}/PhpVersionNumberTest.swift | 0 .../ValetVersionExtractorTest.swift} | 2 +- .../{ => Versions}/VersionExtractorTest.swift | 0 phpmon/Common/Core/Process.swift | 59 ++++++++ phpmon/Common/Core/Shell.swift | 36 ----- .../Integrations/Composer/ComposerJson.swift | 1 + .../Composer/ComposerWindow.swift | 126 ++++++++++++++++++ phpmon/Domain/Menu/MainMenu+Composer.swift | 102 -------------- phpmon/Domain/Menu/MainMenu+Switcher.swift | 10 +- phpmon/Domain/Menu/MainMenu.swift | 5 +- 16 files changed, 246 insertions(+), 161 deletions(-) rename phpmon-tests/{ => Commands}/CommandTest.swift (100%) rename phpmon-tests/{ => Parsers}/BrewJsonParserTest.swift (100%) rename phpmon-tests/{ => Parsers}/ExtensionParserTest.swift (100%) rename phpmon-tests/{ => Parsers}/ValetConfigParserTest.swift (100%) rename phpmon-tests/{ => Versions}/PhpVersionDetectionTest.swift (100%) rename phpmon-tests/{ => Versions}/PhpVersionNumberTest.swift (100%) rename phpmon-tests/{ValetTest.swift => Versions/ValetVersionExtractorTest.swift} (87%) rename phpmon-tests/{ => Versions}/VersionExtractorTest.swift (100%) create mode 100644 phpmon/Common/Core/Process.swift create mode 100644 phpmon/Domain/Integrations/Composer/ComposerWindow.swift delete mode 100644 phpmon/Domain/Menu/MainMenu+Composer.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index a17604d..c104747 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -116,7 +116,7 @@ C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */; }; C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; - C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetTest.swift */; }; + C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */; }; C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5635D276AB09000F12CCB /* VersionExtractor.swift */; }; C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5635D276AB09000F12CCB /* VersionExtractor.swift */; }; C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */; }; @@ -134,6 +134,8 @@ C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; }; C4C1019627C659B7001FACC2 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4C1019527C659B7001FACC2 /* HotKey */; }; + C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; }; + C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; }; C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; }; C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; }; C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; @@ -143,9 +145,9 @@ C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; - C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; }; + C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; - C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; }; + C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; }; C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; }; @@ -285,7 +287,7 @@ C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = ""; }; C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigParserTest.swift; sourceTree = ""; }; C4AF9F792754499000D44ED0 /* Valet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valet.swift; sourceTree = ""; }; - C4AF9F7C275454A900D44ED0 /* ValetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetTest.swift; sourceTree = ""; }; + C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetVersionExtractorTest.swift; sourceTree = ""; }; C4B5635D276AB09000F12CCB /* VersionExtractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionExtractor.swift; sourceTree = ""; }; C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionExtractorTest.swift; sourceTree = ""; }; C4B5853B2770FE3900DA4FBE /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; @@ -294,13 +296,14 @@ C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = ""; }; C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = ""; }; C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = ""; }; + C4C1019A27C65C6F001FACC2 /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = ""; }; C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.swift"; sourceTree = ""; }; C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = ""; }; C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = ""; }; C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = ""; }; C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = ""; }; C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = ""; }; - C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Composer.swift"; sourceTree = ""; }; + C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = ""; }; C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = ""; }; @@ -431,6 +434,7 @@ C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, + C4C1019A27C65C6F001FACC2 /* Process.swift */, C40C7F2F27722E8D00DDDCDC /* Logger.swift */, C417DC73277614690015E6EE /* Helpers.swift */, ); @@ -538,7 +542,6 @@ C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */, C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */, C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */, - C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */, C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */, C47331A1247093B7009A0597 /* StatusMenu.swift */, C48D0C9525CC80B100CC7490 /* HeaderView.swift */, @@ -630,6 +633,35 @@ path = Common; sourceTree = ""; }; + C4C1019727C65A11001FACC2 /* Parsers */ = { + isa = PBXGroup; + children = ( + C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */, + C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */, + C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */, + ); + path = Parsers; + sourceTree = ""; + }; + C4C1019827C65A1A001FACC2 /* Versions */ = { + isa = PBXGroup; + children = ( + C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */, + C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */, + C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */, + C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */, + ); + path = Versions; + sourceTree = ""; + }; + C4C1019927C65A4D001FACC2 /* Commands */ = { + isa = PBXGroup; + children = ( + C4F7809B25D80344000DBC97 /* CommandTest.swift */, + ); + path = Commands; + sourceTree = ""; + }; C4C8E81D276F5686003AC782 /* Watcher */ = { isa = PBXGroup; children = ( @@ -649,6 +681,7 @@ C4D89BC42783C98800A02B68 /* Composer */ = { isa = PBXGroup; children = ( + C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */, C4D89BC52783C99400A02B68 /* ComposerJson.swift */, C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */, ); @@ -696,16 +729,11 @@ isa = PBXGroup; children = ( C4F7807D25D7F84B000DBC97 /* Info.plist */, - C40C7F1C27720E1400DDDCDC /* Test Files */, - C4F7809B25D80344000DBC97 /* CommandTest.swift */, - C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */, - C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */, - C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */, - C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */, C43A8A1925D9CD1000591B77 /* Utility.swift */, - C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */, - C4AF9F7C275454A900D44ED0 /* ValetTest.swift */, - C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */, + C40C7F1C27720E1400DDDCDC /* Test Files */, + C4C1019927C65A4D001FACC2 /* Commands */, + C4C1019827C65A1A001FACC2 /* Versions */, + C4C1019727C65A11001FACC2 /* Parsers */, ); path = "phpmon-tests"; sourceTree = ""; @@ -906,6 +934,7 @@ C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */, + C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */, C4EC1E73279DFCF40010F296 /* Events.swift in Sources */, C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */, C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */, @@ -918,7 +947,7 @@ C476FF9822B0DD830098105B /* Alert.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */, - C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */, + C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, @@ -962,6 +991,7 @@ C4F319C927B034A500AFF46F /* Stats.swift in Sources */, C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */, C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, + C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */, C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */, @@ -983,7 +1013,7 @@ C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */, - C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */, + C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */, C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */, C417DC75277614690015E6EE /* Helpers.swift in Sources */, C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */, @@ -1004,7 +1034,7 @@ C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */, C40B24F227A310770018C7D2 /* Events.swift in Sources */, C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */, - C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, + C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, C4B585452770FE3900DA4FBE /* Command.swift in Sources */, C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */, diff --git a/phpmon-tests/CommandTest.swift b/phpmon-tests/Commands/CommandTest.swift similarity index 100% rename from phpmon-tests/CommandTest.swift rename to phpmon-tests/Commands/CommandTest.swift diff --git a/phpmon-tests/BrewJsonParserTest.swift b/phpmon-tests/Parsers/BrewJsonParserTest.swift similarity index 100% rename from phpmon-tests/BrewJsonParserTest.swift rename to phpmon-tests/Parsers/BrewJsonParserTest.swift diff --git a/phpmon-tests/ExtensionParserTest.swift b/phpmon-tests/Parsers/ExtensionParserTest.swift similarity index 100% rename from phpmon-tests/ExtensionParserTest.swift rename to phpmon-tests/Parsers/ExtensionParserTest.swift diff --git a/phpmon-tests/ValetConfigParserTest.swift b/phpmon-tests/Parsers/ValetConfigParserTest.swift similarity index 100% rename from phpmon-tests/ValetConfigParserTest.swift rename to phpmon-tests/Parsers/ValetConfigParserTest.swift diff --git a/phpmon-tests/PhpVersionDetectionTest.swift b/phpmon-tests/Versions/PhpVersionDetectionTest.swift similarity index 100% rename from phpmon-tests/PhpVersionDetectionTest.swift rename to phpmon-tests/Versions/PhpVersionDetectionTest.swift diff --git a/phpmon-tests/PhpVersionNumberTest.swift b/phpmon-tests/Versions/PhpVersionNumberTest.swift similarity index 100% rename from phpmon-tests/PhpVersionNumberTest.swift rename to phpmon-tests/Versions/PhpVersionNumberTest.swift diff --git a/phpmon-tests/ValetTest.swift b/phpmon-tests/Versions/ValetVersionExtractorTest.swift similarity index 87% rename from phpmon-tests/ValetTest.swift rename to phpmon-tests/Versions/ValetVersionExtractorTest.swift index fa8cca7..8d7ceb6 100644 --- a/phpmon-tests/ValetTest.swift +++ b/phpmon-tests/Versions/ValetVersionExtractorTest.swift @@ -8,7 +8,7 @@ import XCTest -class ValetTest: XCTestCase { +class ValetVersionExtractorTest: XCTestCase { func testDetermineValetVersion() { let version = valet("--version", sudo: false) diff --git a/phpmon-tests/VersionExtractorTest.swift b/phpmon-tests/Versions/VersionExtractorTest.swift similarity index 100% rename from phpmon-tests/VersionExtractorTest.swift rename to phpmon-tests/Versions/VersionExtractorTest.swift diff --git a/phpmon/Common/Core/Process.swift b/phpmon/Common/Core/Process.swift new file mode 100644 index 0000000..48c7543 --- /dev/null +++ b/phpmon/Common/Core/Process.swift @@ -0,0 +1,59 @@ +// +// Process.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/02/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension Process { + + /** + When a process is running in the background, it can send content to standard + output or standard error, just like it would in a terminal. Using `listen` + allows us to react whenever data is received by running a particular closure, + depending on which type of data is received. + */ + public func listen( + didReceiveStandardOutputData: @escaping (String) -> Void, + didReceiveStandardErrorData: @escaping (String) -> Void + ) { + let outputPipe = Pipe() + let errorPipe = Pipe() + + self.standardOutput = outputPipe + self.standardError = errorPipe + + [ + (outputPipe, didReceiveStandardOutputData), + (errorPipe, didReceiveStandardErrorData) + ].forEach { (pipe: Pipe, callback: @escaping (String) -> Void) in + pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + NotificationCenter.default.addObserver( + forName: NSNotification.Name.NSFileHandleDataAvailable, + object: pipe.fileHandleForReading, + queue: nil + ) { notification in + if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) { + callback(outputString) + } + pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + } + } + } + + /** + After the process is done running, you'll want to stop listening. + */ + public func haltListening() { + if let pipe = self.standardOutput as? Pipe { + NotificationCenter.default.removeObserver(pipe.fileHandleForReading) + } + if let pipe = self.standardError as? Pipe { + NotificationCenter.default.removeObserver(pipe.fileHandleForReading) + } + } + +} diff --git a/phpmon/Common/Core/Shell.swift b/phpmon/Common/Core/Shell.swift index 5f7c89c..b225fb9 100644 --- a/phpmon/Common/Core/Shell.swift +++ b/phpmon/Common/Core/Shell.swift @@ -119,42 +119,6 @@ public class Shell { return task } - public 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, didReceiveStdOutData), (errorPipe, didReceiveStdErrData)].forEach { - (pipe: Pipe, callback: @escaping (String) -> Void) in - pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() - NotificationCenter.default.addObserver( - forName: NSNotification.Name.NSFileHandleDataAvailable, - object: pipe.fileHandleForReading, - queue: nil - ) { notification in - if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) { - callback(outputString) - } - pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() - } - } - } - - public static func haltCapturingOutput(_ task: Process) { - if let pipe = task.standardOutput as? Pipe { - NotificationCenter.default.removeObserver(pipe.fileHandleForReading) - } - if let pipe = task.standardError as? Pipe { - NotificationCenter.default.removeObserver(pipe.fileHandleForReading) - } - } - public class Output { public let standardOutput: String public let errorOutput: String diff --git a/phpmon/Domain/Integrations/Composer/ComposerJson.swift b/phpmon/Domain/Integrations/Composer/ComposerJson.swift index f18601b..5d8e3ff 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerJson.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerJson.swift @@ -29,6 +29,7 @@ struct ComposerJson: Decodable { struct Config: Decodable { let platform: Platform? } + struct Platform: Decodable { let php: String? } diff --git a/phpmon/Domain/Integrations/Composer/ComposerWindow.swift b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift new file mode 100644 index 0000000..c0db2fc --- /dev/null +++ b/phpmon/Domain/Integrations/Composer/ComposerWindow.swift @@ -0,0 +1,126 @@ +// +// MainMenu+Composer.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 08/02/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class ComposerWindow { + + private var menu: MainMenu? = nil + private var shouldNotify: Bool! = nil + private var completion: ((Bool) -> Void)! = nil + private var window: ProgressWindowController? = nil + + /** + Updates the global dependencies and runs the completion callback when done. + */ + func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { + self.menu = MainMenu.shared + self.shouldNotify = notify + self.completion = completion + + if !Filesystem.fileExists("/usr/local/bin/composer") { + presentMissingSymlinkAlert() + return + } + + PhpEnv.shared.isBusy = true + menu?.setBusyImage() + menu?.rebuild() + + window = ProgressWindowController.display( + title: "alert.composer_progress.title".localized, + description: "alert.composer_progress.info".localized + ) + + window?.setType(info: true) + + DispatchQueue.global(qos: .userInitiated).async { [self] in + let task = Shell.user.createTask( + for: "/usr/local/bin/composer global update", requiresPath: true + ) + + DispatchQueue.main.async { + self.window?.addToConsole("/usr/local/bin/composer global update\n") + } + + task.listen( + didReceiveStandardOutputData: { string in + DispatchQueue.main.async { + self.window?.addToConsole(string) + } + // Log.perf("\(string.trimmingCharacters(in: .newlines))") + }, + didReceiveStandardErrorData: { string in + DispatchQueue.main.async { + self.window?.addToConsole(string) + } + // Log.perf("\(string.trimmingCharacters(in: .newlines))") + } + ) + + task.launch() + task.waitUntilExit() + task.haltListening() + + if task.terminationStatus <= 0 { + composerUpdateSucceeded() + } else { + composerUpdateFailed() + } + } + } + + private func composerUpdateSucceeded() { + // Closing the window should happen after a slight delay + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [self] in + window?.close() + if (shouldNotify) { + LocalNotification.send( + title: "alert.composer_success.title".localized, + subtitle: "alert.composer_success.info".localized + ) + } + window = nil + removeBusyStatus() + completion(true) + } + } + + private func composerUpdateFailed() { + // Showing that something failed should be shown immediately + DispatchQueue.main.async { [self] in + window?.setType(info: false) + window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized + window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized + window = nil + removeBusyStatus() + completion(false) + } + } + + // MARK: Main Menu Update + + private func removeBusyStatus() { + PhpEnv.shared.isBusy = false + DispatchQueue.main.async { [self] in + menu?.updatePhpVersionInStatusBar() + } + } + + // MARK: Alert + + private func presentMissingSymlinkAlert() { + BetterAlert() + .withInformation( + title: "alert.composer_missing.title".localized, + subtitle: "alert.composer_missing.info".localized + ) + .withPrimary(text: "OK") + .show() + } +} diff --git a/phpmon/Domain/Menu/MainMenu+Composer.swift b/phpmon/Domain/Menu/MainMenu+Composer.swift deleted file mode 100644 index 65c4cb7..0000000 --- a/phpmon/Domain/Menu/MainMenu+Composer.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// MainMenu+Composer.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 08/02/2022. -// Copyright © 2022 Nico Verbruggen. All rights reserved. -// - -import Foundation - -extension MainMenu { - - /** - Updates the global dependencies and runs the completion callback when done. - This method should probably be broken up into several smaller methods at some point. - */ - func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { - if !Filesystem.fileExists("/usr/local/bin/composer") { - BetterAlert() - .withInformation( - title: "alert.composer_missing.title".localized, - subtitle: "alert.composer_missing.info".localized - ) - .withPrimary(text: "OK") - .show() - - return - } - - PhpEnv.shared.isBusy = true - setBusyImage() - self.rebuild() - - let noLongerBusy = { - PhpEnv.shared.isBusy = false - DispatchQueue.main.async { [self] in - self.updatePhpVersionInStatusBar() - self.rebuild() - } - } - - var window: ProgressWindowController? = ProgressWindowController.display( - title: "alert.composer_progress.title".localized, - description: "alert.composer_progress.info".localized - ) - window?.setType(info: true) - - DispatchQueue.global(qos: .userInitiated).async { - let task = Shell.user.createTask( - for: "/usr/local/bin/composer global update", requiresPath: true - ) - - DispatchQueue.main.async { - window?.addToConsole("/usr/local/bin/composer global update\n") - } - - Shell.captureOutput( - task, - didReceiveStdOutData: { string in - DispatchQueue.main.async { - window?.addToConsole(string) - } - Log.perf("\(string.trimmingCharacters(in: .newlines))") - }, - didReceiveStdErrData: { string in - DispatchQueue.main.async { - window?.addToConsole(string) - } - Log.perf("\(string.trimmingCharacters(in: .newlines))") - } - ) - - task.launch() - task.waitUntilExit() - Shell.haltCapturingOutput(task) - - DispatchQueue.main.async { - if task.terminationStatus <= 0 { - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - window?.close() - if (notify) { - LocalNotification.send( - title: "alert.composer_success.title".localized, - subtitle: "alert.composer_success.info".localized - ) - } - window = nil - noLongerBusy() - completion(true) - } - } else { - window?.setType(info: false) - window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized - window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized - window = nil - noLongerBusy() - completion(false) - } - } - } - } -} diff --git a/phpmon/Domain/Menu/MainMenu+Switcher.swift b/phpmon/Domain/Menu/MainMenu+Switcher.swift index 6438ead..1bf33d0 100644 --- a/phpmon/Domain/Menu/MainMenu+Switcher.swift +++ b/phpmon/Domain/Menu/MainMenu+Switcher.swift @@ -39,9 +39,13 @@ extension MainMenu { // Run composer updates if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) { - self.updateGlobalDependencies(notify: false, completion: { _ in - self.notifyAboutVersionChange(to: version) - }) + ComposerWindow().updateGlobalDependencies( + notify: false, + completion: { _ in + self.notifyAboutVersionChange(to: version) + } + ) + } else { self.notifyAboutVersionChange(to: version) } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 2ad7713..982b639 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -265,7 +265,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate } @objc func updateGlobalComposerDependencies() { - self.updateGlobalDependencies(notify: true, completion: { _ in }) + ComposerWindow().updateGlobalDependencies( + notify: true, + completion: { _ in } + ) } @objc func openActiveConfigFolder() {