From 313e806414d36e34e44a65de9fa3e06130d8e575 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 17 Dec 2021 16:10:45 +0100 Subject: [PATCH 001/105] =?UTF-8?q?=E2=9C=A8=20Added=20menu=20item=20to=20?= =?UTF-8?q?run=20`composer=20global=20update`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu.swift | 24 ++++++++++++++++++++++++ phpmon/Domain/Menu/StatusMenu.swift | 7 ++++++- phpmon/Localizable.strings | 11 +++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 0a6936c..d2b71e2 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -327,6 +327,30 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } } + @objc func updateComposerDependencies() { + DispatchQueue.global(qos: .userInitiated).async { + let output = Shell.user.execute( + "composer global update", requiresPath: true, waitUntilExit: true + ) + DispatchQueue.main.async { + if output.task.terminationStatus > 0 { + // Error code means > 0 + Alert.notify( + message: "alert.composer_failure.title".localized, + info: "alert.composer_failure.info".localized, + style: .critical + ) + } else { + // Error code -1 is OK + LocalNotification.send( + title: "alert.composer_success.title".localized, + subtitle: "alert.composer_success.info".localized + ) + } + } + } + } + @objc func openActiveConfigFolder() { if (App.phpInstall!.version.error) { // php version was not identified diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 5f4662c..ee29557 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -87,10 +87,15 @@ class StatusMenu : NSMenu { // Configuration self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized)) - self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) self.addItem(NSMenuItem(title: "mi_php_config".localized, action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c")) self.addItem(NSMenuItem(title: "mi_phpinfo".localized, action: #selector(MainMenu.openPhpInfo), keyEquivalent: "i")) + // Composer + self.addItem(NSMenuItem.separator()) + self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) + self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) + self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) + if (App.shared.busy) { return } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 42050fc..916b79c 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -35,10 +35,12 @@ "mi_post_max_size" = "Max POST"; "mi_upload_max_filesize" = "Max Upload"; +"mi_composer" = "Composer"; "mi_valet_config" = "Locate Valet folder (.config/valet)"; "mi_php_config" = "Locate PHP configuration file (php.ini)"; "mi_global_composer" = "Locate global composer.json file (.composer)"; "mi_phpinfo" = "Show current configuration (phpinfo)"; +"mi_update_global_composer" = "Update global Composer dependencies"; "mi_detected_extensions" = "Detected Extensions"; "mi_no_extensions_detected" = "No additional extensions detected."; @@ -128,6 +130,15 @@ directories, since PHP Monitor controls the services."; // ALERTS +// Composer Update +"alert.composer_success.title" = "Global dependencies updated"; +"alert.composer_success.info" = "Your global Composer dependencies have been updated."; + +"alert.composer_failure.title" = "Updating global dependencies failed"; +"alert.composer_failure.info" = "Something went wrong updating your global Composer dependencies. + +To find out what went wrong, try running `composer global update` in a terminal window."; + // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP"; "alert.force_reload.info" = "This can take a while. You'll get another alert when the force reload has completed."; From 35ae681c2d28772a60256d1275d7d6b6060cf33a Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 18 Dec 2021 15:53:04 +0100 Subject: [PATCH 002/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rework=20how=20out?= =?UTF-8?q?put=20is=20handled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Helpers/Application.swift | 5 +- phpmon/Domain/Menu/MainMenu.swift | 20 +++++++- phpmon/Domain/Terminal/Shell.swift | 68 ++++++++++++++++++------- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/phpmon/Domain/Helpers/Application.swift b/phpmon/Domain/Helpers/Application.swift index 6bd64f1..e337ed5 100644 --- a/phpmon/Domain/Helpers/Application.swift +++ b/phpmon/Domain/Helpers/Application.swift @@ -40,10 +40,9 @@ class Application { /** Checks if the app is installed. */ func isInstalled() -> Bool { // If this script does not complain, the app exists! - return Shell.user.execute( + return Shell.user.executeSynchronously( "/usr/bin/open -Ra \"\(name)\"", - requiresPath: false, - waitUntilExit: true + requiresPath: false ).task.terminationStatus == 0 } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index d2b71e2..fb4e431 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -329,9 +329,25 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { @objc func updateComposerDependencies() { DispatchQueue.global(qos: .userInitiated).async { - let output = Shell.user.execute( - "composer global update", requiresPath: true, waitUntilExit: true + let output = Shell.user.executeSynchronously( + "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 { if output.task.terminationStatus > 0 { // Error code means > 0 diff --git a/phpmon/Domain/Terminal/Shell.swift b/phpmon/Domain/Terminal/Shell.swift index ddfeb02..f3e6270 100644 --- a/phpmon/Domain/Terminal/Shell.swift +++ b/phpmon/Domain/Terminal/Shell.swift @@ -62,7 +62,7 @@ class Shell { _ command: String, requiresPath: Bool = false ) -> String { - let shellOutput = self.execute(command, requiresPath: requiresPath) + let shellOutput = self.executeSynchronously(command, requiresPath: requiresPath) let hasError = ( shellOutput.standardOutput == "" && 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 waitUntilExit: Waits for the command to complete before returning the `ShellOutput` */ - func execute( + func executeSynchronously( _ command: String, - requiresPath: Bool = false, - waitUntilExit: Bool = false + requiresPath: Bool = false ) -> ShellOutput { - let task = Process() + let outputPipe = Pipe() let errorPipe = Pipe() - let tailoredCommand = requiresPath - ? "export PATH=\(Paths.binPath):$PATH && \(command)" - : command - - task.launchPath = self.shell - task.arguments = ["--login", "-c", tailoredCommand] + let task = self.createTask(for: command, requiresPath: requiresPath) task.standardOutput = outputPipe task.standardError = errorPipe task.launch() - - if waitUntilExit { - task.waitUntilExit() - } + task.waitUntilExit() return ShellOutput( standardOutput: String( - data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8 + data: outputPipe.fileHandleForReading.readDataToEndOfFile(), + encoding: .utf8 )!, errorOutput: String( - data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8 + data: errorPipe.fileHandleForReading.readDataToEndOfFile(), + encoding: .utf8 )!, task: task ) @@ -118,6 +111,47 @@ class Shell { public static func fileExists(_ path: String) -> Bool { 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 { From bd79f42e961dc76f74888885dd45465b777f9549 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 18 Dec 2021 16:06:39 +0100 Subject: [PATCH 003/105] =?UTF-8?q?=F0=9F=91=8C=20Cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Terminal/Shell.swift | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/phpmon/Domain/Terminal/Shell.swift b/phpmon/Domain/Terminal/Shell.swift index f3e6270..4f26e7c 100644 --- a/phpmon/Domain/Terminal/Shell.swift +++ b/phpmon/Domain/Terminal/Shell.swift @@ -138,18 +138,19 @@ class Shell { 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() + [(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() + } } } } From 3f0f070245a3146307463022501fb9649a1a2c22 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 18 Dec 2021 16:17:59 +0100 Subject: [PATCH 004/105] =?UTF-8?q?=F0=9F=91=8C=20Clear=20out=20extra=20ne?= =?UTF-8?q?wlines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index fb4e431..5c6eb4b 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -338,10 +338,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { Shell.captureOutput( task, didReceiveStdOutData: { string in - print("\(string)") + print("\(string.trimmingCharacters(in: .newlines))") }, didReceiveStdErrData: { string in - print("\(string)") + print("\(string.trimmingCharacters(in: .newlines))") } ) From ed493622919927d3c194454c255520506ceb08ff Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 18 Dec 2021 19:45:50 +0100 Subject: [PATCH 005/105] =?UTF-8?q?=E2=9C=A8=20Add=20option=20to=20automat?= =?UTF-8?q?ically=20run=20`composer=20global=20update`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 28 +++- .../Domain/Helpers/PMWindowController.swift | 20 +-- phpmon/Domain/Menu/MainMenu.swift | 71 ++++++--- phpmon/Domain/Preferences/Preferences.swift | 5 +- phpmon/Domain/Preferences/PrefsVC.swift | 7 + phpmon/Domain/Preferences/PrefsWC.swift | 6 - .../Domain/Progress/ProgressWindow.storyboard | 135 ++++++++++++++++++ phpmon/Domain/Progress/ProgressWindow.swift | 76 ++++++++++ phpmon/Domain/Terminal/Shell.swift | 9 ++ phpmon/Localizable.strings | 11 +- 10 files changed, 328 insertions(+), 40 deletions(-) create mode 100644 phpmon/Domain/Progress/ProgressWindow.storyboard create mode 100644 phpmon/Domain/Progress/ProgressWindow.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 71dde44..67bc156 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -46,6 +46,10 @@ C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; }; C43A8A2025D9D1D700591B77 /* brew.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew.json */; }; C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; }; + C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; + C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; + C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; + C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; }; C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; }; C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; }; @@ -164,6 +168,8 @@ C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; }; C43A8A1F25D9D1D700591B77 /* brew.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = brew.json; sourceTree = ""; }; C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = ""; }; + C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = ""; }; + C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = ""; }; C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = ""; }; C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = ""; }; C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = ""; }; @@ -325,12 +331,22 @@ C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* SiteList */, 5420395726135DB800FB00FA /* Preferences */, + C44C198F276E3A380072762D /* Progress */, C4811D2822D70D9C00B5F6B3 /* Helpers */, C4F8C0A222D4F100002EFE61 /* Extensions */, ); path = Domain; sourceTree = ""; }; + C44C198F276E3A380072762D /* Progress */ = { + isa = PBXGroup; + children = ( + C44C198C276E3A1C0072762D /* ProgressWindow.swift */, + C44C1990276E44CB0072762D /* ProgressWindow.storyboard */, + ); + path = Progress; + sourceTree = ""; + }; C464ADAA275A7A25003FCD53 /* SiteList */ = { isa = PBXGroup; children = ( @@ -544,6 +560,7 @@ C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */, C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */, C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */, + C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */, C4232EE52612526500158FC6 /* Credits.html in Resources */, 54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */, C473319F2470923A009A0597 /* Localizable.strings in Resources */, @@ -562,6 +579,7 @@ C4F780A825D80AE8000DBC97 /* php.ini in Resources */, C43A8A2025D9D1D700591B77 /* brew.json in Resources */, C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */, + C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -589,6 +607,7 @@ C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C42295DD2358D02000E263B2 /* Command.swift in Sources */, + C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, @@ -661,6 +680,7 @@ C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */, + C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, @@ -825,7 +845,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 135; + CURRENT_PROJECT_VERSION = 200; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -834,7 +854,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "4.1-rc4"; + MARKETING_VERSION = "5.0-wip"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -850,7 +870,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 135; + CURRENT_PROJECT_VERSION = 200; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -859,7 +879,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "4.1-rc4"; + MARKETING_VERSION = "5.0-wip"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/phpmon/Domain/Helpers/PMWindowController.swift b/phpmon/Domain/Helpers/PMWindowController.swift index 3165a36..d6c8f89 100644 --- a/phpmon/Domain/Helpers/PMWindowController.swift +++ b/phpmon/Domain/Helpers/PMWindowController.swift @@ -25,6 +25,18 @@ class PMWindowController: NSWindowController, NSWindowDelegate { App.shared.register(window: windowName) } + func windowWillClose(_ notification: Notification) { + App.shared.remove(window: windowName) + } + + deinit { + print("Window controller '\(windowName)' was deinitialized") + } + +} + +extension NSWindowController { + public func positionWindowInTopLeftCorner() { guard let frame = NSScreen.main?.frame else { return } guard let window = self.window else { return } @@ -37,12 +49,4 @@ class PMWindowController: NSWindowController, NSWindowDelegate { ), display: true) } - func windowWillClose(_ notification: Notification) { - App.shared.remove(window: windowName) - } - - deinit { - print("Window controller '\(windowName)' was deinitialized") - } - } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 5c6eb4b..3a21662 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -328,6 +328,16 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } @objc func updateComposerDependencies() { + self.updateGlobalDependencies(notify: true, completion: { _ in }) + } + + func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { + 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 output = Shell.user.executeSynchronously( "composer global update", requiresPath: true @@ -335,33 +345,49 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { let task = Shell.user.createTask(for: "composer global update", requiresPath: true) + DispatchQueue.main.async { + window?.addToConsole("composer global update\n") + } + Shell.captureOutput( task, didReceiveStdOutData: { string in + DispatchQueue.main.async { + window?.addToConsole(string) + } print("\(string.trimmingCharacters(in: .newlines))") }, didReceiveStdErrData: { string in + DispatchQueue.main.async { + window?.addToConsole(string) + } print("\(string.trimmingCharacters(in: .newlines))") } ) task.launch() task.waitUntilExit() + Shell.haltCapturingOutput(task) DispatchQueue.main.async { - if output.task.terminationStatus > 0 { - // Error code means > 0 - Alert.notify( - message: "alert.composer_failure.title".localized, - info: "alert.composer_failure.info".localized, - style: .critical - ) + if output.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 + completion(true) + } } else { - // Error code -1 is OK - LocalNotification.send( - title: "alert.composer_success.title".localized, - subtitle: "alert.composer_success.info".localized - ) + window?.setType(info: false) + window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized + window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized + window = nil + completion(false) } } } @@ -406,13 +432,22 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { updatePhpVersionInStatusBar() update() - // Send a notification that the switch has been completed - LocalNotification.send( - title: String(format: "notification.version_changed_title".localized, sender.version), - subtitle: String(format: "notification.version_changed_desc".localized, sender.version) - ) + let sendLocalNotification = { + LocalNotification.send( + title: String(format: "notification.version_changed_title".localized, sender.version), + subtitle: String(format: "notification.version_changed_desc".localized, sender.version) + ) + App.phpInstall?.notifyAboutBrokenPhpFpm() + } - App.phpInstall?.notifyAboutBrokenPhpFpm() + // Run composer updates + if Preferences.preferences[.autoComposerGlobalUpdateAfterSwitch] as! Bool == true { + self.updateGlobalDependencies(notify: false, completion: { success in + sendLocalNotification() + }) + } else { + sendLocalNotification() + } } } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 4cb9efc..7897c06 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -13,6 +13,7 @@ enum PreferenceName: String { case shouldDisplayDynamicIcon = "use_dynamic_icon" case fullPhpVersionDynamicIcon = "full_php_in_menu_bar" case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle" + case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch" case useInternalSwitcher = "use_phpmon_switcher" case globalHotkey = "global_hotkey" } @@ -47,7 +48,8 @@ class Preferences { PreferenceName.shouldDisplayDynamicIcon.rawValue: true, PreferenceName.fullPhpVersionDynamicIcon.rawValue: false, PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true, - PreferenceName.useInternalSwitcher.rawValue: false + PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false, + PreferenceName.useInternalSwitcher.rawValue: false, ]) if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) { @@ -72,6 +74,7 @@ class Preferences { .shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any, .fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any, .autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any, + .autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any, .useInternalSwitcher: UserDefaults.standard.bool(forKey: PreferenceName.useInternalSwitcher.rawValue) as Any, // Part 2: Always Strings diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index ee3208d..d8603b1 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -74,6 +74,13 @@ class PrefsVC: NSViewController { preference: .autoServiceRestartAfterExtensionToggle, action: {} ), + CheckboxPreferenceView.make( + sectionText: "prefs.switcher".localized, + descriptionText: "prefs.auto_composer_update_desc".localized, + checkboxText: "prefs.auto_composer_update_title".localized, + preference: .autoComposerGlobalUpdateAfterSwitch, + action: {} + ), /* DISABLED UNTIL VALET SWITCHING IS OK (see #34) CheckboxPreferenceView.make( sectionText: "", diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PrefsWC.swift index a2c69c3..f2ef5b9 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PrefsWC.swift @@ -21,12 +21,6 @@ class PrefsWC: PMWindowController { return "Preferences" } - // MARK: - Window Lifecycle - - override func windowDidLoad() { - super.windowDidLoad() - } - // MARK: - Key Interaction override func keyDown(with event: NSEvent) { diff --git a/phpmon/Domain/Progress/ProgressWindow.storyboard b/phpmon/Domain/Progress/ProgressWindow.storyboard new file mode 100644 index 0000000..d6c2e1d --- /dev/null +++ b/phpmon/Domain/Progress/ProgressWindow.storyboard @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon/Domain/Progress/ProgressWindow.swift b/phpmon/Domain/Progress/ProgressWindow.swift new file mode 100644 index 0000000..5b9bbec --- /dev/null +++ b/phpmon/Domain/Progress/ProgressWindow.swift @@ -0,0 +1,76 @@ +// +// ProgressView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 18/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation +import AppKit + +class ProgressWindowController: NSWindowController, NSWindowDelegate { + + static func display(title: String, description: String) -> ProgressWindowController { + let storyboard = NSStoryboard(name: "ProgressWindow" , bundle : nil) + + let windowController = storyboard.instantiateController( + withIdentifier: "progressWindow" + ) as! ProgressWindowController + + windowController.showWindow(windowController) + windowController.window?.makeKeyAndOrderFront(nil) + windowController.positionWindowInTopLeftCorner() + + windowController.progressView?.labelTitle.stringValue = title + windowController.progressView?.labelDescription.stringValue = description + + NSApp.activate(ignoringOtherApps: true) + + return windowController + } + + var progressView: ProgressViewController? { + return self.contentViewController as? ProgressViewController + } + + public func addToConsole(_ string: String) { + guard let textView = self.progressView?.textView else { + return + } + + textView.string = textView.string + string + textView.scrollToEndOfDocument(nil) + } + + public func setType(info: Bool = true) { + guard let imageView = self.progressView?.imageViewType else { + return + } + + imageView.image = NSImage(named: info ? "NSInfo" : "NSCaution") + } + + func windowWillClose(_ notification: Notification) { + self.contentViewController = nil + } + + deinit { + print("Deinitializing Progress Window Controller") + } + +} + +class ProgressViewController: NSViewController { + + @IBOutlet weak var labelTitle: NSTextField! + @IBOutlet weak var labelDescription: NSTextField! + + @IBOutlet var textView: NSTextView! + @IBOutlet weak var imageViewType: NSImageView! + + deinit { + print("Deinitializing Progress View Controller") + } + +} diff --git a/phpmon/Domain/Terminal/Shell.swift b/phpmon/Domain/Terminal/Shell.swift index 4f26e7c..fd4d650 100644 --- a/phpmon/Domain/Terminal/Shell.swift +++ b/phpmon/Domain/Terminal/Shell.swift @@ -153,6 +153,15 @@ class Shell { } } } + + 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) + } + } } class ShellOutput { diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 916b79c..589d46c 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -90,6 +90,7 @@ "prefs.global_shortcut" = "Global shortcut:"; "prefs.dynamic_icon" = "Dynamic icon:"; "prefs.services" = "Services:"; +"prefs.switcher" = "Switcher:"; "prefs.auto_restart_services_title" = "Auto-restart PHP-FPM"; "prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when\nyou check or uncheck an extension. Slightly slower when enabled, \nbut this applies the extension change immediately for all sites \nyou're serving, no need to restart PHP-FPM manually."; @@ -108,6 +109,9 @@ switcher or are having issues with `valet use php`, you may use PHP Monitor's own switcher which is slightly faster, but might cause issues with permissions in your Homebrew directories, since PHP Monitor controls the services."; + +"prefs.auto_composer_update_title" = "Automatically run `composer global update`"; +"prefs.auto_composer_update_desc" = "When checked, will automatically ask Composer to run\n`global update` whenever you switch versions. This will update\nall global dependencies every time you switch."; "prefs.shortcut_set" = "Set global shortcut"; "prefs.shortcut_listening" = ""; @@ -131,13 +135,14 @@ directories, since PHP Monitor controls the services."; // ALERTS // Composer Update +"alert.composer_progress.title" = "Updating global dependencies..."; +"alert.composer_progress.info" = "Your global Composer dependencies are being updated. Please wait a bit!"; + "alert.composer_success.title" = "Global dependencies updated"; "alert.composer_success.info" = "Your global Composer dependencies have been updated."; "alert.composer_failure.title" = "Updating global dependencies failed"; -"alert.composer_failure.info" = "Something went wrong updating your global Composer dependencies. - -To find out what went wrong, try running `composer global update` in a terminal window."; +"alert.composer_failure.info" = "Something went wrong updating your global Composer dependencies. You can find more information in the terminal output below. You’ll have to manually fix this problem in your own terminal."; // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP"; From 267a1dac9416bc0acd57d7f2269a5f627f9211a5 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Dec 2021 12:27:34 +0100 Subject: [PATCH 006/105] =?UTF-8?q?=F0=9F=91=8C=20Various=20QoL=20improvem?= =?UTF-8?q?ents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ensure composer global update cannot run twice (#71) - Set busy status when updating dependencies (#71) - Further reorganized menu items (#69) - Use consistent capitals in menu items - Fix preferences screen layout (auto newlines due to fixed width) --- phpmon/Domain/Core/Base.lproj/Main.storyboard | 7 ++- phpmon/Domain/Menu/MainMenu.swift | 18 ++++-- phpmon/Domain/Menu/StatusMenu.swift | 47 +++++++------- .../Views/HotkeyPreferenceView.xib | 2 +- .../Domain/Progress/ProgressWindow.storyboard | 2 +- phpmon/Domain/Progress/ProgressWindow.swift | 4 +- phpmon/Localizable.strings | 62 +++++++++---------- 7 files changed, 78 insertions(+), 64 deletions(-) diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index 3182914..05e731f 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -334,11 +334,14 @@ - + - + + + + diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 3a21662..f18d89d 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -131,10 +131,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { menu.addItem(NSMenuItem.separator()) // Add services - menu.addServicesMenuItems() - menu.addItem(NSMenuItem.separator()) - - // Add information about services & actions menu.addPhpConfigurationMenuItems() menu.addItem(NSMenuItem.separator()) @@ -332,6 +328,18 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { + App.shared.busy = true + setBusyImage() + self.update() + + let noLongerBusy = { + App.shared.busy = false + DispatchQueue.main.async { [self] in + self.updatePhpVersionInStatusBar() + self.update() + } + } + var window: ProgressWindowController? = ProgressWindowController.display( title: "alert.composer_progress.title".localized, description: "alert.composer_progress.info".localized @@ -380,6 +388,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { ) } window = nil + noLongerBusy() completion(true) } } else { @@ -387,6 +396,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { 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/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index ee29557..74bde44 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -39,38 +39,36 @@ class StatusMenu : NSMenu { } func addServicesMenuItems() { - self.addItem(HeaderView.asMenuItem(text: "mi_active_services".localized)) - let services = NSMenuItem(title: "mi_manage_services".localized, action: nil, keyEquivalent: "") let servicesMenu = NSMenu() - servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")) - servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")) - servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n")) - servicesMenu.addItem( - NSMenuItem(title: "mi_stop_all_services".localized, action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"), - withKeyModifier: [.command, .shift]) - servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s")) - for item in servicesMenu.items { - item.target = MainMenu.shared - } - self.setSubmenu(servicesMenu, for: services) - self.addForceLoadLatestVersion() - self.addItem(services) - - } - - func addForceLoadLatestVersion() { if !App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion) { - self.addItem(NSMenuItem( + servicesMenu.addItem(NSMenuItem( title: "mi_force_load_latest_unavailable".localized(App.shared.brewPhpVersion), action: nil, keyEquivalent: "f" )) } else { - self.addItem(NSMenuItem( + servicesMenu.addItem(NSMenuItem( title: "mi_force_load_latest".localized(App.shared.brewPhpVersion), action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f")) } + + servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")) + servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")) + servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n")) + + servicesMenu.addItem( + NSMenuItem(title: "mi_stop_all_services".localized, action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"), + withKeyModifier: [.command, .shift]) + + servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s")) + + for item in servicesMenu.items { + item.target = MainMenu.shared + } + + self.setSubmenu(servicesMenu, for: services) + self.addItem(services) } func addValetMenuItems() { @@ -94,7 +92,7 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) - self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) + self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: App.shared.busy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) if (App.shared.busy) { return @@ -127,6 +125,11 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenu), keyEquivalent: "r")) + + self.addItem(NSMenuItem.separator()) + + self.addServicesMenuItems() + } private func addSwitchToPhpMenuItems() { diff --git a/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.xib b/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.xib index 08ea47d..aa835db 100644 --- a/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.xib +++ b/phpmon/Domain/Preferences/Views/HotkeyPreferenceView.xib @@ -63,7 +63,7 @@ - + diff --git a/phpmon/Domain/Progress/ProgressWindow.storyboard b/phpmon/Domain/Progress/ProgressWindow.storyboard index d6c2e1d..05cd210 100644 --- a/phpmon/Domain/Progress/ProgressWindow.storyboard +++ b/phpmon/Domain/Progress/ProgressWindow.storyboard @@ -11,7 +11,7 @@ - + diff --git a/phpmon/Domain/Progress/ProgressWindow.swift b/phpmon/Domain/Progress/ProgressWindow.swift index 5b9bbec..15613c1 100644 --- a/phpmon/Domain/Progress/ProgressWindow.swift +++ b/phpmon/Domain/Progress/ProgressWindow.swift @@ -56,7 +56,7 @@ class ProgressWindowController: NSWindowController, NSWindowDelegate { } deinit { - print("Deinitializing Progress Window Controller") + // print("Deinitializing Progress Window Controller") } } @@ -70,7 +70,7 @@ class ProgressViewController: NSViewController { @IBOutlet weak var imageViewType: NSImageView! deinit { - print("Deinitializing Progress View Controller") + // print("Deinitializing Progress View Controller") } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 589d46c..7a3f73f 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -19,17 +19,17 @@ "mi_diagnostics" = "Diagnostics"; "mi_active_services" = "Active Services"; -"mi_restart_php_fpm" = "Restart service: php"; -"mi_restart_nginx" = "Restart service: nginx"; -"mi_restart_dnsmasq" = "Restart service: dnsmasq"; -"mi_manage_services" = "Manage services"; -"mi_restart_all_services" = "Restart all services"; -"mi_stop_all_services" = "Stop all services"; -"mi_force_load_latest" = "Force load (latest) PHP %@"; -"mi_force_load_latest_unavailable" = "Force load unavailable (PHP %@ not installed)"; -"mi_php_refresh" = "Refresh information"; +"mi_restart_php_fpm" = "Restart Service: php"; +"mi_restart_nginx" = "Restart Service: nginx"; +"mi_restart_dnsmasq" = "Restart Service: dnsmasq"; +"mi_manage_services" = "Manage Services"; +"mi_restart_all_services" = "Restart All Services"; +"mi_stop_all_services" = "Stop All Services"; +"mi_force_load_latest" = "Force Load (Latest) PHP %@"; +"mi_force_load_latest_unavailable" = "Force Load Unavailable (PHP %@ Not Installed)"; +"mi_php_refresh" = "Refresh Information"; -"mi_configuration" = "Configuration"; +"mi_configuration" = "PHP Configuration"; "mi_limits" = "Limits Configuration"; "mi_memory_limit" = "Memory Limit"; "mi_post_max_size" = "Max POST"; @@ -37,15 +37,15 @@ "mi_composer" = "Composer"; "mi_valet_config" = "Locate Valet folder (.config/valet)"; -"mi_php_config" = "Locate PHP configuration file (php.ini)"; -"mi_global_composer" = "Locate global composer.json file (.composer)"; -"mi_phpinfo" = "Show current configuration (phpinfo)"; -"mi_update_global_composer" = "Update global Composer dependencies"; +"mi_php_config" = "Locate PHP Configuration File (php.ini)"; +"mi_global_composer" = "Locate Global Composer File (.composer)"; +"mi_phpinfo" = "Show Current Configuration (phpinfo)"; +"mi_update_global_composer" = "Update Global Composer Dependencies..."; "mi_detected_extensions" = "Detected Extensions"; "mi_no_extensions_detected" = "No additional extensions detected."; "mi_valet" = "Laravel Valet"; -"mi_sitelist" = "View linked & parked domains..."; +"mi_sitelist" = "View Linked and Parked Domains..."; "mi_preferences" = "Preferences..."; "mi_quit" = "Quit PHP Monitor"; @@ -93,30 +93,25 @@ "prefs.switcher" = "Switcher:"; "prefs.auto_restart_services_title" = "Auto-restart PHP-FPM"; -"prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when\nyou check or uncheck an extension. Slightly slower when enabled, \nbut this applies the extension change immediately for all sites \nyou're serving, no need to restart PHP-FPM manually."; +"prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when you check or uncheck an extension. Slightly slower when enabled, but this applies the extension change immediately for all sites you're serving, no need to restart PHP-FPM manually."; "prefs.dynamic_icon_title" = "Display dynamic icon in menu bar"; -"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible.\nIf checked, it will display the major version number of the\ncurrently linked PHP version."; +"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible. If checked, it will display the major version number of the currently linked PHP version."; "prefs.display_full_php_version" = "Display full PHP version in menu bar"; -"prefs.display_full_php_version_desc" = "Display the full version instead of the major version only.\n(This may be undesirable on smaller displays,\nso this is disabled by default.)"; +"prefs.display_full_php_version_desc" = "Display the full version instead of the major version only. (This may be undesirable on smaller displays, so this is disabled by default.)"; "prefs.use_internal_switcher" = "Use PHP Monitor’s own version switcher"; "prefs.use_internal_switcher_desc" = -"By default, PHP Monitor will attempt to use Laravel Valet -in order to switch PHP versions. If you prefer a different -switcher or are having issues with `valet use php`, you may -use PHP Monitor's own switcher which is slightly faster, -but might cause issues with permissions in your Homebrew -directories, since PHP Monitor controls the services."; +"By default, PHP Monitor will attempt to use Laravel Valet in order to switch PHP versions. If you prefer a different switcher or are having issues with `valet use php`, you may use PHP Monitor's own switcher which is slightly faster, but might cause issues with permissions in your Homebrew directories, since PHP Monitor controls the services."; -"prefs.auto_composer_update_title" = "Automatically run `composer global update`"; -"prefs.auto_composer_update_desc" = "When checked, will automatically ask Composer to run\n`global update` whenever you switch versions. This will update\nall global dependencies every time you switch."; +"prefs.auto_composer_update_title" = "Automatically update global dependencies"; +"prefs.auto_composer_update_desc" = "When checked, will automatically ask Composer to run `composer global update` whenever you switch between different PHP versions. You will be able to see what changes are being made, or should this fail."; "prefs.shortcut_set" = "Set global shortcut"; "prefs.shortcut_listening" = ""; "prefs.shortcut_clear" = "Clear"; -"prefs.shortcut_desc" = "If a shortcut combination is set up, you can toggle PHP Monitor\nwherever you are by pressing the key combination you chose.\n(Cancel choosing a shortcut by pressing the spacebar.)"; +"prefs.shortcut_desc" = "If a shortcut combination is set up, you can toggle PHP Monitor wherever you are by pressing the key combination you chose. (Cancel choosing a shortcut by pressing the spacebar.)"; // NOTIFICATIONS @@ -136,13 +131,16 @@ directories, since PHP Monitor controls the services."; // Composer Update "alert.composer_progress.title" = "Updating global dependencies..."; -"alert.composer_progress.info" = "Your global Composer dependencies are being updated. Please wait a bit!"; +"alert.composer_progress.info" = "You can see the progress in the terminal output below."; -"alert.composer_success.title" = "Global dependencies updated"; -"alert.composer_success.info" = "Your global Composer dependencies have been updated."; +"alert.composer_failure.title" = "Something went wrong!"; +"alert.composer_failure.info" = "Your global Composer dependencies could not be updated. +You can find more information in the terminal output below. You’ll have to fix this +problem manually, using your own Terminal app (this just shows you the output)."; -"alert.composer_failure.title" = "Updating global dependencies failed"; -"alert.composer_failure.info" = "Something went wrong updating your global Composer dependencies. You can find more information in the terminal output below. You’ll have to manually fix this problem in your own terminal."; + +"alert.composer_success.title" = "Composer’s done updating!"; +"alert.composer_success.info" = "Your global Composer dependencies have been successfully updated."; // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP"; From 0d75e4c3b2f88ae703480ad0639bd7213a082658 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Dec 2021 12:56:16 +0100 Subject: [PATCH 007/105] =?UTF-8?q?=F0=9F=94=80=20Merge=20WIP=20changes=20?= =?UTF-8?q?from=20feature/config-watcher=20into=205.x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 +++ phpmon/Domain/Core/App+ConfigWatch.swift | 44 +++++++++ phpmon/Domain/Core/App.swift | 14 ++- phpmon/Domain/Core/PhpConfigWatcher.swift | 113 ++++++++++++++++++++++ phpmon/Domain/Menu/MainMenu.swift | 7 ++ phpmon/Domain/Menu/StatusMenu.swift | 2 +- 6 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 phpmon/Domain/Core/App+ConfigWatch.swift create mode 100644 phpmon/Domain/Core/PhpConfigWatcher.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 67bc156..a6967db 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -90,6 +90,10 @@ C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; }; C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; + C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; + C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; + C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; + C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; @@ -197,6 +201,8 @@ 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 = ""; }; + 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 = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; @@ -421,7 +427,9 @@ C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */, C4811D2322D70A4700B5F6B3 /* App.swift */, C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */, + C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */, C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, + C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, C41C1B4C22B0215A00E7CF16 /* Actions.swift */, ); @@ -597,6 +605,7 @@ C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, + C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, @@ -618,6 +627,7 @@ C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, 54AB03262763858F00A29D5F /* Timer.swift in Sources */, + C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, @@ -654,6 +664,7 @@ C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */, C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */, + C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */, @@ -661,6 +672,7 @@ C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */, C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */, C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */, + C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C42759682627662800093CAE /* NSMenuExtension.swift in Sources */, C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, diff --git a/phpmon/Domain/Core/App+ConfigWatch.swift b/phpmon/Domain/Core/App+ConfigWatch.swift new file mode 100644 index 0000000..33e11bd --- /dev/null +++ b/phpmon/Domain/Core/App+ConfigWatch.swift @@ -0,0 +1,44 @@ +// +// App+ConfigWatch.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 30/03/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension App { + + func startWatcher(_ url: URL) { + print("No watcher currently active...") + self.watcher = PhpConfigWatcher(for: url) + + self.watcher.didChange = { url in + // TODO: Make sure this is debounced, because a single process may update the config file many times; this occurs when installing Xdebug, for example + print("Something has changed in: \(url)") + MainMenu.shared.reloadPhpMonitorMenuInBackground() + } + } + + func handlePhpConfigWatcher() { + if self.currentInstall != nil { + // Determine the path of the config folder + let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(self.currentInstall!.version.short)") + + // Watcher needs to be created + if self.watcher == nil { + startWatcher(url) + } + + // Watcher needs to be updated + if self.watcher.url != url { + self.watcher.disable() + self.watcher = nil + print("Watcher has stopped watching files. Starting new one...") + startWatcher(url) + } + } + } + +} diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 73ec015..9b77ed1 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -45,9 +45,13 @@ class App { /** Whether the application is busy switching versions. */ var busy: Bool = false - + /** The currently active installation of PHP. */ - var currentInstall: ActivePhpInstallation? = nil + var currentInstall: ActivePhpInstallation? = nil { + didSet { + handlePhpConfigWatcher() + } + } /** All available versions of PHP. */ var availablePhpVersions: [String] = [] @@ -102,4 +106,10 @@ class App { */ var openWindows: [String] = [] + // MARK: - App Watchers + + /** + The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder. + */ + var watcher: PhpConfigWatcher! } diff --git a/phpmon/Domain/Core/PhpConfigWatcher.swift b/phpmon/Domain/Core/PhpConfigWatcher.swift new file mode 100644 index 0000000..5312d92 --- /dev/null +++ b/phpmon/Domain/Core/PhpConfigWatcher.swift @@ -0,0 +1,113 @@ +// +// FolderWatcher.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 30/03/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class PhpConfigWatcher { + + let folderMonitorQueue = DispatchQueue(label: "FolderMonitorQueue", attributes: .concurrent) + + let url: URL + + var didChange: ((URL) -> Void)? + + var watchers: [FSWatcher] = [] + + init(for url: URL) { + self.url = url + + // Add a watcher for php.ini + self.addWatcher(for: self.url.appendingPathComponent("php.ini"), eventMask: .write) + + // Add a watcher for conf.d (in case a new file is added or a file is deleted) + // TODO: Make sure that the contents of the conf.d folder is checked each time... this might mean + // that watchers are due for deletion / need to be created + self.addWatcher(for: self.url.appendingPathComponent("conf.d"), eventMask: .all) + + // Scan the conf.d folder for .ini files, and add a watcher for each file + let enumerator = FileManager.default.enumerator(atPath: self.url.appendingPathComponent("conf.d").path) + let filePaths = enumerator?.allObjects as! [String] + + // Loop over the .ini files that we discovered + filePaths.filter { $0.contains(".ini") }.forEach { (file) in + // Add a watcher for each file we have discovered + self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write) + } + + print("A watcher exists for the following config paths:") + for watcher in self.watchers { + print(watcher.url) + } + } + + func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent) { + let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self) + self.watchers.append(watcher) + } + + func disable() { + print("Turning off existing watchers...") + self.watchers.forEach { (watcher) in + watcher.stopMonitoring() + } + } + + deinit { + print("An existing config watcher has been deinitialized.") + } + +} + +class FSWatcher { + + private var parent: PhpConfigWatcher! + + private var monitoredFolderFileDescriptor: CInt = -1 + + private var folderMonitorSource: DispatchSourceFileSystemObject? + + let url: URL + + init(for url: URL, eventMask: DispatchSource.FileSystemEvent, parent: PhpConfigWatcher) { + self.url = url + self.parent = parent + self.startMonitoring(eventMask) + } + + func startMonitoring(_ eventMask: DispatchSource.FileSystemEvent) { + guard folderMonitorSource == nil && monitoredFolderFileDescriptor == -1 else { + return + } + + // Open the file or folder referenced by URL for monitoring only. + monitoredFolderFileDescriptor = open(url.path, O_EVTONLY) + + folderMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredFolderFileDescriptor, eventMask: eventMask, queue: parent.folderMonitorQueue) + + // Define the block to call when a file change is detected. + folderMonitorSource?.setEventHandler { [weak self] in + self?.parent.didChange?(self!.url) + } + + // Define a cancel handler to ensure the directory is closed when the source is cancelled. + folderMonitorSource?.setCancelHandler { [weak self] in + guard let self = self else { return } + close(self.monitoredFolderFileDescriptor) + self.monitoredFolderFileDescriptor = -1 + self.folderMonitorSource = nil + } + + // Start monitoring the directory via the source. + folderMonitorSource?.resume() + } + + func stopMonitoring() { + folderMonitorSource?.cancel() + self.parent = nil + } +} diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index f18d89d..9f5486a 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -221,6 +221,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } } + @objc func reloadPhpMonitorMenuInBackground() { + waitAndExecute { + // This automatically reloads the menu + print("Reloading information about the PHP installation (in the background)...") + } + } + @objc func reloadPhpMonitorMenu() { waitAndExecute { // This automatically reloads the menu diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 74bde44..5a10369 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -189,4 +189,4 @@ class ExtensionMenuItem: NSMenuItem { class EditorMenuItem: NSMenuItem { var editor: Application? = nil -} +} \ No newline at end of file From ea9538f116fe28d0d8c170d54a9f4b2fa50a8ec8 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Dec 2021 13:58:25 +0100 Subject: [PATCH 008/105] =?UTF-8?q?=E2=9C=A8=20Ensure=20watcher=20does=20n?= =?UTF-8?q?ot=20fire=20too=20many=20times?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 ++++++++++-- phpmon/Domain/Menu/StatusMenu.swift | 18 ++++++++++++------ .../{Core => Watcher}/App+ConfigWatch.swift | 10 ++++++++-- .../{Core => Watcher}/PhpConfigWatcher.swift | 2 ++ phpmon/Localizable.strings | 4 ++++ 5 files changed, 36 insertions(+), 10 deletions(-) rename phpmon/Domain/{Core => Watcher}/App+ConfigWatch.swift (71%) rename phpmon/Domain/{Core => Watcher}/PhpConfigWatcher.swift (98%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index a6967db..616592a 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -338,6 +338,7 @@ C464ADAA275A7A25003FCD53 /* SiteList */, 5420395726135DB800FB00FA /* Preferences */, C44C198F276E3A380072762D /* Progress */, + C4C8E81D276F5686003AC782 /* Watcher */, C4811D2822D70D9C00B5F6B3 /* Helpers */, C4F8C0A222D4F100002EFE61 /* Extensions */, ); @@ -427,15 +428,22 @@ C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */, C4811D2322D70A4700B5F6B3 /* App.swift */, C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */, - C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */, C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, - C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, C41C1B4C22B0215A00E7CF16 /* Actions.swift */, ); path = Core; sourceTree = ""; }; + C4C8E81D276F5686003AC782 /* Watcher */ = { + isa = PBXGroup; + children = ( + C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */, + C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */, + ); + path = Watcher; + sourceTree = ""; + }; C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = { isa = PBXGroup; children = ( diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 5a10369..609f2cc 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -39,9 +39,11 @@ class StatusMenu : NSMenu { } func addServicesMenuItems() { - let services = NSMenuItem(title: "mi_manage_services".localized, action: nil, keyEquivalent: "") + let services = NSMenuItem(title: "mi_toolkit".localized, action: nil, keyEquivalent: "") let servicesMenu = NSMenu() + servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) + if !App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion) { servicesMenu.addItem(NSMenuItem( title: "mi_force_load_latest_unavailable".localized(App.shared.brewPhpVersion), @@ -53,6 +55,8 @@ class StatusMenu : NSMenu { action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f")) } + servicesMenu.addItem(NSMenuItem(title: "mi_services".localized, action: nil, keyEquivalent: "")) + servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")) servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")) servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n")) @@ -63,10 +67,16 @@ class StatusMenu : NSMenu { servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s")) + servicesMenu.addItem(NSMenuItem(title: "mi_manual_actions".localized, action: nil, keyEquivalent: "")) + + servicesMenu.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenu), keyEquivalent: "r")) + for item in servicesMenu.items { item.target = MainMenu.shared } + + self.setSubmenu(servicesMenu, for: services) self.addItem(services) } @@ -124,10 +134,6 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) - self.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenu), keyEquivalent: "r")) - - self.addItem(NSMenuItem.separator()) - self.addServicesMenuItems() } @@ -189,4 +195,4 @@ class ExtensionMenuItem: NSMenuItem { class EditorMenuItem: NSMenuItem { var editor: Application? = nil -} \ No newline at end of file +} diff --git a/phpmon/Domain/Core/App+ConfigWatch.swift b/phpmon/Domain/Watcher/App+ConfigWatch.swift similarity index 71% rename from phpmon/Domain/Core/App+ConfigWatch.swift rename to phpmon/Domain/Watcher/App+ConfigWatch.swift index 33e11bd..c550f8f 100644 --- a/phpmon/Domain/Core/App+ConfigWatch.swift +++ b/phpmon/Domain/Watcher/App+ConfigWatch.swift @@ -15,9 +15,15 @@ extension App { self.watcher = PhpConfigWatcher(for: url) self.watcher.didChange = { url in - // TODO: Make sure this is debounced, because a single process may update the config file many times; this occurs when installing Xdebug, for example print("Something has changed in: \(url)") - MainMenu.shared.reloadPhpMonitorMenuInBackground() + + // Check if the watcher has last updated the menu less than 0.75s ago + let distance = self.watcher.lastUpdate?.distance(to: Date().timeIntervalSince1970) + if distance == nil || distance != nil && distance! > 0.75 { + print("Refreshing menu...") + MainMenu.shared.reloadPhpMonitorMenuInBackground() + self.watcher.lastUpdate = Date().timeIntervalSince1970 + } } } diff --git a/phpmon/Domain/Core/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift similarity index 98% rename from phpmon/Domain/Core/PhpConfigWatcher.swift rename to phpmon/Domain/Watcher/PhpConfigWatcher.swift index 5312d92..0d6608e 100644 --- a/phpmon/Domain/Core/PhpConfigWatcher.swift +++ b/phpmon/Domain/Watcher/PhpConfigWatcher.swift @@ -16,6 +16,8 @@ class PhpConfigWatcher { var didChange: ((URL) -> Void)? + var lastUpdate: TimeInterval? = nil + var watchers: [FSWatcher] = [] init(for url: URL) { diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 7a3f73f..7e95f30 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -34,6 +34,10 @@ "mi_memory_limit" = "Memory Limit"; "mi_post_max_size" = "Max POST"; "mi_upload_max_filesize" = "Max Upload"; +"mi_manual_actions" = "Manual Actions"; +"mi_services" = "Services"; +"mi_help" = "First Aid"; +"mi_toolkit" = "Toolkit"; "mi_composer" = "Composer"; "mi_valet_config" = "Locate Valet folder (.config/valet)"; From e7df254dccfe9b86b16dce716b37f35577fe8115 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Dec 2021 14:11:25 +0100 Subject: [PATCH 009/105] =?UTF-8?q?=E2=9C=A8=20Make=20sure=20that=20watche?= =?UTF-8?q?rs=20reload=20if=20the=20.conf.d=20dir=20contents=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Watcher/App+ConfigWatch.swift | 4 +-- phpmon/Domain/Watcher/PhpConfigWatcher.swift | 37 ++++++++++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/phpmon/Domain/Watcher/App+ConfigWatch.swift b/phpmon/Domain/Watcher/App+ConfigWatch.swift index c550f8f..518518a 100644 --- a/phpmon/Domain/Watcher/App+ConfigWatch.swift +++ b/phpmon/Domain/Watcher/App+ConfigWatch.swift @@ -27,7 +27,7 @@ extension App { } } - func handlePhpConfigWatcher() { + func handlePhpConfigWatcher(forceReload: Bool = false) { if self.currentInstall != nil { // Determine the path of the config folder let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(self.currentInstall!.version.short)") @@ -38,7 +38,7 @@ extension App { } // Watcher needs to be updated - if self.watcher.url != url { + if self.watcher.url != url || forceReload { self.watcher.disable() self.watcher = nil print("Watcher has stopped watching files. Starting new one...") diff --git a/phpmon/Domain/Watcher/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift index 0d6608e..23b1051 100644 --- a/phpmon/Domain/Watcher/PhpConfigWatcher.swift +++ b/phpmon/Domain/Watcher/PhpConfigWatcher.swift @@ -27,9 +27,8 @@ class PhpConfigWatcher { self.addWatcher(for: self.url.appendingPathComponent("php.ini"), eventMask: .write) // Add a watcher for conf.d (in case a new file is added or a file is deleted) - // TODO: Make sure that the contents of the conf.d folder is checked each time... this might mean - // that watchers are due for deletion / need to be created - self.addWatcher(for: self.url.appendingPathComponent("conf.d"), eventMask: .all) + // This watcher, when triggered, will restart all watchers + self.addWatcher(for: self.url.appendingPathComponent("conf.d"), eventMask: .all, behaviour: .reloadsWatchers) // Scan the conf.d folder for .ini files, and add a watcher for each file let enumerator = FileManager.default.enumerator(atPath: self.url.appendingPathComponent("conf.d").path) @@ -47,8 +46,8 @@ class PhpConfigWatcher { } } - func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent) { - let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self) + func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour = .reloadsMenu) { + let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self, behaviour: behaviour) self.watchers.append(watcher) } @@ -65,6 +64,11 @@ class PhpConfigWatcher { } +enum FSWatcherBehaviour { + case reloadsMenu + case reloadsWatchers +} + class FSWatcher { private var parent: PhpConfigWatcher! @@ -75,25 +79,36 @@ class FSWatcher { let url: URL - init(for url: URL, eventMask: DispatchSource.FileSystemEvent, parent: PhpConfigWatcher) { + init(for url: URL, eventMask: DispatchSource.FileSystemEvent, parent: PhpConfigWatcher, behaviour: FSWatcherBehaviour = .reloadsMenu) { self.url = url self.parent = parent - self.startMonitoring(eventMask) + self.startMonitoring(eventMask, behaviour: behaviour) } - func startMonitoring(_ eventMask: DispatchSource.FileSystemEvent) { + func startMonitoring(_ eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour) { guard folderMonitorSource == nil && monitoredFolderFileDescriptor == -1 else { return } // Open the file or folder referenced by URL for monitoring only. monitoredFolderFileDescriptor = open(url.path, O_EVTONLY) - - folderMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredFolderFileDescriptor, eventMask: eventMask, queue: parent.folderMonitorQueue) + folderMonitorSource = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: monitoredFolderFileDescriptor, + eventMask: eventMask, + queue: parent.folderMonitorQueue + ) // Define the block to call when a file change is detected. folderMonitorSource?.setEventHandler { [weak self] in - self?.parent.didChange?(self!.url) + // The default behaviour is to reload the menu + switch behaviour { + case .reloadsMenu: + // Default behaviour: reload the menu items + self?.parent.didChange?(self!.url) + case .reloadsWatchers: + // Alternative behaviour: reload all watchers + App.shared.handlePhpConfigWatcher(forceReload: true) + } } // Define a cancel handler to ensure the directory is closed when the source is cancelled. From fe695bb026bb24d752e56499f9f128f0a64fc346 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 19 Dec 2021 14:16:49 +0100 Subject: [PATCH 010/105] =?UTF-8?q?=F0=9F=91=8C=20Nicer=20logging=20of=20m?= =?UTF-8?q?ultiple=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Watcher/PhpConfigWatcher.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpmon/Domain/Watcher/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift index 23b1051..cd5e6df 100644 --- a/phpmon/Domain/Watcher/PhpConfigWatcher.swift +++ b/phpmon/Domain/Watcher/PhpConfigWatcher.swift @@ -41,9 +41,9 @@ class PhpConfigWatcher { } print("A watcher exists for the following config paths:") - for watcher in self.watchers { - print(watcher.url) - } + print(self.watchers.map({ watcher in + return watcher.url.relativePath + })) } func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour = .reloadsMenu) { From fa2de1f77cf79d07db779fdc956c625315823932 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 20 Dec 2021 17:20:43 +0100 Subject: [PATCH 011/105] =?UTF-8?q?=F0=9F=94=80=20Merge=20in=20changes=20f?= =?UTF-8?q?rom=20SwiftUI=20previews=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 26 +++++++++++++++++++++ phpmon/Domain/Preferences/PrefsVC.swift | 2 +- phpmon/Domain/SwiftUI/PMHeaderView.swift | 29 ++++++++++++++++++++++++ phpmon/Domain/SwiftUI/PMStatsView.swift | 29 ++++++++++++++++++++++++ phpmon/Domain/SwiftUI/Preview.swift | 19 ++++++++++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 phpmon/Domain/SwiftUI/PMHeaderView.swift create mode 100644 phpmon/Domain/SwiftUI/PMStatsView.swift create mode 100644 phpmon/Domain/SwiftUI/Preview.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 616592a..b91113f 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -98,6 +98,12 @@ C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; + C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; + C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; + C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A727708B9E001DF387 /* Preview.swift */; }; + C4EE55AC27708B9E001DF387 /* Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A727708B9E001DF387 /* Preview.swift */; }; + C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A827708B9E001DF387 /* PMStatsView.swift */; }; + C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A827708B9E001DF387 /* PMStatsView.swift */; }; C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; @@ -208,6 +214,9 @@ C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + C4EE55A627708B9E001DF387 /* PMHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMHeaderView.swift; sourceTree = ""; }; + C4EE55A727708B9E001DF387 /* Preview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preview.swift; sourceTree = ""; }; + C4EE55A827708B9E001DF387 /* PMStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMStatsView.swift; sourceTree = ""; }; C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewDiagnostics.swift; sourceTree = ""; }; C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = ""; }; C4F7807425D7F7E5000DBC97 /* RELEASE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RELEASE.md; sourceTree = ""; }; @@ -341,6 +350,7 @@ C4C8E81D276F5686003AC782 /* Watcher */, C4811D2822D70D9C00B5F6B3 /* Helpers */, C4F8C0A222D4F100002EFE61 /* Extensions */, + C4EE55B027708BB2001DF387 /* SwiftUI */, ); path = Domain; sourceTree = ""; @@ -444,6 +454,16 @@ path = Watcher; sourceTree = ""; }; + C4EE55B027708BB2001DF387 /* SwiftUI */ = { + isa = PBXGroup; + children = ( + C4EE55A627708B9E001DF387 /* PMHeaderView.swift */, + C4EE55A827708B9E001DF387 /* PMStatsView.swift */, + C4EE55A727708B9E001DF387 /* Preview.swift */, + ); + path = SwiftUI; + sourceTree = ""; + }; C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = { isa = PBXGroup; children = ( @@ -613,6 +633,7 @@ C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, + C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */, C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */, @@ -620,9 +641,11 @@ C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */, C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */, + C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, + C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */, C42295DD2358D02000E263B2 /* Command.swift in Sources */, C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, @@ -661,9 +684,11 @@ buildActionMask = 2147483647; files = ( 54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */, + C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */, C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */, 54AB03272763858F00A29D5F /* Timer.swift in Sources */, 54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, + C4EE55AC27708B9E001DF387 /* Preview.swift in Sources */, 54B48B60275F66AE006D90C5 /* Application.swift in Sources */, C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, @@ -714,6 +739,7 @@ C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */, C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */, C4F7809F25D8037C000DBC97 /* Command.swift in Sources */, + C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */, C4F780B425D80B51000DBC97 /* Actions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index d8603b1..b2c872b 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -97,7 +97,7 @@ class PrefsVC: NSViewController { ].forEach({ self.stackView.addArrangedSubview($0) }) } - // MARK: - Listening for hotkey dleegate + // MARK: - Listening for hotkey delegate var listeningForHotkeyView: HotkeyPreferenceView? = nil diff --git a/phpmon/Domain/SwiftUI/PMHeaderView.swift b/phpmon/Domain/SwiftUI/PMHeaderView.swift new file mode 100644 index 0000000..ac2317c --- /dev/null +++ b/phpmon/Domain/SwiftUI/PMHeaderView.swift @@ -0,0 +1,29 @@ +// +// PMHeaderView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 15/04/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +@available(OSX 11.0, *) +struct PMHeaderView: View { + @State var content: String = "Your Title Here" + + var body: some View { + PMHeader(labelText: $content).frame(minWidth: 0, maxWidth: 450, minHeight: 0, maxHeight: 50) + } +} + +@available(OSX 11.0, *) +struct PMHeader: NSViewRepresentable { + @Binding var labelText: String + + func makeNSView(context: Context) -> some NSView { + return HeaderView.asMenuItem(text: labelText).view! + } + + func updateNSView(_ nsView: NSViewType, context: Context) {} +} diff --git a/phpmon/Domain/SwiftUI/PMStatsView.swift b/phpmon/Domain/SwiftUI/PMStatsView.swift new file mode 100644 index 0000000..151bedf --- /dev/null +++ b/phpmon/Domain/SwiftUI/PMStatsView.swift @@ -0,0 +1,29 @@ +// +// PMStatsView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 15/04/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import SwiftUI + +@available(OSX 11.0, *) +struct PMStats: NSViewRepresentable { + @Binding var labelText: String + + func makeNSView(context: Context) -> some NSView { + return StatsView.asMenuItem(memory: labelText, post: labelText, upload: labelText).view! + } + + func updateNSView(_ nsView: NSViewType, context: Context) {} +} + +@available(OSX 11.0, *) +struct PMStatsView: View { + @State var content: String = "5 MB" + + var body: some View { + PMStats(labelText: $content).frame(minWidth: 0, maxWidth: 450, minHeight: 0, maxHeight: 80) + } +} diff --git a/phpmon/Domain/SwiftUI/Preview.swift b/phpmon/Domain/SwiftUI/Preview.swift new file mode 100644 index 0000000..d5b1af6 --- /dev/null +++ b/phpmon/Domain/SwiftUI/Preview.swift @@ -0,0 +1,19 @@ +// +// Preview.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 15/04/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import SwiftUI +import Cocoa + +@available(OSX 11.0, *) +struct Preview_Previews: PreviewProvider { + static var previews: some View { + PMHeaderView(content: "You are running PHP 8.1") + PMStatsView(content: "15 MB") + PMStatsView(content: "2 GB") + } +} From 1ca49f6cbc10419c005add007191c294eccc2c20 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 20 Dec 2021 18:25:52 +0100 Subject: [PATCH 012/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Reorganise=20code?= =?UTF-8?q?=20for=20optimal=20code=20sharing,=20add=20phpmon-cli?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Moved over common functionality to package - Added phpmon-cli target (for fast switching via the terminal) --- PHP Monitor.xcodeproj/project.pbxproj | 157 ++++++++++++++---- .../xcschemes/PHP Monitor CLI.xcscheme | 88 ++++++++++ phpmon-cli/AllowedArguments.swift | 23 +++ phpmon-cli/main.swift | 12 ++ phpmon-cli/phpmon-cli/main.swift | 11 ++ phpmon-common/.gitignore | 7 + .../xcshareddata/xcschemes/PMCommon.xcscheme | 67 ++++++++ phpmon-common/Package.swift | 15 ++ .../Sources/PMCommon}/Command.swift | 4 +- .../Sources/PMCommon}/Paths.swift | 8 +- .../Sources/PMCommon}/Shell.swift | 28 ++-- phpmon/Domain/Core/AppDelegate.swift | 1 + phpmon/Domain/Core/Startup.swift | 1 + phpmon/Domain/Helpers/Application.swift | 1 + phpmon/Domain/Helpers/Filesystem.swift | 1 + .../Homebrew/HomebrewDiagnostics.swift | 1 + phpmon/Domain/Integrations/Valet/Valet.swift | 1 + phpmon/Domain/Menu/MainMenu.swift | 1 + phpmon/Domain/PHP/ActivePhpInstallation.swift | 1 + phpmon/Domain/PHP/PhpInstallation.swift | 1 + phpmon/Domain/SiteList/SiteListCell.swift | 1 + phpmon/Domain/SiteList/SiteListVC.swift | 1 + phpmon/Domain/Watcher/App+ConfigWatch.swift | 1 + 23 files changed, 379 insertions(+), 53 deletions(-) create mode 100644 PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme create mode 100644 phpmon-cli/AllowedArguments.swift create mode 100644 phpmon-cli/main.swift create mode 100644 phpmon-cli/phpmon-cli/main.swift create mode 100644 phpmon-common/.gitignore create mode 100644 phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme create mode 100644 phpmon-common/Package.swift rename {phpmon/Domain/Terminal => phpmon-common/Sources/PMCommon}/Command.swift (96%) rename {phpmon/Domain/Terminal => phpmon-common/Sources/PMCommon}/Paths.swift (94%) rename {phpmon/Domain/Terminal => phpmon-common/Sources/PMCommon}/Shell.swift (91%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index b91113f..8241199 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -25,19 +25,20 @@ C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; }; C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; }; C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; + C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; + C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; + C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DE2770F34D005EF286 /* AllowedArguments.swift */; }; + C415D3E22770F34D005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DF2770F34D005EF286 /* main.swift */; }; C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; }; C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; }; - C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; }; C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; }; C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; }; - C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Actions.swift */; }; C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; }; C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */; }; C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */; }; - C42295DD2358D02000E263B2 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; }; C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; }; C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; }; C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; }; @@ -73,7 +74,6 @@ C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; }; C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; - C49EAB46259FC305007F6C3B /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; }; C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; @@ -96,6 +96,7 @@ C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; + C4D309F02770F0B200958BCF /* PMCommon in Frameworks */ = {isa = PBXBuildFile; productRef = C4D309EF2770F0B200958BCF /* PMCommon */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; @@ -108,14 +109,10 @@ C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; - C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; }; C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; }; - C4F7809F25D8037C000DBC97 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; }; - C4F780A225D804AA000DBC97 /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; }; C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; }; C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */; }; C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; - C4F780B425D80B51000DBC97 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Actions.swift */; }; C4F780B725D80B5D000DBC97 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; @@ -146,6 +143,18 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + C415D3D42770F341005EF286 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = ""; }; 5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; @@ -158,6 +167,10 @@ C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = ""; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; + C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; + C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; + C415D3DE2770F34D005EF286 /* AllowedArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllowedArguments.swift; sourceTree = ""; }; + C415D3DF2770F34D005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = "../../../Desktop/phpmon-cli/main.swift"; sourceTree = ""; }; C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = ""; }; C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -165,13 +178,10 @@ C41C1B3D22B0098000E7CF16 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C41C1B3F22B0098000E7CF16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = ""; }; - C41C1B4622B009A400E7CF16 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = ""; }; C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = ""; }; - C41C1B4C22B0215A00E7CF16 /* Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = ""; }; C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+ContextMenu.swift"; sourceTree = ""; }; - C42295DC2358D02000E263B2 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = ""; }; C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = ""; }; C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = ""; }; @@ -196,7 +206,6 @@ C48D0C9925CC888B00CC7490 /* HeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = ""; }; C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; }; C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = ""; }; - C49EAB45259FC305007F6C3B /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; 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 = ""; }; @@ -210,6 +219,7 @@ 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 = ""; }; + C4D309E42770EC6F00958BCF /* phpmon-common */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "phpmon-common"; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; @@ -231,10 +241,18 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + C415D3D32770F341005EF286 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; C41C1B3022B0097F00E7CF16 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C4D309F02770F0B200958BCF /* PMCommon in Frameworks */, C4998F0626175E7200B2526E /* HotKey in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -291,6 +309,15 @@ path = IAP; sourceTree = ""; }; + C415D3D72770F341005EF286 /* phpmon-cli */ = { + isa = PBXGroup; + children = ( + C415D3DF2770F34D005EF286 /* main.swift */, + C415D3DE2770F34D005EF286 /* AllowedArguments.swift */, + ); + path = "phpmon-cli"; + sourceTree = ""; + }; C41C1B2A22B0097F00E7CF16 = { isa = PBXGroup; children = ( @@ -300,7 +327,10 @@ C4E713572570151400007428 /* docs */, C41C1B3522B0097F00E7CF16 /* phpmon */, C4F7807A25D7F84B000DBC97 /* phpmon-tests */, + C415D3D72770F341005EF286 /* phpmon-cli */, + C4D309E42770EC6F00958BCF /* phpmon-common */, C41C1B3422B0097F00E7CF16 /* Products */, + C4D309E72770EF2F00958BCF /* Frameworks */, ); sourceTree = ""; }; @@ -309,6 +339,7 @@ children = ( C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */, C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */, + C415D3D62770F341005EF286 /* phpmon-cli */, ); name = Products; sourceTree = ""; @@ -342,7 +373,6 @@ C4AF9F6B275445D300D44ED0 /* Integrations */, C4B13B1D25C4915000548C3A /* Core */, 54B20EDF263AA22C00D3250E /* PHP */, - C4F7808A25D7F918000DBC97 /* Terminal */, C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* SiteList */, 5420395726135DB800FB00FA /* Preferences */, @@ -440,7 +470,7 @@ C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */, C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, - C41C1B4C22B0215A00E7CF16 /* Actions.swift */, + C415D3B62770F294005EF286 /* Actions.swift */, ); path = Core; sourceTree = ""; @@ -454,6 +484,13 @@ path = Watcher; sourceTree = ""; }; + C4D309E72770EF2F00958BCF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; C4EE55B027708BB2001DF387 /* SwiftUI */ = { isa = PBXGroup; children = ( @@ -483,16 +520,6 @@ path = "phpmon-tests"; sourceTree = ""; }; - C4F7808A25D7F918000DBC97 /* Terminal */ = { - isa = PBXGroup; - children = ( - C49EAB45259FC305007F6C3B /* Paths.swift */, - C42295DC2358D02000E263B2 /* Command.swift */, - C41C1B4622B009A400E7CF16 /* Shell.swift */, - ); - path = Terminal; - sourceTree = ""; - }; C4F8C0A222D4F100002EFE61 /* Extensions */ = { isa = PBXGroup; children = ( @@ -507,6 +534,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + C415D3D52770F341005EF286 /* phpmon-cli */ = { + isa = PBXNativeTarget; + buildConfigurationList = C415D3DA2770F341005EF286 /* Build configuration list for PBXNativeTarget "phpmon-cli" */; + buildPhases = ( + C415D3D22770F341005EF286 /* Sources */, + C415D3D32770F341005EF286 /* Frameworks */, + C415D3D42770F341005EF286 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "phpmon-cli"; + productName = "phpmon-cli"; + productReference = C415D3D62770F341005EF286 /* phpmon-cli */; + productType = "com.apple.product-type.tool"; + }; C41C1B3222B0097F00E7CF16 /* PHP Monitor */ = { isa = PBXNativeTarget; buildConfigurationList = C41C1B4322B0098000E7CF16 /* Build configuration list for PBXNativeTarget "PHP Monitor" */; @@ -522,6 +566,7 @@ name = "PHP Monitor"; packageProductDependencies = ( C4998F0526175E7200B2526E /* HotKey */, + C4D309EF2770F0B200958BCF /* PMCommon */, ); productName = phpmon; productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */; @@ -551,10 +596,13 @@ C41C1B2B22B0097F00E7CF16 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1240; + LastSwiftUpdateCheck = 1320; LastUpgradeCheck = 1220; ORGANIZATIONNAME = "Nico Verbruggen"; TargetAttributes = { + C415D3D52770F341005EF286 = { + CreatedOnToolsVersion = 13.2.1; + }; C41C1B3222B0097F00E7CF16 = { CreatedOnToolsVersion = 10.2.1; }; @@ -582,6 +630,7 @@ targets = ( C41C1B3222B0097F00E7CF16 /* PHP Monitor */, C4F7807825D7F84B000DBC97 /* phpmon-tests */, + C415D3D52770F341005EF286 /* phpmon-cli */, ); }; /* End PBXProject section */ @@ -622,6 +671,15 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + C415D3D22770F341005EF286 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, + C415D3E22770F34D005EF286 /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C41C1B2F22B0097F00E7CF16 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -636,9 +694,7 @@ C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */, C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, - C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, - C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */, C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */, C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */, @@ -646,7 +702,7 @@ C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */, - C42295DD2358D02000E263B2 /* Command.swift in Sources */, + C415D3B72770F294005EF286 /* Actions.swift in Sources */, C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, @@ -663,7 +719,6 @@ C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, - C49EAB46259FC305007F6C3B /* Paths.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, @@ -688,6 +743,7 @@ C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */, 54AB03272763858F00A29D5F /* Timer.swift in Sources */, 54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, + C415D3B82770F294005EF286 /* Actions.swift in Sources */, C4EE55AC27708B9E001DF387 /* Preview.swift in Sources */, 54B48B60275F66AE006D90C5 /* Application.swift in Sources */, C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, @@ -720,11 +776,9 @@ C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */, 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */, - C4F780A225D804AA000DBC97 /* Paths.swift in Sources */, C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */, C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, - C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */, C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, @@ -738,9 +792,7 @@ C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */, C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */, - C4F7809F25D8037C000DBC97 /* Command.swift in Sources */, C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */, - C4F780B425D80B51000DBC97 /* Actions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -766,6 +818,32 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + C415D3DB2770F341005EF286 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 8M54J5J787; + ENABLE_HARDENED_RUNTIME = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + C415D3DC2770F341005EF286 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 8M54J5J787; + ENABLE_HARDENED_RUNTIME = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; C41C1B4122B0098000E7CF16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -978,6 +1056,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + C415D3DA2770F341005EF286 /* Build configuration list for PBXNativeTarget "phpmon-cli" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C415D3DB2770F341005EF286 /* Debug */, + C415D3DC2770F341005EF286 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C41C1B2E22B0097F00E7CF16 /* Build configuration list for PBXProject "PHP Monitor" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1024,6 +1111,10 @@ package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */; productName = HotKey; }; + C4D309EF2770F0B200958BCF /* PMCommon */ = { + isa = XCSwiftPackageProductDependency; + productName = PMCommon; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */; diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme new file mode 100644 index 0000000..b54454c --- /dev/null +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon-cli/AllowedArguments.swift b/phpmon-cli/AllowedArguments.swift new file mode 100644 index 0000000..17d6c2a --- /dev/null +++ b/phpmon-cli/AllowedArguments.swift @@ -0,0 +1,23 @@ +// +// AllowedArguments.swift +// phpmon-cli +// +// Created by Nico Verbruggen on 20/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +enum AllowedArguments: String, CaseIterable { + case use = "use" + + static func has(_ string: String) -> Bool { + return Self.allCases.contains { arg in + return arg.rawValue == string + } + } + + static var rawValues: [String] { + return Self.allCases.map { $0.rawValue } + } +} diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift new file mode 100644 index 0000000..574c395 --- /dev/null +++ b/phpmon-cli/main.swift @@ -0,0 +1,12 @@ +// +// main.swift +// phpmon-cli +// +// Created by Nico Verbruggen on 20/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +print("Hello, World!") + diff --git a/phpmon-cli/phpmon-cli/main.swift b/phpmon-cli/phpmon-cli/main.swift new file mode 100644 index 0000000..86ed8e3 --- /dev/null +++ b/phpmon-cli/phpmon-cli/main.swift @@ -0,0 +1,11 @@ +// +// main.swift +// phpmon-cli +// +// Created by Nico Verbruggen on 20/12/2021. +// + +import Foundation + +print("Hello, World!") + diff --git a/phpmon-common/.gitignore b/phpmon-common/.gitignore new file mode 100644 index 0000000..bb460e7 --- /dev/null +++ b/phpmon-common/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme b/phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme new file mode 100644 index 0000000..082c0e6 --- /dev/null +++ b/phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon-common/Package.swift b/phpmon-common/Package.swift new file mode 100644 index 0000000..cffde46 --- /dev/null +++ b/phpmon-common/Package.swift @@ -0,0 +1,15 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "PMCommon", + products: [ + .library(name: "PMCommon", targets: ["PMCommon"]), + ], + dependencies: [], + targets: [ + .target(name: "PMCommon", dependencies: []), + ] +) diff --git a/phpmon/Domain/Terminal/Command.swift b/phpmon-common/Sources/PMCommon/Command.swift similarity index 96% rename from phpmon/Domain/Terminal/Command.swift rename to phpmon-common/Sources/PMCommon/Command.swift index 31a075a..7fc07e4 100644 --- a/phpmon/Domain/Terminal/Command.swift +++ b/phpmon-common/Sources/PMCommon/Command.swift @@ -1,13 +1,13 @@ // // Command.swift -// PHP Monitor +// PMCommon // // Copyright © 2021 Nico Verbruggen. All rights reserved. // import Cocoa -class Command { +public class Command { /** Immediately executes a command. diff --git a/phpmon/Domain/Terminal/Paths.swift b/phpmon-common/Sources/PMCommon/Paths.swift similarity index 94% rename from phpmon/Domain/Terminal/Paths.swift rename to phpmon-common/Sources/PMCommon/Paths.swift index 6ffe80a..9d766ce 100644 --- a/phpmon/Domain/Terminal/Paths.swift +++ b/phpmon-common/Sources/PMCommon/Paths.swift @@ -1,20 +1,20 @@ // // Paths.swift -// PHP Monitor +// PMCommon // // Copyright © 2021 Nico Verbruggen. All rights reserved. // import Foundation -enum HomebrewDir: String { +public enum HomebrewDir: String { case opt = "/opt/homebrew" case usr = "/usr/local" } -class Paths { +public class Paths { - static let shared = Paths() + public static let shared = Paths() var baseDir : HomebrewDir init() { diff --git a/phpmon/Domain/Terminal/Shell.swift b/phpmon-common/Sources/PMCommon/Shell.swift similarity index 91% rename from phpmon/Domain/Terminal/Shell.swift rename to phpmon-common/Sources/PMCommon/Shell.swift index fd4d650..87ef921 100644 --- a/phpmon/Domain/Terminal/Shell.swift +++ b/phpmon-common/Sources/PMCommon/Shell.swift @@ -1,13 +1,13 @@ // // Shell.swift -// PHP Monitor +// PMCommon // // Copyright © 2021 Nico Verbruggen. All rights reserved. // import Cocoa -class Shell { +public class Shell { // MARK: - Invoke static functions @@ -30,12 +30,12 @@ class Shell { /** We now require macOS 11, so no need to detect which terminal to use. */ - var shell: String = "/bin/sh" + public var shell: String = "/bin/sh" /** Singleton to access a user shell (with --login) */ - static let user = Shell() + public static let user = Shell() /** Runs a shell command without using the output. @@ -44,7 +44,7 @@ class Shell { - Parameter command: The command to run - Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this */ - func run( + private func run( _ command: String, requiresPath: Bool = false ) { @@ -58,7 +58,7 @@ class Shell { - Parameter command: The command to run - Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this */ - func pipe( + private func pipe( _ command: String, requiresPath: Bool = false ) -> String { @@ -77,7 +77,7 @@ class Shell { - 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` */ - func executeSynchronously( + public func executeSynchronously( _ command: String, requiresPath: Bool = false ) -> ShellOutput { @@ -115,7 +115,7 @@ class Shell { /** Creates a new process with the correct PATH and shell. */ - func createTask(for command: String, requiresPath: Bool) -> Process { + public func createTask(for command: String, requiresPath: Bool) -> Process { let tailoredCommand = requiresPath ? "export PATH=\(Paths.binPath):$PATH && \(command)" : command @@ -127,7 +127,7 @@ class Shell { return task } - static func captureOutput( + public static func captureOutput( _ task: Process, didReceiveStdOutData: @escaping (String) -> Void, didReceiveStdErrData: @escaping (String) -> Void @@ -154,7 +154,7 @@ class Shell { } } - static func haltCapturingOutput(_ task: Process) { + public static func haltCapturingOutput(_ task: Process) { if let pipe = task.standardOutput as? Pipe { NotificationCenter.default.removeObserver(pipe.fileHandleForReading) } @@ -164,10 +164,10 @@ class Shell { } } -class ShellOutput { - let standardOutput: String - let errorOutput: String - let task: Process +public class ShellOutput { + public let standardOutput: String + public let errorOutput: String + public let task: Process init(standardOutput: String, errorOutput: String, diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index a963b6e..8e1d933 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -7,6 +7,7 @@ import Cocoa import UserNotifications +import PMCommon @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate { diff --git a/phpmon/Domain/Core/Startup.swift b/phpmon/Domain/Core/Startup.swift index b42c8ab..232dd99 100644 --- a/phpmon/Domain/Core/Startup.swift +++ b/phpmon/Domain/Core/Startup.swift @@ -6,6 +6,7 @@ // import Foundation +import PMCommon class Startup { diff --git a/phpmon/Domain/Helpers/Application.swift b/phpmon/Domain/Helpers/Application.swift index e337ed5..c7ef192 100644 --- a/phpmon/Domain/Helpers/Application.swift +++ b/phpmon/Domain/Helpers/Application.swift @@ -7,6 +7,7 @@ // import Foundation +import PMCommon /// An application that is capable of opening a particular directory (usually of a PHP project). /// In most cases this is going to be a code editor, but it could also be another application diff --git a/phpmon/Domain/Helpers/Filesystem.swift b/phpmon/Domain/Helpers/Filesystem.swift index 808f05e..2a03f73 100644 --- a/phpmon/Domain/Helpers/Filesystem.swift +++ b/phpmon/Domain/Helpers/Filesystem.swift @@ -7,6 +7,7 @@ // import Cocoa +import PMCommon class Filesystem { diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index fd327a8..811c8fb 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -7,6 +7,7 @@ // import Foundation +import PMCommon class HomebrewDiagnostics { diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 0759406..9a07f95 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -7,6 +7,7 @@ // import Foundation +import PMCommon class Valet { diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 9f5486a..764e297 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -6,6 +6,7 @@ // import Cocoa +import PMCommon class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { diff --git a/phpmon/Domain/PHP/ActivePhpInstallation.swift b/phpmon/Domain/PHP/ActivePhpInstallation.swift index 2ab3579..7867669 100644 --- a/phpmon/Domain/PHP/ActivePhpInstallation.swift +++ b/phpmon/Domain/PHP/ActivePhpInstallation.swift @@ -6,6 +6,7 @@ // import Foundation +import PMCommon /** An installed version of PHP, that was detected by scanning the `/opt/php@version/bin` directory. diff --git a/phpmon/Domain/PHP/PhpInstallation.swift b/phpmon/Domain/PHP/PhpInstallation.swift index 02577e2..1f3f45f 100644 --- a/phpmon/Domain/PHP/PhpInstallation.swift +++ b/phpmon/Domain/PHP/PhpInstallation.swift @@ -7,6 +7,7 @@ // import Foundation +import PMCommon class PhpInstallation { diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index 1bcf0f3..1463dcb 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -8,6 +8,7 @@ import Cocoa import AppKit +import PMCommon class SiteListCell: NSTableCellView { diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index b03fad1..90ef5c5 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -9,6 +9,7 @@ import Cocoa import HotKey import Carbon +import PMCommon class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { diff --git a/phpmon/Domain/Watcher/App+ConfigWatch.swift b/phpmon/Domain/Watcher/App+ConfigWatch.swift index 518518a..1bfcf3c 100644 --- a/phpmon/Domain/Watcher/App+ConfigWatch.swift +++ b/phpmon/Domain/Watcher/App+ConfigWatch.swift @@ -7,6 +7,7 @@ // import Foundation +import PMCommon extension App { From 7a3dc9a145bb6c0f7f44ef4afe35de763ea5e6bb Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 20 Dec 2021 18:31:16 +0100 Subject: [PATCH 013/105] =?UTF-8?q?=F0=9F=91=8C=20Fix=20incorrect=20main.s?= =?UTF-8?q?wift=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 8 ++++---- phpmon-cli/main.swift | 28 ++++++++++++++++++++++++++- phpmon-cli/phpmon-cli/main.swift | 11 ----------- 3 files changed, 31 insertions(+), 16 deletions(-) delete mode 100644 phpmon-cli/phpmon-cli/main.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 8241199..46c8219 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -28,7 +28,7 @@ C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DE2770F34D005EF286 /* AllowedArguments.swift */; }; - C415D3E22770F34D005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DF2770F34D005EF286 /* main.swift */; }; + C415D3E62770F540005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E52770F540005EF286 /* main.swift */; }; C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; @@ -170,7 +170,7 @@ C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; C415D3DE2770F34D005EF286 /* AllowedArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllowedArguments.swift; sourceTree = ""; }; - C415D3DF2770F34D005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = "../../../Desktop/phpmon-cli/main.swift"; sourceTree = ""; }; + C415D3E52770F540005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = ""; }; C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -312,7 +312,7 @@ C415D3D72770F341005EF286 /* phpmon-cli */ = { isa = PBXGroup; children = ( - C415D3DF2770F34D005EF286 /* main.swift */, + C415D3E52770F540005EF286 /* main.swift */, C415D3DE2770F34D005EF286 /* AllowedArguments.swift */, ); path = "phpmon-cli"; @@ -675,8 +675,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C415D3E62770F540005EF286 /* main.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, - C415D3E22770F34D005EF286 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 574c395..5070216 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -8,5 +8,31 @@ import Foundation -print("Hello, World!") +// First, let's read the initial command line argument +print(CommandLine.arguments) + +if CommandLine.arguments.count != 3 { + print("You must enter two arguments.") + exit(1) +} + +let argument = CommandLine.arguments[1] + +if !AllowedArguments.has(argument) { + print("The supported arguments are: \(AllowedArguments.rawValues)") + exit(1) +} + +let action = AllowedArguments.init(rawValue: argument) + +switch action { +case .use: + // Read the PHP value + let version = CommandLine.arguments[2] + print("Switching to PHP \(version)...") + break +case .none: + print("Action not recognized!") + exit(1) +} diff --git a/phpmon-cli/phpmon-cli/main.swift b/phpmon-cli/phpmon-cli/main.swift deleted file mode 100644 index 86ed8e3..0000000 --- a/phpmon-cli/phpmon-cli/main.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// main.swift -// phpmon-cli -// -// Created by Nico Verbruggen on 20/12/2021. -// - -import Foundation - -print("Hello, World!") - From acdcce7f7ad733c896cbab5325841ddb3cc13c1f Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 20 Dec 2021 18:39:59 +0100 Subject: [PATCH 014/105] =?UTF-8?q?=E2=9C=A8=20Add=20scheme=20to=20support?= =?UTF-8?q?=20inter-app=20communication=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 10 +++++++++- .../xcschemes/PHP Monitor.xcscheme | 2 +- phpmon/Domain/Core/Actions.swift | 1 + phpmon/Domain/Core/AppDelegate+InterApp.swift | 19 +++++++++++++++++++ phpmon/Info.plist | 13 +++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 phpmon/Domain/Core/AppDelegate+InterApp.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 46c8219..8b47551 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DE2770F34D005EF286 /* AllowedArguments.swift */; }; C415D3E62770F540005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E52770F540005EF286 /* main.swift */; }; + C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; }; + C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; }; C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; @@ -171,6 +173,7 @@ C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; C415D3DE2770F34D005EF286 /* AllowedArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllowedArguments.swift; sourceTree = ""; }; C415D3E52770F540005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+InterApp.swift"; sourceTree = ""; }; C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = ""; }; C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -466,6 +469,7 @@ C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */, C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */, C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */, + C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */, C4811D2322D70A4700B5F6B3 /* App.swift */, C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */, C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, @@ -597,7 +601,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1320; - LastUpgradeCheck = 1220; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = "Nico Verbruggen"; TargetAttributes = { C415D3D52770F341005EF286 = { @@ -715,6 +719,7 @@ C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, 54AB03262763858F00A29D5F /* Timer.swift in Sources */, C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, + C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, @@ -769,6 +774,7 @@ C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */, + C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */, C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, @@ -822,6 +828,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -835,6 +842,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme index a78bcde..cb237ab 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor.xcscheme @@ -1,6 +1,6 @@ APPL CFBundleShortVersionString $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Viewer + CFBundleURLName + com.nicoverbruggen.phpmon + CFBundleURLSchemes + + phpmon + + + CFBundleVersion $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType From 2dbf775ad61dcab1527713a97dcb1bb5e6fbf93a Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 20 Dec 2021 19:10:58 +0100 Subject: [PATCH 015/105] =?UTF-8?q?=F0=9F=94=A5=20Remove=20Swift=20Package?= =?UTF-8?q?=20for=20common=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 41 +++++++++--- phpmon-common/.gitignore | 7 -- .../xcshareddata/xcschemes/PMCommon.xcscheme | 67 ------------------- .../{Sources/PMCommon => }/Command.swift | 0 phpmon-common/Package.swift | 15 ----- .../{Sources/PMCommon => }/Paths.swift | 0 .../{Sources/PMCommon => }/Shell.swift | 0 phpmon/Domain/Core/Actions.swift | 1 - phpmon/Domain/Core/AppDelegate.swift | 1 - phpmon/Domain/Core/Startup.swift | 1 - phpmon/Domain/Helpers/Application.swift | 1 - phpmon/Domain/Helpers/Filesystem.swift | 1 - .../Homebrew/HomebrewDiagnostics.swift | 1 - phpmon/Domain/Integrations/Valet/Valet.swift | 1 - phpmon/Domain/Menu/MainMenu.swift | 1 - phpmon/Domain/PHP/ActivePhpInstallation.swift | 1 - phpmon/Domain/PHP/PhpInstallation.swift | 1 - phpmon/Domain/SiteList/SiteListCell.swift | 1 - phpmon/Domain/SiteList/SiteListVC.swift | 1 - phpmon/Domain/Watcher/App+ConfigWatch.swift | 1 - 20 files changed, 32 insertions(+), 111 deletions(-) delete mode 100644 phpmon-common/.gitignore delete mode 100644 phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme rename phpmon-common/{Sources/PMCommon => }/Command.swift (100%) delete mode 100644 phpmon-common/Package.swift rename phpmon-common/{Sources/PMCommon => }/Paths.swift (100%) rename phpmon-common/{Sources/PMCommon => }/Shell.swift (100%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 8b47551..fc53ebe 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -86,6 +86,15 @@ 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 */; }; + C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; }; + C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; }; + C4B585402770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; }; + C4B585412770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; }; + C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; }; + C4B585432770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; }; + C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; }; + C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; }; + C4B585462770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; }; C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; }; C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; }; C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; }; @@ -98,7 +107,6 @@ C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; - C4D309F02770F0B200958BCF /* PMCommon in Frameworks */ = {isa = PBXBuildFile; productRef = C4D309EF2770F0B200958BCF /* PMCommon */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; @@ -216,13 +224,15 @@ C4AF9F7C275454A900D44ED0 /* ValetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetTest.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 = ""; }; + C4B5853C2770FE3900DA4FBE /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; + C4B5853D2770FE3900DA4FBE /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; - C4D309E42770EC6F00958BCF /* phpmon-common */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "phpmon-common"; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; @@ -255,7 +265,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C4D309F02770F0B200958BCF /* PMCommon in Frameworks */, C4998F0626175E7200B2526E /* HotKey in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -331,7 +340,7 @@ C41C1B3522B0097F00E7CF16 /* phpmon */, C4F7807A25D7F84B000DBC97 /* phpmon-tests */, C415D3D72770F341005EF286 /* phpmon-cli */, - C4D309E42770EC6F00958BCF /* phpmon-common */, + C4B5853A2770FE2500DA4FBE /* phpmon-common */, C41C1B3422B0097F00E7CF16 /* Products */, C4D309E72770EF2F00958BCF /* Frameworks */, ); @@ -479,6 +488,16 @@ path = Core; sourceTree = ""; }; + C4B5853A2770FE2500DA4FBE /* phpmon-common */ = { + isa = PBXGroup; + children = ( + C4B5853D2770FE3900DA4FBE /* Command.swift */, + C4B5853B2770FE3900DA4FBE /* Paths.swift */, + C4B5853C2770FE3900DA4FBE /* Shell.swift */, + ); + path = "phpmon-common"; + sourceTree = ""; + }; C4C8E81D276F5686003AC782 /* Watcher */ = { isa = PBXGroup; children = ( @@ -570,7 +589,6 @@ name = "PHP Monitor"; packageProductDependencies = ( C4998F0526175E7200B2526E /* HotKey */, - C4D309EF2770F0B200958BCF /* PMCommon */, ); productName = phpmon; productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */; @@ -679,7 +697,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C4B585402770FE3900DA4FBE /* Paths.swift in Sources */, C415D3E62770F540005EF286 /* main.swift in Sources */, + C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, + C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -690,6 +711,7 @@ files = ( C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, + C4B585412770FE3900DA4FBE /* Shell.swift in Sources */, C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, @@ -704,6 +726,7 @@ C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, + C4B585442770FE3900DA4FBE /* Command.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */, @@ -723,6 +746,7 @@ C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, + C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, @@ -788,10 +812,13 @@ C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, + C4B585452770FE3900DA4FBE /* Command.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, C4F780B725D80B5D000DBC97 /* App.swift in Sources */, C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */, + C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */, C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, + C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */, C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */, C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */, @@ -1119,10 +1146,6 @@ package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */; productName = HotKey; }; - C4D309EF2770F0B200958BCF /* PMCommon */ = { - isa = XCSwiftPackageProductDependency; - productName = PMCommon; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */; diff --git a/phpmon-common/.gitignore b/phpmon-common/.gitignore deleted file mode 100644 index bb460e7..0000000 --- a/phpmon-common/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.DS_Store -/.build -/Packages -/*.xcodeproj -xcuserdata/ -DerivedData/ -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme b/phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme deleted file mode 100644 index 082c0e6..0000000 --- a/phpmon-common/.swiftpm/xcode/xcshareddata/xcschemes/PMCommon.xcscheme +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/phpmon-common/Sources/PMCommon/Command.swift b/phpmon-common/Command.swift similarity index 100% rename from phpmon-common/Sources/PMCommon/Command.swift rename to phpmon-common/Command.swift diff --git a/phpmon-common/Package.swift b/phpmon-common/Package.swift deleted file mode 100644 index cffde46..0000000 --- a/phpmon-common/Package.swift +++ /dev/null @@ -1,15 +0,0 @@ -// swift-tools-version:5.5 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "PMCommon", - products: [ - .library(name: "PMCommon", targets: ["PMCommon"]), - ], - dependencies: [], - targets: [ - .target(name: "PMCommon", dependencies: []), - ] -) diff --git a/phpmon-common/Sources/PMCommon/Paths.swift b/phpmon-common/Paths.swift similarity index 100% rename from phpmon-common/Sources/PMCommon/Paths.swift rename to phpmon-common/Paths.swift diff --git a/phpmon-common/Sources/PMCommon/Shell.swift b/phpmon-common/Shell.swift similarity index 100% rename from phpmon-common/Sources/PMCommon/Shell.swift rename to phpmon-common/Shell.swift diff --git a/phpmon/Domain/Core/Actions.swift b/phpmon/Domain/Core/Actions.swift index df7010d..5bbc098 100644 --- a/phpmon/Domain/Core/Actions.swift +++ b/phpmon/Domain/Core/Actions.swift @@ -7,7 +7,6 @@ import Foundation import AppKit -import PMCommon class Actions { diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 8e1d933..a963b6e 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -7,7 +7,6 @@ import Cocoa import UserNotifications -import PMCommon @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate { diff --git a/phpmon/Domain/Core/Startup.swift b/phpmon/Domain/Core/Startup.swift index 232dd99..b42c8ab 100644 --- a/phpmon/Domain/Core/Startup.swift +++ b/phpmon/Domain/Core/Startup.swift @@ -6,7 +6,6 @@ // import Foundation -import PMCommon class Startup { diff --git a/phpmon/Domain/Helpers/Application.swift b/phpmon/Domain/Helpers/Application.swift index c7ef192..e337ed5 100644 --- a/phpmon/Domain/Helpers/Application.swift +++ b/phpmon/Domain/Helpers/Application.swift @@ -7,7 +7,6 @@ // import Foundation -import PMCommon /// An application that is capable of opening a particular directory (usually of a PHP project). /// In most cases this is going to be a code editor, but it could also be another application diff --git a/phpmon/Domain/Helpers/Filesystem.swift b/phpmon/Domain/Helpers/Filesystem.swift index 2a03f73..808f05e 100644 --- a/phpmon/Domain/Helpers/Filesystem.swift +++ b/phpmon/Domain/Helpers/Filesystem.swift @@ -7,7 +7,6 @@ // import Cocoa -import PMCommon class Filesystem { diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index 811c8fb..fd327a8 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -7,7 +7,6 @@ // import Foundation -import PMCommon class HomebrewDiagnostics { diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 9a07f95..0759406 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -7,7 +7,6 @@ // import Foundation -import PMCommon class Valet { diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 764e297..9f5486a 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -6,7 +6,6 @@ // import Cocoa -import PMCommon class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { diff --git a/phpmon/Domain/PHP/ActivePhpInstallation.swift b/phpmon/Domain/PHP/ActivePhpInstallation.swift index 7867669..2ab3579 100644 --- a/phpmon/Domain/PHP/ActivePhpInstallation.swift +++ b/phpmon/Domain/PHP/ActivePhpInstallation.swift @@ -6,7 +6,6 @@ // import Foundation -import PMCommon /** An installed version of PHP, that was detected by scanning the `/opt/php@version/bin` directory. diff --git a/phpmon/Domain/PHP/PhpInstallation.swift b/phpmon/Domain/PHP/PhpInstallation.swift index 1f3f45f..02577e2 100644 --- a/phpmon/Domain/PHP/PhpInstallation.swift +++ b/phpmon/Domain/PHP/PhpInstallation.swift @@ -7,7 +7,6 @@ // import Foundation -import PMCommon class PhpInstallation { diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index 1463dcb..1bcf0f3 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -8,7 +8,6 @@ import Cocoa import AppKit -import PMCommon class SiteListCell: NSTableCellView { diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index 90ef5c5..b03fad1 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -9,7 +9,6 @@ import Cocoa import HotKey import Carbon -import PMCommon class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { diff --git a/phpmon/Domain/Watcher/App+ConfigWatch.swift b/phpmon/Domain/Watcher/App+ConfigWatch.swift index 1bfcf3c..518518a 100644 --- a/phpmon/Domain/Watcher/App+ConfigWatch.swift +++ b/phpmon/Domain/Watcher/App+ConfigWatch.swift @@ -7,7 +7,6 @@ // import Foundation -import PMCommon extension App { From a6387e96e78fe75121fdb3bb02bfaf207e4b8439 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 21 Dec 2021 15:30:50 +0100 Subject: [PATCH 016/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Separate=20some=20?= =?UTF-8?q?of=20the=20PHP=20config=20logic=20from=20the=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 22 ++++++- phpmon-cli/main.swift | 5 ++ phpmon-common/Command.swift | 2 +- phpmon-common/Paths.swift | 37 +++++------- phpmon-common/PhpSwitcher.swift | 60 +++++++++++++++++++ phpmon-common/Shell.swift | 30 +++++----- phpmon-tests/{ => Test Files}/brew.json | 0 phpmon-tests/{ => Test Files}/php.ini | 0 .../{ => Test Files}/valet-config.json | 0 phpmon/Domain/Core/Actions.swift | 18 +++--- phpmon/Domain/Core/App.swift | 58 ++++++------------ phpmon/Domain/Core/AppDelegate.swift | 11 ++++ phpmon/Domain/Core/Startup.swift | 4 +- .../Homebrew/HomebrewDiagnostics.swift | 6 +- phpmon/Domain/Menu/MainMenu.swift | 38 +++++++----- phpmon/Domain/Menu/StatusMenu.swift | 42 +++++-------- phpmon/Domain/PHP/ActivePhpInstallation.swift | 2 +- phpmon/Domain/Watcher/App+ConfigWatch.swift | 29 ++++----- 18 files changed, 211 insertions(+), 153 deletions(-) create mode 100644 phpmon-common/PhpSwitcher.swift rename phpmon-tests/{ => Test Files}/brew.json (100%) rename phpmon-tests/{ => Test Files}/php.ini (100%) rename phpmon-tests/{ => Test Files}/valet-config.json (100%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index fc53ebe..89d4e01 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -24,6 +24,9 @@ 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; }; C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; }; C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; }; + C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; + C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; + C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; @@ -176,6 +179,7 @@ 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = ""; }; C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = ""; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; + C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = ""; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -321,6 +325,16 @@ path = IAP; sourceTree = ""; }; + C40C7F1C27720E1400DDDCDC /* Test Files */ = { + isa = PBXGroup; + children = ( + C4AF9F70275445FF00D44ED0 /* valet-config.json */, + C43A8A1F25D9D1D700591B77 /* brew.json */, + C4F780A725D80AE8000DBC97 /* php.ini */, + ); + path = "Test Files"; + sourceTree = ""; + }; C415D3D72770F341005EF286 /* phpmon-cli */ = { isa = PBXGroup; children = ( @@ -494,6 +508,7 @@ C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, + C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */, ); path = "phpmon-common"; sourceTree = ""; @@ -527,10 +542,8 @@ C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = { isa = PBXGroup; children = ( - C4AF9F70275445FF00D44ED0 /* valet-config.json */, - C43A8A1F25D9D1D700591B77 /* brew.json */, - C4F780A725D80AE8000DBC97 /* php.ini */, C4F7807D25D7F84B000DBC97 /* Info.plist */, + C40C7F1C27720E1400DDDCDC /* Test Files */, C4F7809B25D80344000DBC97 /* CommandTest.swift */, C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */, C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */, @@ -702,6 +715,7 @@ C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, + C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -750,6 +764,7 @@ C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, + C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */, @@ -820,6 +835,7 @@ C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */, + C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */, C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */, C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */, C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 5070216..64ece82 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -10,6 +10,11 @@ import Foundation // First, let's read the initial command line argument +// REFACTOR REQUIRED +// Information about the Homebrew linked alias +// Information about the PHP versions +// etc.: needs to be stored in a separate object we can instantiate here and in PHP Monitor. + print(CommandLine.arguments) if CommandLine.arguments.count != 3 { diff --git a/phpmon-common/Command.swift b/phpmon-common/Command.swift index 7fc07e4..941172a 100644 --- a/phpmon-common/Command.swift +++ b/phpmon-common/Command.swift @@ -1,6 +1,6 @@ // // Command.swift -// PMCommon +// phpmon-common // // Copyright © 2021 Nico Verbruggen. All rights reserved. // diff --git a/phpmon-common/Paths.swift b/phpmon-common/Paths.swift index 9d766ce..5231893 100644 --- a/phpmon-common/Paths.swift +++ b/phpmon-common/Paths.swift @@ -1,38 +1,24 @@ // // Paths.swift -// PMCommon +// phpmon-common // // Copyright © 2021 Nico Verbruggen. All rights reserved. // import Foundation -public enum HomebrewDir: String { - case opt = "/opt/homebrew" - case usr = "/usr/local" -} - +/** + The `Paths` class is used to locate various binaries on the system, + and provides a full + */ public class Paths { public static let shared = Paths() - var baseDir : HomebrewDir + + private var baseDir : Paths.HomebrewDir init() { - let optBrewFound = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") - let usrBrewFound = Shell.fileExists("\(HomebrewDir.usr.rawValue)/bin/brew") - - if (optBrewFound) { - // This is usually the case with Homebrew installed on Apple Silicon - baseDir = .opt - } else if (usrBrewFound) { - // This is usually the case with Homebrew installed on Intel (or Rosetta 2) - baseDir = .usr - } else { - // Falling back to default "legacy" Homebrew location (for Intel) - print("Seems like we couldn't determine the Homebrew directory.") - print("This usually means we're in trouble... (no Homebrew?)") - baseDir = .usr - } + baseDir = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr } // - MARK: Binaries @@ -71,4 +57,11 @@ public class Paths { return "\(shared.baseDir.rawValue)/etc" } + // MARK: - Enum + + public enum HomebrewDir: String { + case opt = "/opt/homebrew" + case usr = "/usr/local" + } + } diff --git a/phpmon-common/PhpSwitcher.swift b/phpmon-common/PhpSwitcher.swift new file mode 100644 index 0000000..17a824c --- /dev/null +++ b/phpmon-common/PhpSwitcher.swift @@ -0,0 +1,60 @@ +// +// PhpSwitcher.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 21/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +protocol PhpSwitcherDelegate: AnyObject { + func switcherDidStartSwitching() + func switcherDidCompleteSwitch() +} + +class PhpSwitcher { + + init() { + self.currentInstall = ActivePhpInstallation() + } + + /** The delegate that is informed of updates. */ + weak var delegate: PhpSwitcherDelegate? + + /** The static app instance. Accessible at any time. */ + static let shared = PhpSwitcher() + + /** Whether the switcher is busy performing any actions. */ + var isBusy: Bool = false + + /** All available versions of PHP. */ + var availablePhpVersions: [String] = [] + + /** Cached information about the PHP installations. */ + var cachedPhpInstallations: [String: PhpInstallation] = [:] + + /** Static accessor for `PhpSwitcher.shared.currentInstall`. */ + static var phpInstall: ActivePhpInstallation { + return Self.shared.currentInstall + } + + /** Information about the currently linked PHP installation. */ + var currentInstall: ActivePhpInstallation + + /** + The version that the `php` formula via Brew is aliased to on the current system. + + If you're up to date, `php` will be aliased to the latest version, + but that might not be the case. + */ + var brewPhpVersion: String { + return homebrewPackage.version + } + + /** + Information we were able to discern from the Homebrew info command. + */ + var homebrewPackage: HomebrewPackage! = nil + +} diff --git a/phpmon-common/Shell.swift b/phpmon-common/Shell.swift index 87ef921..11c6730 100644 --- a/phpmon-common/Shell.swift +++ b/phpmon-common/Shell.swift @@ -1,6 +1,6 @@ // // Shell.swift -// PMCommon +// phpmon-common // // Copyright © 2021 Nico Verbruggen. All rights reserved. // @@ -80,7 +80,7 @@ public class Shell { public func executeSynchronously( _ command: String, requiresPath: Bool = false - ) -> ShellOutput { + ) -> Shell.Output { let outputPipe = Pipe() let errorPipe = Pipe() @@ -91,7 +91,7 @@ public class Shell { task.launch() task.waitUntilExit() - return ShellOutput( + return Shell.Output( standardOutput: String( data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8 @@ -162,18 +162,18 @@ public class Shell { NotificationCenter.default.removeObserver(pipe.fileHandleForReading) } } -} - -public class ShellOutput { - public let standardOutput: String - public let errorOutput: String - public let task: Process - init(standardOutput: String, - errorOutput: String, - task: Process) { - self.standardOutput = standardOutput - self.errorOutput = errorOutput - self.task = task + public class Output { + public let standardOutput: String + public let errorOutput: String + public let task: Process + + init(standardOutput: String, + errorOutput: String, + task: Process) { + self.standardOutput = standardOutput + self.errorOutput = errorOutput + self.task = task + } } } diff --git a/phpmon-tests/brew.json b/phpmon-tests/Test Files/brew.json similarity index 100% rename from phpmon-tests/brew.json rename to phpmon-tests/Test Files/brew.json diff --git a/phpmon-tests/php.ini b/phpmon-tests/Test Files/php.ini similarity index 100% rename from phpmon-tests/php.ini rename to phpmon-tests/Test Files/php.ini diff --git a/phpmon-tests/valet-config.json b/phpmon-tests/Test Files/valet-config.json similarity index 100% rename from phpmon-tests/valet-config.json rename to phpmon-tests/Test Files/valet-config.json diff --git a/phpmon/Domain/Core/Actions.swift b/phpmon/Domain/Core/Actions.swift index 5bbc098..19773f6 100644 --- a/phpmon/Domain/Core/Actions.swift +++ b/phpmon/Domain/Core/Actions.swift @@ -20,7 +20,7 @@ class Actions { // Make sure the aliased version is detected // The user may have `php` installed, but not e.g. `php@8.0` // We should also detect that as a version that is installed - let phpAlias = App.shared.brewPhpVersion + let phpAlias = PhpSwitcher.shared.brewPhpVersion // Avoid inserting a duplicate if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) { @@ -29,7 +29,7 @@ class Actions { print("The PHP versions that were detected are: \(versionsOnly)") - App.shared.availablePhpVersions = versionsOnly + PhpSwitcher.shared.availablePhpVersions = versionsOnly Actions.extractPhpLongVersions() return versionsOnly @@ -42,11 +42,11 @@ class Actions { public static func extractPhpLongVersions() { var mappedVersions: [String: PhpInstallation] = [:] - App.shared.availablePhpVersions.forEach { version in + PhpSwitcher.shared.availablePhpVersions.forEach { version in mappedVersions[version] = PhpInstallation(version) } - App.shared.cachedPhpInstallations = mappedVersions + PhpSwitcher.shared.cachedPhpInstallations = mappedVersions } /** @@ -82,7 +82,7 @@ class Actions { public static func restartPhpFpm() { - brew("services restart \(App.phpInstall!.formula)", sudo: true) + brew("services restart \(PhpSwitcher.phpInstall.formula)", sudo: true) } public static func restartNginx() @@ -97,7 +97,7 @@ class Actions { public static func stopAllServices() { - brew("services stop \(App.phpInstall!.formula)", sudo: true) + brew("services stop \(PhpSwitcher.phpInstall.formula)", sudo: true) brew("services stop nginx", sudo: true) brew("services stop dnsmasq", sudo: true) } @@ -137,7 +137,7 @@ class Actions { group.enter() DispatchQueue.global(qos: .userInitiated).async { - let formula = (available == App.shared.brewPhpVersion) + let formula = (available == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(available)" brew("unlink \(formula)") @@ -151,7 +151,7 @@ class Actions { print("All versions have been unlinked!") print("Linking the new version!") - let formula = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)" + let formula = (version == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version)" brew("link \(formula) --overwrite --force") brew("services start \(formula)", sudo: true) @@ -205,7 +205,7 @@ class Actions { brew("services restart dnsmasq", sudo: true) detectPhpVersions().forEach { (version) in - let formula = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)" + let formula = (version == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version)" brew("unlink php@\(version)") brew("services stop \(formula)") brew("services stop \(formula)", sudo: true) diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 9b77ed1..2a578d1 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -8,13 +8,17 @@ import Cocoa import HotKey -class App { +class App: PhpSwitcherDelegate { // MARK: Static Vars /** The static app instance. Accessible at any time. */ static let shared = App() + init() { + PhpSwitcher.shared.delegate = self + } + /** Retrieve the version number from the main info dictionary, Info.plist. */ static var version: String { let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String @@ -22,14 +26,9 @@ class App { return "\(version) (\(build))" } - /** Information about the currently linked PHP installation. */ - static var phpInstall: ActivePhpInstallation? { - return App.shared.currentInstall - } - /** Whether the app is busy doing something. Used to determine what UI to display. */ static var busy: Bool { - return App.shared.busy + return PhpSwitcher.shared.isBusy } // MARK: Variables @@ -43,46 +42,13 @@ class App { /** The window controller of the currently active site list window. */ var siteListWindowController: SiteListWC? = nil - /** Whether the application is busy switching versions. */ - var busy: Bool = false - - /** The currently active installation of PHP. */ - var currentInstall: ActivePhpInstallation? = nil { - didSet { - handlePhpConfigWatcher() - } - } - - /** All available versions of PHP. */ - var availablePhpVersions: [String] = [] - - /** Cached information about the PHP installations. */ - var cachedPhpInstallations: [String: PhpInstallation] = [:] - /** List of detected (installed) applications that PHP Monitor can work with. */ var detectedApplications: [Application] = [] /** Timer that will periodically reload info about the user's PHP installation. */ var timer: Timer? - /** Information we were able to discern from the Homebrew info command (as JSON). */ - var brewPhpPackage: HomebrewPackage! = nil { - didSet { - brewPhpVersion = brewPhpPackage!.version - } - } - - /** - The version that the `php` formula via Brew is aliased to on the current system. - - If you're up to date, `php` will be aliased to the latest version, - but that might not be the case. - - We'll technically default to the version in Constants.swift, but the information - should always be loaded from Homebrew itself upon startup. - */ - var brewPhpVersion: String = Constants.LatestStablePhpVersion - + // MARK: - Global Hotkey /** @@ -112,4 +78,14 @@ class App { The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder. */ var watcher: PhpConfigWatcher! + + // MARK: - PhpSwitcherDelegate + + func switcherDidStartSwitching() { + } + + func switcherDidCompleteSwitch() { + PhpSwitcher.shared.currentInstall = ActivePhpInstallation() + handlePhpConfigWatcher() + } } diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index a963b6e..ecd5420 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -20,6 +20,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele */ let sharedShell: Shell + /** + The PhpSwitcher singleton that handles PHP version + detection, as well as switching. + + - Note: It is important to initialize the switcher + before the `App` singleton, so that the delegate + is set correctly. + */ + let switcher: PhpSwitcher + /** The App singleton contains information about the state of the application and global variables. @@ -55,6 +65,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele print("Version \(App.version)") print("==================================") self.sharedShell = Shell.user + self.switcher = PhpSwitcher.shared self.state = App.shared self.menu = MainMenu.shared self.paths = Paths.shared diff --git a/phpmon/Domain/Core/Startup.swift b/phpmon/Domain/Core/Startup.swift index b42c8ab..747a0d2 100644 --- a/phpmon/Domain/Core/Startup.swift +++ b/phpmon/Domain/Core/Startup.swift @@ -91,12 +91,12 @@ class Startup { let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json"); - App.shared.brewPhpPackage = try! JSONDecoder().decode( + PhpSwitcher.shared.homebrewPackage = try! JSONDecoder().decode( [HomebrewPackage].self, from: brewPhpAlias.data(using: .utf8)! ).first! - print("When on your system, the `php` formula means version \(App.shared.brewPhpVersion)!") + print("When on your system, the `php` formula means version \(PhpSwitcher.shared.brewPhpVersion)!") } /** diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index fd327a8..a41695c 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -46,12 +46,12 @@ class HomebrewDiagnostics { from: tapAlias.data(using: .utf8)! ).first! - if tapPhp.version != App.shared.brewPhpVersion { + if tapPhp.version != PhpSwitcher.shared.brewPhpVersion { print("The `php` formula alias seems to be the different between the tap and core. This could be a problem!") print("Determining whether both of these versions are installed...") - let bothInstalled = App.shared.availablePhpVersions.contains(tapPhp.version) - && App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion) + let bothInstalled = PhpSwitcher.shared.availablePhpVersions.contains(tapPhp.version) + && PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.shared.brewPhpVersion) if bothInstalled { print("Both conflicting aliases seem to be installed, warning the user!") diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 9f5486a..899bb94 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -57,9 +57,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { print("Determining broken PHP-FPM...") // Attempt to find out if PHP-FPM is broken - let installation = App.phpInstall! + let installation = PhpSwitcher.phpInstall installation.notifyAboutBrokenPhpFpm() + // Set up the config watchers on launch (these are automatically updated via delegate methods if the user switches) + print("Setting up watchers...") + App.shared.handlePhpConfigWatcher() + print("Detecting applications...") // Attempt to load list of applications App.shared.detectedApplications = Application.detectPresetApplications() @@ -82,7 +86,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { App.shared.timer = Timer.scheduledTimer( timeInterval: 60, target: self, - selector: #selector(updatePhpVersionInStatusBar), + selector: #selector(refreshActiveInstallation), userInfo: nil, repeats: true ) @@ -181,12 +185,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { */ private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {}) { - App.shared.busy = true + PhpSwitcher.shared.isBusy = true setBusyImage() DispatchQueue.global(qos: .userInitiated).async { [unowned self] in update() execute() - App.shared.busy = false + PhpSwitcher.shared.isBusy = false DispatchQueue.main.async { [self] in updatePhpVersionInStatusBar() @@ -198,8 +202,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // MARK: - User Interface + @objc func refreshActiveInstallation() { + PhpSwitcher.shared.currentInstall = ActivePhpInstallation() + updatePhpVersionInStatusBar() + } + @objc func updatePhpVersionInStatusBar() { - App.shared.currentInstall = ActivePhpInstallation() refreshIcon() update() } @@ -215,7 +223,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } else { // The dynamic icon has been requested let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool - setStatusBarImage(version: long ? App.phpInstall!.version.long : App.phpInstall!.version.short) + setStatusBarImage(version: long ? PhpSwitcher.phpInstall.version.long : PhpSwitcher.phpInstall.version.short) } } } @@ -335,12 +343,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { - App.shared.busy = true + PhpSwitcher.shared.isBusy = true setBusyImage() self.update() let noLongerBusy = { - App.shared.busy = false + PhpSwitcher.shared.isBusy = false DispatchQueue.main.async { [self] in self.updatePhpVersionInStatusBar() self.update() @@ -411,14 +419,14 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } @objc func openActiveConfigFolder() { - if (App.phpInstall!.version.error) { + if (PhpSwitcher.phpInstall.version.error) { // php version was not identified Actions.openGenericPhpConfigFolder() return } // php version was identified - Actions.openPhpConfigFolder(version: App.phpInstall!.version.short) + Actions.openPhpConfigFolder(version: PhpSwitcher.phpInstall.version.short) } @objc func openGlobalComposerFolder() { @@ -431,7 +439,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { @objc func switchToPhpVersion(sender: PhpMenuItem) { setBusyImage() - App.shared.busy = true + PhpSwitcher.shared.isBusy = true DispatchQueue.global(qos: .userInitiated).async { [unowned self] in // Update the PHP version in the status bar @@ -441,8 +449,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { update() let completion = { + PhpSwitcher.shared.delegate?.switcherDidCompleteSwitch() + // Mark as no longer busy - App.shared.busy = false + PhpSwitcher.shared.isBusy = false // Perform UI updates on main thread DispatchQueue.main.async { [self] in @@ -454,7 +464,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { title: String(format: "notification.version_changed_title".localized, sender.version), subtitle: String(format: "notification.version_changed_desc".localized, sender.version) ) - App.phpInstall?.notifyAboutBrokenPhpFpm() + PhpSwitcher.phpInstall.notifyAboutBrokenPhpFpm() } // Run composer updates @@ -482,7 +492,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // Will cause more issues with Homebrew and is faster Actions.switchToPhpVersion( version: sender.version, - availableVersions: App.shared.availablePhpVersions, + availableVersions: PhpSwitcher.shared.availablePhpVersions, completed: completion ) /* } */ diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 609f2cc..7326462 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -9,18 +9,14 @@ import Cocoa class StatusMenu : NSMenu { func addPhpVersionMenuItems() { - if App.shared.currentInstall == nil { - return - } - - if App.phpInstall!.version.error { + if PhpSwitcher.phpInstall.version.error { for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] { addItem(NSMenuItem(title: message.localized, action: nil, keyEquivalent: "")) } return } - let phpVersionText = "\("mi_php_version".localized) \(App.phpInstall!.version.long)" + let phpVersionText = "\("mi_php_version".localized) \(PhpSwitcher.phpInstall.version.long)" addItem(HeaderView.asMenuItem(text: phpVersionText)) } @@ -30,7 +26,7 @@ class StatusMenu : NSMenu { return } - if App.shared.availablePhpVersions.count == 0 { + if PhpSwitcher.shared.availablePhpVersions.count == 0 { return } @@ -44,14 +40,14 @@ class StatusMenu : NSMenu { servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) - if !App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion) { + if !PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.shared.brewPhpVersion) { servicesMenu.addItem(NSMenuItem( - title: "mi_force_load_latest_unavailable".localized(App.shared.brewPhpVersion), + title: "mi_force_load_latest_unavailable".localized(PhpSwitcher.shared.brewPhpVersion), action: nil, keyEquivalent: "f" )) } else { servicesMenu.addItem(NSMenuItem( - title: "mi_force_load_latest".localized(App.shared.brewPhpVersion), + title: "mi_force_load_latest".localized(PhpSwitcher.shared.brewPhpVersion), action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f")) } @@ -75,8 +71,6 @@ class StatusMenu : NSMenu { item.target = MainMenu.shared } - - self.setSubmenu(servicesMenu, for: services) self.addItem(services) } @@ -89,10 +83,6 @@ class StatusMenu : NSMenu { } func addPhpConfigurationMenuItems() { - if App.shared.currentInstall == nil { - return - } - // Configuration self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized)) self.addItem(NSMenuItem(title: "mi_php_config".localized, action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c")) @@ -102,13 +92,13 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) - self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: App.shared.busy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) + self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: PhpSwitcher.shared.isBusy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) - if (App.shared.busy) { + if (PhpSwitcher.shared.isBusy) { return } - let stats = App.phpInstall!.limits + let stats = PhpSwitcher.phpInstall.limits // Stats self.addItem(NSMenuItem.separator()) @@ -122,12 +112,12 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized)) - if (App.phpInstall!.extensions.count == 0) { + if (PhpSwitcher.phpInstall.extensions.count == 0) { self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: "")) } var shortcutKey = 1 - for phpExtension in App.phpInstall!.extensions { + for phpExtension in PhpSwitcher.phpInstall.extensions { self.addExtensionItem(phpExtension, shortcutKey) shortcutKey += 1 } @@ -140,20 +130,20 @@ class StatusMenu : NSMenu { private func addSwitchToPhpMenuItems() { var shortcutKey = 1 - for index in (0.. Date: Tue, 21 Dec 2021 16:00:27 +0100 Subject: [PATCH 017/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rework=20various?= =?UTF-8?q?=20common=20classes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 54 +++++-- README.md | 4 +- phpmon-cli/main.swift | 4 +- .../Core/Actions.swift | 76 +-------- phpmon-common/{ => Core}/Command.swift | 2 +- .../Core}/Constants.swift | 0 phpmon-common/{ => Core}/Paths.swift | 2 +- phpmon-common/{ => Core}/Shell.swift | 2 +- .../PHP/ActivePhpInstallation.swift | 24 +-- .../PHP}/HomebrewPackage.swift | 0 .../PHP/PhpExtension.swift | 0 .../PHP/PhpInstallation.swift | 0 phpmon-common/PHP/PhpSwitcher.swift | 146 ++++++++++++++++++ phpmon-common/PhpSwitcher.swift | 60 ------- phpmon/Domain/Core/App.swift | 4 - phpmon/Domain/Core/AppDelegate.swift | 23 +-- phpmon/Domain/Core/Startup.swift | 40 ++--- .../Homebrew/HomebrewDiagnostics.swift | 4 +- phpmon/Domain/Menu/MainMenu.swift | 2 +- phpmon/Domain/Menu/StatusMenu.swift | 8 +- .../PHP/ActivePhpInstallation+Checks.swift | 33 ++++ 21 files changed, 272 insertions(+), 216 deletions(-) rename {phpmon/Domain => phpmon-common}/Core/Actions.swift (67%) rename phpmon-common/{ => Core}/Command.swift (98%) rename {phpmon => phpmon-common/Core}/Constants.swift (100%) rename phpmon-common/{ => Core}/Paths.swift (98%) rename phpmon-common/{ => Core}/Shell.swift (99%) rename {phpmon/Domain => phpmon-common}/PHP/ActivePhpInstallation.swift (85%) rename {phpmon/Domain/Integrations/Homebrew => phpmon-common/PHP}/HomebrewPackage.swift (100%) rename {phpmon/Domain => phpmon-common}/PHP/PhpExtension.swift (100%) rename {phpmon/Domain => phpmon-common}/PHP/PhpInstallation.swift (100%) create mode 100644 phpmon-common/PHP/PhpSwitcher.swift delete mode 100644 phpmon-common/PhpSwitcher.swift create mode 100644 phpmon/Domain/PHP/ActivePhpInstallation+Checks.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 89d4e01..39cbd70 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -27,6 +27,14 @@ C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; + C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; + C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; }; + C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; + C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; + C40C7F2627721FA200DDDCDC /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; + C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; }; + C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; }; + C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; @@ -180,6 +188,7 @@ C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = ""; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = ""; }; + C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = ""; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -298,6 +307,8 @@ 54B20EDF263AA22C00D3250E /* PHP */ = { isa = PBXGroup; children = ( + C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, + C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */, C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */, C4F2E4392752F7D00020E974 /* PhpInstallation.swift */, C4ACA38E25C754C100060C66 /* PhpExtension.swift */, @@ -335,6 +346,26 @@ path = "Test Files"; sourceTree = ""; }; + C40C7F2127721F7300DDDCDC /* Core */ = { + isa = PBXGroup; + children = ( + C415D3B62770F294005EF286 /* Actions.swift */, + C4EE188322D3386B00E126E5 /* Constants.swift */, + C4B5853D2770FE3900DA4FBE /* Command.swift */, + C4B5853B2770FE3900DA4FBE /* Paths.swift */, + C4B5853C2770FE3900DA4FBE /* Shell.swift */, + ); + path = Core; + sourceTree = ""; + }; + C40C7F2C2772204700DDDCDC /* PHP */ = { + isa = PBXGroup; + children = ( + C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */, + ); + path = PHP; + sourceTree = ""; + }; C415D3D72770F341005EF286 /* phpmon-cli */ = { isa = PBXGroup; children = ( @@ -373,7 +404,6 @@ C41C1B3522B0097F00E7CF16 /* phpmon */ = { isa = PBXGroup; children = ( - C4EE188322D3386B00E126E5 /* Constants.swift */, C41E181722CB61EB0072CF09 /* Domain */, C41C1B3F22B0098000E7CF16 /* Info.plist */, C4232EE42612526500158FC6 /* Credits.html */, @@ -398,7 +428,7 @@ children = ( C4AF9F6B275445D300D44ED0 /* Integrations */, C4B13B1D25C4915000548C3A /* Core */, - 54B20EDF263AA22C00D3250E /* PHP */, + C40C7F2C2772204700DDDCDC /* PHP */, C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* SiteList */, 5420395726135DB800FB00FA /* Preferences */, @@ -480,7 +510,6 @@ isa = PBXGroup; children = ( C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */, - C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, ); path = Homebrew; sourceTree = ""; @@ -497,7 +526,6 @@ C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */, C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, - C415D3B62770F294005EF286 /* Actions.swift */, ); path = Core; sourceTree = ""; @@ -505,10 +533,8 @@ C4B5853A2770FE2500DA4FBE /* phpmon-common */ = { isa = PBXGroup; children = ( - C4B5853D2770FE3900DA4FBE /* Command.swift */, - C4B5853B2770FE3900DA4FBE /* Paths.swift */, - C4B5853C2770FE3900DA4FBE /* Shell.swift */, - C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */, + C40C7F2127721F7300DDDCDC /* Core */, + 54B20EDF263AA22C00D3250E /* PHP */, ); path = "phpmon-common"; sourceTree = ""; @@ -710,10 +736,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */, + C40C7F2627721FA200DDDCDC /* Constants.swift in Sources */, C4B585402770FE3900DA4FBE /* Paths.swift in Sources */, C415D3E62770F540005EF286 /* main.swift in Sources */, + C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */, C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, + C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, + C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */, + C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */, ); @@ -737,6 +769,7 @@ C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */, + C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */, C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, @@ -794,6 +827,7 @@ C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */, C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */, + C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */, C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */, C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */, @@ -1020,7 +1054,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 200; + CURRENT_PROJECT_VERSION = 220; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -1045,7 +1079,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 200; + CURRENT_PROJECT_VERSION = 220; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; diff --git a/README.md b/README.md index a3e5c3c..68bc7de 100644 --- a/README.md +++ b/README.md @@ -301,9 +301,7 @@ The switcher will disable all PHP-FPM services not belonging to the version you ### Want to know more? -If you want to know more about how this works, I recommend you check out the source code. - -This app isn't very complicated after all. In the end, this just (conveniently) executes some shell commands. +If you want to know more about how this works, I recommend you check out the source code. ## 🔧 Build instructions diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 64ece82..72fab89 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -31,9 +31,11 @@ if !AllowedArguments.has(argument) { let action = AllowedArguments.init(rawValue: argument) +let switcher = PhpSwitcher.shared +PhpSwitcher.detectPhpVersions() + switch action { case .use: - // Read the PHP value let version = CommandLine.arguments[2] print("Switching to PHP \(version)...") break diff --git a/phpmon/Domain/Core/Actions.swift b/phpmon-common/Core/Actions.swift similarity index 67% rename from phpmon/Domain/Core/Actions.swift rename to phpmon-common/Core/Actions.swift index 19773f6..0921845 100644 --- a/phpmon/Domain/Core/Actions.swift +++ b/phpmon-common/Core/Actions.swift @@ -10,74 +10,6 @@ import AppKit class Actions { - // MARK: - Detect PHP Versions - - public static func detectPhpVersions() -> [String] - { - let files = Shell.pipe("ls \(Paths.optPath) | grep php@") - var versionsOnly = Self.extractPhpVersions(from: files.components(separatedBy: "\n")) - - // Make sure the aliased version is detected - // The user may have `php` installed, but not e.g. `php@8.0` - // We should also detect that as a version that is installed - let phpAlias = PhpSwitcher.shared.brewPhpVersion - - // Avoid inserting a duplicate - if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) { - versionsOnly.append(phpAlias); - } - - print("The PHP versions that were detected are: \(versionsOnly)") - - PhpSwitcher.shared.availablePhpVersions = versionsOnly - Actions.extractPhpLongVersions() - - return versionsOnly - } - - /** - This method extracts the PHP full version number after finding the php installation folders. - To be refactored at some later point, I'd like to cache the `PhpInstallation` objects instead of just the version number at some point. - */ - public static func extractPhpLongVersions() - { - var mappedVersions: [String: PhpInstallation] = [:] - PhpSwitcher.shared.availablePhpVersions.forEach { version in - mappedVersions[version] = PhpInstallation(version) - } - - PhpSwitcher.shared.cachedPhpInstallations = mappedVersions - } - - /** - Extracts valid PHP versions from an array of strings. - This array of strings is usually retrieved from `grep`. - */ - public static func extractPhpVersions( - from versions: [String], - checkBinaries: Bool = true - ) -> [String] { - var output : [String] = [] - - versions.filter { (version) -> Bool in - // Omit everything that doesn't start with php@ - // (e.g. something-php@8.0 won't be detected) - return version.starts(with: "php@") - }.forEach { (string) in - let version = string.components(separatedBy: "php@")[1] - // Only append the version if it doesn't already exist (avoid dupes), - // is supported and where the binary exists (avoids broken installs) - if !output.contains(version) - && Constants.SupportedPhpVersions.contains(version) - && (checkBinaries ? Shell.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true) - { - output.append(version) - } - } - - return output - } - // MARK: - Services public static func restartPhpFpm() @@ -137,7 +69,7 @@ class Actions { group.enter() DispatchQueue.global(qos: .userInitiated).async { - let formula = (available == PhpSwitcher.shared.brewPhpVersion) + let formula = (available == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(available)" brew("unlink \(formula)") @@ -151,7 +83,7 @@ class Actions { print("All versions have been unlinked!") print("Linking the new version!") - let formula = (version == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version)" + let formula = (version == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version)" brew("link \(formula) --overwrite --force") brew("services start \(formula)", sudo: true) @@ -204,8 +136,8 @@ class Actions { { brew("services restart dnsmasq", sudo: true) - detectPhpVersions().forEach { (version) in - let formula = (version == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version)" + PhpSwitcher.shared.detectPhpVersions().forEach { (version) in + let formula = (version == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version)" brew("unlink php@\(version)") brew("services stop \(formula)") brew("services stop \(formula)", sudo: true) diff --git a/phpmon-common/Command.swift b/phpmon-common/Core/Command.swift similarity index 98% rename from phpmon-common/Command.swift rename to phpmon-common/Core/Command.swift index 941172a..8da8bd2 100644 --- a/phpmon-common/Command.swift +++ b/phpmon-common/Core/Command.swift @@ -1,6 +1,6 @@ // // Command.swift -// phpmon-common +// PHP Monitor // // Copyright © 2021 Nico Verbruggen. All rights reserved. // diff --git a/phpmon/Constants.swift b/phpmon-common/Core/Constants.swift similarity index 100% rename from phpmon/Constants.swift rename to phpmon-common/Core/Constants.swift diff --git a/phpmon-common/Paths.swift b/phpmon-common/Core/Paths.swift similarity index 98% rename from phpmon-common/Paths.swift rename to phpmon-common/Core/Paths.swift index 5231893..202e790 100644 --- a/phpmon-common/Paths.swift +++ b/phpmon-common/Core/Paths.swift @@ -1,6 +1,6 @@ // // Paths.swift -// phpmon-common +// PHP Monitor // // Copyright © 2021 Nico Verbruggen. All rights reserved. // diff --git a/phpmon-common/Shell.swift b/phpmon-common/Core/Shell.swift similarity index 99% rename from phpmon-common/Shell.swift rename to phpmon-common/Core/Shell.swift index 11c6730..59aa4b9 100644 --- a/phpmon-common/Shell.swift +++ b/phpmon-common/Core/Shell.swift @@ -1,6 +1,6 @@ // // Shell.swift -// phpmon-common +// PHP Monitor // // Copyright © 2021 Nico Verbruggen. All rights reserved. // diff --git a/phpmon/Domain/PHP/ActivePhpInstallation.swift b/phpmon-common/PHP/ActivePhpInstallation.swift similarity index 85% rename from phpmon/Domain/PHP/ActivePhpInstallation.swift rename to phpmon-common/PHP/ActivePhpInstallation.swift index 10e2b35..6111ffe 100644 --- a/phpmon/Domain/PHP/ActivePhpInstallation.swift +++ b/phpmon-common/PHP/ActivePhpInstallation.swift @@ -25,7 +25,7 @@ class ActivePhpInstallation { // MARK: - Computed var formula: String { - return (version.short == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version.short)" + return (version.short == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version.short)" } // MARK: - Initializer @@ -122,26 +122,6 @@ class ActivePhpInstallation { return (match == nil) ? "⚠️" : "\(value)B" } - /** - It is always possible that the system configuration for PHP-FPM has not been set up for Valet. - This can occur when a user manually installs a new PHP version, but does not run `valet install`. - In that case, we should alert the user! - - - Important: The underlying check is `checkPhpFpmStatus`, which can be run multiple times. - This method actively presents a modal if said checks fails, so don't call this method too many times. - */ - public func notifyAboutBrokenPhpFpm() { - if !self.checkPhpFpmStatus() { - DispatchQueue.main.async { - Alert.notify( - message: "alert.php_fpm_broken.title".localized, - info: "alert.php_fpm_broken.info".localized, - style: .critical - ) - } - } - } - /** Determine if PHP-FPM is configured correctly. @@ -149,7 +129,7 @@ class ActivePhpInstallation { versions of PHP, we can just check for the existence of the `valet-fpm.conf` file. If the check here fails, that means that Valet won't work properly. */ - private func checkPhpFpmStatus() -> Bool { + func checkPhpFpmStatus() -> Bool { if self.version.short == "5.6" { // The main PHP config file should contain `valet.sock` and then we're probably fine? let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf" diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewPackage.swift b/phpmon-common/PHP/HomebrewPackage.swift similarity index 100% rename from phpmon/Domain/Integrations/Homebrew/HomebrewPackage.swift rename to phpmon-common/PHP/HomebrewPackage.swift diff --git a/phpmon/Domain/PHP/PhpExtension.swift b/phpmon-common/PHP/PhpExtension.swift similarity index 100% rename from phpmon/Domain/PHP/PhpExtension.swift rename to phpmon-common/PHP/PhpExtension.swift diff --git a/phpmon/Domain/PHP/PhpInstallation.swift b/phpmon-common/PHP/PhpInstallation.swift similarity index 100% rename from phpmon/Domain/PHP/PhpInstallation.swift rename to phpmon-common/PHP/PhpInstallation.swift diff --git a/phpmon-common/PHP/PhpSwitcher.swift b/phpmon-common/PHP/PhpSwitcher.swift new file mode 100644 index 0000000..213a237 --- /dev/null +++ b/phpmon-common/PHP/PhpSwitcher.swift @@ -0,0 +1,146 @@ +// +// PhpSwitcher.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 21/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +protocol PhpSwitcherDelegate: AnyObject { + func switcherDidStartSwitching() + func switcherDidCompleteSwitch() +} + +class PhpSwitcher { + + // MARK: - Initializer + + init() { + self.currentInstall = ActivePhpInstallation() + + let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json"); + + self.homebrewPackage = try! JSONDecoder().decode( + [HomebrewPackage].self, + from: brewPhpAlias.data(using: .utf8)! + ).first! + + print("When on your system, the `php` formula means version \(homebrewPackage.version)!") + } + + // MARK: - Properties + + /** The delegate that is informed of updates. */ + weak var delegate: PhpSwitcherDelegate? + + /** The static app instance. Accessible at any time. */ + static let shared = PhpSwitcher() + + /** Whether the switcher is busy performing any actions. */ + var isBusy: Bool = false + + /** All available versions of PHP. */ + var availablePhpVersions: [String] = [] + + /** Cached information about the PHP installations. */ + var cachedPhpInstallations: [String: PhpInstallation] = [:] + + /** Information about the currently linked PHP installation. */ + var currentInstall: ActivePhpInstallation + + /** + The version that the `php` formula via Brew is aliased to on the current system. + + If you're up to date, `php` will be aliased to the latest version, + but that might not be the case since not everyone keeps their + software up-to-date. + + As such, we take that information from Homebrew. + */ + static var brewPhpVersion: String { + return Self.shared.homebrewPackage.version + } + + /** + The currently linked and active PHP installation. + */ + static var phpInstall: ActivePhpInstallation { + return Self.shared.currentInstall + } + + /** + Information we were able to discern from the Homebrew info command. + */ + var homebrewPackage: HomebrewPackage! = nil + + // MARK: - Methods + + public static func detectPhpVersions() -> Void { + _ = Self.shared.detectPhpVersions() + } + + /** + Detects which versions of PHP are installed. + */ + public func detectPhpVersions() -> [String] + { + let files = Shell.pipe("ls \(Paths.optPath) | grep php@") + + var versionsOnly = extractPhpVersions(from: files.components(separatedBy: "\n")) + + // Make sure the aliased version is detected + // The user may have `php` installed, but not e.g. `php@8.0` + // We should also detect that as a version that is installed + let phpAlias = homebrewPackage.version + + // Avoid inserting a duplicate + if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) { + versionsOnly.append(phpAlias) + } + + print("The PHP versions that were detected are: \(versionsOnly)") + + availablePhpVersions = versionsOnly + + var mappedVersions: [String: PhpInstallation] = [:] + + availablePhpVersions.forEach { version in + mappedVersions[version] = PhpInstallation(version) + } + + cachedPhpInstallations = mappedVersions + + return versionsOnly + } + + /** + Extracts valid PHP versions from an array of strings. + This array of strings is usually retrieved from `grep`. + */ + public func extractPhpVersions( + from versions: [String], + checkBinaries: Bool = true + ) -> [String] { + var output : [String] = [] + + versions.filter { (version) -> Bool in + // Omit everything that doesn't start with php@ + // (e.g. something-php@8.0 won't be detected) + return version.starts(with: "php@") + }.forEach { (string) in + let version = string.components(separatedBy: "php@")[1] + // Only append the version if it doesn't already exist (avoid dupes), + // is supported and where the binary exists (avoids broken installs) + if !output.contains(version) + && Constants.SupportedPhpVersions.contains(version) + && (checkBinaries ? Shell.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true) + { + output.append(version) + } + } + + return output + } +} diff --git a/phpmon-common/PhpSwitcher.swift b/phpmon-common/PhpSwitcher.swift deleted file mode 100644 index 17a824c..0000000 --- a/phpmon-common/PhpSwitcher.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// PhpSwitcher.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 21/12/2021. -// Copyright © 2021 Nico Verbruggen. All rights reserved. -// - -import Foundation - -protocol PhpSwitcherDelegate: AnyObject { - func switcherDidStartSwitching() - func switcherDidCompleteSwitch() -} - -class PhpSwitcher { - - init() { - self.currentInstall = ActivePhpInstallation() - } - - /** The delegate that is informed of updates. */ - weak var delegate: PhpSwitcherDelegate? - - /** The static app instance. Accessible at any time. */ - static let shared = PhpSwitcher() - - /** Whether the switcher is busy performing any actions. */ - var isBusy: Bool = false - - /** All available versions of PHP. */ - var availablePhpVersions: [String] = [] - - /** Cached information about the PHP installations. */ - var cachedPhpInstallations: [String: PhpInstallation] = [:] - - /** Static accessor for `PhpSwitcher.shared.currentInstall`. */ - static var phpInstall: ActivePhpInstallation { - return Self.shared.currentInstall - } - - /** Information about the currently linked PHP installation. */ - var currentInstall: ActivePhpInstallation - - /** - The version that the `php` formula via Brew is aliased to on the current system. - - If you're up to date, `php` will be aliased to the latest version, - but that might not be the case. - */ - var brewPhpVersion: String { - return homebrewPackage.version - } - - /** - Information we were able to discern from the Homebrew info command. - */ - var homebrewPackage: HomebrewPackage! = nil - -} diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 2a578d1..1743377 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -15,10 +15,6 @@ class App: PhpSwitcherDelegate { /** The static app instance. Accessible at any time. */ static let shared = App() - init() { - PhpSwitcher.shared.delegate = self - } - /** Retrieve the version number from the main info dictionary, Info.plist. */ static var version: String { let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index ecd5420..6729135 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -20,16 +20,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele */ let sharedShell: Shell - /** - The PhpSwitcher singleton that handles PHP version - detection, as well as switching. - - - Note: It is important to initialize the switcher - before the `App` singleton, so that the delegate - is set correctly. - */ - let switcher: PhpSwitcher - /** The App singleton contains information about the state of the application and global variables. @@ -54,6 +44,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele */ let valet: Valet + /** + The PhpSwitcher singleton that handles PHP version + detection, as well as switching. It is initialized + when the app is ready and passed all checks. + */ + var switcher: PhpSwitcher! = nil + // MARK: - Initializer /** @@ -65,7 +62,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele print("Version \(App.version)") print("==================================") self.sharedShell = Shell.user - self.switcher = PhpSwitcher.shared self.state = App.shared self.menu = MainMenu.shared self.paths = Paths.shared @@ -73,6 +69,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele super.init() } + func initializeSwitcher() { + self.switcher = PhpSwitcher.shared + self.switcher.delegate = self.state + } + // MARK: - Lifecycle /** diff --git a/phpmon/Domain/Core/Startup.swift b/phpmon/Domain/Core/Startup.swift index 747a0d2..987cf45 100644 --- a/phpmon/Domain/Core/Startup.swift +++ b/phpmon/Domain/Core/Startup.swift @@ -6,6 +6,7 @@ // import Foundation +import AppKit class Startup { @@ -75,37 +76,30 @@ class Startup { ) if (!failed) { - determineBrewAliasVersion() + initializeSwitcher() + print("PHP Monitor has determined the application has successfully passed all checks.") success() } } /** - * In order to avoid having to hard-code which version of PHP is aliased to what specific subversion, - * PHP Monitor now determines the alias by checking the user's system. + Because the Switcher requires various environment guarantees, the switcher is only + initialized when it is done working. */ - private func determineBrewAliasVersion() - { - print("PHP Monitor has determined the application has successfully passed all checks.") - print("Determining which version of PHP is aliased to `php` via Homebrew...") - - let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json"); - - PhpSwitcher.shared.homebrewPackage = try! JSONDecoder().decode( - [HomebrewPackage].self, - from: brewPhpAlias.data(using: .utf8)! - ).first! - - print("When on your system, the `php` formula means version \(PhpSwitcher.shared.brewPhpVersion)!") + private func initializeSwitcher() { + DispatchQueue.main.async { + let appDelegate = NSApplication.shared.delegate as! AppDelegate + appDelegate.initializeSwitcher() + } } - + /** - * Perform an environment check. Will cause the application to terminate, if `breaking` is set to true. - * - * - Parameter condition: Fail condition to check for; if this returns `true`, the alert will be shown - * - Parameter messageText: Short description of what is wrong - * - Parameter informativeText: Expanded description of the environment check that failed - * - Parameter breaking: If the application should terminate afterwards + Perform an environment check. Will cause the application to terminate, if `breaking` is set to true. + + - Parameter condition: Fail condition to check for; if this returns `true`, the alert will be shown + - Parameter messageText: Short description of what is wrong + - Parameter informativeText: Expanded description of the environment check that failed + - Parameter breaking: If the application should terminate afterwards */ private func performEnvironmentCheck( _ condition: Bool, diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index a41695c..b473c6f 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -46,12 +46,12 @@ class HomebrewDiagnostics { from: tapAlias.data(using: .utf8)! ).first! - if tapPhp.version != PhpSwitcher.shared.brewPhpVersion { + if tapPhp.version != PhpSwitcher.brewPhpVersion { print("The `php` formula alias seems to be the different between the tap and core. This could be a problem!") print("Determining whether both of these versions are installed...") let bothInstalled = PhpSwitcher.shared.availablePhpVersions.contains(tapPhp.version) - && PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.shared.brewPhpVersion) + && PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.brewPhpVersion) if bothInstalled { print("Both conflicting aliases seem to be installed, warning the user!") diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 899bb94..3d6ccfa 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -41,7 +41,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { When the environment is all clear and the app can run, let's go. */ private func onEnvironmentPass() { - _ = Actions.detectPhpVersions() + PhpSwitcher.detectPhpVersions() if HomebrewDiagnostics.shared.errors.contains(.aliasConflict) { DispatchQueue.main.async { diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 7326462..8c8a5a7 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -40,14 +40,14 @@ class StatusMenu : NSMenu { servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) - if !PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.shared.brewPhpVersion) { + if !PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.brewPhpVersion) { servicesMenu.addItem(NSMenuItem( - title: "mi_force_load_latest_unavailable".localized(PhpSwitcher.shared.brewPhpVersion), + title: "mi_force_load_latest_unavailable".localized(PhpSwitcher.brewPhpVersion), action: nil, keyEquivalent: "f" )) } else { servicesMenu.addItem(NSMenuItem( - title: "mi_force_load_latest".localized(PhpSwitcher.shared.brewPhpVersion), + title: "mi_force_load_latest".localized(PhpSwitcher.brewPhpVersion), action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f")) } @@ -140,7 +140,7 @@ class StatusMenu : NSMenu { let versionString = long ? longVersion : shortVersion let action = #selector(MainMenu.switchToPhpVersion(sender:)) - let brew = (shortVersion == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(shortVersion)" + let brew = (shortVersion == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(shortVersion)" let menuItem = PhpMenuItem( title: "\("mi_php_switch".localized) \(versionString) (\(brew))", action: (shortVersion == PhpSwitcher.phpInstall.version.short) ? nil : action, keyEquivalent: "\(shortcutKey)" diff --git a/phpmon/Domain/PHP/ActivePhpInstallation+Checks.swift b/phpmon/Domain/PHP/ActivePhpInstallation+Checks.swift new file mode 100644 index 0000000..c1b3124 --- /dev/null +++ b/phpmon/Domain/PHP/ActivePhpInstallation+Checks.swift @@ -0,0 +1,33 @@ +// +// ActivePhpInstallation.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 21/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension ActivePhpInstallation { + + /** + It is always possible that the system configuration for PHP-FPM has not been set up for Valet. + This can occur when a user manually installs a new PHP version, but does not run `valet install`. + In that case, we should alert the user! + + - Important: The underlying check is `checkPhpFpmStatus`, which can be run multiple times. + This method actively presents a modal if said checks fails, so don't call this method too many times. + */ + public func notifyAboutBrokenPhpFpm() { + if !self.checkPhpFpmStatus() { + DispatchQueue.main.async { + Alert.notify( + message: "alert.php_fpm_broken.title".localized, + info: "alert.php_fpm_broken.info".localized, + style: .critical + ) + } + } + } + +} From e76c6e14e4a0039d7a47c122cce079b799ba90e3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 21 Dec 2021 17:06:03 +0100 Subject: [PATCH 018/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Added=20logger=20c?= =?UTF-8?q?lass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 8 +++ phpmon-cli/main.swift | 20 ++++--- phpmon-common/Core/Actions.swift | 12 ++--- phpmon-common/Core/Logger.swift | 52 +++++++++++++++++++ phpmon-common/PHP/PhpExtension.swift | 2 +- phpmon-common/PHP/PhpSwitcher.swift | 4 +- phpmon-tests/Utility.swift | 2 +- phpmon/Domain/Core/App+GlobalHotkey.swift | 4 +- .../Core/AppDelegate+Notifications.swift | 6 +-- phpmon/Domain/Core/AppDelegate.swift | 11 ++-- phpmon/Domain/Core/Startup.swift | 2 +- phpmon/Domain/Helpers/LocalNotification.swift | 2 +- .../Domain/Helpers/PMWindowController.swift | 2 +- .../Homebrew/HomebrewDiagnostics.swift | 16 +++--- phpmon/Domain/Integrations/Valet/Valet.swift | 8 +-- phpmon/Domain/Menu/MainMenu.swift | 20 +++---- phpmon/Domain/Preferences/Preferences.swift | 2 +- phpmon/Domain/Preferences/PrefsVC.swift | 2 +- phpmon/Domain/Preferences/PrefsWC.swift | 2 +- phpmon/Domain/Progress/ProgressWindow.swift | 4 +- phpmon/Domain/SiteList/SiteListVC.swift | 2 +- phpmon/Domain/Watcher/App+ConfigWatch.swift | 8 +-- phpmon/Domain/Watcher/PhpConfigWatcher.swift | 8 +-- 23 files changed, 135 insertions(+), 64 deletions(-) create mode 100644 phpmon-common/Core/Logger.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 39cbd70..3a76475 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -35,6 +35,9 @@ C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; }; C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; }; C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; + C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; }; + C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; }; + C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; }; C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; @@ -189,6 +192,7 @@ C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = ""; }; C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = ""; }; + C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -354,6 +358,7 @@ C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, + C40C7F2F27722E8D00DDDCDC /* Logger.swift */, ); path = Core; sourceTree = ""; @@ -745,6 +750,7 @@ C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */, + C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */, C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */, @@ -786,6 +792,7 @@ C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */, 54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, + C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, 54AB03262763858F00A29D5F /* Timer.swift in Sources */, C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, @@ -846,6 +853,7 @@ C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */, C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, + C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */, diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 72fab89..decf4bb 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -15,17 +15,25 @@ import Foundation // Information about the PHP versions // etc.: needs to be stored in a separate object we can instantiate here and in PHP Monitor. -print(CommandLine.arguments) +var logger = Log.shared +logger.verbosity = .warning -if CommandLine.arguments.count != 3 { - print("You must enter two arguments.") +if CommandLine.arguments.count < 3 { + Log.err("You must enter at least two additional arguments.") exit(1) } +if CommandLine.arguments.contains("-v") || CommandLine.arguments.contains("--verbose") { + logger.verbosity = .info +} +if CommandLine.arguments.contains("-p") || CommandLine.arguments.contains("--performance") { + logger.verbosity = .performance +} + let argument = CommandLine.arguments[1] if !AllowedArguments.has(argument) { - print("The supported arguments are: \(AllowedArguments.rawValues)") + Log.err("The supported arguments are: \(AllowedArguments.rawValues)") exit(1) } @@ -37,9 +45,9 @@ PhpSwitcher.detectPhpVersions() switch action { case .use: let version = CommandLine.arguments[2] - print("Switching to PHP \(version)...") + Log.info("Switching to PHP \(version)...") break case .none: - print("Action not recognized!") + Log.err("Action not recognized!") exit(1) } diff --git a/phpmon-common/Core/Actions.swift b/phpmon-common/Core/Actions.swift index 0921845..7b3fdb1 100644 --- a/phpmon-common/Core/Actions.swift +++ b/phpmon-common/Core/Actions.swift @@ -42,8 +42,8 @@ class Actions { availableVersions: [String], completed: @escaping () -> Void ) { - print("Switching to \(version) using Valet") - print(valet("use php@\(version)")) + Log.info("Switching to \(version) using Valet") + Log.info(valet("use php@\(version)")) completed() } @@ -61,7 +61,7 @@ class Actions { availableVersions: [String], completed: @escaping () -> Void ) { - print("Switching to \(version), unlinking all versions...") + Log.info("Switching to \(version), unlinking all versions...") let group = DispatchGroup() @@ -80,14 +80,14 @@ class Actions { } group.notify(queue: .global(qos: .userInitiated)) { - print("All versions have been unlinked!") - print("Linking the new version!") + Log.info("All versions have been unlinked!") + Log.info("Linking the new version!") let formula = (version == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version)" brew("link \(formula) --overwrite --force") brew("services start \(formula)", sudo: true) - print("The new version has been linked!") + Log.info("The new version has been linked!") completed() } } diff --git a/phpmon-common/Core/Logger.swift b/phpmon-common/Core/Logger.swift new file mode 100644 index 0000000..a618ede --- /dev/null +++ b/phpmon-common/Core/Logger.swift @@ -0,0 +1,52 @@ +// +// Logger.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 21/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class Log { + + enum Verbosity: Int { + case error = 1, + info = 2, + warning = 3, + performance = 4 + + public func isApplicable() -> Bool { + return Log.shared.verbosity.rawValue >= self.rawValue + } + } + + static var shared = Log() + + var verbosity: Verbosity = .info + + static func info(_ item: Any) { + if Verbosity.info.isApplicable() { + print(item) + } + } + + static func err(_ item: Any) { + if Verbosity.error.isApplicable() { + print(item) + } + } + + static func warn(_ item: Any) { + if Verbosity.warning.isApplicable() { + print(item) + } + } + + static func perf(_ item: Any) { + if Verbosity.performance.isApplicable() { + print(item) + } + } + +} diff --git a/phpmon-common/PHP/PhpExtension.swift b/phpmon-common/PHP/PhpExtension.swift index 5268649..0996691 100644 --- a/phpmon-common/PHP/PhpExtension.swift +++ b/phpmon-common/PHP/PhpExtension.swift @@ -92,7 +92,7 @@ class PhpExtension { let file = try? String(contentsOf: path, encoding: .utf8) if (file == nil) { - print("There was an issue reading the file. Assuming no extensions were found.") + Log.err("There was an issue reading the file. Assuming no extensions were found.") return [] } diff --git a/phpmon-common/PHP/PhpSwitcher.swift b/phpmon-common/PHP/PhpSwitcher.swift index 213a237..8927d1e 100644 --- a/phpmon-common/PHP/PhpSwitcher.swift +++ b/phpmon-common/PHP/PhpSwitcher.swift @@ -27,7 +27,7 @@ class PhpSwitcher { from: brewPhpAlias.data(using: .utf8)! ).first! - print("When on your system, the `php` formula means version \(homebrewPackage.version)!") + Log.info("When on your system, the `php` formula means version \(homebrewPackage.version)!") } // MARK: - Properties @@ -100,7 +100,7 @@ class PhpSwitcher { versionsOnly.append(phpAlias) } - print("The PHP versions that were detected are: \(versionsOnly)") + Log.info("The PHP versions that were detected are: \(versionsOnly)") availablePhpVersions = versionsOnly diff --git a/phpmon-tests/Utility.swift b/phpmon-tests/Utility.swift index 218a459..ac55f18 100644 --- a/phpmon-tests/Utility.swift +++ b/phpmon-tests/Utility.swift @@ -19,7 +19,7 @@ class Utility { try FileManager.default.copyItem(at: bundleURL, to: targetURL) return targetURL } catch let error { - print("Unable to copy file: \(error)") + Log.err("Unable to copy file: \(error)") } } diff --git a/phpmon/Domain/Core/App+GlobalHotkey.swift b/phpmon/Domain/Core/App+GlobalHotkey.swift index 8f4f54c..bea998e 100644 --- a/phpmon/Domain/Core/App+GlobalHotkey.swift +++ b/phpmon/Domain/Core/App+GlobalHotkey.swift @@ -20,13 +20,13 @@ extension App { func loadGlobalHotkey() { // Make sure we can retrieve the hotkey from preferences guard let hotkey = Preferences.preferences[.globalHotkey] as? String else { - print("No global hotkey was saved in preferences. None set.") + Log.info("No global hotkey was saved in preferences. None set.") return } // Make sure we can parse the JSON into the desired format guard let keybindPref = GlobalKeybindPreference.fromJson(hotkey) else { - print("No global hotkey loaded, could not be parsed!") + Log.err("No global hotkey loaded, could not be parsed!") shortcutHotkey = nil return } diff --git a/phpmon/Domain/Core/AppDelegate+Notifications.swift b/phpmon/Domain/Core/AppDelegate+Notifications.swift index 3feff20..f848b57 100644 --- a/phpmon/Domain/Core/AppDelegate+Notifications.swift +++ b/phpmon/Domain/Core/AppDelegate+Notifications.swift @@ -18,11 +18,11 @@ extension AppDelegate { notificationCenter.delegate = self notificationCenter.requestAuthorization(options: [.alert], completionHandler: { granted, error in if !granted { - print("PHP Monitor does not have permission to show notifications.") + Log.warn("PHP Monitor does not have permission to show notifications.") } if let error = error { - print("PHP Monitor encounted an error determining notification permissions:") - print(error) + Log.err("PHP Monitor encounted an error determining notification permissions:") + Log.err(error) } }) } diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 6729135..852839d 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -51,16 +51,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele */ var switcher: PhpSwitcher! = nil + var logger: Log = Log.shared + // MARK: - Initializer /** When the application initializes, create all singletons. */ override init() { - print("==================================") - print("PHP MONITOR by Nico Verbruggen") - print("Version \(App.version)") - print("==================================") + Log.shared.verbosity = .info + Log.info("==================================") + Log.info("PHP MONITOR by Nico Verbruggen") + Log.info("Version \(App.version)") + Log.info("==================================") self.sharedShell = Shell.user self.state = App.shared self.menu = MainMenu.shared diff --git a/phpmon/Domain/Core/Startup.swift b/phpmon/Domain/Core/Startup.swift index 987cf45..df35fcb 100644 --- a/phpmon/Domain/Core/Startup.swift +++ b/phpmon/Domain/Core/Startup.swift @@ -77,7 +77,7 @@ class Startup { if (!failed) { initializeSwitcher() - print("PHP Monitor has determined the application has successfully passed all checks.") + Log.info("PHP Monitor has determined the application has successfully passed all checks.") success() } } diff --git a/phpmon/Domain/Helpers/LocalNotification.swift b/phpmon/Domain/Helpers/LocalNotification.swift index 8bf0da2..0c391a3 100644 --- a/phpmon/Domain/Helpers/LocalNotification.swift +++ b/phpmon/Domain/Helpers/LocalNotification.swift @@ -25,7 +25,7 @@ class LocalNotification { let notificationCenter = UNUserNotificationCenter.current() notificationCenter.add(request) { (error) in if error != nil { - print(error!) + Log.err(error!) } } } diff --git a/phpmon/Domain/Helpers/PMWindowController.swift b/phpmon/Domain/Helpers/PMWindowController.swift index d6c8f89..9b26a14 100644 --- a/phpmon/Domain/Helpers/PMWindowController.swift +++ b/phpmon/Domain/Helpers/PMWindowController.swift @@ -30,7 +30,7 @@ class PMWindowController: NSWindowController, NSWindowDelegate { } deinit { - print("Window controller '\(windowName)' was deinitialized") + Log.perf("Window controller '\(windowName)' was deinitialized") } } diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index b473c6f..29faf62 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -35,11 +35,11 @@ class HomebrewDiagnostics { let tapAlias = Shell.pipe("\(Paths.brew) info shivammathur/php/php --json") if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") { - print("The user does not appear to have tapped: shivammathur/php") + Log.info("The user does not appear to have tapped: shivammathur/php") return false } else { - print("The user DOES have the following tapped: shivammathur/php") - print("Checking for `php` formula conflicts...") + Log.info("The user DOES have the following tapped: shivammathur/php") + Log.info("Checking for `php` formula conflicts...") let tapPhp = try! JSONDecoder().decode( [HomebrewPackage].self, @@ -47,22 +47,22 @@ class HomebrewDiagnostics { ).first! if tapPhp.version != PhpSwitcher.brewPhpVersion { - print("The `php` formula alias seems to be the different between the tap and core. This could be a problem!") - print("Determining whether both of these versions are installed...") + Log.warn("The `php` formula alias seems to be the different between the tap and core. This could be a problem!") + Log.info("Determining whether both of these versions are installed...") let bothInstalled = PhpSwitcher.shared.availablePhpVersions.contains(tapPhp.version) && PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.brewPhpVersion) if bothInstalled { - print("Both conflicting aliases seem to be installed, warning the user!") + Log.warn("Both conflicting aliases seem to be installed, warning the user!") } else { - print("Conflicting aliases are not both installed, seems fine!") + Log.info("Conflicting aliases are not both installed, seems fine!") } return bothInstalled } - print("All seems to be OK. No conflicts, both are PHP \(tapPhp.version).") + Log.info("All seems to be OK. No conflicts, both are PHP \(tapPhp.version).") return false } diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 0759406..f42ff61 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -39,7 +39,7 @@ class Valet { public func startPreloadingSites() { if self.sites.count <= 10 { // Preload the sites and their drivers - print("Fewer than or 11 sites found, preloading list of sites...") + Log.info("Fewer than or 11 sites found, preloading list of sites...") self.reloadSites() } } @@ -50,17 +50,17 @@ class Valet { public func validateVersion() -> Void { if version == "UNKNOWN" { - return print("The Valet version could not be extracted... that does not bode well.") + return Log.warn("The Valet version could not be extracted... that does not bode well.") } if version.versionCompare(Constants.MinimumRecommendedValetVersion) == .orderedAscending { let version = version - print("Valet version \(version) is too old! (recommended: \(Constants.MinimumRecommendedValetVersion))") + Log.warn("Valet version \(version) is too old! (recommended: \(Constants.MinimumRecommendedValetVersion))") DispatchQueue.main.async { Alert.notify(message: "alert.min_valet_version.title".localized, info: "alert.min_valet_version.info".localized(version, Constants.MinimumRecommendedValetVersion)) } } else { - print("Valet version \(version) is recent enough, OK (recommended: \(Constants.MinimumRecommendedValetVersion))") + Log.info("Valet version \(version) is recent enough, OK (recommended: \(Constants.MinimumRecommendedValetVersion))") } } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 3d6ccfa..121d86e 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -55,31 +55,31 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { updatePhpVersionInStatusBar() - print("Determining broken PHP-FPM...") + Log.info("Determining broken PHP-FPM...") // Attempt to find out if PHP-FPM is broken let installation = PhpSwitcher.phpInstall installation.notifyAboutBrokenPhpFpm() // Set up the config watchers on launch (these are automatically updated via delegate methods if the user switches) - print("Setting up watchers...") + Log.info("Setting up watchers...") App.shared.handlePhpConfigWatcher() - print("Detecting applications...") + Log.info("Detecting applications...") // Attempt to load list of applications App.shared.detectedApplications = Application.detectPresetApplications() let appNames = App.shared.detectedApplications.map { app in return app.name } - print("Detected applications: \(appNames)") + Log.info("Detected applications: \(appNames)") // Load the global hotkey App.shared.loadGlobalHotkey() // Attempt to find out more info about Valet - print("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)") + Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)") Valet.shared.validateVersion() Valet.shared.startPreloadingSites() - print("PHP Monitor is ready to serve!") + Log.info("PHP Monitor is ready to serve!") // Schedule a request to fetch the PHP version every 60 seconds DispatchQueue.main.async { [self] in @@ -232,14 +232,14 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { @objc func reloadPhpMonitorMenuInBackground() { waitAndExecute { // This automatically reloads the menu - print("Reloading information about the PHP installation (in the background)...") + Log.info("Reloading information about the PHP installation (in the background)...") } } @objc func reloadPhpMonitorMenu() { waitAndExecute { // This automatically reloads the menu - print("Reloading information about the PHP installation...") + Log.info("Reloading information about the PHP installation...") } completion: { // Add a slight delay to make sure it loads the new menu DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { @@ -378,13 +378,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { DispatchQueue.main.async { window?.addToConsole(string) } - print("\(string.trimmingCharacters(in: .newlines))") + Log.perf("\(string.trimmingCharacters(in: .newlines))") }, didReceiveStdErrData: { string in DispatchQueue.main.async { window?.addToConsole(string) } - print("\(string.trimmingCharacters(in: .newlines))") + Log.perf("\(string.trimmingCharacters(in: .newlines))") } ) diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 7897c06..45c8bff 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -55,7 +55,7 @@ class Preferences { if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) { return } - print("Saving first-time preferences!") + Log.info("Saving first-time preferences!") UserDefaults.standard.setValue(true, forKey: PreferenceName.wasLaunchedBefore.rawValue) UserDefaults.standard.synchronize() } diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index b2c872b..a1aaaab 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -110,6 +110,6 @@ class PrefsVC: NSViewController { // MARK: - Deinitialization deinit { - print("VC deallocated") + Log.perf("PrefsVC deallocated") } } diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PrefsWC.swift index f2ef5b9..6cd24ba 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PrefsWC.swift @@ -29,7 +29,7 @@ class PrefsWC: PMWindowController { if let vc = contentViewController as? PrefsVC { if vc.listeningForHotkeyView != nil { if event.keyCode == Keys.Escape || event.keyCode == Keys.Space { - print("A blacklisted key was pressed, canceling listen") + Log.info("A blacklisted key was pressed, canceling listen!") vc.listeningForHotkeyView = nil } else { vc.listeningForHotkeyView!.updateShortcut(event) diff --git a/phpmon/Domain/Progress/ProgressWindow.swift b/phpmon/Domain/Progress/ProgressWindow.swift index 15613c1..0030f93 100644 --- a/phpmon/Domain/Progress/ProgressWindow.swift +++ b/phpmon/Domain/Progress/ProgressWindow.swift @@ -56,7 +56,7 @@ class ProgressWindowController: NSWindowController, NSWindowDelegate { } deinit { - // print("Deinitializing Progress Window Controller") + Log.perf("Deinitializing ProgressWindowController") } } @@ -70,7 +70,7 @@ class ProgressViewController: NSViewController { @IBOutlet weak var imageViewType: NSImageView! deinit { - // print("Deinitializing Progress View Controller") + Log.perf("Deinitializing ProgressViewController") } } diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index b03fad1..b56d4c6 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -268,6 +268,6 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { // MARK: - Deinitialization deinit { - print("VC deallocated") + Log.perf("SiteListVC deallocated") } } diff --git a/phpmon/Domain/Watcher/App+ConfigWatch.swift b/phpmon/Domain/Watcher/App+ConfigWatch.swift index 6e170f2..ae710c9 100644 --- a/phpmon/Domain/Watcher/App+ConfigWatch.swift +++ b/phpmon/Domain/Watcher/App+ConfigWatch.swift @@ -11,16 +11,16 @@ import Foundation extension App { func startWatcher(_ url: URL) { - print("No watcher currently active...") + Log.info("No watcher currently active...") self.watcher = PhpConfigWatcher(for: url) self.watcher.didChange = { url in - print("Something has changed in: \(url)") + Log.info("Something has changed in: \(url)") // Check if the watcher has last updated the menu less than 0.75s ago let distance = self.watcher.lastUpdate?.distance(to: Date().timeIntervalSince1970) if distance == nil || distance != nil && distance! > 0.75 { - print("Refreshing menu...") + Log.info("Refreshing menu...") MainMenu.shared.reloadPhpMonitorMenuInBackground() self.watcher.lastUpdate = Date().timeIntervalSince1970 } @@ -39,7 +39,7 @@ extension App { if self.watcher.url != url || forceReload { self.watcher.disable() self.watcher = nil - print("Watcher has stopped watching files. Starting new one...") + Log.info("Watcher has stopped watching files. Starting new one...") startWatcher(url) } } diff --git a/phpmon/Domain/Watcher/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift index cd5e6df..e97cfe5 100644 --- a/phpmon/Domain/Watcher/PhpConfigWatcher.swift +++ b/phpmon/Domain/Watcher/PhpConfigWatcher.swift @@ -40,8 +40,8 @@ class PhpConfigWatcher { self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write) } - print("A watcher exists for the following config paths:") - print(self.watchers.map({ watcher in + Log.info("A watcher exists for the following config paths:") + Log.info(self.watchers.map({ watcher in return watcher.url.relativePath })) } @@ -52,14 +52,14 @@ class PhpConfigWatcher { } func disable() { - print("Turning off existing watchers...") + Log.info("Turning off existing watchers...") self.watchers.forEach { (watcher) in watcher.stopMonitoring() } } deinit { - print("An existing config watcher has been deinitialized.") + Log.perf("A PhpConfigWatcher has been deinitialized.") } } From 63f4f8b07890c04c229fc3e0af13081db22c3bdc Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 21 Dec 2021 17:52:13 +0100 Subject: [PATCH 019/105] =?UTF-8?q?=E2=9C=A8=20Added=20prototype=20binary?= =?UTF-8?q?=20to=20switch=20quickly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-cli/AllowedArguments.swift | 2 + phpmon-cli/main.swift | 89 +++++++++++++++++++++------- phpmon-common/Core/Actions.swift | 2 + phpmon-common/Core/Logger.swift | 22 +++---- phpmon/Domain/Core/AppDelegate.swift | 4 +- 5 files changed, 83 insertions(+), 36 deletions(-) diff --git a/phpmon-cli/AllowedArguments.swift b/phpmon-cli/AllowedArguments.swift index 17d6c2a..4945cfe 100644 --- a/phpmon-cli/AllowedArguments.swift +++ b/phpmon-cli/AllowedArguments.swift @@ -10,6 +10,8 @@ import Foundation enum AllowedArguments: String, CaseIterable { case use = "use" + case performSwitch = "switch" + case help = "help" static func has(_ string: String) -> Bool { return Self.allCases.contains { arg in diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index decf4bb..e6c03ba 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -8,29 +8,21 @@ import Foundation -// First, let's read the initial command line argument +let toolver = "1.0" -// REFACTOR REQUIRED -// Information about the Homebrew linked alias -// Information about the PHP versions -// etc.: needs to be stored in a separate object we can instantiate here and in PHP Monitor. - -var logger = Log.shared -logger.verbosity = .warning - -if CommandLine.arguments.count < 3 { - Log.err("You must enter at least two additional arguments.") - exit(1) -} +let log = Log.shared if CommandLine.arguments.contains("-v") || CommandLine.arguments.contains("--verbose") { - logger.verbosity = .info + Log.shared.verbosity = .info } if CommandLine.arguments.contains("-p") || CommandLine.arguments.contains("--performance") { - logger.verbosity = .performance + Log.shared.verbosity = .performance } -let argument = CommandLine.arguments[1] +var argument = "help" +if CommandLine.arguments.count > 1 { + argument = CommandLine.arguments[1] +} if !AllowedArguments.has(argument) { Log.err("The supported arguments are: \(AllowedArguments.rawValues)") @@ -39,15 +31,66 @@ if !AllowedArguments.has(argument) { let action = AllowedArguments.init(rawValue: argument) -let switcher = PhpSwitcher.shared -PhpSwitcher.detectPhpVersions() - switch action { -case .use: - let version = CommandLine.arguments[2] - Log.info("Switching to PHP \(version)...") - break +case .use, .performSwitch: + if !Shell.fileExists("\(Paths.binPath)/php") { + Log.err("PHP is currently not linked. Attempting to link `php` at least...") + _ = Shell.user.executeSynchronously("brew link php", requiresPath: true) + } + + let switcher = PhpSwitcher.shared + PhpSwitcher.detectPhpVersions() + + if CommandLine.arguments.count < 3 { + Log.err("You must enter at least two additional arguments when using this command.") + exit(1) + } + + let version = CommandLine.arguments[2].replacingOccurrences(of: "php@", with: "") + if switcher.availablePhpVersions.contains(version) { + Log.info("Switching to PHP \(version)...") + Actions.switchToPhpVersion( + version: version, + availableVersions: switcher.availablePhpVersions, + completed: { + Log.info("The switch has been completed.") + exit(0) + } + ) + } else { + Log.err("A PHP installation with version \(version) is not installed.") + Log.err("The installed versions are: \(switcher.availablePhpVersions.joined(separator: ", ")).") + Log.err("If this version is available, you may be able to install it by using `brew install php@\(version)`.") + exit(1) + } + +case .help: + print(""" + =============================================================== + PHP MONITOR CLI \(toolver) + by Nico Verbruggen + =============================================================== + + Gives access to the quick version switcher from PHP Monitor, + but without the GUI and 100% of the speed! + + SUPPORTED COMMANDS + + * use {version}: Switch to a specific version of PHP. + (e.g. `phpmon-cli use 8.0`) + * switch {version}: Alias for the `use` command. + * help: Show this help. + + SUPPORTED FLAGS + + * `-v / --verbose`: Enables verbose mode. + * `-p / --perf`: Enables performance mode. + + """) + exit(0) case .none: Log.err("Action not recognized!") exit(1) } + +RunLoop.main.run() diff --git a/phpmon-common/Core/Actions.swift b/phpmon-common/Core/Actions.swift index 7b3fdb1..bbfc80d 100644 --- a/phpmon-common/Core/Actions.swift +++ b/phpmon-common/Core/Actions.swift @@ -75,6 +75,8 @@ class Actions { brew("unlink \(formula)") brew("services stop \(formula)", sudo: true) + Log.perf("Unlinked and stopped services for \(formula)") + group.leave() } } diff --git a/phpmon-common/Core/Logger.swift b/phpmon-common/Core/Logger.swift index a618ede..87f2e81 100644 --- a/phpmon-common/Core/Logger.swift +++ b/phpmon-common/Core/Logger.swift @@ -10,10 +10,12 @@ import Foundation class Log { + static var shared = Log() + enum Verbosity: Int { case error = 1, - info = 2, - warning = 3, + warning = 2, + info = 3, performance = 4 public func isApplicable() -> Bool { @@ -21,15 +23,7 @@ class Log { } } - static var shared = Log() - - var verbosity: Verbosity = .info - - static func info(_ item: Any) { - if Verbosity.info.isApplicable() { - print(item) - } - } + var verbosity: Verbosity = .warning static func err(_ item: Any) { if Verbosity.error.isApplicable() { @@ -43,6 +37,12 @@ class Log { } } + static func info(_ item: Any) { + if Verbosity.info.isApplicable() { + print(item) + } + } + static func perf(_ item: Any) { if Verbosity.performance.isApplicable() { print(item) diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 852839d..1e890d6 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -51,7 +51,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele */ var switcher: PhpSwitcher! = nil - var logger: Log = Log.shared + var logger = Log.shared // MARK: - Initializer @@ -59,7 +59,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele When the application initializes, create all singletons. */ override init() { - Log.shared.verbosity = .info + logger.verbosity = .info Log.info("==================================") Log.info("PHP MONITOR by Nico Verbruggen") Log.info("Version \(App.version)") From 69042042ea8fee3a929cb9e9983a510f27a6a579 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 21 Dec 2021 18:00:07 +0100 Subject: [PATCH 020/105] =?UTF-8?q?=F0=9F=91=8C=20Cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/xcschemes/PHP Monitor CLI.xcscheme | 6 +----- phpmon-cli/main.swift | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme index b54454c..41be842 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor CLI.xcscheme @@ -52,11 +52,7 @@ - - diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index e6c03ba..2a50be7 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -8,7 +8,7 @@ import Foundation -let toolver = "1.0" +let toolver = "0.1 (early access)" let log = Log.shared @@ -34,7 +34,7 @@ let action = AllowedArguments.init(rawValue: argument) switch action { case .use, .performSwitch: if !Shell.fileExists("\(Paths.binPath)/php") { - Log.err("PHP is currently not linked. Attempting to link `php` at least...") + Log.err("PHP is currently not linked. Attempting quick fix...") _ = Shell.user.executeSynchronously("brew link php", requiresPath: true) } From 0d29fbf796a2891148ff48956b986a31bd8591a3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 22 Dec 2021 16:54:27 +0100 Subject: [PATCH 021/105] =?UTF-8?q?=E2=9C=A8=20Add=20`phpmon-cli=20fix`=20?= =?UTF-8?q?command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-cli/AllowedArguments.swift | 1 + phpmon-cli/main.swift | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/phpmon-cli/AllowedArguments.swift b/phpmon-cli/AllowedArguments.swift index 4945cfe..e12a279 100644 --- a/phpmon-cli/AllowedArguments.swift +++ b/phpmon-cli/AllowedArguments.swift @@ -11,6 +11,7 @@ import Foundation enum AllowedArguments: String, CaseIterable { case use = "use" case performSwitch = "switch" + case fix = "fix" case help = "help" static func has(_ string: String) -> Bool { diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 2a50be7..0972646 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -63,7 +63,11 @@ case .use, .performSwitch: Log.err("If this version is available, you may be able to install it by using `brew install php@\(version)`.") exit(1) } - +case .fix: + Log.info("Fixing your PHP installation...") + Actions.fixMyPhp() + Log.info("All operations completed. You can check which version of PHP is linked by using `php -v`.") + exit(0) case .help: print(""" =============================================================== @@ -79,6 +83,8 @@ case .help: * use {version}: Switch to a specific version of PHP. (e.g. `phpmon-cli use 8.0`) * switch {version}: Alias for the `use` command. + * fix Attempts to unlink all PHP versions, + and link the latest version of PHP. * help: Show this help. SUPPORTED FLAGS From 665bba86dd4449e5111f96342fa1892eeb96f455 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 22 Dec 2021 16:55:32 +0100 Subject: [PATCH 022/105] =?UTF-8?q?=F0=9F=94=A7=20Tweak=20default=20verbos?= =?UTF-8?q?ity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-cli/main.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 0972646..27254d4 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -11,9 +11,10 @@ import Foundation let toolver = "0.1 (early access)" let log = Log.shared +log.verbosity = .info -if CommandLine.arguments.contains("-v") || CommandLine.arguments.contains("--verbose") { - Log.shared.verbosity = .info +if CommandLine.arguments.contains("-q") || CommandLine.arguments.contains("--quiet") { + Log.shared.verbosity = .warning } if CommandLine.arguments.contains("-p") || CommandLine.arguments.contains("--performance") { Log.shared.verbosity = .performance @@ -89,7 +90,7 @@ case .help: SUPPORTED FLAGS - * `-v / --verbose`: Enables verbose mode. + * `-q / --quiet`: Silences all logs except for warnings and exceptions. * `-p / --perf`: Enables performance mode. """) From dd251936b929e8849bd2fd8a49dc1701f257704d Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 24 Dec 2021 16:09:51 +0100 Subject: [PATCH 023/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20PhpSwit?= =?UTF-8?q?cher=20into=20PhpEnv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 58 ++++++++-- phpmon-cli/main.swift | 10 +- phpmon-common/Core/Actions.swift | 104 +----------------- phpmon-common/Core/Helpers.swift | 55 +++++++++ phpmon-common/PHP/ActivePhpInstallation.swift | 2 +- .../PHP/{PhpSwitcher.swift => PhpEnv.swift} | 10 +- phpmon-common/PHP/PhpExtension.swift | 2 +- .../PHP/Switcher/InternalSwitcher.swift | 58 ++++++++++ phpmon-common/PHP/Switcher/PhpSwitcher.swift | 15 +++ phpmon/Domain/Core/App.swift | 4 +- phpmon/Domain/Core/AppDelegate.swift | 4 +- .../Homebrew/HomebrewDiagnostics.swift | 6 +- phpmon/Domain/Integrations/Valet/Valet.swift | 2 +- phpmon/Domain/Menu/MainMenu.swift | 50 +++------ phpmon/Domain/Menu/StatusMenu.swift | 32 +++--- .../PHP/ActivePhpInstallation+Checks.swift | 0 phpmon/Domain/Watcher/App+ConfigWatch.swift | 2 +- 17 files changed, 239 insertions(+), 175 deletions(-) create mode 100644 phpmon-common/Core/Helpers.swift rename phpmon-common/PHP/{PhpSwitcher.swift => PhpEnv.swift} (94%) create mode 100644 phpmon-common/PHP/Switcher/InternalSwitcher.swift create mode 100644 phpmon-common/PHP/Switcher/PhpSwitcher.swift rename {phpmon-common => phpmon/Domain}/PHP/ActivePhpInstallation+Checks.swift (100%) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index c3e04e0..09cb6c2 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -24,9 +24,9 @@ 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; }; C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; }; C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; }; - C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; - C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; - C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; }; + C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; }; + C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; }; + C40C7F202772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; }; C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; }; C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; @@ -45,6 +45,9 @@ C415D3E62770F540005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E52770F540005EF286 /* main.swift */; }; C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; }; C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; }; + C417DC74277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; }; + C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; }; + C417DC76277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; }; C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; }; C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; @@ -124,6 +127,12 @@ C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; + C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; }; + C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; }; + C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; }; + C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; + C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; + C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; @@ -192,7 +201,7 @@ 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = ""; }; C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = ""; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; - C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = ""; }; + C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = ""; }; C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = ""; }; C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; @@ -201,6 +210,7 @@ C415D3DE2770F34D005EF286 /* AllowedArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllowedArguments.swift; sourceTree = ""; }; C415D3E52770F540005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+InterApp.swift"; sourceTree = ""; }; + C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = ""; }; C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -254,6 +264,8 @@ 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 = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; + C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = ""; }; + C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; @@ -314,10 +326,10 @@ 54B20EDF263AA22C00D3250E /* PHP */ = { isa = PBXGroup; children = ( + C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, + C4D9ADC2277610E4007277F4 /* Switcher */, C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, - C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */, C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */, - C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */, C4F2E4392752F7D00020E974 /* PhpInstallation.swift */, C4ACA38E25C754C100060C66 /* PhpExtension.swift */, ); @@ -363,6 +375,7 @@ C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, C40C7F2F27722E8D00DDDCDC /* Logger.swift */, + C417DC73277614690015E6EE /* Helpers.swift */, ); path = Core; sourceTree = ""; @@ -429,6 +442,7 @@ children = ( C4AF9F6B275445D300D44ED0 /* Integrations */, C4B13B1D25C4915000548C3A /* Core */, + C4D9ADBD27761084007277F4 /* PHP */, C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* SiteList */, 5420395726135DB800FB00FA /* Preferences */, @@ -556,6 +570,23 @@ name = Frameworks; sourceTree = ""; }; + C4D9ADBD27761084007277F4 /* PHP */ = { + isa = PBXGroup; + children = ( + C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */, + ); + path = PHP; + sourceTree = ""; + }; + C4D9ADC2277610E4007277F4 /* Switcher */ = { + isa = PBXGroup; + children = ( + C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */, + C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */, + ); + path = Switcher; + sourceTree = ""; + }; C4EE55B027708BB2001DF387 /* SwiftUI */ = { isa = PBXGroup; children = ( @@ -743,13 +774,16 @@ C415D3E62770F540005EF286 /* main.swift in Sources */, C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */, C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, + C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */, C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, + C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */, C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */, + C417DC76277614690015E6EE /* Helpers.swift in Sources */, C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */, C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, - C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */, + C40C7F202772136000DDDCDC /* PhpEnv.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -791,8 +825,10 @@ C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, + C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */, 54AB03262763858F00A29D5F /* Timer.swift in Sources */, C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, + C417DC74277614690015E6EE /* Helpers.swift in Sources */, C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, @@ -801,10 +837,11 @@ C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, - C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */, + C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */, + C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, @@ -829,6 +866,7 @@ C4EE55AC27708B9E001DF387 /* Preview.swift in Sources */, 54B48B60275F66AE006D90C5 /* Application.swift in Sources */, C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, + C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */, C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */, @@ -855,6 +893,7 @@ C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */, + C417DC75277614690015E6EE /* Helpers.swift in Sources */, C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */, @@ -865,6 +904,7 @@ C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, + C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, C4B585452770FE3900DA4FBE /* Command.swift in Sources */, @@ -875,7 +915,7 @@ C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */, - C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */, + C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */, C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */, C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */, C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, diff --git a/phpmon-cli/main.swift b/phpmon-cli/main.swift index 27254d4..8b986a2 100644 --- a/phpmon-cli/main.swift +++ b/phpmon-cli/main.swift @@ -39,8 +39,8 @@ case .use, .performSwitch: _ = Shell.user.executeSynchronously("brew link php", requiresPath: true) } - let switcher = PhpSwitcher.shared - PhpSwitcher.detectPhpVersions() + let phpenv = PhpEnv.shared + PhpEnv.detectPhpVersions() if CommandLine.arguments.count < 3 { Log.err("You must enter at least two additional arguments when using this command.") @@ -48,11 +48,11 @@ case .use, .performSwitch: } let version = CommandLine.arguments[2].replacingOccurrences(of: "php@", with: "") - if switcher.availablePhpVersions.contains(version) { + if phpenv.availablePhpVersions.contains(version) { Log.info("Switching to PHP \(version)...") Actions.switchToPhpVersion( version: version, - availableVersions: switcher.availablePhpVersions, + availableVersions: phpenv.availablePhpVersions, completed: { Log.info("The switch has been completed.") exit(0) @@ -60,7 +60,7 @@ case .use, .performSwitch: ) } else { Log.err("A PHP installation with version \(version) is not installed.") - Log.err("The installed versions are: \(switcher.availablePhpVersions.joined(separator: ", ")).") + Log.err("The installed versions are: \(phpenv.availablePhpVersions.joined(separator: ", ")).") Log.err("If this version is available, you may be able to install it by using `brew install php@\(version)`.") exit(1) } diff --git a/phpmon-common/Core/Actions.swift b/phpmon-common/Core/Actions.swift index bbfc80d..bb5163a 100644 --- a/phpmon-common/Core/Actions.swift +++ b/phpmon-common/Core/Actions.swift @@ -14,7 +14,7 @@ class Actions { public static func restartPhpFpm() { - brew("services restart \(PhpSwitcher.phpInstall.formula)", sudo: true) + brew("services restart \(PhpEnv.phpInstall.formula)", sudo: true) } public static func restartNginx() @@ -29,7 +29,7 @@ class Actions { public static func stopAllServices() { - brew("services stop \(PhpSwitcher.phpInstall.formula)", sudo: true) + brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true) brew("services stop nginx", sudo: true) brew("services stop dnsmasq", sudo: true) } @@ -47,53 +47,6 @@ class Actions { completed() } - /** - Switching to a new PHP version involves: - - unlinking the current version - - stopping the active services - - linking the new desired version - - Please note that depending on which version is installed, - the version that is switched to may or may not be identical to `php` (without @version). - */ - public static func switchToPhpVersion( - version: String, - availableVersions: [String], - completed: @escaping () -> Void - ) { - Log.info("Switching to \(version), unlinking all versions...") - - let group = DispatchGroup() - - availableVersions.forEach { (available) in - group.enter() - - DispatchQueue.global(qos: .userInitiated).async { - let formula = (available == PhpSwitcher.brewPhpVersion) - ? "php" : "php@\(available)" - - brew("unlink \(formula)") - brew("services stop \(formula)", sudo: true) - - Log.perf("Unlinked and stopped services for \(formula)") - - group.leave() - } - } - - group.notify(queue: .global(qos: .userInitiated)) { - Log.info("All versions have been unlinked!") - Log.info("Linking the new version!") - - let formula = (version == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version)" - brew("link \(formula) --overwrite --force") - brew("services start \(formula)", sudo: true) - - Log.info("The new version has been linked!") - completed() - } - } - // MARK: - Finding Config Files public static func openGenericPhpConfigFolder() @@ -138,8 +91,8 @@ class Actions { { brew("services restart dnsmasq", sudo: true) - PhpSwitcher.shared.detectPhpVersions().forEach { (version) in - let formula = (version == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version)" + PhpEnv.shared.detectPhpVersions().forEach { (version) in + let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)" brew("unlink php@\(version)") brew("services stop \(formula)") brew("services stop \(formula)", sudo: true) @@ -152,53 +105,4 @@ class Actions { brew("services stop php", sudo: true) brew("services stop nginx", sudo: true) } - - // MARK: Common Shell Commands - - /** - Runs a `valet` command. - */ - public static func valet(_ command: String) -> String - { - return Shell.pipe("sudo \(Paths.valet) \(command)", requiresPath: true) - } - - /** - Runs a `brew` command. Can run as superuser. - */ - public static func brew(_ command: String, sudo: Bool = false) - { - Shell.run("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)") - } - - /** - Runs `sed` in order to replace all occurrences of a string in a specific file with another. - */ - public static func sed(file: String, original: String, replacement: String) - { - // Escape slashes (or `sed` won't work) - let e_original = original.replacingOccurrences(of: "/", with: "\\/") - let e_replacement = replacement.replacingOccurrences(of: "/", with: "\\/") - - // Check if gsed exists; it is able to follow symlinks, - // which we want to do to toggle the extension - if Shell.fileExists("\(Paths.binPath)/gsed") { - Shell.run("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)") - } else { - Shell.run("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)") - } - } - - /** - Uses `grep` to determine whether a particular query string can be found in a particular file. - */ - public static func grepContains(file: String, query: String) -> Bool - { - return Shell.pipe(""" - grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO" - """) - .trimmingCharacters(in: .whitespacesAndNewlines) - .contains("YES") - } - } diff --git a/phpmon-common/Core/Helpers.swift b/phpmon-common/Core/Helpers.swift new file mode 100644 index 0000000..369b605 --- /dev/null +++ b/phpmon-common/Core/Helpers.swift @@ -0,0 +1,55 @@ +// +// Helpers.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 24/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +// MARK: Common Shell Commands + +/** + Runs a `valet` command. + */ +func valet(_ command: String) -> String +{ + return Shell.pipe("sudo \(Paths.valet) \(command)", requiresPath: true) +} + +/** + Runs a `brew` command. Can run as superuser. + */ +func brew(_ command: String, sudo: Bool = false) +{ + Shell.run("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)") +} + +/** + Runs `sed` in order to replace all occurrences of a string in a specific file with another. + */ +func sed(file: String, original: String, replacement: String) +{ + // Escape slashes (or `sed` won't work) + let e_original = original.replacingOccurrences(of: "/", with: "\\/") + let e_replacement = replacement.replacingOccurrences(of: "/", with: "\\/") + + // Check if gsed exists; it is able to follow symlinks, + // which we want to do to toggle the extension + if Shell.fileExists("\(Paths.binPath)/gsed") { + Shell.run("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)") + } else { + Shell.run("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)") + } +} + +/** + Uses `grep` to determine whether a particular query string can be found in a particular file. + */ +func grepContains(file: String, query: String) -> Bool +{ + return Shell.pipe(""" + grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO" + """) + .trimmingCharacters(in: .whitespacesAndNewlines) + .contains("YES") +} diff --git a/phpmon-common/PHP/ActivePhpInstallation.swift b/phpmon-common/PHP/ActivePhpInstallation.swift index 6111ffe..f4b124e 100644 --- a/phpmon-common/PHP/ActivePhpInstallation.swift +++ b/phpmon-common/PHP/ActivePhpInstallation.swift @@ -25,7 +25,7 @@ class ActivePhpInstallation { // MARK: - Computed var formula: String { - return (version.short == PhpSwitcher.brewPhpVersion) ? "php" : "php@\(version.short)" + return (version.short == PhpEnv.brewPhpVersion) ? "php" : "php@\(version.short)" } // MARK: - Initializer diff --git a/phpmon-common/PHP/PhpSwitcher.swift b/phpmon-common/PHP/PhpEnv.swift similarity index 94% rename from phpmon-common/PHP/PhpSwitcher.swift rename to phpmon-common/PHP/PhpEnv.swift index 8927d1e..c5dcb55 100644 --- a/phpmon-common/PHP/PhpSwitcher.swift +++ b/phpmon-common/PHP/PhpEnv.swift @@ -13,7 +13,7 @@ protocol PhpSwitcherDelegate: AnyObject { func switcherDidCompleteSwitch() } -class PhpSwitcher { +class PhpEnv { // MARK: - Initializer @@ -36,7 +36,7 @@ class PhpSwitcher { weak var delegate: PhpSwitcherDelegate? /** The static app instance. Accessible at any time. */ - static let shared = PhpSwitcher() + static let shared = PhpEnv() /** Whether the switcher is busy performing any actions. */ var isBusy: Bool = false @@ -77,6 +77,12 @@ class PhpSwitcher { // MARK: - Methods + public static var switcher: PhpSwitcher { + // Based on the setting, use a particular switcher + // For now, we'll hardcode the internal switcher though + return InternalSwitcher() + } + public static func detectPhpVersions() -> Void { _ = Self.shared.detectPhpVersions() } diff --git a/phpmon-common/PHP/PhpExtension.swift b/phpmon-common/PHP/PhpExtension.swift index 0996691..05f1a06 100644 --- a/phpmon-common/PHP/PhpExtension.swift +++ b/phpmon-common/PHP/PhpExtension.swift @@ -78,7 +78,7 @@ class PhpExtension { // ENABLED: Line where the comment delimiter (;) is removed : line.replacingOccurrences(of: "; ", with: "") - Actions.sed(file: file, original: line, replacement: newLine) + sed(file: file, original: line, replacement: newLine) enabled.toggle() } diff --git a/phpmon-common/PHP/Switcher/InternalSwitcher.swift b/phpmon-common/PHP/Switcher/InternalSwitcher.swift new file mode 100644 index 0000000..59fccbb --- /dev/null +++ b/phpmon-common/PHP/Switcher/InternalSwitcher.swift @@ -0,0 +1,58 @@ +// +// InternalSwitcher.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 24/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class InternalSwitcher: PhpSwitcher { + + /** + Switching to a new PHP version involves: + - unlinking the current version + - stopping the active services + - linking the new desired version + + Please note that depending on which version is installed, + the version that is switched to may or may not be identical to `php` + (without @version). + */ + func performSwitch(to version: String, completion: @escaping () -> Void) + { + Log.info("Switching to \(version), unlinking all versions...") + + let group = DispatchGroup() + + PhpEnv.shared.availablePhpVersions.forEach { (available) in + group.enter() + + DispatchQueue.global(qos: .userInitiated).async { + let formula = (available == PhpEnv.brewPhpVersion) + ? "php" : "php@\(available)" + + brew("unlink \(formula)") + brew("services stop \(formula)", sudo: true) + + Log.perf("Unlinked and stopped services for \(formula)") + + group.leave() + } + } + + group.notify(queue: .global(qos: .userInitiated)) { + Log.info("All versions have been unlinked!") + Log.info("Linking the new version!") + + let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)" + brew("link \(formula) --overwrite --force") + brew("services start \(formula)", sudo: true) + + Log.info("The new version has been linked!") + completion() + } + } + +} diff --git a/phpmon-common/PHP/Switcher/PhpSwitcher.swift b/phpmon-common/PHP/Switcher/PhpSwitcher.swift new file mode 100644 index 0000000..e167e15 --- /dev/null +++ b/phpmon-common/PHP/Switcher/PhpSwitcher.swift @@ -0,0 +1,15 @@ +// +// PhpVersionSwitchContract.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 24/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +protocol PhpSwitcher { + + func performSwitch(to version: String, completion: @escaping () -> Void) + +} diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 1743377..788b618 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -24,7 +24,7 @@ class App: PhpSwitcherDelegate { /** Whether the app is busy doing something. Used to determine what UI to display. */ static var busy: Bool { - return PhpSwitcher.shared.isBusy + return PhpEnv.shared.isBusy } // MARK: Variables @@ -81,7 +81,7 @@ class App: PhpSwitcherDelegate { } func switcherDidCompleteSwitch() { - PhpSwitcher.shared.currentInstall = ActivePhpInstallation() + PhpEnv.shared.currentInstall = ActivePhpInstallation() handlePhpConfigWatcher() } } diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 1e890d6..3183636 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -49,7 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele detection, as well as switching. It is initialized when the app is ready and passed all checks. */ - var switcher: PhpSwitcher! = nil + var switcher: PhpEnv! = nil var logger = Log.shared @@ -73,7 +73,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele } func initializeSwitcher() { - self.switcher = PhpSwitcher.shared + self.switcher = PhpEnv.shared self.switcher.delegate = self.state } diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index 29faf62..0f8dec2 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -46,12 +46,12 @@ class HomebrewDiagnostics { from: tapAlias.data(using: .utf8)! ).first! - if tapPhp.version != PhpSwitcher.brewPhpVersion { + if tapPhp.version != PhpEnv.brewPhpVersion { Log.warn("The `php` formula alias seems to be the different between the tap and core. This could be a problem!") Log.info("Determining whether both of these versions are installed...") - let bothInstalled = PhpSwitcher.shared.availablePhpVersions.contains(tapPhp.version) - && PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.brewPhpVersion) + let bothInstalled = PhpEnv.shared.availablePhpVersions.contains(tapPhp.version) + && PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) if bothInstalled { Log.warn("Both conflicting aliases seem to be installed, warning the user!") diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index a40295b..ce8ae55 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -22,7 +22,7 @@ class Valet { var sites: [Site] = [] init() { - version = VersionExtractor.from(Actions.valet("--version")) + version = VersionExtractor.from(valet("--version")) ?? "UNKNOWN" let file = FileManager.default.homeDirectoryForCurrentUser diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 121d86e..8cbb51f 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -41,7 +41,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { When the environment is all clear and the app can run, let's go. */ private func onEnvironmentPass() { - PhpSwitcher.detectPhpVersions() + PhpEnv.detectPhpVersions() if HomebrewDiagnostics.shared.errors.contains(.aliasConflict) { DispatchQueue.main.async { @@ -57,7 +57,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { Log.info("Determining broken PHP-FPM...") // Attempt to find out if PHP-FPM is broken - let installation = PhpSwitcher.phpInstall + let installation = PhpEnv.phpInstall installation.notifyAboutBrokenPhpFpm() // Set up the config watchers on launch (these are automatically updated via delegate methods if the user switches) @@ -185,12 +185,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { */ private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {}) { - PhpSwitcher.shared.isBusy = true + PhpEnv.shared.isBusy = true setBusyImage() DispatchQueue.global(qos: .userInitiated).async { [unowned self] in update() execute() - PhpSwitcher.shared.isBusy = false + PhpEnv.shared.isBusy = false DispatchQueue.main.async { [self] in updatePhpVersionInStatusBar() @@ -203,7 +203,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // MARK: - User Interface @objc func refreshActiveInstallation() { - PhpSwitcher.shared.currentInstall = ActivePhpInstallation() + PhpEnv.shared.currentInstall = ActivePhpInstallation() updatePhpVersionInStatusBar() } @@ -223,7 +223,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } else { // The dynamic icon has been requested let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool - setStatusBarImage(version: long ? PhpSwitcher.phpInstall.version.long : PhpSwitcher.phpInstall.version.short) + setStatusBarImage(version: long ? PhpEnv.phpInstall.version.long : PhpEnv.phpInstall.version.short) } } } @@ -343,12 +343,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { - PhpSwitcher.shared.isBusy = true + PhpEnv.shared.isBusy = true setBusyImage() self.update() let noLongerBusy = { - PhpSwitcher.shared.isBusy = false + PhpEnv.shared.isBusy = false DispatchQueue.main.async { [self] in self.updatePhpVersionInStatusBar() self.update() @@ -419,14 +419,14 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } @objc func openActiveConfigFolder() { - if (PhpSwitcher.phpInstall.version.error) { + if (PhpEnv.phpInstall.version.error) { // php version was not identified Actions.openGenericPhpConfigFolder() return } // php version was identified - Actions.openPhpConfigFolder(version: PhpSwitcher.phpInstall.version.short) + Actions.openPhpConfigFolder(version: PhpEnv.phpInstall.version.short) } @objc func openGlobalComposerFolder() { @@ -439,7 +439,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { @objc func switchToPhpVersion(sender: PhpMenuItem) { setBusyImage() - PhpSwitcher.shared.isBusy = true + PhpEnv.shared.isBusy = true DispatchQueue.global(qos: .userInitiated).async { [unowned self] in // Update the PHP version in the status bar @@ -449,10 +449,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { update() let completion = { - PhpSwitcher.shared.delegate?.switcherDidCompleteSwitch() + PhpEnv.shared.delegate?.switcherDidCompleteSwitch() // Mark as no longer busy - PhpSwitcher.shared.isBusy = false + PhpEnv.shared.isBusy = false // Perform UI updates on main thread DispatchQueue.main.async { [self] in @@ -464,7 +464,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { title: String(format: "notification.version_changed_title".localized, sender.version), subtitle: String(format: "notification.version_changed_desc".localized, sender.version) ) - PhpSwitcher.phpInstall.notifyAboutBrokenPhpFpm() + PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() } // Run composer updates @@ -478,24 +478,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } } - /* DISABLED UNTIL VALET SWITCHING IS OK (see #34) - if Preferences.preferences[.useInternalSwitcher] as! Bool == false { - // 1. Default switcher using Valet - // Will cause less issues, but is slower - Actions.switchToPhpVersionUsingValet( - version: sender.version, - availableVersions: App.shared.availablePhpVersions, - completed: completion - ) - } else { */ - // 2. Custom switcher (internal) - // Will cause more issues with Homebrew and is faster - Actions.switchToPhpVersion( - version: sender.version, - availableVersions: PhpSwitcher.shared.availablePhpVersions, - completed: completion - ) - /* } */ + PhpEnv.switcher.performSwitch( + to: sender.version, + completion: completion + ) } } diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 8c8a5a7..58facdd 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -9,14 +9,14 @@ import Cocoa class StatusMenu : NSMenu { func addPhpVersionMenuItems() { - if PhpSwitcher.phpInstall.version.error { + if PhpEnv.phpInstall.version.error { for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] { addItem(NSMenuItem(title: message.localized, action: nil, keyEquivalent: "")) } return } - let phpVersionText = "\("mi_php_version".localized) \(PhpSwitcher.phpInstall.version.long)" + let phpVersionText = "\("mi_php_version".localized) \(PhpEnv.phpInstall.version.long)" addItem(HeaderView.asMenuItem(text: phpVersionText)) } @@ -26,7 +26,7 @@ class StatusMenu : NSMenu { return } - if PhpSwitcher.shared.availablePhpVersions.count == 0 { + if PhpEnv.shared.availablePhpVersions.count == 0 { return } @@ -40,14 +40,14 @@ class StatusMenu : NSMenu { servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) - if !PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.brewPhpVersion) { + if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) { servicesMenu.addItem(NSMenuItem( - title: "mi_force_load_latest_unavailable".localized(PhpSwitcher.brewPhpVersion), + title: "mi_force_load_latest_unavailable".localized(PhpEnv.brewPhpVersion), action: nil, keyEquivalent: "f" )) } else { servicesMenu.addItem(NSMenuItem( - title: "mi_force_load_latest".localized(PhpSwitcher.brewPhpVersion), + title: "mi_force_load_latest".localized(PhpEnv.brewPhpVersion), action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f")) } @@ -92,13 +92,13 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) - self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: PhpSwitcher.shared.isBusy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) + self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: PhpEnv.shared.isBusy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) - if (PhpSwitcher.shared.isBusy) { + if (PhpEnv.shared.isBusy) { return } - let stats = PhpSwitcher.phpInstall.limits + let stats = PhpEnv.phpInstall.limits // Stats self.addItem(NSMenuItem.separator()) @@ -112,12 +112,12 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized)) - if (PhpSwitcher.phpInstall.extensions.count == 0) { + if (PhpEnv.phpInstall.extensions.count == 0) { self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: "")) } var shortcutKey = 1 - for phpExtension in PhpSwitcher.phpInstall.extensions { + for phpExtension in PhpEnv.phpInstall.extensions { self.addExtensionItem(phpExtension, shortcutKey) shortcutKey += 1 } @@ -130,20 +130,20 @@ class StatusMenu : NSMenu { private func addSwitchToPhpMenuItems() { var shortcutKey = 1 - for index in (0.. Date: Mon, 3 Jan 2022 16:02:18 +0100 Subject: [PATCH 024/105] =?UTF-8?q?=F0=9F=91=8C=20Cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-common/Core/Actions.swift | 13 +++++++++ phpmon/Domain/Menu/MainMenu.swift | 28 ++++++++++--------- phpmon/Domain/Preferences/Preferences.swift | 4 +++ .../Views/CheckboxPreferenceView.swift | 3 +- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/phpmon-common/Core/Actions.swift b/phpmon-common/Core/Actions.swift index bb5163a..11467a0 100644 --- a/phpmon-common/Core/Actions.swift +++ b/phpmon-common/Core/Actions.swift @@ -74,6 +74,19 @@ class Actions { .appendingPathComponent(".config/valet") NSWorkspace.shared.activateFileViewerSelecting([file] as [URL]) } + + // MARK: - Other Actions + + public static func createTempPhpInfoFile() -> URL + { + // Write a file called `phpmon_phpinfo.php` to /tmp + try! " /tmp/phpmon_phpinfo.html") + + return URL(string: "file:///private/tmp/phpmon_phpinfo.html")! + } // MARK: - Quick Fix diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 8cbb51f..f7f4dd9 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -307,34 +307,38 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { waitAndExecute { sender.phpExtension?.toggle() - if Preferences.preferences[.autoServiceRestartAfterExtensionToggle] as! Bool == true { + if Preferences.isTrue(.autoServiceRestartAfterExtensionToggle) { Actions.restartPhpFpm() } } } @objc func openPhpInfo() { + var url: URL? = nil + waitAndExecute { - // Write a file called `phpmon_phpinfo.php` to /tmp - try! " /tmp/phpmon_phpinfo.html") + url = Actions.createTempPhpInfoFile() } completion: { // When this has been completed, open the URL to the file in the browser - NSWorkspace.shared.open(URL(string: "file:///private/tmp/phpmon_phpinfo.html")!) + NSWorkspace.shared.open(url!) } } @objc func forceRestartLatestPhp() { // Tell the user the switch is about to occur - Alert.notify(message: "alert.force_reload.title".localized, info: "alert.force_reload.info".localized) + Alert.notify( + message: "alert.force_reload.title".localized, + info: "alert.force_reload.info".localized + ) // Start switching waitAndExecute { Actions.fixMyPhp() } completion: { - Alert.notify(message: "alert.force_reload_done.title".localized, info: "alert.force_reload_done.info".localized) + Alert.notify( + message: "alert.force_reload_done.title".localized, + info: "alert.force_reload_done.info".localized + ) } } @@ -468,10 +472,8 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } // Run composer updates - if Preferences.preferences[.autoComposerGlobalUpdateAfterSwitch] as! Bool == true { - self.updateGlobalDependencies(notify: false, completion: { success in - sendLocalNotification() - }) + if Preferences.isTrue(.autoComposerGlobalUpdateAfterSwitch) { + self.updateGlobalDependencies(notify: false, completion: { _ in sendLocalNotification() }) } else { sendLocalNotification() } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 45c8bff..9b2e9bd 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -66,6 +66,10 @@ class Preferences { return Self.shared.cachedPreferences } + static func isTrue(_ preference: PreferenceName) -> Bool { + return Preferences.preferences[preference] as! Bool == true + } + // MARK: - Internal Functionality static func cache() -> [PreferenceName: Any] { diff --git a/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift b/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift index 2736e08..941aef5 100644 --- a/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift +++ b/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift @@ -21,8 +21,7 @@ class CheckboxPreferenceView: NSView, XibLoadable { var preference: PreferenceName! { didSet { - let shouldDisplay = Preferences.preferences[self.preference] as! Bool == true - self.buttonCheckbox.state = shouldDisplay ? .on : .off + self.buttonCheckbox.state = Preferences.isTrue(self.preference) ? .on : .off } } From 78510ea3fe027c48615cc6fd5821e11dd5ceb8db Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 3 Jan 2022 16:09:49 +0100 Subject: [PATCH 025/105] =?UTF-8?q?=F0=9F=91=8C=20Even=20more=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu.swift | 4 ++-- phpmon/Domain/Preferences/Preferences.swift | 14 +++++++++++--- .../Preferences/Views/CheckboxPreferenceView.swift | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index f7f4dd9..6abfa62 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -307,7 +307,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { waitAndExecute { sender.phpExtension?.toggle() - if Preferences.isTrue(.autoServiceRestartAfterExtensionToggle) { + if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) { Actions.restartPhpFpm() } } @@ -472,7 +472,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } // Run composer updates - if Preferences.isTrue(.autoComposerGlobalUpdateAfterSwitch) { + if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) { self.updateGlobalDependencies(notify: false, completion: { _ in sendLocalNotification() }) } else { sendLocalNotification() diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 9b2e9bd..efe9ab9 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -66,13 +66,21 @@ class Preferences { return Self.shared.cachedPreferences } - static func isTrue(_ preference: PreferenceName) -> Bool { - return Preferences.preferences[preference] as! Bool == true + /** + Determine whether a particular preference is enabled. + - Important: Requires the preference to have a corresponding boolean value, or a fatal error will be thrown. + */ + static func isEnabled(_ preference: PreferenceName) -> Bool { + if let bool = Preferences.preferences[preference] as? Bool { + return bool == true + } else { + fatalError("\(preference) is not a valid boolean preference!") + } } // MARK: - Internal Functionality - static func cache() -> [PreferenceName: Any] { + private static func cache() -> [PreferenceName: Any] { return [ // Part 1: Always Booleans .shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any, diff --git a/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift b/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift index 941aef5..7040cb6 100644 --- a/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift +++ b/phpmon/Domain/Preferences/Views/CheckboxPreferenceView.swift @@ -21,7 +21,7 @@ class CheckboxPreferenceView: NSView, XibLoadable { var preference: PreferenceName! { didSet { - self.buttonCheckbox.state = Preferences.isTrue(self.preference) ? .on : .off + self.buttonCheckbox.state = Preferences.isEnabled(self.preference) ? .on : .off } } From 40a0bd6caba70499d9aaa572acb5430d74fc6554 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 3 Jan 2022 16:19:18 +0100 Subject: [PATCH 026/105] =?UTF-8?q?=F0=9F=91=8C=20Prevent=20crash=20when?= =?UTF-8?q?=20refresh=20and=20switcher=20run=20at=20same=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 + phpmon/Domain/Menu/MainMenu+Startup.swift | 102 +++++++++ phpmon/Domain/Menu/MainMenu.swift | 258 ++++++++-------------- phpmon/Domain/Menu/StatusMenu.swift | 2 +- 4 files changed, 195 insertions(+), 171 deletions(-) create mode 100644 phpmon/Domain/Menu/MainMenu+Startup.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 09cb6c2..c4a4f98 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -120,6 +120,7 @@ C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; }; C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; + C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; }; C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; @@ -260,6 +261,7 @@ 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 = ""; }; + C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.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 = ""; }; @@ -480,6 +482,7 @@ isa = PBXGroup; children = ( C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */, + C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */, C47331A1247093B7009A0597 /* StatusMenu.swift */, C48D0C9525CC80B100CC7490 /* HeaderView.swift */, C48D0C9925CC888B00CC7490 /* HeaderView.xib */, @@ -844,6 +847,7 @@ C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, + C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */, diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift new file mode 100644 index 0000000..8dd7494 --- /dev/null +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -0,0 +1,102 @@ +// +// MainMenu+Startup.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 03/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Cocoa + +extension MainMenu { + /** + Kick off the startup of the rendering of the main menu. + */ + func startup() { + // Start with the icon + setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!) + + // Perform environment boot checks + DispatchQueue.global(qos: .userInitiated).async { [unowned self] in + Startup().checkEnvironment(success: { onEnvironmentPass() }, + failure: { onEnvironmentFail() } + ) + } + } + + /** + When the environment is all clear and the app can run, let's go. + */ + private func onEnvironmentPass() { + PhpEnv.detectPhpVersions() + + if HomebrewDiagnostics.shared.errors.contains(.aliasConflict) { + DispatchQueue.main.async { + Alert.notify( + message: "alert.php_alias_conflict.title".localized, + info: "alert.php_alias_conflict.info".localized, + style: .critical + ) + } + } + + updatePhpVersionInStatusBar() + + Log.info("Determining broken PHP-FPM...") + // Attempt to find out if PHP-FPM is broken + let installation = PhpEnv.phpInstall + installation.notifyAboutBrokenPhpFpm() + + // Set up the config watchers on launch (these are automatically updated via delegate methods if the user switches) + Log.info("Setting up watchers...") + App.shared.handlePhpConfigWatcher() + + Log.info("Detecting applications...") + // Attempt to load list of applications + App.shared.detectedApplications = Application.detectPresetApplications() + let appNames = App.shared.detectedApplications.map { app in + return app.name + } + Log.info("Detected applications: \(appNames)") + + // Load the global hotkey + App.shared.loadGlobalHotkey() + + // Attempt to find out more info about Valet + Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)") + Valet.shared.validateVersion() + Valet.shared.startPreloadingSites() + Log.info("PHP Monitor is ready to serve!") + + // Schedule a request to fetch the PHP version every 60 seconds + DispatchQueue.main.async { [self] in + App.shared.timer = Timer.scheduledTimer( + timeInterval: 60, + target: self, + selector: #selector(refreshActiveInstallation), + userInfo: nil, + repeats: true + ) + } + } + + /** + When the environment is not OK, present an alert to inform the user. + */ + private func onEnvironmentFail() { + DispatchQueue.main.async { [self] in + let close = Alert.present( + messageText: "alert.cannot_start.title".localized, + informativeText: "alert.cannot_start.info".localized, + buttonTitle: "alert.cannot_start.close".localized, + secondButtonTitle: "alert.cannot_start.retry".localized + ) + + if (close) { + exit(1) + } + + startup() + } + } +} diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 6abfa62..7cda0f7 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -22,97 +22,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // MARK: - UI related - /** - Kick off the startup of the rendering of the main menu. - */ - func startup() { - // Start with the icon - setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!) - - // Perform environment boot checks - DispatchQueue.global(qos: .userInitiated).async { [unowned self] in - Startup().checkEnvironment(success: { onEnvironmentPass() }, - failure: { onEnvironmentFail() } - ) - } - } - - /** - When the environment is all clear and the app can run, let's go. - */ - private func onEnvironmentPass() { - PhpEnv.detectPhpVersions() - - if HomebrewDiagnostics.shared.errors.contains(.aliasConflict) { - DispatchQueue.main.async { - Alert.notify( - message: "alert.php_alias_conflict.title".localized, - info: "alert.php_alias_conflict.info".localized, - style: .critical - ) - } - } - - updatePhpVersionInStatusBar() - - Log.info("Determining broken PHP-FPM...") - // Attempt to find out if PHP-FPM is broken - let installation = PhpEnv.phpInstall - installation.notifyAboutBrokenPhpFpm() - - // Set up the config watchers on launch (these are automatically updated via delegate methods if the user switches) - Log.info("Setting up watchers...") - App.shared.handlePhpConfigWatcher() - - Log.info("Detecting applications...") - // Attempt to load list of applications - App.shared.detectedApplications = Application.detectPresetApplications() - let appNames = App.shared.detectedApplications.map { app in - return app.name - } - Log.info("Detected applications: \(appNames)") - - // Load the global hotkey - App.shared.loadGlobalHotkey() - - // Attempt to find out more info about Valet - Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)") - Valet.shared.validateVersion() - Valet.shared.startPreloadingSites() - Log.info("PHP Monitor is ready to serve!") - - // Schedule a request to fetch the PHP version every 60 seconds - DispatchQueue.main.async { [self] in - App.shared.timer = Timer.scheduledTimer( - timeInterval: 60, - target: self, - selector: #selector(refreshActiveInstallation), - userInfo: nil, - repeats: true - ) - } - } - - /** - When the environment is not OK, present an alert to inform the user. - */ - private func onEnvironmentFail() { - DispatchQueue.main.async { [self] in - let close = Alert.present( - messageText: "alert.cannot_start.title".localized, - informativeText: "alert.cannot_start.info".localized, - buttonTitle: "alert.cannot_start.close".localized, - secondButtonTitle: "alert.cannot_start.retry".localized - ) - - if (close) { - exit(1) - } - - startup() - } - } - /** Update the menu's contents, based on what's going on. */ @@ -203,8 +112,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // MARK: - User Interface @objc func refreshActiveInstallation() { - PhpEnv.shared.currentInstall = ActivePhpInstallation() - updatePhpVersionInStatusBar() + if !PhpEnv.shared.isBusy { + PhpEnv.shared.currentInstall = ActivePhpInstallation() + updatePhpVersionInStatusBar() + } else { + Log.perf("Skipping version refresh due to busy status") + } } @objc func updatePhpVersionInStatusBar() { @@ -342,86 +255,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } } - @objc func updateComposerDependencies() { + @objc func updateGlobalComposerDependencies() { self.updateGlobalDependencies(notify: true, completion: { _ in }) } - func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { - PhpEnv.shared.isBusy = true - setBusyImage() - self.update() - - let noLongerBusy = { - PhpEnv.shared.isBusy = false - DispatchQueue.main.async { [self] in - self.updatePhpVersionInStatusBar() - self.update() - } - } - - 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 output = Shell.user.executeSynchronously( - "composer global update", requiresPath: true - ) - - let task = Shell.user.createTask(for: "composer global update", requiresPath: true) - - DispatchQueue.main.async { - window?.addToConsole("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 output.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) - } - } - } - } - @objc func openActiveConfigFolder() { if (PhpEnv.phpInstall.version.error) { // php version was not identified @@ -515,4 +352,85 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // When the menu is closed, allow the shortcut to work again App.shared.shortcutHotkey?.isPaused = false } + + // MARK: - Private Methods + + /** + + */ + private func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { + PhpEnv.shared.isBusy = true + setBusyImage() + self.update() + + let noLongerBusy = { + PhpEnv.shared.isBusy = false + DispatchQueue.main.async { [self] in + self.updatePhpVersionInStatusBar() + self.update() + } + } + + 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 output = Shell.user.executeSynchronously( + "composer global update", requiresPath: true + ) + + let task = Shell.user.createTask(for: "composer global update", requiresPath: true) + + DispatchQueue.main.async { + window?.addToConsole("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 output.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/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 58facdd..2644681 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -92,7 +92,7 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem.separator()) self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) - self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: PhpEnv.shared.isBusy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) + self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: PhpEnv.shared.isBusy ? nil : #selector(MainMenu.updateGlobalComposerDependencies), keyEquivalent: "")) if (PhpEnv.shared.isBusy) { return From 722e082526c7d07d8c456ea51b658da2e86992ca Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 3 Jan 2022 16:20:53 +0100 Subject: [PATCH 027/105] =?UTF-8?q?=F0=9F=91=8C=20Rename=20method=20from?= =?UTF-8?q?=20update=20to=20rebuild?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/MainMenu.swift | 17 +++++++++-------- phpmon/Domain/Preferences/PrefsVC.swift | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 7cda0f7..ae12f5f 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -24,8 +24,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { /** Update the menu's contents, based on what's going on. + This will rebuild the entire menu, so this can take a few moments. */ - func update() { + func rebuild() { // Update the menu item on the main thread DispatchQueue.main.async { [self] in // Create a new menu @@ -97,13 +98,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { PhpEnv.shared.isBusy = true setBusyImage() DispatchQueue.global(qos: .userInitiated).async { [unowned self] in - update() + rebuild() execute() PhpEnv.shared.isBusy = false DispatchQueue.main.async { [self] in updatePhpVersionInStatusBar() - update() + rebuild() completion() } } @@ -122,7 +123,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { @objc func updatePhpVersionInStatusBar() { refreshIcon() - update() + rebuild() } func refreshIcon() { @@ -287,7 +288,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { updatePhpVersionInStatusBar() // Update the menu - update() + rebuild() let completion = { PhpEnv.shared.delegate?.switcherDidCompleteSwitch() @@ -298,7 +299,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { // Perform UI updates on main thread DispatchQueue.main.async { [self] in updatePhpVersionInStatusBar() - update() + rebuild() let sendLocalNotification = { LocalNotification.send( @@ -361,13 +362,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { private func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { PhpEnv.shared.isBusy = true setBusyImage() - self.update() + self.rebuild() let noLongerBusy = { PhpEnv.shared.isBusy = false DispatchQueue.main.async { [self] in self.updatePhpVersionInStatusBar() - self.update() + self.rebuild() } } diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index a1aaaab..b2555ef 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -64,7 +64,7 @@ class PrefsVC: NSViewController { preference: .fullPhpVersionDynamicIcon, action: { MainMenu.shared.refreshIcon() - MainMenu.shared.update() + MainMenu.shared.rebuild() } ), CheckboxPreferenceView.make( From 3bca3117f98d2cd0db4fcd72800662a0485a4c0d Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 3 Jan 2022 16:49:50 +0100 Subject: [PATCH 028/105] =?UTF-8?q?=E2=9C=A8=20Load=20custom=20preferences?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++++ phpmon/Domain/Preferences/CustomPrefs.swift | 17 +++++++++++++++++ phpmon/Domain/Preferences/Preferences.swift | 10 ++++++++++ 3 files changed, 31 insertions(+) create mode 100644 phpmon/Domain/Preferences/CustomPrefs.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index c4a4f98..7cdf41a 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -121,6 +121,7 @@ C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.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 */; }; C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; @@ -262,6 +263,7 @@ 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 = ""; }; 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 = ""; }; @@ -319,6 +321,7 @@ C4998F092617633900B2526E /* PrefsWC.swift */, 5420395826135DC100FB00FA /* PrefsVC.swift */, 5420395E2613607600FB00FA /* Preferences.swift */, + C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */, C41CD0272628D8E20065BBED /* Keybinds */, 54FCFD28276C88C0004CE748 /* Views */, ); @@ -817,6 +820,7 @@ C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */, C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, + C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, diff --git a/phpmon/Domain/Preferences/CustomPrefs.swift b/phpmon/Domain/Preferences/CustomPrefs.swift new file mode 100644 index 0000000..e095ad4 --- /dev/null +++ b/phpmon/Domain/Preferences/CustomPrefs.swift @@ -0,0 +1,17 @@ +// +// CustomPrefs.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 03/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct CustomPrefs: Decodable { + let scanApps: [String] + + private enum CodingKeys: String, CodingKey { + case scanApps = "scan_apps" + } +} diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index efe9ab9..94f6c83 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -24,11 +24,21 @@ class Preferences { static var shared = Preferences() + var customPreferences: CustomPrefs + var cachedPreferences: [PreferenceName: Any?] public init() { Preferences.handleFirstTimeLaunch() cachedPreferences = Self.cache() + do { + customPreferences = try JSONDecoder().decode( + CustomPrefs.self, + from: try! String(contentsOf: URL(fileURLWithPath: "/Users/\(Paths.whoami)/.phpmon.conf.json"), encoding: .utf8).data(using: .utf8)! + ) + } catch { + customPreferences = CustomPrefs(scanApps: []) + } } // MARK: - First Time Run From 9ef184331e5cd446c4f9f1f0f6035f9bf2343ddf Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 3 Jan 2022 17:00:03 +0100 Subject: [PATCH 029/105] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issue=20with=20Val?= =?UTF-8?q?et=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++++- phpmon-common/Core/Paths.swift | 2 +- phpmon/Domain/Core/Startup.swift | 3 +-- phpmon/Localizable.strings | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8736b15..4064038 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,15 @@ If you're on an Apple Silicon-based Mac, you'll need to add: # on an M1 Mac export PATH=$HOME/bin:/opt/homebrew/bin:$PATH -and add the following to your .zshrc: +and add the following to your .zshrc, but add this BEFORE the homebrew PATH additions: export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH + +If you're adding composer and Homebrew binaries, ensure that Homebrew binaries are preferred by adding these to the path last. On my system, that looks like this: + + export PATH=$HOME/bin:/usr/local/bin:$PATH + export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH + export PATH=$HOME/bin:/opt/homebrew/bin:$PATH Make sure PHP is linked correctly: diff --git a/phpmon-common/Core/Paths.swift b/phpmon-common/Core/Paths.swift index 202e790..355da3e 100644 --- a/phpmon-common/Core/Paths.swift +++ b/phpmon-common/Core/Paths.swift @@ -24,7 +24,7 @@ public class Paths { // - MARK: Binaries public static var valet: String { - return "/Users/\(whoami)/.composer/vendor/bin/valet" + return "\(binPath)/valet" } public static var brew: String { diff --git a/phpmon/Domain/Core/Startup.swift b/phpmon/Domain/Core/Startup.swift index df35fcb..b1e9060 100644 --- a/phpmon/Domain/Core/Startup.swift +++ b/phpmon/Domain/Core/Startup.swift @@ -57,10 +57,9 @@ class Startup { ) performEnvironmentCheck( - // Check for Valet; it can be symlinked or in .composer/vendor/bin + // Check for Valet; it MUST be symlinked thanks to sudoers !(Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet") || Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/opt/homebrew/bin/valet") - || Shell.pipe("cat /private/etc/sudoers.d/valet").contains(".composer/vendor/bin/valet") ), messageText: "startup.errors.sudoers_valet.title".localized, informativeText: "startup.errors.sudoers_valet.desc".localized, diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 4371640..d3bf975 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -200,7 +200,7 @@ You can do this by running `composer global update` in your terminal. After that /// 5. Valet & sudoers "startup.errors.sudoers_valet.title" = "Valet has not been added to sudoers.d"; -"startup.errors.sudoers_valet.desc" = "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue."; +"startup.errors.sudoers_valet.desc" = "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue. If you did this before, please run `sudo valet trust` again."; /// 6. Multiple services active "startup.errors.services.title" = "Multiple PHP services are active"; From 7f320897be32124115c2d6b954773c9053ccff59 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 00:20:47 +0100 Subject: [PATCH 030/105] =?UTF-8?q?=F0=9F=91=8C=20Make=20user-supplied=20a?= =?UTF-8?q?pps=20available=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++++++ phpmon/Domain/Helpers/Application.swift | 2 +- phpmon/Domain/Menu/MainMenu+Startup.swift | 8 +++++++- phpmon/Domain/Preferences/Preferences.swift | 4 ++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4064038..2fa7932 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,16 @@ The supported apps are: PhpStorm, Visual Studio Code, Sublime Text, Sublime M All of these apps should just be detected correctly, no matter their location on your system. If you can open it using `open -a "appname"`, the app should be detected and work. If you have renamed the app, there might be an issue getting it detected. To see which files are checked to determine availability, see [this file](./phpmon/Domain/Helpers/Application.swift). + +You can add your own apps by creating and editing a `~/.phpmon.conf.json` file, with the following entry: + + +{ + "scan_apps": ["Xcode", "Kraken"] +} + + +You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary.
diff --git a/phpmon/Domain/Helpers/Application.swift b/phpmon/Domain/Helpers/Application.swift index 6617a01..6cb2bfe 100644 --- a/phpmon/Domain/Helpers/Application.swift +++ b/phpmon/Domain/Helpers/Application.swift @@ -14,7 +14,7 @@ import Foundation class Application { enum AppType { - case editor, browser, git_gui, terminal + case editor, browser, git_gui, terminal, user_supplied } /// Name of the app. Used for display purposes and to determine `name.app` exists. diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 8dd7494..573684a 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -51,9 +51,15 @@ extension MainMenu { Log.info("Setting up watchers...") App.shared.handlePhpConfigWatcher() + // Detect applications (preset + custom) Log.info("Detecting applications...") - // Attempt to load list of applications App.shared.detectedApplications = Application.detectPresetApplications() + let customApps = Preferences.custom.scanApps.map { appName in + return Application(appName, .user_supplied) + }.filter { app in + return app.isInstalled() + } + App.shared.detectedApplications.append(contentsOf: customApps) let appNames = App.shared.detectedApplications.map { app in return app.name } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 94f6c83..18e69fd 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -76,6 +76,10 @@ class Preferences { return Self.shared.cachedPreferences } + static var custom: CustomPrefs { + return Self.shared.customPreferences + } + /** Determine whether a particular preference is enabled. - Important: Requires the preference to have a corresponding boolean value, or a fatal error will be thrown. From e4c3e78a8a397a53af093e977d99e47fc82327b0 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 00:21:52 +0100 Subject: [PATCH 031/105] =?UTF-8?q?=F0=9F=93=9D=20Adjust=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fa7932..b24cfba 100644 --- a/README.md +++ b/README.md @@ -249,11 +249,11 @@ To see which files are checked to determine availability, see [this file](./phpm You can add your own apps by creating and editing a `~/.phpmon.conf.json` file, with the following entry: - +
 {
     "scan_apps": ["Xcode", "Kraken"]
 }
-
+
You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary.
From 0cdeeec0a495e3ca8e344fd8d3c4b84e6343f641 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 00:30:27 +0100 Subject: [PATCH 032/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Load=20the=20custo?= =?UTF-8?q?m=20preferences=20file=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Preferences/Preferences.swift | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 18e69fd..0858831 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -31,13 +31,26 @@ class Preferences { public init() { Preferences.handleFirstTimeLaunch() cachedPreferences = Self.cache() + customPreferences = CustomPrefs(scanApps: []) + + let url = URL(fileURLWithPath: "/Users/\(Paths.whoami)/.phpmon.conf.json") + if Filesystem.fileExists(url.path) { + Log.info("A custom .phpmon.conf.json file was found. Attempting to parse...") + loadCustomPreferencesFile(url) + } else { + Log.info("There was no .phpmon.conf.json file to be loaded.") + } + } + + private func loadCustomPreferencesFile(_ url: URL) { do { customPreferences = try JSONDecoder().decode( CustomPrefs.self, - from: try! String(contentsOf: URL(fileURLWithPath: "/Users/\(Paths.whoami)/.phpmon.conf.json"), encoding: .utf8).data(using: .utf8)! + from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)! ) + Log.info("The .phpmon.conf.json file was successfully parsed.") } catch { - customPreferences = CustomPrefs(scanApps: []) + Log.warn("The .phpmon.conf.json file seems to be missing or malformed.") } } From 3c3a0c8b45328de3d559b5b754f5d0b09eaac4c1 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 00:32:30 +0100 Subject: [PATCH 033/105] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Rework=20loading?= =?UTF-8?q?=20of=20custom=20preferences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Preferences/Preferences.swift | 45 ++++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 0858831..67abc71 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -32,26 +32,7 @@ class Preferences { Preferences.handleFirstTimeLaunch() cachedPreferences = Self.cache() customPreferences = CustomPrefs(scanApps: []) - - let url = URL(fileURLWithPath: "/Users/\(Paths.whoami)/.phpmon.conf.json") - if Filesystem.fileExists(url.path) { - Log.info("A custom .phpmon.conf.json file was found. Attempting to parse...") - loadCustomPreferencesFile(url) - } else { - Log.info("There was no .phpmon.conf.json file to be loaded.") - } - } - - private func loadCustomPreferencesFile(_ url: URL) { - do { - customPreferences = try JSONDecoder().decode( - CustomPrefs.self, - from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)! - ) - Log.info("The .phpmon.conf.json file was successfully parsed.") - } catch { - Log.warn("The .phpmon.conf.json file seems to be missing or malformed.") - } + loadCustomPreferences() } // MARK: - First Time Run @@ -133,4 +114,28 @@ class Preferences { Preferences.shared.cachedPreferences = Preferences.cache() } + // MARK: - Custom Preferences + + private func loadCustomPreferences() { + let url = URL(fileURLWithPath: "/Users/\(Paths.whoami)/.phpmon.conf.json") + if Filesystem.fileExists(url.path) { + Log.info("A custom .phpmon.conf.json file was found. Attempting to parse...") + loadCustomPreferencesFile(url) + } else { + Log.info("There was no .phpmon.conf.json file to be loaded.") + } + } + + private func loadCustomPreferencesFile(_ url: URL) { + do { + customPreferences = try JSONDecoder().decode( + CustomPrefs.self, + from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)! + ) + Log.info("The .phpmon.conf.json file was successfully parsed.") + } catch { + Log.warn("The .phpmon.conf.json file seems to be missing or malformed.") + } + } + } From 422aefe831fc6bf30159c252995508b8b6cb7c3d Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 02:33:53 +0100 Subject: [PATCH 034/105] =?UTF-8?q?=E2=9C=A8=20Read=20composer.json=20for?= =?UTF-8?q?=20version=20requirement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 +++++ phpmon/Domain/Core/Base.lproj/Main.storyboard | 30 ++++++++---- phpmon/Domain/Helpers/Alert.swift | 3 +- .../Integrations/Composer/ComposerJson.swift | 46 +++++++++++++++++++ phpmon/Domain/Integrations/Valet/Valet.swift | 22 +++++++++ phpmon/Domain/SiteList/SiteListCell.swift | 26 ++++++++++- phpmon/Domain/SiteList/SiteListVC.swift | 4 +- phpmon/Localizable.strings | 5 +- 8 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 phpmon/Domain/Integrations/Composer/ComposerJson.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 7cdf41a..4afdae5 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -129,6 +129,7 @@ C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.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 */; }; C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; }; C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; }; @@ -268,6 +269,7 @@ 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 = ""; }; 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 = ""; }; C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; @@ -521,6 +523,7 @@ C4AF9F6B275445D300D44ED0 /* Integrations */ = { isa = PBXGroup; children = ( + C4D89BC42783C98800A02B68 /* Composer */, C4AF9F6C275445D900D44ED0 /* Homebrew */, C4AF9F6A275445C900D44ED0 /* Valet */, ); @@ -576,6 +579,14 @@ name = Frameworks; sourceTree = ""; }; + C4D89BC42783C98800A02B68 /* Composer */ = { + isa = PBXGroup; + children = ( + C4D89BC52783C99400A02B68 /* ComposerJson.swift */, + ); + path = Composer; + sourceTree = ""; + }; C4D9ADBD27761084007277F4 /* PHP */ = { isa = PBXGroup; children = ( @@ -852,6 +863,7 @@ C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */, + C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */, diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index acc1270..446cd12 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -473,7 +473,7 @@ - + @@ -481,7 +481,7 @@ - + @@ -492,7 +492,7 @@ - + @@ -506,22 +506,33 @@ - + - + + - + @@ -534,18 +545,21 @@ - + + + + @@ -608,7 +622,7 @@ - + diff --git a/phpmon/Domain/Helpers/Alert.swift b/phpmon/Domain/Helpers/Alert.swift index d59c2f2..897e310 100644 --- a/phpmon/Domain/Helpers/Alert.swift +++ b/phpmon/Domain/Helpers/Alert.swift @@ -59,6 +59,5 @@ class Alert { secondButtonTitle: "", style: style ) - } - + } } diff --git a/phpmon/Domain/Integrations/Composer/ComposerJson.swift b/phpmon/Domain/Integrations/Composer/ComposerJson.swift new file mode 100644 index 0000000..860b484 --- /dev/null +++ b/phpmon/Domain/Integrations/Composer/ComposerJson.swift @@ -0,0 +1,46 @@ +// +// ComposerJson.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 04/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct ComposerJson: Decodable { + let dependencies: Dictionary? + let devDependencies: Dictionary? + let configuration: Config? + + public func getPhpVersion() -> (String, String) { + // Check if in platform + if configuration?.platform?.php != nil { + return (configuration!.platform!.php!, "platform") + } + + // Check if in dependencies + if dependencies?["php"] != nil { + return (dependencies!["php"]!, "dependency list") + } + + // Unknown! + return ("", "unknown") + } + + private enum CodingKeys: String, CodingKey { + case dependencies = "require" + case devDependencies = "require-dev" + case configuration = "config" + } + + struct Config: Decodable { + let platform: Platform? + } + + struct Platform: Decodable { + let php: String? + } +} + + diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index ce8ae55..fcb132e 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -160,6 +160,12 @@ class Valet { /// What driver is currently in use. If not detected, defaults to nil. var driver: String? = nil + /// The PHP version as discovered in composer.json + var composerPhp: String = "???" + + /// How the PHP version was determined + var composerPhpSource: String = "unknown" + init() {} convenience init(absolutePath: String, tld: String) { @@ -169,6 +175,7 @@ class Valet { self.aliasPath = nil determineSecured(tld) determineDriver() + determineComposerPhpVersion() } convenience init(aliasPath: String, tld: String) { @@ -178,6 +185,7 @@ class Valet { self.aliasPath = aliasPath determineSecured(tld) determineDriver() + determineComposerPhpVersion() } public func determineSecured(_ tld: String) { @@ -195,6 +203,20 @@ class Valet { self.driver = nil } } + + public func determineComposerPhpVersion() { + let path = "\(absolutePath!)/composer.json" + do { + if Filesystem.fileExists(path) { + (self.composerPhp, self.composerPhpSource) = try JSONDecoder().decode( + ComposerJson.self, + from: String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8).data(using: .utf8)! + ).getPhpVersion() + } + } catch { + Log.err("Something went wrong reading the composer JSON file.") + } + } } struct Configuration: Decodable { diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index 4af0450..ec640a5 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -11,6 +11,8 @@ import AppKit class SiteListCell: NSTableCellView { + var site: Valet.Site? = nil + @IBOutlet weak var labelSiteName: NSTextField! @IBOutlet weak var labelPathName: NSTextField! @@ -22,11 +24,15 @@ class SiteListCell: NSTableCellView @IBOutlet weak var buttonWarning: NSButton! @IBOutlet weak var labelWarning: NSTextField! + @IBOutlet weak var buttonPhpVersion: NSButton! + override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) } func populateCell(with site: Valet.Site) { + self.site = site + // Make sure to show the TLD labelSiteName.stringValue = "\(site.name!).\(Valet.shared.config.tld)" @@ -54,6 +60,24 @@ class SiteListCell: NSTableCellView : NSColor.init(red: 246/255, green: 71/255, blue: 71/255, alpha: 1.0) // red // Show the current driver - labelDriver.stringValue = site.driver ?? "???" + labelDriver.stringValue = "\(site.driver ?? "???")" + + // Show the PHP version + buttonPhpVersion.title = " PHP \(site.composerPhp) " + buttonPhpVersion.isHidden = (site.composerPhp == "???") + } + + @IBAction func pressedPhpVersion(_ sender: Any) { + guard let site = self.site else { return } + + Alert.confirm( + onWindow: App.shared.siteListWindowController!.window!, + messageText: "alert.composer_php_requirement.title" + .localized("\(site.name!).\(Valet.shared.config.tld)", site.composerPhp), + informativeText: "alert.composer_php_requirement.info" + .localized(site.composerPhpSource), + secondButtonTitle: "", + onFirstButtonPressed: {} + ) } } diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index 2caeaf5..d416fd7 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -179,8 +179,10 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { sites = Valet.shared.sites.filter({ site in return site.name.lowercased().contains(searchString) }) + DispatchQueue.main.async { + self.tableView.reloadData() + } - tableView.reloadData() } // MARK: - Deinitialization diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index d3bf975..7e6b5b3 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -147,10 +147,13 @@ You can find more information in the terminal output below. You’ll have to fix this problem manually, using your own Terminal app (this just shows you the output)."; - "alert.composer_success.title" = "Composer’s done updating!"; "alert.composer_success.info" = "Your global Composer dependencies have been successfully updated."; +// Composer Version +"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: \"php\": \"%@\"."; +"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the %@ in `composer.json`.\n\nPlease note that the lockfile is not checked and this reflects the current Composer configuration file as it existed at the time of refreshing the site list."; + // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP"; "alert.force_reload.info" = "This can take a while. You'll get another alert when the force reload has completed."; From 6dc74e94aa52dd47216f790ccf3c26728a6a21ae Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 02:51:33 +0100 Subject: [PATCH 035/105] =?UTF-8?q?=F0=9F=93=9D=20Document=20functionality?= =?UTF-8?q?=20in=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 +++++++++++++++++++ SECURITY.md | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b24cfba..79e0fdc 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,7 @@ While I did make this application during my own free time, I have been lucky eno * Everyone in the Laravel community who shared the app (thanks!) * Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) * Everyone who left feedback via issues +* Everyone who donated to keep the project up and running Thank you very much for your contributions, kind words and support. @@ -313,6 +314,24 @@ This utility will detect which PHP versions you have installed via Homebrew, and The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be much faster than Valet’s switcher. +### Config change detection + +PHP Monitor watches your filesystem in the relevant `conf.d` directory for the currently linked PHP version. + +Whenever an .ini file is modified, PHP Monitor will attempt to reload the current information about the active PHP installation. + +If an extension or other process writes to a single file a bunch of times in a short span of time (< 1 sec), PHP Monitor will only reload the active configuration information after a while (with a slight delay). + +### Site detection + +PHP Monitor uses the Valet configuration file to determine which folders to look into. Each folder is scanned and then PHP Monitor will validate if a composer.json file exists to determine the desired PHP version. + +If the app has been secured by checking if a matching certificate exists under Valet's `Certificates` directory. + +PHP Monitor also runs `valet which` to determine which driver is currently in use for each individual site. This command is executed once for each site whenever the site list is refreshed. + +*Note*: If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly. + ### Want to know more? If you want to know more about how this works, I recommend you check out the source code. diff --git a/SECURITY.md b/SECURITY.md index c0a3e75..9477f1e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,7 @@ Generally speaking, only the latest version of **PHP Monitor** is supported, exc | Version | Apple silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version | | ------- | ------------- | ------------------ | ----- | ----- | ----- | ---- -| 4.1 | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 | +| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | TBD | ## Legacy versions @@ -14,6 +14,7 @@ These versions of PHP Monitor are no longer supported, but if you’re using an | Version | Apple silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version | | ------- | ------------- | ------------------ | ----- | ----- | ----- | ---- +| 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 | | 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 | | 3.5 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 | | 3.0—3.4 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.1 | 2.13 | From 23f3204fa88b14b8bc936bcf09707b764fb6d434 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 02:52:33 +0100 Subject: [PATCH 036/105] =?UTF-8?q?=F0=9F=93=9D=20Phrasing=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79e0fdc..d0f6da4 100644 --- a/README.md +++ b/README.md @@ -326,7 +326,7 @@ If an extension or other process writes to a single file a bunch of times in a s PHP Monitor uses the Valet configuration file to determine which folders to look into. Each folder is scanned and then PHP Monitor will validate if a composer.json file exists to determine the desired PHP version. -If the app has been secured by checking if a matching certificate exists under Valet's `Certificates` directory. +If the directory has been secured by checking if a matching certificate exists under Valet's `Certificates` directory. PHP Monitor also runs `valet which` to determine which driver is currently in use for each individual site. This command is executed once for each site whenever the site list is refreshed. From 04bf5a3251e3cce2c95046eaf42c11784fd95eeb Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 02:56:00 +0100 Subject: [PATCH 037/105] =?UTF-8?q?=F0=9F=93=9D=20Updated=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d0f6da4..4cf7fd8 100644 --- a/README.md +++ b/README.md @@ -324,11 +324,9 @@ If an extension or other process writes to a single file a bunch of times in a s ### Site detection -PHP Monitor uses the Valet configuration file to determine which folders to look into. Each folder is scanned and then PHP Monitor will validate if a composer.json file exists to determine the desired PHP version. - -If the directory has been secured by checking if a matching certificate exists under Valet's `Certificates` directory. - -PHP Monitor also runs `valet which` to determine which driver is currently in use for each individual site. This command is executed once for each site whenever the site list is refreshed. +1. **Location of your sites**: PHP Monitor uses the Valet configuration file to determine which folders to look into. Each folder is scanned and then PHP Monitor will validate if a composer.json file exists to determine the desired PHP version. +1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name. +1. **Site drivers**: PHP Monitor runs `valet which` to determine which driver is currently in use for each individual site. This command is executed once for each site whenever the site list is refreshed. *Note*: If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly. From dc44538a7b7fabf1488724e3e1fe7c10b2fadab5 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 20:17:26 +0100 Subject: [PATCH 038/105] =?UTF-8?q?=F0=9F=91=8C=20Adjust=20preference=20de?= =?UTF-8?q?scription=20(see=20also=20#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Localizable.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 7e6b5b3..adf3cad 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -107,8 +107,8 @@ "prefs.dynamic_icon_title" = "Display dynamic icon in menu bar"; "prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible. If checked, it will display the major version number of the currently linked PHP version."; -"prefs.display_full_php_version" = "Display full PHP version in menu bar"; -"prefs.display_full_php_version_desc" = "Display the full version instead of the major version only. (This may be undesirable on smaller displays, so this is disabled by default.)"; +"prefs.display_full_php_version" = "Display full PHP version everywhere"; +"prefs.display_full_php_version_desc" = "Display the full version instead of the major version displayed in the menu bar and the dropdown menu. (This may be undesirable on smaller displays, so this is disabled by default.)"; "prefs.use_internal_switcher" = "Use PHP Monitor’s own version switcher"; "prefs.use_internal_switcher_desc" = From 9ab6231337cc145111244f918ba78e0daa28b028 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 4 Jan 2022 20:49:44 +0100 Subject: [PATCH 039/105] =?UTF-8?q?=F0=9F=93=9D=20PR=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/pull_request_template.md | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..85293b0 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,35 @@ +Hello there! Thank you for considering a pull request for PHP Monitor. + +Please read the text below first before you submit your PR. + +## Do not PR unless... + +In order to make development and maintenance of PHP Monitor easier, I ask that you _avoid_ making a pull request in the following situations: + +* No issue has been associated with the changes you‘d like to merge +* You have not announced you will be addressing a particular issue +* The PR is a low effort change: e.g. commits that only fix typos or phrasing may not be accepted + +(If you believe the phrasing of particular text in the app is unclear or incorrect, please open an issue first.) + +In short: It is usually best to *get in touch first* if you are making substantial changes. + +## About destination branches + +Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Merge requests that target `main` will be closed without mercy.** + +Usually, the best target is the stable `dev/x.x` branch that corresponds with the latest major version that is released. + +There may be a newer branch available, which is an appropriate place for bigger changes, but please keep in mind that it is usually best to announce you‘ll be working on such a change before you spend the time, since as the lead contributor I might not even want said change in the app. Thank you. + +## Your changes + +(feel free to remove the disclaimer above) + +* Affected parts of the app: shared code / UI code / CLI (remove what does not apply) +* Estimated impact on performance: none / low / high (remove what does not apply) +* Made a new build with Xcode and tested this: yes / no (remove what does not apply) +* Tested on macOS version + architecture: (e.g. "Monterey on M1" or "Big Sur on Intel") +* References issue(s): (please reference the issue here, using # and the number of the issue) + +(please describe what you have changed here) \ No newline at end of file From 3b4a1a0654a988491587c67ccecf4391ca85f441 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 11 Jan 2022 21:06:57 +0100 Subject: [PATCH 040/105] =?UTF-8?q?=E2=9C=A8=20Enable=20parsing=20of=20Hom?= =?UTF-8?q?ebrew=20services=20JSON=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 34 ++++- .../PHP/{ => Homebrew}/HomebrewPackage.swift | 0 .../PHP/Homebrew/HomebrewService.swift | 21 +++ phpmon-tests/BrewJsonParserTest.swift | 19 ++- phpmon-tests/PhpVersionDetectionTest.swift | 2 +- phpmon-tests/Test Files/brew-services.json | 135 ++++++++++++++++++ phpmon-tests/ValetTest.swift | 2 +- phpmon/Domain/Core/AppDelegate.swift | 2 +- 8 files changed, 206 insertions(+), 9 deletions(-) rename phpmon-common/PHP/{ => Homebrew}/HomebrewPackage.swift (100%) create mode 100644 phpmon-common/PHP/Homebrew/HomebrewService.swift create mode 100644 phpmon-tests/Test Files/brew-services.json diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 4afdae5..33e21a8 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -147,6 +147,13 @@ C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; + C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; }; + C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; }; + C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; }; + C4F30B07278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; }; + C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; }; + C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; }; + C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; }; C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; }; C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; }; C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */; }; @@ -280,6 +287,8 @@ C4EE55A827708B9E001DF387 /* PMStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMStatsView.swift; sourceTree = ""; }; C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewDiagnostics.swift; sourceTree = ""; }; C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = ""; }; + C4F30B02278E16BA00755FCE /* HomebrewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewService.swift; sourceTree = ""; }; + C4F30B06278E195800755FCE /* brew-services.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "brew-services.json"; sourceTree = ""; }; C4F7807425D7F7E5000DBC97 /* RELEASE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RELEASE.md; sourceTree = ""; }; C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -333,9 +342,9 @@ 54B20EDF263AA22C00D3250E /* PHP */ = { isa = PBXGroup; children = ( - C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, C4D9ADC2277610E4007277F4 /* Switcher */, - C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, + C4F30B01278E169B00755FCE /* Homebrew */, + C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */, C4F2E4392752F7D00020E974 /* PhpInstallation.swift */, C4ACA38E25C754C100060C66 /* PhpExtension.swift */, @@ -368,6 +377,7 @@ children = ( C4AF9F70275445FF00D44ED0 /* valet-config.json */, C43A8A1F25D9D1D700591B77 /* brew.json */, + C4F30B06278E195800755FCE /* brew-services.json */, C4F780A725D80AE8000DBC97 /* php.ini */, ); path = "Test Files"; @@ -614,6 +624,15 @@ path = SwiftUI; sourceTree = ""; }; + C4F30B01278E169B00755FCE /* Homebrew */ = { + isa = PBXGroup; + children = ( + C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, + C4F30B02278E16BA00755FCE /* HomebrewService.swift */, + ); + path = Homebrew; + sourceTree = ""; + }; C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = { isa = PBXGroup; children = ( @@ -759,6 +778,7 @@ C4232EE52612526500158FC6 /* Credits.html in Resources */, 54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */, C473319F2470923A009A0597 /* Localizable.strings in Resources */, + C4F30B07278E195800755FCE /* brew-services.json in Resources */, 54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */, C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */, C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */, @@ -775,6 +795,7 @@ C43A8A2025D9D1D700591B77 /* brew.json in Resources */, C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */, C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */, + C4F30B08278E195800755FCE /* brew-services.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -792,6 +813,7 @@ C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */, C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */, + C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */, C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */, @@ -836,6 +858,7 @@ C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, + C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */, 5420395F2613607600FB00FA /* Preferences.swift in Sources */, C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */, 54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, @@ -895,6 +918,7 @@ C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */, C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, + C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */, C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */, @@ -910,6 +934,7 @@ C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, + C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */, C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */, @@ -925,6 +950,7 @@ C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, C4B585452770FE3900DA4FBE /* Command.swift in Sources */, @@ -1120,7 +1146,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 220; + CURRENT_PROJECT_VERSION = 230; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -1145,7 +1171,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 220; + CURRENT_PROJECT_VERSION = 230; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; diff --git a/phpmon-common/PHP/HomebrewPackage.swift b/phpmon-common/PHP/Homebrew/HomebrewPackage.swift similarity index 100% rename from phpmon-common/PHP/HomebrewPackage.swift rename to phpmon-common/PHP/Homebrew/HomebrewPackage.swift diff --git a/phpmon-common/PHP/Homebrew/HomebrewService.swift b/phpmon-common/PHP/Homebrew/HomebrewService.swift new file mode 100644 index 0000000..1d92861 --- /dev/null +++ b/phpmon-common/PHP/Homebrew/HomebrewService.swift @@ -0,0 +1,21 @@ +// +// HomebrewService.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 11/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct HomebrewService: Decodable { + let name: String + let service_name: String + let running: Bool + let loaded: Bool + let pid: Int? + let user: String? + let status: String? + let log_path: String? + let error_log_path: String? +} diff --git a/phpmon-tests/BrewJsonParserTest.swift b/phpmon-tests/BrewJsonParserTest.swift index a91cbee..7db5889 100644 --- a/phpmon-tests/BrewJsonParserTest.swift +++ b/phpmon-tests/BrewJsonParserTest.swift @@ -15,9 +15,9 @@ class BrewJsonParserTest: XCTestCase { } func testCanLoadExtension() throws { - let json = try? String(contentsOf: Self.jsonBrewFile, encoding: .utf8) + let json = try! String(contentsOf: Self.jsonBrewFile, encoding: .utf8) let package = try! JSONDecoder().decode( - [HomebrewPackage].self, from: json!.data(using: .utf8)! + [HomebrewPackage].self, from: json.data(using: .utf8)! ).first! XCTAssertEqual(package.name, "php") @@ -27,5 +27,20 @@ class BrewJsonParserTest: XCTestCase { installed.version.starts(with: "8.0") }), true) } + + static var jsonBrewServicesFile: URL { + return Bundle(for: Self.self).url(forResource: "brew-services", withExtension: "json")! + } + + func testCanParseServicesJson() throws { + let json = try! String(contentsOf: Self.jsonBrewServicesFile, encoding: .utf8) + let services = try! JSONDecoder().decode( + [HomebrewService].self, from: json.data(using: .utf8)! + ) + + XCTAssertGreaterThan(services.count, 0) + XCTAssertEqual(services.first?.name, "dnsmasq") + XCTAssertEqual(services.first?.service_name, "homebrew.mxcl.dnsmasq") + } } diff --git a/phpmon-tests/PhpVersionDetectionTest.swift b/phpmon-tests/PhpVersionDetectionTest.swift index 660e128..8585680 100644 --- a/phpmon-tests/PhpVersionDetectionTest.swift +++ b/phpmon-tests/PhpVersionDetectionTest.swift @@ -11,7 +11,7 @@ import XCTest class PhpVersionDetectionTest: XCTestCase { func testCanDetectValidPhpVersions() throws { - let outcome = Actions.extractPhpVersions(from: [ + let outcome = PhpEnv.shared.extractPhpVersions(from: [ "", // empty lines should be omitted "php@8.0", "php@8.0", // should only be detected once diff --git a/phpmon-tests/Test Files/brew-services.json b/phpmon-tests/Test Files/brew-services.json new file mode 100644 index 0000000..52dc263 --- /dev/null +++ b/phpmon-tests/Test Files/brew-services.json @@ -0,0 +1,135 @@ +[ + { + "name": "dnsmasq", + "service_name": "homebrew.mxcl.dnsmasq", + "running": true, + "loaded": true, + "schedulable": false, + "pid": 106, + "exit_code": 0, + "user": "root", + "status": "started", + "file": "/Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist", + "command": "/opt/homebrew/opt/dnsmasq/sbin/dnsmasq --keep-in-foreground -C /opt/homebrew/etc/dnsmasq.conf -7 /opt/homebrew/etc/dnsmasq.d,*.conf", + "working_dir": null, + "root_dir": null, + "log_path": null, + "error_log_path": null, + "interval": null, + "cron": null + }, + { + "name": "httpd", + "service_name": "homebrew.mxcl.httpd", + "running": false, + "loaded": false, + "schedulable": false, + "pid": null, + "exit_code": null, + "user": null, + "status": "none", + "file": "/opt/homebrew/opt/httpd/homebrew.mxcl.httpd.plist", + "command": "/opt/homebrew/opt/httpd/bin/httpd -D FOREGROUND", + "working_dir": null, + "root_dir": null, + "log_path": null, + "error_log_path": null, + "interval": null, + "cron": null + }, + { + "name": "mailhog", + "service_name": "homebrew.mxcl.mailhog", + "running": false, + "loaded": false, + "schedulable": false, + "pid": null, + "exit_code": null, + "user": null, + "status": "none", + "file": "/opt/homebrew/opt/mailhog/homebrew.mxcl.mailhog.plist", + "command": "/opt/homebrew/opt/mailhog/bin/MailHog", + "working_dir": null, + "root_dir": null, + "log_path": "/opt/homebrew/var/log/mailhog.log", + "error_log_path": "/opt/homebrew/var/log/mailhog.log", + "interval": null, + "cron": null + }, + { + "name": "nginx", + "service_name": "homebrew.mxcl.nginx", + "running": true, + "loaded": true, + "schedulable": false, + "pid": 116, + "exit_code": 0, + "user": "root", + "status": "started", + "file": "/Library/LaunchDaemons/homebrew.mxcl.nginx.plist", + "command": "/opt/homebrew/opt/nginx/bin/nginx -g daemon off;", + "working_dir": "/opt/homebrew", + "root_dir": null, + "log_path": null, + "error_log_path": null, + "interval": null, + "cron": null + }, + { + "name": "php", + "service_name": "homebrew.mxcl.php", + "running": true, + "loaded": true, + "schedulable": false, + "pid": 142, + "exit_code": 0, + "user": "root", + "status": "started", + "file": "/Library/LaunchDaemons/homebrew.mxcl.php.plist", + "command": "/opt/homebrew/opt/php/sbin/php-fpm --nodaemonize", + "working_dir": "/opt/homebrew/var", + "root_dir": null, + "log_path": null, + "error_log_path": "/opt/homebrew/var/log/php-fpm.log", + "interval": null, + "cron": null + }, + { + "name": "php@8.0", + "service_name": "homebrew.mxcl.php@8.0", + "running": false, + "loaded": false, + "schedulable": false, + "pid": null, + "exit_code": null, + "user": null, + "status": "none", + "file": "/opt/homebrew/opt/php@8.0/homebrew.mxcl.php@8.0.plist", + "command": "/opt/homebrew/opt/php@8.0/sbin/php-fpm --nodaemonize", + "working_dir": "/opt/homebrew/var", + "root_dir": null, + "log_path": null, + "error_log_path": "/opt/homebrew/var/log/php-fpm.log", + "interval": null, + "cron": null + }, + { + "name": "unbound", + "service_name": "homebrew.mxcl.unbound", + "running": false, + "loaded": false, + "schedulable": false, + "pid": null, + "exit_code": null, + "user": null, + "status": "none", + "file": "/opt/homebrew/opt/unbound/homebrew.mxcl.unbound.plist", + "command": "/opt/homebrew/opt/unbound/sbin/unbound -d -c /opt/homebrew/etc/unbound/unbound.conf", + "working_dir": null, + "root_dir": null, + "log_path": null, + "error_log_path": null, + "interval": null, + "cron": null + } +] diff --git a/phpmon-tests/ValetTest.swift b/phpmon-tests/ValetTest.swift index 6d5b31d..b7713c4 100644 --- a/phpmon-tests/ValetTest.swift +++ b/phpmon-tests/ValetTest.swift @@ -11,7 +11,7 @@ import XCTest class ValetTest: XCTestCase { func testDetermineValetVersion() { - let version = Actions.valet("--version") + let version = valet("--version") XCTAssert(version.contains("Laravel Valet 2.")) } diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 3183636..a8e3c2e 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -88,7 +88,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele // Make sure notifications will work setupNotifications() // Make sure the menu performs its initial checks - menu.startup() + menu.rebuild() } } From b16250c2be141654bd65f41556526acd21aac0a3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 11 Jan 2022 21:18:05 +0100 Subject: [PATCH 041/105] =?UTF-8?q?=E2=9C=85=20Added=20tests=20for=20Homeb?= =?UTF-8?q?rew=20service=20parsing=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-tests/BrewJsonParserTest.swift | 42 +++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/phpmon-tests/BrewJsonParserTest.swift b/phpmon-tests/BrewJsonParserTest.swift index 7db5889..1e27816 100644 --- a/phpmon-tests/BrewJsonParserTest.swift +++ b/phpmon-tests/BrewJsonParserTest.swift @@ -9,12 +9,15 @@ import XCTest class BrewJsonParserTest: XCTestCase { + + // - MARK: SYNTHETIC TESTS static var jsonBrewFile: URL { - return Bundle(for: Self.self).url(forResource: "brew", withExtension: "json")! + return Bundle(for: Self.self) + .url(forResource: "brew", withExtension: "json")! } - func testCanLoadExtension() throws { + func testCanLoadExtensionJson() throws { let json = try! String(contentsOf: Self.jsonBrewFile, encoding: .utf8) let package = try! JSONDecoder().decode( [HomebrewPackage].self, from: json.data(using: .utf8)! @@ -29,7 +32,8 @@ class BrewJsonParserTest: XCTestCase { } static var jsonBrewServicesFile: URL { - return Bundle(for: Self.self).url(forResource: "brew-services", withExtension: "json")! + return Bundle(for: Self.self) + .url(forResource: "brew-services", withExtension: "json")! } func testCanParseServicesJson() throws { @@ -42,5 +46,37 @@ class BrewJsonParserTest: XCTestCase { XCTAssertEqual(services.first?.name, "dnsmasq") XCTAssertEqual(services.first?.service_name, "homebrew.mxcl.dnsmasq") } + + // - MARK: LIVE TESTS + /// This test requires that you have a valid Homebrew installation set up, + /// and requires the Valet services to be installed: php, nginx and dnsmasq. + /// Otherwise, this test won't fail. If this test fails, there is an issue with + /// your Homebrew installation or the JSON API of the Homebrew output may have changed. + func testCanParseServicesJsonFromCliOutput() throws { + let services = try! JSONDecoder().decode( + [HomebrewService].self, + from: Shell.pipe( + "sudo \(Paths.brew) services info --all --json", + requiresPath: true + ).data(using: .utf8)! + ) + + XCTAssertTrue(services.contains(where: {$0.name == "php"} )) + XCTAssertTrue(services.contains(where: {$0.name == "nginx"} )) + XCTAssertTrue(services.contains(where: {$0.name == "dnsmasq"} )) + } + + /// This test requires that you have a valid Homebrew installation set up, + /// and requires the Valet services to be installed: php, nginx and dnsmasq. + /// Otherwise, this test won't fail. If this test fails, there is an issue with + /// your Homebrew installation or the JSON API of the Homebrew output may have changed. + func testCanLoadExtensionJsonFromCliOutput() throws { + let package = try! JSONDecoder().decode( + [HomebrewPackage].self, + from: Shell.pipe("\(Paths.brew) info php --json", requiresPath: true).data(using: .utf8)! + ).first! + + XCTAssertTrue(package.name == "php") + } } From a5212b436e8bf15c43b9556a0079c81fd5a7f099 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 11 Jan 2022 21:19:23 +0100 Subject: [PATCH 042/105] =?UTF-8?q?=E2=9C=85=20Tweaked=20test=20copy=20(#7?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-tests/BrewJsonParserTest.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/phpmon-tests/BrewJsonParserTest.swift b/phpmon-tests/BrewJsonParserTest.swift index 1e27816..a129575 100644 --- a/phpmon-tests/BrewJsonParserTest.swift +++ b/phpmon-tests/BrewJsonParserTest.swift @@ -51,8 +51,8 @@ class BrewJsonParserTest: XCTestCase { /// This test requires that you have a valid Homebrew installation set up, /// and requires the Valet services to be installed: php, nginx and dnsmasq. - /// Otherwise, this test won't fail. If this test fails, there is an issue with - /// your Homebrew installation or the JSON API of the Homebrew output may have changed. + /// If this test fails, there is an issue with your Homebrew installation + /// or the JSON API of the Homebrew output may have changed. func testCanParseServicesJsonFromCliOutput() throws { let services = try! JSONDecoder().decode( [HomebrewService].self, @@ -68,9 +68,9 @@ class BrewJsonParserTest: XCTestCase { } /// This test requires that you have a valid Homebrew installation set up, - /// and requires the Valet services to be installed: php, nginx and dnsmasq. - /// Otherwise, this test won't fail. If this test fails, there is an issue with - /// your Homebrew installation or the JSON API of the Homebrew output may have changed. + /// and requires the `php` formula to be installed. + /// If this test fails, there is an issue with your Homebrew installation + /// or the JSON API of the Homebrew output may have changed. func testCanLoadExtensionJsonFromCliOutput() throws { let package = try! JSONDecoder().decode( [HomebrewPackage].self, From d2cd567fd260b194699f95979a363047924e6df0 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 11 Jan 2022 21:21:46 +0100 Subject: [PATCH 043/105] =?UTF-8?q?=E2=9C=85=20Filter=20only=20needed=20se?= =?UTF-8?q?rvices=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-tests/BrewJsonParserTest.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/phpmon-tests/BrewJsonParserTest.swift b/phpmon-tests/BrewJsonParserTest.swift index a129575..a12cb3f 100644 --- a/phpmon-tests/BrewJsonParserTest.swift +++ b/phpmon-tests/BrewJsonParserTest.swift @@ -60,11 +60,14 @@ class BrewJsonParserTest: XCTestCase { "sudo \(Paths.brew) services info --all --json", requiresPath: true ).data(using: .utf8)! - ) + ).filter({ service in + return ["php", "nginx", "dnsmasq"].contains(service.name) + }) XCTAssertTrue(services.contains(where: {$0.name == "php"} )) XCTAssertTrue(services.contains(where: {$0.name == "nginx"} )) XCTAssertTrue(services.contains(where: {$0.name == "dnsmasq"} )) + XCTAssertEqual(services.count, 3) } /// This test requires that you have a valid Homebrew installation set up, From d8579bd7d1dfb79e57515fe43c21b521a2c51268 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 11 Jan 2022 21:30:18 +0100 Subject: [PATCH 044/105] =?UTF-8?q?=F0=9F=90=9B=20Fix=20incorrect=20change?= =?UTF-8?q?=20in=20AppDelegate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 2 ++ phpmon/Domain/Core/AppDelegate.swift | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 33e21a8..058f62b 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -154,6 +154,7 @@ C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; }; C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; }; C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; }; + C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; }; C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; }; C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; }; C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */; }; @@ -950,6 +951,7 @@ C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */, C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index a8e3c2e..3183636 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -88,7 +88,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele // Make sure notifications will work setupNotifications() // Make sure the menu performs its initial checks - menu.rebuild() + menu.startup() } } From c3f8a53ac3d2d7c2741410004906d5d2d78dfa92 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 16 Jan 2022 13:14:54 +0100 Subject: [PATCH 045/105] =?UTF-8?q?=E2=9C=A8=20Updated=20preferences=20(ad?= =?UTF-8?q?ded=20option=20to=20disable=20PHP=20hint=20next=20to=20icon)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Only works with dynamic icon enabled - Preference defaults to true on new or existing installs (because we want to display PHP next to the version number by default) For those who love a minimal menu bar setup but still want to see what PHP version is currently enabled, this is perfect. --- phpmon/Domain/Menu/MainMenu.swift | 4 +++- phpmon/Domain/Preferences/Preferences.swift | 3 +++ phpmon/Domain/Preferences/PrefsVC.swift | 9 +++++++++ phpmon/Localizable.strings | 6 +++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index ae12f5f..69f4c51 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -68,7 +68,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { */ func setStatusBarImage(version: String) { setStatusBar( - image: MenuBarImageGenerator.textToImageWithIcon(text: version) + image: Preferences.isEnabled(.shouldDisplayPhpHintInIcon) + ? MenuBarImageGenerator.textToImageWithIcon(text: version) + : MenuBarImageGenerator.textToImage(text: version) ) } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 67abc71..ec3df1e 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -11,6 +11,7 @@ import Foundation enum PreferenceName: String { case wasLaunchedBefore = "launched_before" case shouldDisplayDynamicIcon = "use_dynamic_icon" + case shouldDisplayPhpHintInIcon = "add_php_to_icon" case fullPhpVersionDynamicIcon = "full_php_in_menu_bar" case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle" case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch" @@ -50,6 +51,7 @@ class Preferences { static func handleFirstTimeLaunch() { UserDefaults.standard.register(defaults: [ PreferenceName.shouldDisplayDynamicIcon.rawValue: true, + PreferenceName.shouldDisplayPhpHintInIcon.rawValue: true, PreferenceName.fullPhpVersionDynamicIcon.rawValue: false, PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true, PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false, @@ -92,6 +94,7 @@ class Preferences { return [ // Part 1: Always Booleans .shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any, + .shouldDisplayPhpHintInIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayPhpHintInIcon.rawValue) as Any, .fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any, .autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any, .autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any, diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index b2555ef..a4208e4 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -59,6 +59,15 @@ class PrefsVC: NSViewController { ), CheckboxPreferenceView.make( sectionText: "", + descriptionText: "prefs.icon_hint_desc".localized, + checkboxText: "prefs.icon_hint_title".localized, + preference: .shouldDisplayPhpHintInIcon, + action: { + MainMenu.shared.refreshIcon() + } + ), + CheckboxPreferenceView.make( + sectionText: "prefs.info_density".localized, descriptionText: "prefs.display_full_php_version_desc".localized, checkboxText: "prefs.display_full_php_version".localized, preference: .fullPhpVersionDynamicIcon, diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index adf3cad..7d31ab9 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -97,7 +97,8 @@ "prefs.close" = "Close"; "prefs.global_shortcut" = "Global shortcut:"; -"prefs.dynamic_icon" = "Dynamic icon:"; +"prefs.dynamic_icon" = "Icon customization:"; +"prefs.info_density" = "Info density:"; "prefs.services" = "Services:"; "prefs.switcher" = "Switcher:"; @@ -107,6 +108,9 @@ "prefs.dynamic_icon_title" = "Display dynamic icon in menu bar"; "prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible. If checked, it will display the major version number of the currently linked PHP version."; +"prefs.icon_hint_title" = "Display PHP hint next to version number"; +"prefs.icon_hint_desc" = "If you uncheck this box, the icon in the menu bar will only show the version number and not the PHP hint. This preference does not do anything if the dynamic icon has been disabled."; + "prefs.display_full_php_version" = "Display full PHP version everywhere"; "prefs.display_full_php_version_desc" = "Display the full version instead of the major version displayed in the menu bar and the dropdown menu. (This may be undesirable on smaller displays, so this is disabled by default.)"; From e0dd778bb3d16717c72abb5e3aef17feb2ac606b Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 22 Jan 2022 20:50:17 +0100 Subject: [PATCH 046/105] =?UTF-8?q?=F0=9F=91=8C=20Add=20debounce=20to=20si?= =?UTF-8?q?te=20search=20(#82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++-- phpmon/Domain/SiteList/SiteListVC.swift | 6 +++++- phpmon/Domain/SiteList/SiteListWC.swift | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 058f62b..96961a9 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1157,7 +1157,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "5.0-wip"; + MARKETING_VERSION = "5.0-dev"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1182,7 +1182,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "5.0-wip"; + MARKETING_VERSION = "5.0-dev"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index d416fd7..d2dc655 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -172,13 +172,17 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { if searchString.isEmpty { sites = Valet.shared.sites - tableView.reloadData() + + DispatchQueue.main.async { + self.tableView.reloadData() + } return } sites = Valet.shared.sites.filter({ site in return site.name.lowercased().contains(searchString) }) + DispatchQueue.main.async { self.tableView.reloadData() } diff --git a/phpmon/Domain/SiteList/SiteListWC.swift b/phpmon/Domain/SiteList/SiteListWC.swift index d652dad..9f032c9 100644 --- a/phpmon/Domain/SiteList/SiteListWC.swift +++ b/phpmon/Domain/SiteList/SiteListWC.swift @@ -34,12 +34,18 @@ class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate { return self.contentViewController as! SiteListVC } + var searchTimer: Timer? + func controlTextDidChange(_ notification: Notification) { guard let searchField = notification.object as? NSSearchField else { return } - contentVC.searchedFor(text: searchField.stringValue) + self.searchTimer?.invalidate() + + searchTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { _ in + self.contentVC.searchedFor(text: searchField.stringValue) + }) } // MARK: - Reload functionality From 03a409281a25f2299485481a357eba36401b6df7 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 22 Jan 2022 20:54:59 +0100 Subject: [PATCH 047/105] =?UTF-8?q?=F0=9F=91=8C=20Sort=20site=20list=20by?= =?UTF-8?q?=20absolute=20path=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Integrations/Valet/Valet.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index fcb132e..6223a55 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -96,6 +96,8 @@ class Valet { resolvePath(entry, forPath: path, tld: tld) } } + + sites = sites.sorted { $0.absolutePath < $1.absolutePath } } /** From 46005a3c689e2d01493eccbece16722050c40729 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 22 Jan 2022 21:35:32 +0100 Subject: [PATCH 048/105] =?UTF-8?q?=F0=9F=91=8C=20Load=20notable=20depende?= =?UTF-8?q?ncies=20(incl.=20laravel,=20#80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Core/Base.lproj/Main.storyboard | 43 ++++--------------- .../Integrations/Composer/ComposerJson.swift | 13 ++++++ phpmon/Domain/Integrations/Valet/Valet.swift | 14 ++++-- phpmon/Domain/SiteList/SiteListCell.swift | 13 +++--- phpmon/Domain/SiteList/SiteListVC.swift | 1 - 5 files changed, 36 insertions(+), 48 deletions(-) diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index 446cd12..d30390e 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -473,7 +473,7 @@ - + @@ -481,7 +481,7 @@ - + @@ -492,7 +492,7 @@ - + @@ -500,46 +500,25 @@ - - - - - - - - - - + - - @@ -547,26 +526,21 @@ - - - - + - - @@ -616,13 +590,12 @@
- + - diff --git a/phpmon/Domain/Integrations/Composer/ComposerJson.swift b/phpmon/Domain/Integrations/Composer/ComposerJson.swift index 860b484..0752733 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerJson.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerJson.swift @@ -28,6 +28,19 @@ struct ComposerJson: Decodable { return ("", "unknown") } + public func getNotableDependencies() -> [String: String] { + var notable: [String: String] = [:] + let scan = ["php", "laravel/framework"] + + scan.forEach { dependency in + if dependencies?[dependency] != nil { + notable[dependency] = dependencies![dependency] + } + } + + return notable + } + private enum CodingKeys: String, CodingKey { case dependencies = "require" case devDependencies = "require-dev" diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 6223a55..8cc1ad4 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -162,10 +162,13 @@ class Valet { /// What driver is currently in use. If not detected, defaults to nil. var driver: String? = nil - /// The PHP version as discovered in composer.json + /// A list of notable Composer dependencies. + var notableComposerDependencies: [String: String] = [:] + + /// The PHP version as discovered in composer.json. var composerPhp: String = "???" - /// How the PHP version was determined + /// How the PHP version was determined. var composerPhpSource: String = "unknown" init() {} @@ -210,10 +213,13 @@ class Valet { let path = "\(absolutePath!)/composer.json" do { if Filesystem.fileExists(path) { - (self.composerPhp, self.composerPhpSource) = try JSONDecoder().decode( + let decoded = try JSONDecoder().decode( ComposerJson.self, from: String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8).data(using: .utf8)! - ).getPhpVersion() + ) + + (self.composerPhp, self.composerPhpSource) = decoded.getPhpVersion() + self.notableComposerDependencies = decoded.getNotableDependencies() } } catch { Log.err("Something went wrong reading the composer JSON file.") diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index ec640a5..d4eea96 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -21,9 +21,6 @@ class SiteListCell: NSTableCellView @IBOutlet weak var labelDriver: NSTextField! - @IBOutlet weak var buttonWarning: NSButton! - @IBOutlet weak var labelWarning: NSTextField! - @IBOutlet weak var buttonPhpVersion: NSButton! override func draw(_ dirtyRect: NSRect) { @@ -36,11 +33,6 @@ class SiteListCell: NSTableCellView // Make sure to show the TLD labelSiteName.stringValue = "\(site.name!).\(Valet.shared.config.tld)" - let isProblematic = site.name.contains(" ") - buttonWarning.isHidden = !isProblematic - labelWarning.isHidden = !isProblematic - labelWarning.stringValue = "site_list.warning.spaces".localized - // Show the absolute path, except make sure to replace the /Users/username segment with ~ for readability labelPathName.stringValue = site.absolutePath .replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~") @@ -62,6 +54,11 @@ class SiteListCell: NSTableCellView // Show the current driver labelDriver.stringValue = "\(site.driver ?? "???")" + if site.driver == "Laravel" && site.notableComposerDependencies.keys.contains("laravel/framework") { + let constraint = site.notableComposerDependencies["laravel/framework"]! + labelDriver.stringValue = "Laravel (\(constraint))" + } + // Show the PHP version buttonPhpVersion.title = " PHP \(site.composerPhp) " buttonPhpVersion.isHidden = (site.composerPhp == "???") diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index d2dc655..039180b 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -186,7 +186,6 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { DispatchQueue.main.async { self.tableView.reloadData() } - } // MARK: - Deinitialization From 32e8878a68307930119f1101c6655da1e0fe26d1 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 22 Jan 2022 22:08:45 +0100 Subject: [PATCH 049/105] =?UTF-8?q?=F0=9F=8F=97=20WIP:=20UI=20for=20switch?= =?UTF-8?q?=20to=20version=20options=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/SiteList/SiteListCell.swift | 39 +++++++++++++++++------ 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index d4eea96..9f026c1 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -67,14 +67,35 @@ class SiteListCell: NSTableCellView @IBAction func pressedPhpVersion(_ sender: Any) { guard let site = self.site else { return } - Alert.confirm( - onWindow: App.shared.siteListWindowController!.window!, - messageText: "alert.composer_php_requirement.title" - .localized("\(site.name!).\(Valet.shared.config.tld)", site.composerPhp), - informativeText: "alert.composer_php_requirement.info" - .localized(site.composerPhpSource), - secondButtonTitle: "", - onFirstButtonPressed: {} - ) + let alert = NSAlert.init() + alert.alertStyle = .informational + + alert.messageText = "alert.composer_php_requirement.title" + .localized("\(site.name!).\(Valet.shared.config.tld)", site.composerPhp) + alert.informativeText = "alert.composer_php_requirement.info" + .localized(site.composerPhpSource) + + alert.addButton(withTitle: "Close") + + var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue + var map: [Int: String] = [:] + + var versions: [String] = [] + // TODO: Based on the version constraint, insert the desired version to switch to + + versions.forEach { version in + alert.addButton(withTitle: "Switch to PHP \(version)") + map[mapIndex] = version + mapIndex += 1 + } + + alert.beginSheetModal(for: App.shared.siteListWindowController!.window!) { response in + if response.rawValue > NSApplication.ModalResponse.alertFirstButtonReturn.rawValue { + if map.keys.contains(response.rawValue) { + let version = map[response.rawValue]! + print("Pressed button to switch to \(version)") + } + } + } } } From 567373f8da05f4bd421f4266d967ef6798216883 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 23 Jan 2022 03:59:29 +0100 Subject: [PATCH 050/105] =?UTF-8?q?=E2=9C=A8=20Version=20constraint=20chec?= =?UTF-8?q?ks=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The version constraint checks will also be used in the future to evaluate whether any given site's PHP constraint (if set) is valid for the currently linked version of PHP. For example, assuming you have PHP 8.1.2 linked, we could evaluate: * A site requires "8.0" -> invalid * A site requires "^8.0" -> valid * A site requires "^8.0.0" -> valid * A site requires "~8.0" -> valid * A site requires "~8.0.0" -> invalid Currently, this constraint check is used to determine which versions that are currently installed are good suggestions to switch to. If you have a site with constraint "^8.0" for example, and you have PHP 8.0 and 8.1 installed (with 8.1 linked), then you will get a suggestion to switch back to 8.0. --- PHP Monitor.xcodeproj/project.pbxproj | 22 +- .../PHP/{ => PHP Version}/PhpEnv.swift | 6 + .../PHP/PHP Version/PhpVersionNumber.swift | 141 +++++++++++++ phpmon-common/PHP/PhpInstallation.swift | 14 +- phpmon-tests/PhpVersionDetectionTest.swift | 1 - phpmon-tests/PhpVersionNumberTest.swift | 194 ++++++++++++++++++ phpmon/Domain/Menu/MainMenu.swift | 10 +- phpmon/Domain/Menu/StatusMenu.swift | 2 +- phpmon/Domain/SiteList/SiteListCell.swift | 14 +- phpmon/Localizable.strings | 2 +- 10 files changed, 389 insertions(+), 17 deletions(-) rename phpmon-common/PHP/{ => PHP Version}/PhpEnv.swift (95%) create mode 100644 phpmon-common/PHP/PHP Version/PhpVersionNumber.swift create mode 100644 phpmon-tests/PhpVersionNumberTest.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 96961a9..7dbc8cb 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -92,6 +92,10 @@ C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; }; C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C9925CC888B00CC7490 /* HeaderView.xib */; }; C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; }; + C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; + C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; + C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; + C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; }; C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; }; C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; @@ -257,6 +261,8 @@ C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; C48D0C9925CC888B00CC7490 /* HeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = ""; }; C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; }; + C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = ""; }; + C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = ""; }; C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = ""; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = ""; }; @@ -343,9 +349,9 @@ 54B20EDF263AA22C00D3250E /* PHP */ = { isa = PBXGroup; children = ( + C48D6C6E279CD29C00F26D7E /* PHP Version */, C4D9ADC2277610E4007277F4 /* Switcher */, C4F30B01278E169B00755FCE /* Homebrew */, - C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */, C4F2E4392752F7D00020E974 /* PhpInstallation.swift */, C4ACA38E25C754C100060C66 /* PhpExtension.swift */, @@ -523,6 +529,15 @@ path = Helpers; sourceTree = ""; }; + C48D6C6E279CD29C00F26D7E /* PHP Version */ = { + isa = PBXGroup; + children = ( + C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, + C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */, + ); + path = "PHP Version"; + sourceTree = ""; + }; C4AF9F6A275445C900D44ED0 /* Valet */ = { isa = PBXGroup; children = ( @@ -643,6 +658,7 @@ C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */, C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */, C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */, + C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */, C43A8A1925D9CD1000591B77 /* Utility.swift */, C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */, C4AF9F7C275454A900D44ED0 /* ValetTest.swift */, @@ -818,6 +834,7 @@ C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */, C417DC76277614690015E6EE /* Helpers.swift in Sources */, C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */, @@ -833,6 +850,7 @@ files = ( C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, + C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C4B585412770FE3900DA4FBE /* Shell.swift in Sources */, C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, @@ -928,6 +946,7 @@ C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */, C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */, C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, + C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */, C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C42759682627662800093CAE /* NSMenuExtension.swift in Sources */, C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, @@ -958,6 +977,7 @@ C4B585452770FE3900DA4FBE /* Command.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, C4F780B725D80B5D000DBC97 /* App.swift in Sources */, + C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */, C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */, C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, diff --git a/phpmon-common/PHP/PhpEnv.swift b/phpmon-common/PHP/PHP Version/PhpEnv.swift similarity index 95% rename from phpmon-common/PHP/PhpEnv.swift rename to phpmon-common/PHP/PHP Version/PhpEnv.swift index c5dcb55..8c5d1d7 100644 --- a/phpmon-common/PHP/PhpEnv.swift +++ b/phpmon-common/PHP/PHP Version/PhpEnv.swift @@ -149,4 +149,10 @@ class PhpEnv { return output } + + public func validVersions(for constraint: String) -> [PhpVersionNumber] { + return PhpVersionNumberCollection + .make(from: self.availablePhpVersions) + .matching(constraint: constraint) + } } diff --git a/phpmon-common/PHP/PHP Version/PhpVersionNumber.swift b/phpmon-common/PHP/PHP Version/PhpVersionNumber.swift new file mode 100644 index 0000000..356f0bb --- /dev/null +++ b/phpmon-common/PHP/PHP Version/PhpVersionNumber.swift @@ -0,0 +1,141 @@ +// +// PhpVersionNumber.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +public struct PhpVersionNumberCollection: Equatable { + let versions: [PhpVersionNumber] + + public static func make(from versions: [String]) -> Self { + return PhpVersionNumberCollection( + versions: versions.map { PhpVersionNumber.make(from: $0)! } + ) + } + + public var first: PhpVersionNumber? { + return self.versions.first + } + + public var all: [PhpVersionNumber] { + return self.versions + } + + /** + Checks if any versions of PHP are valid for the constraint provided. + Due to the complexity of evaluating these, a important test is maintained. + More information on these constraints can be found here: + https://getcomposer.org/doc/articles/versions.md#writing-version-constraints + + - Parameter constraint: The full constraint as a string (e.g. "^7.0") + - Parameter strict: Whether the minor version check is strict. See more below. + + The strict mode does not matter if a patch version is provided for all versions in the collection. + + Strict mode assumes that any PHP version lacking precise patch information, e.g. inferred + from Homebrew corresponds to the .0 patch version of that version. The default, which is imprecise, + assumes that the patch version is .999, which means that in all cases the patch version check is + always going to pass. + + **STRICT MODE (= patch precision on)** + + Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in strict mode only 8.1.? will + be considered valid (8.0 translates to 8.0.0 and as such is older than 8.0.1, 8.1.0 is OK). + When checking against actual PHP versions installed by the user, use strict mode. + + **NON-STRICT MODE (= patch precision off)** + + Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in non-strict mode version 8.0 + is assumed to be equal to version 8.0.999, which is actually fine if 8.0.1 is the required version. + In non-strict mode, the patch version is ignored for regular version checks (no caret / tilde). + If checking compatibility with general Homebrew versions of PHP, do NOT use strict mode, since + the patch version there is not used. (The formula php@8.0 suffices for ^8.0.1.) + */ + public func matching(constraint: String, strict: Bool = false) -> [PhpVersionNumber] { + if let version = PhpVersionNumber.make(from: constraint, type: .versionOnly) { + // Strict constraint (e.g. "7.0") -> returns specific version + return self.versions.filter { + $0.major == version.major + && $0.minor == version.minor + && (strict ? $0.patch(strict, version) == version.patch(strict) : true) + } + } + + if let version = PhpVersionNumber.make(from: constraint, type: .caretVersionRange) { + // Caret range means that the major version is never higher but minor version can be higher + // ^7.2 will be compatible with all versions between 7.2 and 8.0 + return self.versions.filter { + $0.major == version.major && + ( + // Either the minor version is the same and the patch is higher or equal + $0.minor == version.minor && $0.patch(strict) >= version.patch(strict, $0) + // or the minor version number has been bumped + || $0.minor > version.minor + ) + } + } + + if let version = PhpVersionNumber.make(from: constraint, type: .tildeVersionRange) { + // Tilde range means that most specific digit is used as the basis. + if version.patch != nil { + // If a patch is provided then the minor version cannot be bumped. + return self.versions.filter { + $0.major == version.major && $0.minor == version.minor + && $0.patch(strict, version) >= version.patch! + } + } else { + // If a patch is not provided then the major version cannot be bumped. + return self.versions.filter { + $0.major == version.major && $0.minor >= version.minor + } + } + } + + return [] + } +} + +public struct PhpVersionNumber: Equatable { + let major: Int + let minor: Int + let patch: Int? + + public func patch(_ strictFallback: Bool, _ constraint: PhpVersionNumber? = nil) -> Int { + return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999) + } + + public var homebrewVersion: String { + return "\(major).\(minor)" + } + + public enum MatchType: String { + case versionOnly = #"^(?\d+).(?\d+).?(?\d+)?\z"# + case caretVersionRange = #"^\^(?\d+).(?\d+).?(?\d+)?\z"# + case tildeVersionRange = #"^~(?\d+).(?\d+).?(?\d+)?\z"# + } + + public static func make(from versionString: String, type: MatchType = .versionOnly) -> Self? { + let regex = try! NSRegularExpression(pattern: type.rawValue, options: []) + let match = regex.matches(in: versionString, options: [], range: NSMakeRange(0, versionString.count)).first + + if match != nil { + let major = Int( + versionString[Range(match!.range(withName: "major"), in: versionString)!] + )! + let minor = Int( + versionString[Range(match!.range(withName: "minor"), in: versionString)!] + )! + var patch: Int? = nil + if let minorRange = Range(match!.range(withName: "patch"), in: versionString) { + patch = Int(versionString[minorRange]) + } + return Self(major: major, minor: minor, patch: patch) + } + + return nil + } +} diff --git a/phpmon-common/PHP/PhpInstallation.swift b/phpmon-common/PHP/PhpInstallation.swift index 02577e2..4ecc010 100644 --- a/phpmon-common/PHP/PhpInstallation.swift +++ b/phpmon-common/PHP/PhpInstallation.swift @@ -10,20 +10,26 @@ import Foundation class PhpInstallation { - var longVersion: String + var longVersion: PhpVersionNumber /** In order to determine details about a PHP installation, we’ll simply run `php-config --version` in the relevant directory. */ init(_ version: String) { + let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config" - self.longVersion = version + self.longVersion = PhpVersionNumber.make(from: version)! + if Shell.fileExists(phpConfigExecutablePath) { - self.longVersion = Command.execute( + let longVersionString = Command.execute( path: phpConfigExecutablePath, arguments: ["--version"] - ) + ).trimmingCharacters(in: .whitespacesAndNewlines) + + self.longVersion = PhpVersionNumber.make( + from: String(longVersionString.split(separator: "-")[0]) + )! } } diff --git a/phpmon-tests/PhpVersionDetectionTest.swift b/phpmon-tests/PhpVersionDetectionTest.swift index 8585680..fedd72d 100644 --- a/phpmon-tests/PhpVersionDetectionTest.swift +++ b/phpmon-tests/PhpVersionDetectionTest.swift @@ -26,5 +26,4 @@ class PhpVersionDetectionTest: XCTestCase { XCTAssertEqual(outcome, ["8.0", "7.0", "5.6"]) } - } diff --git a/phpmon-tests/PhpVersionNumberTest.swift b/phpmon-tests/PhpVersionNumberTest.swift new file mode 100644 index 0000000..dc9f1cb --- /dev/null +++ b/phpmon-tests/PhpVersionNumberTest.swift @@ -0,0 +1,194 @@ +// +// PhpVersionNumberTest.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import XCTest + +class PhpVersionNumberTest: XCTestCase { + + func testCanDeconstructPhpVersion() throws { + XCTAssertEqual( + PhpVersionNumber.make(from: "8.0.11"), + PhpVersionNumber(major: 8, minor: 0, patch: 11) + ) + XCTAssertEqual( + PhpVersionNumber.make(from: "7.4.2"), + PhpVersionNumber(major: 7, minor: 4, patch: 2) + ) + XCTAssertEqual( + PhpVersionNumber.make(from: "7.4"), + PhpVersionNumber(major: 7, minor: 4, patch: nil) + ) + XCTAssertEqual( + PhpVersionNumber.make(from: "7"), + nil + ) + } + + func testCanCheckFixedConstraints() throws { + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "7.0"), + PhpVersionNumberCollection + .make(from: ["7.0"]).all + ) + + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.3", "7.3.3", "7.2.3", "7.1.3", "7.0.3"]) + .matching(constraint: "7.0.3"), + PhpVersionNumberCollection + .make(from: ["7.0.3"]).all + ) + + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "7.0.3", strict: false), + PhpVersionNumberCollection + .make(from: ["7.0"]).all + ) + + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "7.0.3", strict: true), + PhpVersionNumberCollection + .make(from: []).all + ) + } + + func testCanCheckCaretConstraints() throws { + // 1. Imprecise checks + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "^7.0", strict: true), + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all + ) + + // 2. Imprecise check with precise constraint (lenient AKA not strict) + // These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc. + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "^7.0.1", strict: false), + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all + ) + + // 3. Imprecise check with precise constraint (strict mode) + // These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc. + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "^7.0.1", strict: true), + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1"]).all + ) + + // 4. Precise members and constraint all around + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]) + .matching(constraint: "^7.0.1", strict: true), + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all + ) + + // 5. Precise members but imprecise constraint (strict mode) + // In strict mode the constraint's patch version is assumed to be 0 + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]) + .matching(constraint: "^7.0", strict: true), + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all + ) + + // 6. Precise members but imprecise constraint (lenient mode) + // In lenient mode the constraint's patch version is assumed to be equal + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]) + .matching(constraint: "^7.0", strict: false), + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all + ) + } + + func testCanCheckTildeConstraints() throws { + // 1. Imprecise checks + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "~7.0", strict: true), + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all + ) + + // 2. Imprecise check with precise constraint (lenient AKA not strict) + // These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc. + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "~7.0.1", strict: false), + // One result because 7.0.1 to 7.0.x is expected. + // 7.0.999 (assumed due to no strictness) is valid. + // 7.1.0 and up are not valid (minor version is too high). + PhpVersionNumberCollection + .make(from: ["7.0"]).all + ) + + // 3. Imprecise check with precise constraint (strict mode) + // These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc. + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]) + .matching(constraint: "~7.0.1", strict: true), + // No results because 7.0.1 to 7.0.x is expected. + // 7.0.0 (assumed due to strictness) is not valid. + // 7.1.0 and up are also not valid (minor version is too high). + PhpVersionNumberCollection + .make(from: []).all + ) + + // 4. Precise members and constraint all around + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]) + .matching(constraint: "~7.0.1", strict: true), + // Only 7.0 with a patch version of .1 or higher is OK. + // In this example, 7.0.10 is OK but all other versions are too new. + PhpVersionNumberCollection + .make(from: ["7.0.10"]).all + ) + + // 5. Precise members but imprecise constraint (strict mode) + // In strict mode the constraint's patch version is assumed to be 0. + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]) + .matching(constraint: "~7.0", strict: true), + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all + ) + + // 6. Precise members but imprecise constraint (lenient mode) + // In lenient mode the constraint's patch version is assumed to be equal. + // (Strictness does not make any difference here, but both should be tested.) + XCTAssertEqual( + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]) + .matching(constraint: "~7.0", strict: false), + PhpVersionNumberCollection + .make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all + ) + } +} diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 69f4c51..c9d9f1f 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -282,6 +282,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } @objc func switchToPhpVersion(sender: PhpMenuItem) { + self.switchToPhpVersion(sender.version) + } + + @objc func switchToPhpVersion(_ version: String) { setBusyImage() PhpEnv.shared.isBusy = true @@ -305,8 +309,8 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { let sendLocalNotification = { LocalNotification.send( - title: String(format: "notification.version_changed_title".localized, sender.version), - subtitle: String(format: "notification.version_changed_desc".localized, sender.version) + title: String(format: "notification.version_changed_title".localized, version), + subtitle: String(format: "notification.version_changed_desc".localized, version) ) PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() } @@ -321,7 +325,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } PhpEnv.switcher.performSwitch( - to: sender.version, + to: version, completion: completion ) } diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 2644681..0a34b91 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -137,7 +137,7 @@ class StatusMenu : NSMenu { let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.longVersion let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool - let versionString = long ? longVersion : shortVersion + let versionString = long ? longVersion.homebrewVersion : shortVersion let action = #selector(MainMenu.switchToPhpVersion(sender:)) let brew = (shortVersion == PhpEnv.brewPhpVersion) ? "php" : "php@\(shortVersion)" diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index 9f026c1..2c68f95 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -80,12 +80,13 @@ class SiteListCell: NSTableCellView var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue var map: [Int: String] = [:] - var versions: [String] = [] - // TODO: Based on the version constraint, insert the desired version to switch to - - versions.forEach { version in - alert.addButton(withTitle: "Switch to PHP \(version)") - map[mapIndex] = version + // Determine which installed versions would be ideal to switch to, + // but make sure to exclude the currently linked version + PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in + version.homebrewVersion != PhpEnv.phpInstall.version.short + }).forEach { version in + alert.addButton(withTitle: "Switch to PHP \(version.homebrewVersion)") + map[mapIndex] = version.homebrewVersion mapIndex += 1 } @@ -94,6 +95,7 @@ class SiteListCell: NSTableCellView if map.keys.contains(response.rawValue) { let version = map[response.rawValue]! print("Pressed button to switch to \(version)") + MainMenu.shared.switchToPhpVersion(version) } } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 7d31ab9..7246903 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -156,7 +156,7 @@ problem manually, using your own Terminal app (this just shows you the output)." // Composer Version "alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: \"php\": \"%@\"."; -"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the %@ in `composer.json`.\n\nPlease note that the lockfile is not checked and this reflects the current Composer configuration file as it existed at the time of refreshing the site list."; +"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the %@ in `composer.json` when the list was last refreshed."; // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP"; From 626b7a735d3551a646a4d6c14a10050912b55ef3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 23 Jan 2022 19:37:10 +0100 Subject: [PATCH 051/105] =?UTF-8?q?=F0=9F=91=8C=20Checkmark=20on=20site=20?= =?UTF-8?q?list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Checkmark.imageset/Contents.json | 24 +++++++++++++++++++ .../Checkmark.imageset/check.svg | 1 + phpmon/Domain/Core/App.swift | 6 +++++ phpmon/Domain/Core/Base.lproj/Main.storyboard | 20 ++++++++++++++-- phpmon/Domain/SiteList/SiteListCell.swift | 7 ++++++ 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 phpmon/Assets.xcassets/Checkmark.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/Checkmark.imageset/check.svg diff --git a/phpmon/Assets.xcassets/Checkmark.imageset/Contents.json b/phpmon/Assets.xcassets/Checkmark.imageset/Contents.json new file mode 100644 index 0000000..9048d37 --- /dev/null +++ b/phpmon/Assets.xcassets/Checkmark.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "check.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/Checkmark.imageset/check.svg b/phpmon/Assets.xcassets/Checkmark.imageset/check.svg new file mode 100644 index 0000000..316a362 --- /dev/null +++ b/phpmon/Assets.xcassets/Checkmark.imageset/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 788b618..450bc84 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -83,5 +83,11 @@ class App: PhpSwitcherDelegate { func switcherDidCompleteSwitch() { PhpEnv.shared.currentInstall = ActivePhpInstallation() handlePhpConfigWatcher() + + if let window = siteListWindowController { + DispatchQueue.main.async { + window.contentVC.reloadSites() + } + } } } diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index d30390e..fb73779 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -501,7 +501,10 @@ + + + + + + + + + + @@ -526,6 +539,7 @@ + @@ -537,6 +551,7 @@ + @@ -590,10 +605,11 @@ - + + diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index 2c68f95..5cf8711 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -22,6 +22,7 @@ class SiteListCell: NSTableCellView @IBOutlet weak var labelDriver: NSTextField! @IBOutlet weak var buttonPhpVersion: NSButton! + @IBOutlet weak var imageViewPhpVersionOK: NSImageView! override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) @@ -62,6 +63,12 @@ class SiteListCell: NSTableCellView // Show the PHP version buttonPhpVersion.title = " PHP \(site.composerPhp) " buttonPhpVersion.isHidden = (site.composerPhp == "???") + + let matchesConstraint = PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long]) + .matching(constraint: site.composerPhp) + .count > 0 + + imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !matchesConstraint) } @IBAction func pressedPhpVersion(_ sender: Any) { From 42fc0e36980ed137637ad1f39ed937c55d952021 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 23 Jan 2022 20:06:20 +0100 Subject: [PATCH 052/105] =?UTF-8?q?=F0=9F=94=A5=20Due=20to=20closing=20#34?= =?UTF-8?q?,=20removed=20switcher=20pref?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-common/PHP/PHP Version/PhpEnv.swift | 2 -- phpmon/Domain/Preferences/Preferences.swift | 5 +---- phpmon/Domain/Preferences/PrefsVC.swift | 8 -------- phpmon/Localizable.strings | 4 ---- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/phpmon-common/PHP/PHP Version/PhpEnv.swift b/phpmon-common/PHP/PHP Version/PhpEnv.swift index 8c5d1d7..bcac813 100644 --- a/phpmon-common/PHP/PHP Version/PhpEnv.swift +++ b/phpmon-common/PHP/PHP Version/PhpEnv.swift @@ -78,8 +78,6 @@ class PhpEnv { // MARK: - Methods public static var switcher: PhpSwitcher { - // Based on the setting, use a particular switcher - // For now, we'll hardcode the internal switcher though return InternalSwitcher() } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index ec3df1e..2b411e4 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -15,7 +15,6 @@ enum PreferenceName: String { case fullPhpVersionDynamicIcon = "full_php_in_menu_bar" case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle" case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch" - case useInternalSwitcher = "use_phpmon_switcher" case globalHotkey = "global_hotkey" } @@ -54,8 +53,7 @@ class Preferences { PreferenceName.shouldDisplayPhpHintInIcon.rawValue: true, PreferenceName.fullPhpVersionDynamicIcon.rawValue: false, PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true, - PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false, - PreferenceName.useInternalSwitcher.rawValue: false, + PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false ]) if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) { @@ -98,7 +96,6 @@ class Preferences { .fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any, .autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any, .autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any, - .useInternalSwitcher: UserDefaults.standard.bool(forKey: PreferenceName.useInternalSwitcher.rawValue) as Any, // Part 2: Always Strings .globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any, diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index a4208e4..04c9fb9 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -90,14 +90,6 @@ class PrefsVC: NSViewController { preference: .autoComposerGlobalUpdateAfterSwitch, action: {} ), - /* DISABLED UNTIL VALET SWITCHING IS OK (see #34) - CheckboxPreferenceView.make( - sectionText: "", - descriptionText: "prefs.use_internal_switcher_desc".localized, - checkboxText: "prefs.use_internal_switcher".localized, - preference: .useInternalSwitcher, - action: {} - ), */ HotkeyPreferenceView.make( sectionText: "prefs.global_shortcut".localized, descriptionText: "prefs.shortcut_desc".localized, diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 7246903..198d6ae 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -114,10 +114,6 @@ "prefs.display_full_php_version" = "Display full PHP version everywhere"; "prefs.display_full_php_version_desc" = "Display the full version instead of the major version displayed in the menu bar and the dropdown menu. (This may be undesirable on smaller displays, so this is disabled by default.)"; -"prefs.use_internal_switcher" = "Use PHP Monitor’s own version switcher"; -"prefs.use_internal_switcher_desc" = -"By default, PHP Monitor will attempt to use Laravel Valet in order to switch PHP versions. If you prefer a different switcher or are having issues with `valet use php`, you may use PHP Monitor's own switcher which is slightly faster, but might cause issues with permissions in your Homebrew directories, since PHP Monitor controls the services."; - "prefs.auto_composer_update_title" = "Automatically update global dependencies"; "prefs.auto_composer_update_desc" = "When checked, will automatically ask Composer to run `composer global update` whenever you switch between different PHP versions. You will be able to see what changes are being made, or should this fail."; From f75bfc9c4a05028b8423c3c354ed476d5e805c3a Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 24 Jan 2022 01:16:17 +0100 Subject: [PATCH 053/105] =?UTF-8?q?=E2=9C=A8=20Initial=20version=20of=20#7?= =?UTF-8?q?2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 20 +++ phpmon-common/Core/Events.swift | 15 ++ .../ServiceLoading.imageset/Contents.json | 24 +++ .../question-circle.svg | 1 + .../ServiceOff.imageset/Contents.json | 24 +++ .../ServiceOff.imageset/times-circle.svg | 1 + .../ServiceOn.imageset/Contents.json | 24 +++ .../ServiceOn.imageset/check-circle.svg | 1 + phpmon/Domain/Core/App.swift | 1 - phpmon/Domain/Core/AppDelegate.swift | 2 +- phpmon/Domain/Helpers/Async.swift | 20 +++ phpmon/Domain/Menu/MainMenu+Startup.swift | 4 + phpmon/Domain/Menu/MainMenu.swift | 9 +- phpmon/Domain/Menu/ServicesView.swift | 78 +++++++++ phpmon/Domain/Menu/ServicesView.xib | 150 ++++++++++++++++++ phpmon/Domain/Menu/StatsView.xib | 16 +- phpmon/Domain/Menu/StatusMenu.swift | 12 +- phpmon/Localizable.strings | 2 +- 18 files changed, 380 insertions(+), 24 deletions(-) create mode 100644 phpmon-common/Core/Events.swift create mode 100644 phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg create mode 100644 phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg create mode 100644 phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg create mode 100644 phpmon/Domain/Helpers/Async.swift create mode 100644 phpmon/Domain/Menu/ServicesView.swift create mode 100644 phpmon/Domain/Menu/ServicesView.xib diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 7dbc8cb..565324f 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -140,6 +140,12 @@ C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; }; + C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; }; + C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; }; + C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; }; + C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; }; + C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; + C4EC1E74279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; }; @@ -288,6 +294,10 @@ C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; + C4EC1E65279DE0380010F296 /* ServicesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServicesView.xib; sourceTree = ""; }; + C4EC1E67279DE0540010F296 /* ServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = ""; }; + C4EC1E6C279DF87A0010F296 /* Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Async.swift; sourceTree = ""; }; + C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; C4EE55A627708B9E001DF387 /* PMHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMHeaderView.swift; sourceTree = ""; }; C4EE55A727708B9E001DF387 /* Preview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preview.swift; sourceTree = ""; }; @@ -395,6 +405,7 @@ children = ( C415D3B62770F294005EF286 /* Actions.swift */, C4EE188322D3386B00E126E5 /* Constants.swift */, + C4EC1E72279DFCF40010F296 /* Events.swift */, C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */, @@ -510,6 +521,8 @@ C48D0C9925CC888B00CC7490 /* HeaderView.xib */, C48D0CA225CC992000CC7490 /* StatsView.swift */, C48D0C8F25CC7FD000CC7490 /* StatsView.xib */, + C4EC1E67279DE0540010F296 /* ServicesView.swift */, + C4EC1E65279DE0380010F296 /* ServicesView.xib */, ); path = Menu; sourceTree = ""; @@ -525,6 +538,7 @@ C4CCBA6B275C567B008C7055 /* PMWindowController.swift */, 54AB03252763858F00A29D5F /* Timer.swift */, C4B5635D276AB09000F12CCB /* VersionExtractor.swift */, + C4EC1E6C279DF87A0010F296 /* Async.swift */, ); path = Helpers; sourceTree = ""; @@ -796,6 +810,7 @@ 54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */, C473319F2470923A009A0597 /* Localizable.strings in Resources */, C4F30B07278E195800755FCE /* brew-services.json in Resources */, + C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */, 54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */, C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */, C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */, @@ -831,9 +846,11 @@ C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */, + C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */, C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */, + C4EC1E74279DFCF40010F296 /* Events.swift in Sources */, C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */, C417DC76277614690015E6EE /* Helpers.swift in Sources */, @@ -860,6 +877,7 @@ C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */, C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, + C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */, C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */, @@ -893,6 +911,8 @@ C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */, + C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */, + C4EC1E73279DFCF40010F296 /* Events.swift in Sources */, C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, diff --git a/phpmon-common/Core/Events.swift b/phpmon-common/Core/Events.swift new file mode 100644 index 0000000..fbc803b --- /dev/null +++ b/phpmon-common/Core/Events.swift @@ -0,0 +1,15 @@ +// +// Events.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class Events { + + static let ServicesUpdated = Notification.Name("ServicesUpdated") + +} diff --git a/phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json b/phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json new file mode 100644 index 0000000..b7c29f9 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceLoading.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "question-circle.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg b/phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg new file mode 100644 index 0000000..df1f089 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceLoading.imageset/question-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json b/phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json new file mode 100644 index 0000000..5aba3d7 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOff.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "times-circle.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg b/phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg new file mode 100644 index 0000000..b5d5eba --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOff.imageset/times-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json b/phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json new file mode 100644 index 0000000..e3569a7 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOn.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "check-circle.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg b/phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg new file mode 100644 index 0000000..5673a79 --- /dev/null +++ b/phpmon/Assets.xcassets/ServiceOn.imageset/check-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/phpmon/Domain/Core/App.swift b/phpmon/Domain/Core/App.swift index 450bc84..c41fbd2 100644 --- a/phpmon/Domain/Core/App.swift +++ b/phpmon/Domain/Core/App.swift @@ -43,7 +43,6 @@ class App: PhpSwitcherDelegate { /** Timer that will periodically reload info about the user's PHP installation. */ var timer: Timer? - // MARK: - Global Hotkey diff --git a/phpmon/Domain/Core/AppDelegate.swift b/phpmon/Domain/Core/AppDelegate.swift index 3183636..3b50045 100644 --- a/phpmon/Domain/Core/AppDelegate.swift +++ b/phpmon/Domain/Core/AppDelegate.swift @@ -59,7 +59,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele When the application initializes, create all singletons. */ override init() { - logger.verbosity = .info + logger.verbosity = .performance Log.info("==================================") Log.info("PHP MONITOR by Nico Verbruggen") Log.info("Version \(App.version)") diff --git a/phpmon/Domain/Helpers/Async.swift b/phpmon/Domain/Helpers/Async.swift new file mode 100644 index 0000000..d427974 --- /dev/null +++ b/phpmon/Domain/Helpers/Async.swift @@ -0,0 +1,20 @@ +// +// Async.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +public func runAsync(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {}) +{ + DispatchQueue.global(qos: .userInitiated).async { + execute() + + DispatchQueue.main.async { + completion() + } + } +} diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 573684a..d418d7e 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -70,8 +70,12 @@ extension MainMenu { // Attempt to find out more info about Valet Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version)") + Valet.shared.validateVersion() Valet.shared.startPreloadingSites() + + NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) + Log.info("PHP Monitor is ready to serve!") // Schedule a request to fetch the PHP version every 60 seconds diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index c9d9f1f..b515d4a 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -100,13 +100,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { PhpEnv.shared.isBusy = true setBusyImage() DispatchQueue.global(qos: .userInitiated).async { [unowned self] in - rebuild() execute() PhpEnv.shared.isBusy = false DispatchQueue.main.async { [self] in updatePhpVersionInStatusBar() - rebuild() + NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) completion() } } @@ -156,12 +155,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { waitAndExecute { // This automatically reloads the menu Log.info("Reloading information about the PHP installation...") - } completion: { - // Add a slight delay to make sure it loads the new menu - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - // Open the menu again - MainMenu.shared.statusItem.button?.performClick(nil) - } } } diff --git a/phpmon/Domain/Menu/ServicesView.swift b/phpmon/Domain/Menu/ServicesView.swift new file mode 100644 index 0000000..a759a5b --- /dev/null +++ b/phpmon/Domain/Menu/ServicesView.swift @@ -0,0 +1,78 @@ +// +// StatsView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 04/02/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation +import Cocoa + +class ServicesView: NSView, XibLoadable { + + @IBOutlet weak var imageViewPhp: NSImageView! + @IBOutlet weak var imageViewNginx: NSImageView! + @IBOutlet weak var imageViewDnsmasq: NSImageView! + + @IBOutlet weak var textFieldPhp: NSTextField! + + var services: [String: HomebrewService] = [:] + + static func asMenuItem() -> NSMenuItem { + let view = Self.createFromXib() + let item = NSMenuItem() + item.view = view + item.target = self + NotificationCenter.default.addObserver( + view!, selector: #selector(self.updateInformation), + name: Events.ServicesUpdated, + object: nil + ) + return item + } + + override func viewWillDraw() { + super.viewWillDraw() + self.loadData() + } + + @objc func updateInformation() { + self.loadData() + } + + func loadData() { + runAsync { + let servicesList = try! JSONDecoder().decode( + [HomebrewService].self, + from: Shell.pipe( + "sudo \(Paths.brew) services info --all --json", + requiresPath: true + ).data(using: .utf8)! + ).filter({ service in + return [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"].contains(service.name) + }) + + self.services = Dictionary(uniqueKeysWithValues: servicesList.map{ ($0.name, $0) }) + } completion: { + self.textFieldPhp.stringValue = PhpEnv.phpInstall.formula.uppercased() + self.applyServiceStyling(PhpEnv.phpInstall.formula, self.imageViewPhp) + self.applyServiceStyling("nginx", self.imageViewNginx) + self.applyServiceStyling("dnsmasq", self.imageViewDnsmasq) + } + } + + func applyServiceStyling(_ serviceName: String, _ imageView: NSImageView) { + if services[serviceName] != nil && services[serviceName]!.running { + imageView.image = NSImage(named: "ServiceOn") + imageView.contentTintColor = NSColor.black + } else { + imageView.image = NSImage(named: "ServiceOff") + imageView.contentTintColor = NSColor.init(red: 246/255, green: 71/255, blue: 71/255, alpha: 1.0) + } + } + + deinit { + NotificationCenter.default.removeObserver(self, name: Events.ServicesUpdated, object: nil) + } +} diff --git a/phpmon/Domain/Menu/ServicesView.xib b/phpmon/Domain/Menu/ServicesView.xib new file mode 100644 index 0000000..c1371ff --- /dev/null +++ b/phpmon/Domain/Menu/ServicesView.xib @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon/Domain/Menu/StatsView.xib b/phpmon/Domain/Menu/StatsView.xib index 1ad4bd9..fd6bca0 100644 --- a/phpmon/Domain/Menu/StatsView.xib +++ b/phpmon/Domain/Menu/StatsView.xib @@ -1,8 +1,8 @@ - + - + @@ -10,11 +10,11 @@ - + - + @@ -75,10 +75,10 @@ - + - + @@ -86,7 +86,7 @@ - + @@ -138,7 +138,7 @@ - + diff --git a/phpmon/Domain/Menu/StatusMenu.swift b/phpmon/Domain/Menu/StatusMenu.swift index 0a34b91..ed780c4 100644 --- a/phpmon/Domain/Menu/StatusMenu.swift +++ b/phpmon/Domain/Menu/StatusMenu.swift @@ -32,10 +32,13 @@ class StatusMenu : NSMenu { self.addSwitchToPhpMenuItems() self.addItem(NSMenuItem.separator()) + + self.addItem(ServicesView.asMenuItem()) + self.addItem(NSMenuItem.separator()) } - func addServicesMenuItems() { - let services = NSMenuItem(title: "mi_toolkit".localized, action: nil, keyEquivalent: "") + func addOtherMenuItems() { + let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "") let servicesMenu = NSMenu() servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) @@ -122,10 +125,9 @@ class StatusMenu : NSMenu { shortcutKey += 1 } + // Other self.addItem(NSMenuItem.separator()) - - self.addServicesMenuItems() - + self.addOtherMenuItems() } private func addSwitchToPhpMenuItems() { diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 198d6ae..bd3f063 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -37,7 +37,7 @@ "mi_manual_actions" = "Manual Actions"; "mi_services" = "Services"; "mi_help" = "First Aid"; -"mi_toolkit" = "Toolkit"; +"mi_other" = "First Aid & Services"; "mi_composer" = "Composer"; "mi_valet_config" = "Locate Valet folder (.config/valet)"; From 5293c437d1e31926e7e6d9620e431cbf23598a69 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 24 Jan 2022 01:17:44 +0100 Subject: [PATCH 054/105] =?UTF-8?q?=F0=9F=94=A7=20Prepare=20for=205.0=20be?= =?UTF-8?q?ta=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 565324f..0d0bc97 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -1183,12 +1183,12 @@ C41C1B4422B0098000E7CF16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIconBeta; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 230; + CURRENT_PROJECT_VERSION = 500; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -1197,8 +1197,8 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "5.0-dev"; - PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; + MARKETING_VERSION = "5.0-b1"; + PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -1208,12 +1208,12 @@ C41C1B4522B0098000E7CF16 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIconBeta; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 230; + CURRENT_PROJECT_VERSION = 500; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -1222,8 +1222,8 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "5.0-dev"; - PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; + MARKETING_VERSION = "5.0-b1"; + PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.beta; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; From d7e8652f5f318f40f3cd2ee6f9d41304d33c6a8f Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 24 Jan 2022 23:41:47 +0100 Subject: [PATCH 055/105] =?UTF-8?q?=F0=9F=91=8C=20Populate=20new=20Service?= =?UTF-8?q?sView=20with=20stale=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This means that the user cannot tell we swapped out the view for another view. The services are re-fetched upon creating the new view, but there is a slight delay. This change conveniently "hides" this delay. BEFORE - Upon creating ServicesView2, ServicesView is deinitialized - ServicesView2 shows question marks (no services data persisted) - ServicesView2 async loads services, when done question marks removed AFTER - Upon creating ServicesView2, ServicesView is deinitialized - ServicesView2 loads stale data (services data was persisted) - ServicesView2 async loads services, when done stale data replaced --- phpmon/Domain/Menu/ServicesView.swift | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/phpmon/Domain/Menu/ServicesView.swift b/phpmon/Domain/Menu/ServicesView.swift index a759a5b..798591e 100644 --- a/phpmon/Domain/Menu/ServicesView.swift +++ b/phpmon/Domain/Menu/ServicesView.swift @@ -17,15 +17,15 @@ class ServicesView: NSView, XibLoadable { @IBOutlet weak var textFieldPhp: NSTextField! - var services: [String: HomebrewService] = [:] + static var services: [String: HomebrewService] = [:] static func asMenuItem() -> NSMenuItem { - let view = Self.createFromXib() + let view = Self.createFromXib()! let item = NSMenuItem() item.view = view item.target = self NotificationCenter.default.addObserver( - view!, selector: #selector(self.updateInformation), + view, selector: #selector(self.updateInformation), name: Events.ServicesUpdated, object: nil ) @@ -42,6 +42,10 @@ class ServicesView: NSView, XibLoadable { } func loadData() { + // Use stale data + self.applyAllInfoFieldsFromCachedValue() + + // Re-fetch services runAsync { let servicesList = try! JSONDecoder().decode( [HomebrewService].self, @@ -53,8 +57,15 @@ class ServicesView: NSView, XibLoadable { return [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"].contains(service.name) }) - self.services = Dictionary(uniqueKeysWithValues: servicesList.map{ ($0.name, $0) }) + ServicesView.services = Dictionary(uniqueKeysWithValues: servicesList.map{ ($0.name, $0) }) } completion: { + // Use fresh data + self.applyAllInfoFieldsFromCachedValue() + } + } + + func applyAllInfoFieldsFromCachedValue() { + DispatchQueue.main.async { self.textFieldPhp.stringValue = PhpEnv.phpInstall.formula.uppercased() self.applyServiceStyling(PhpEnv.phpInstall.formula, self.imageViewPhp) self.applyServiceStyling("nginx", self.imageViewNginx) @@ -63,7 +74,7 @@ class ServicesView: NSView, XibLoadable { } func applyServiceStyling(_ serviceName: String, _ imageView: NSImageView) { - if services[serviceName] != nil && services[serviceName]!.running { + if ServicesView.services[serviceName] != nil && ServicesView.services[serviceName]!.running { imageView.image = NSImage(named: "ServiceOn") imageView.contentTintColor = NSColor.black } else { From f6378e7b730165cbd144de2d2a0e5f226f264b09 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 24 Jan 2022 23:42:22 +0100 Subject: [PATCH 056/105] =?UTF-8?q?=E2=9C=A8=20Add=20option=20to=20add=20a?= =?UTF-8?q?=20linked=20site?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 6 + phpmon/Domain/Core/Base.lproj/Main.storyboard | 187 ++++++++++++++++-- phpmon/Domain/SiteList/AddSiteVC.swift | 71 +++++++ phpmon/Domain/SiteList/SiteListVC.swift | 22 +++ phpmon/Domain/SiteList/SiteListWC.swift | 41 +++- phpmon/Localizable.strings | 2 + 6 files changed, 313 insertions(+), 16 deletions(-) create mode 100644 phpmon/Domain/SiteList/AddSiteVC.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 0d0bc97..2423eb6 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -96,6 +96,8 @@ C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; }; + C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; + C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; }; C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; @@ -269,6 +271,7 @@ C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; }; C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = ""; }; C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = ""; }; + C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = ""; }; C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = ""; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = ""; }; @@ -506,6 +509,7 @@ C464ADAE275A7A69003FCD53 /* SiteListVC.swift */, C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */, C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */, + C4930849279F331F009C240B /* AddSiteVC.swift */, C464ADB1275A87CA003FCD53 /* SiteListCell.swift */, ); path = SiteList; @@ -931,6 +935,7 @@ C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */, C464ADB2275A87CA003FCD53 /* SiteListCell.swift in Sources */, C4EE188422D3386B00E126E5 /* Constants.swift in Sources */, + C493084A279F331F009C240B /* AddSiteVC.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -948,6 +953,7 @@ C4EE55AC27708B9E001DF387 /* Preview.swift in Sources */, 54B48B60275F66AE006D90C5 /* Application.swift in Sources */, C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, + C493084B279F331F009C240B /* AddSiteVC.swift in Sources */, C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */, diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index fb73779..8568c78 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -374,6 +374,12 @@ + + + + + + @@ -394,6 +400,7 @@ + @@ -411,28 +418,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + @@ -445,7 +601,7 @@ - + @@ -473,7 +629,7 @@ - + @@ -481,7 +637,7 @@ - + @@ -489,10 +645,10 @@ - + - + @@ -501,7 +657,7 @@ - + @@ -570,7 +726,7 @@ - + - + @@ -605,7 +761,7 @@ - + @@ -613,5 +769,6 @@ + diff --git a/phpmon/Domain/SiteList/AddSiteVC.swift b/phpmon/Domain/SiteList/AddSiteVC.swift new file mode 100644 index 0000000..9366528 --- /dev/null +++ b/phpmon/Domain/SiteList/AddSiteVC.swift @@ -0,0 +1,71 @@ +// +// AddSiteVC.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 24/01/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation +import Cocoa + +class AddSiteVC: NSViewController, NSTextFieldDelegate { + + @IBOutlet weak var pathControl: NSPathControl! + @IBOutlet weak var linkName: NSTextField! + @IBOutlet weak var previewText: NSTextField! + @IBOutlet weak var buttonSecure: NSButton! + + @IBOutlet weak var buttonCreateLink: NSButton! + + @IBAction func pressedCreateLink(_ sender: Any) { + let path = self.pathControl.url!.path + let name = self.linkName.stringValue + + // TODO: Check if the path still exists + Shell.run("cd '\(path)' && \(Paths.valet) link '\(name)'", requiresPath: true) + self.view.window!.close() + + // Reset search + App.shared.siteListWindowController? + .searchToolbarItem + .searchField.stringValue = "" + + // Add the new item and scrolls to it + App.shared.siteListWindowController? + .contentVC + .addedNewSite( + name: name, + secure: buttonSecure.state == .on + ) + } + + @IBAction func pressedCancel(_ sender: Any) { + self.view.window!.close() + } + + @IBAction func pressedSecure(_ sender: Any) { + updatePreview() + } + + func controlTextDidChange(_ obj: Notification) { + updateTextField() + } + + func updateTextField() { + self.linkName.stringValue = self.linkName.stringValue + .replacingOccurrences(of: " ", with: "-") + + buttonCreateLink.isEnabled = !self.linkName.stringValue.isEmpty + self.updatePreview() + } + + func updatePreview() { + previewText.stringValue = "site_list.add.folder_available" + .localized( + self.buttonSecure.state == .on ? "https" : "http", + self.linkName.stringValue, + Valet.shared.config.tld + ) + } +} diff --git a/phpmon/Domain/SiteList/SiteListVC.swift b/phpmon/Domain/SiteList/SiteListVC.swift index 039180b..c770abe 100644 --- a/phpmon/Domain/SiteList/SiteListVC.swift +++ b/phpmon/Domain/SiteList/SiteListVC.swift @@ -135,6 +135,28 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource { } } + func addedNewSite(name: String, secure: Bool) { + waitAndExecute { + Valet.shared.reloadSites() + } completion: { [self] in + find(name, secure) + } + } + + private func find(_ name: String, _ secure: Bool = false) { + sites = Valet.shared.sites + searchedFor(text: "") + if let site = sites.enumerated().first(where: { $0.element.name == name }) { + DispatchQueue.main.async { + self.tableView.selectRowIndexes([site.offset], byExtendingSelection: false) + self.tableView.scrollRowToVisible(site.offset) + if (secure && !site.element.secured) { + self.toggleSecure() + } + } + } + } + // MARK: - Table View Delegate func numberOfRows(in tableView: NSTableView) -> Int { diff --git a/phpmon/Domain/SiteList/SiteListWC.swift b/phpmon/Domain/SiteList/SiteListWC.swift index 9f032c9..ec3f0cc 100644 --- a/phpmon/Domain/SiteList/SiteListWC.swift +++ b/phpmon/Domain/SiteList/SiteListWC.swift @@ -50,7 +50,46 @@ class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate { // MARK: - Reload functionality - @IBAction func pressedReload(_ sender: Any) { + @IBAction func pressedReload(_ sender: Any?) { contentVC.reloadSites() } + + @IBAction func pressedAddLink(_ sender: Any?) { + selectFolder() + } + + // MARK: - Add a new site + + func selectFolder() { + let dialog = NSOpenPanel() + dialog.title = "Select a Folder" + dialog.showsResizeIndicator = true + dialog.showsHiddenFiles = false + dialog.allowsMultipleSelection = false + dialog.canChooseDirectories = true + dialog.canChooseFiles = false + + if (dialog.runModal() == NSApplication.ModalResponse.OK) { + let result = dialog.url + if (result != nil) { + let path: String = result!.path + self.showSitePopup(path) + } + } + } + + func showSitePopup(_ folder: String) { + let storyboard = NSStoryboard(name: "Main" , bundle : nil) + + let windowController = storyboard.instantiateController( + withIdentifier: "addSiteWindow" + ) as! NSWindowController + + let viewController = windowController.window!.contentViewController as! AddSiteVC + viewController.pathControl.url = URL(fileURLWithPath: folder) + viewController.linkName.stringValue = String(folder.split(separator: "/").last!) + viewController.updateTextField() + + self.window?.beginSheet(windowController.window!) + } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index bd3f063..893a286 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -69,6 +69,8 @@ "site_list.confirm_unlink" = "Are you sure you want to unlink '%@'?"; "site_link.confirm_link" = "No files will be removed. If needed, the site will need to be relinked via the command line."; +"site_list.add.folder_available" = "This site will be available via the following URL: %@://%@.%@"; + // SITE LIST ACTIONS "site_list.unlink" = "Unlink Directory"; From e9ae989200fe6f753cc3adb7c70a80a35c7dc944 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 24 Jan 2022 23:56:26 +0100 Subject: [PATCH 057/105] =?UTF-8?q?=F0=9F=91=8C=20Check=20multiple=20const?= =?UTF-8?q?raints=20(e.g.=20"^7.3|^8.0")?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/SiteList/SiteListCell.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index 5cf8711..e1ce70a 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -55,6 +55,7 @@ class SiteListCell: NSTableCellView // Show the current driver labelDriver.stringValue = "\(site.driver ?? "???")" + // Determine the Laravel version if site.driver == "Laravel" && site.notableComposerDependencies.keys.contains("laravel/framework") { let constraint = site.notableComposerDependencies["laravel/framework"]! labelDriver.stringValue = "Laravel (\(constraint))" @@ -64,9 +65,13 @@ class SiteListCell: NSTableCellView buttonPhpVersion.title = " PHP \(site.composerPhp) " buttonPhpVersion.isHidden = (site.composerPhp == "???") - let matchesConstraint = PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long]) - .matching(constraint: site.composerPhp) - .count > 0 + // Split the composer list (on "|") to evaluate multiple constraints + // For example, for Laravel 8 projects the value is "^7.3|^8.0" + let matchesConstraint = site.composerPhp.split(separator: "|").map { string in + return PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long]) + .matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines)) + .count > 0 + }.contains(true) imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !matchesConstraint) } From aeeecd6996acc0caad240d080a83e2c0fda348b6 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 25 Jan 2022 00:09:36 +0100 Subject: [PATCH 058/105] =?UTF-8?q?=F0=9F=91=8C=20Suggestions=20should=20a?= =?UTF-8?q?lso=20check=20all=20constraints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-common/PHP/Homebrew/HomebrewService.swift | 2 +- phpmon/Domain/SiteList/SiteListCell.swift | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/phpmon-common/PHP/Homebrew/HomebrewService.swift b/phpmon-common/PHP/Homebrew/HomebrewService.swift index 1d92861..6c21af4 100644 --- a/phpmon-common/PHP/Homebrew/HomebrewService.swift +++ b/phpmon-common/PHP/Homebrew/HomebrewService.swift @@ -8,7 +8,7 @@ import Foundation -struct HomebrewService: Decodable { +struct HomebrewService: Decodable, Equatable { let name: String let service_name: String let running: Bool diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index e1ce70a..d525c8a 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -94,7 +94,9 @@ class SiteListCell: NSTableCellView // Determine which installed versions would be ideal to switch to, // but make sure to exclude the currently linked version - PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in + site.composerPhp.split(separator: "|").flatMap { string in + return PhpEnv.shared.validVersions(for: string.trimmingCharacters(in: .whitespacesAndNewlines)) + }.filter({ version in version.homebrewVersion != PhpEnv.phpInstall.version.short }).forEach { version in alert.addButton(withTitle: "Switch to PHP \(version.homebrewVersion)") From 20959501c9674353fd634f2a8253617e93477613 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 25 Jan 2022 00:09:50 +0100 Subject: [PATCH 059/105] =?UTF-8?q?=F0=9F=91=8C=20Fix=20flickering=20of=20?= =?UTF-8?q?incorrect=20data=20on=20first=20load?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Menu/ServicesView.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phpmon/Domain/Menu/ServicesView.swift b/phpmon/Domain/Menu/ServicesView.swift index 798591e..b2d3fb8 100644 --- a/phpmon/Domain/Menu/ServicesView.swift +++ b/phpmon/Domain/Menu/ServicesView.swift @@ -65,6 +65,10 @@ class ServicesView: NSView, XibLoadable { } func applyAllInfoFieldsFromCachedValue() { + if ServicesView.services.keys.isEmpty { + return + } + DispatchQueue.main.async { self.textFieldPhp.stringValue = PhpEnv.phpInstall.formula.uppercased() self.applyServiceStyling(PhpEnv.phpInstall.formula, self.imageViewPhp) From 0f0e91273e2c0d30888fb3280782ed99dca58c80 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 25 Jan 2022 18:28:10 +0100 Subject: [PATCH 060/105] =?UTF-8?q?=F0=9F=91=8C=20Localisation=20improveme?= =?UTF-8?q?nts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon-common/PHP/PHP Version/PhpEnv.swift | 8 +++++--- .../Integrations/Composer/ComposerJson.swift | 2 +- phpmon/Domain/SiteList/SiteListCell.swift | 8 +++----- phpmon/Localizable.strings | 15 +++++++++++++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/phpmon-common/PHP/PHP Version/PhpEnv.swift b/phpmon-common/PHP/PHP Version/PhpEnv.swift index bcac813..c42bcbd 100644 --- a/phpmon-common/PHP/PHP Version/PhpEnv.swift +++ b/phpmon-common/PHP/PHP Version/PhpEnv.swift @@ -149,8 +149,10 @@ class PhpEnv { } public func validVersions(for constraint: String) -> [PhpVersionNumber] { - return PhpVersionNumberCollection - .make(from: self.availablePhpVersions) - .matching(constraint: constraint) + constraint.split(separator: "|").flatMap { + return PhpVersionNumberCollection + .make(from: self.availablePhpVersions) + .matching(constraint: $0.trimmingCharacters(in: .whitespacesAndNewlines)) + } } } diff --git a/phpmon/Domain/Integrations/Composer/ComposerJson.swift b/phpmon/Domain/Integrations/Composer/ComposerJson.swift index 0752733..011fc78 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerJson.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerJson.swift @@ -21,7 +21,7 @@ struct ComposerJson: Decodable { // Check if in dependencies if dependencies?["php"] != nil { - return (dependencies!["php"]!, "dependency list") + return (dependencies!["php"]!, "require") } // Unknown! diff --git a/phpmon/Domain/SiteList/SiteListCell.swift b/phpmon/Domain/SiteList/SiteListCell.swift index d525c8a..4d976ec 100644 --- a/phpmon/Domain/SiteList/SiteListCell.swift +++ b/phpmon/Domain/SiteList/SiteListCell.swift @@ -87,19 +87,17 @@ class SiteListCell: NSTableCellView alert.informativeText = "alert.composer_php_requirement.info" .localized(site.composerPhpSource) - alert.addButton(withTitle: "Close") + alert.addButton(withTitle: "site_link.close".localized) var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue var map: [Int: String] = [:] // Determine which installed versions would be ideal to switch to, // but make sure to exclude the currently linked version - site.composerPhp.split(separator: "|").flatMap { string in - return PhpEnv.shared.validVersions(for: string.trimmingCharacters(in: .whitespacesAndNewlines)) - }.filter({ version in + PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in version.homebrewVersion != PhpEnv.phpInstall.version.short }).forEach { version in - alert.addButton(withTitle: "Switch to PHP \(version.homebrewVersion)") + alert.addButton(withTitle: "site_link.switch_to_php".localized(version.homebrewVersion)) map[mapIndex] = version.homebrewVersion mapIndex += 1 } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 893a286..a637e66 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -69,6 +69,17 @@ "site_list.confirm_unlink" = "Are you sure you want to unlink '%@'?"; "site_link.confirm_link" = "No files will be removed. If needed, the site will need to be relinked via the command line."; +"site_link.close" = "Close"; +"site_link.switch_to_php" = "Switch to PHP %@"; + +// ADD SITE TO SITE LIST + +"site_list.add.link_folder" = "Link a Folder"; +"site_list.add.domain_name_placeholder" = "Enter a domain name here"; +"site_list.add.secure_after_creation" = "Secure %@.%@ after creation"; +"site_list.add.secure_description" = "Securing a site requires administrative privileges.\nYou will be prompted for your password or Touch ID."; +"site_list.add.create_link" = "Create Link"; +"site_list.add.cancel" = "Cancel"; "site_list.add.folder_available" = "This site will be available via the following URL: %@://%@.%@"; // SITE LIST ACTIONS @@ -153,8 +164,8 @@ problem manually, using your own Terminal app (this just shows you the output)." "alert.composer_success.info" = "Your global Composer dependencies have been successfully updated."; // Composer Version -"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: \"php\": \"%@\"."; -"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the %@ in `composer.json` when the list was last refreshed."; +"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: \"php\":\n\"%@\"."; +"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the `%@` field in the `composer.json` file when the site list was last refreshed."; // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP"; From 338a87d503cf3a6657aa1a59f8f5559d253137f2 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 25 Jan 2022 18:50:35 +0100 Subject: [PATCH 061/105] =?UTF-8?q?=F0=9F=91=8C=20Show=20what=20prevents?= =?UTF-8?q?=20creation=20of=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Site name already exists? - Site name empty? --- phpmon/Domain/Core/Base.lproj/Main.storyboard | 15 +++++- phpmon/Domain/SiteList/AddSiteVC.swift | 51 ++++++++++++++++++- phpmon/Localizable.strings | 3 ++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/phpmon/Domain/Core/Base.lproj/Main.storyboard b/phpmon/Domain/Core/Base.lproj/Main.storyboard index 8568c78..fce3884 100644 --- a/phpmon/Domain/Core/Base.lproj/Main.storyboard +++ b/phpmon/Domain/Core/Base.lproj/Main.storyboard @@ -529,15 +529,25 @@ Gw + + + @@ -561,6 +571,9 @@ Gw + + + @@ -729,7 +742,7 @@ Gw