1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 03:50:08 +02:00

🚀 Version 5.8.0

This commit is contained in:
2023-03-02 16:29:06 +01:00
62 changed files with 1130 additions and 493 deletions

1
.gitignore vendored
View File

@ -2,4 +2,5 @@ phpmon.xcodeproj/project.xcworkspace
phpmon.xcodeproj/xcuserdata phpmon.xcodeproj/xcuserdata
PHP Monitor.xcodeproj/project.xcworkspace PHP Monitor.xcodeproj/project.xcworkspace
PHP Monitor.xcodeproj/xcuserdata PHP Monitor.xcodeproj/xcuserdata
phpmon-updater/PHP Monitor Self-Updater.app
.DS_Store .DS_Store

View File

@ -112,6 +112,7 @@
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; }; C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; }; C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; };
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; }; C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; };
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; }; C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; }; C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; }; C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
@ -140,6 +141,7 @@
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; }; C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */; };
C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; }; C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; };
C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; }; C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; }; C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
@ -182,9 +184,6 @@
C469E700294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C469E6FD294CF7B200A82AB2 /* FakeValetProxy.swift */; }; C469E700294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C469E6FD294CF7B200A82AB2 /* FakeValetProxy.swift */; };
C469E701294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C469E6FD294CF7B200A82AB2 /* FakeValetProxy.swift */; }; C469E701294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C469E6FD294CF7B200A82AB2 /* FakeValetProxy.swift */; };
C469E706294CFDF700A82AB2 /* DomainsListTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C469E702294CFDF700A82AB2 /* DomainsListTest.swift */; }; C469E706294CFDF700A82AB2 /* DomainsListTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C469E702294CFDF700A82AB2 /* DomainsListTest.swift */; };
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */; };
C46EBC4428DB95F0007ACC74 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; }; C46EBC4428DB95F0007ACC74 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; };
C46EBC4528DB95F0007ACC74 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; }; C46EBC4528DB95F0007ACC74 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; };
C46EBC4728DB9644007ACC74 /* RealShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4628DB9644007ACC74 /* RealShell.swift */; }; C46EBC4728DB9644007ACC74 /* RealShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4628DB9644007ACC74 /* RealShell.swift */; };
@ -324,7 +323,6 @@
C471E84628F9BB650021E251 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; }; C471E84628F9BB650021E251 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
C471E84728F9BB650021E251 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C471E84728F9BB650021E251 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
C471E84828F9BB650021E251 /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; }; C471E84828F9BB650021E251 /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
C471E84928F9BB650021E251 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C471E84A28F9BB650021E251 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; }; C471E84A28F9BB650021E251 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; };
C471E84B28F9BB650021E251 /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C471E84B28F9BB650021E251 /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
C471E84C28F9BB650021E251 /* EnvironmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */; }; C471E84C28F9BB650021E251 /* EnvironmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */; };
@ -414,7 +412,6 @@
C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; }; C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
C471E8AB28F9BB8F0021E251 /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; }; C471E8AB28F9BB8F0021E251 /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
C471E8AC28F9BB8F0021E251 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
C471E8AD28F9BB8F0021E251 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; }; C471E8AD28F9BB8F0021E251 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; };
C471E8AE28F9BB8F0021E251 /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C471E8AE28F9BB8F0021E251 /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
C471E8AF28F9BB8F0021E251 /* EnvironmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */; }; C471E8AF28F9BB8F0021E251 /* EnvironmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */; };
@ -486,6 +483,10 @@
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; }; C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; }; C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C47DF1AF299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B0299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B1299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B2299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; }; C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
@ -511,6 +512,17 @@
C48D6C70279CD2AC00F26D7E /* VersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */; }; C48D6C70279CD2AC00F26D7E /* VersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */; };
C48D6C71279CD2AC00F26D7E /* VersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */; }; C48D6C71279CD2AC00F26D7E /* VersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */; };
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; }; C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
C491997729901DD6001F3A21 /* CaskFileParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997629901DD6001F3A21 /* CaskFileParserTest.swift */; };
C491997929901DE2001F3A21 /* phpmon-dev.rb in Resources */ = {isa = PBXBuildFile; fileRef = C491997829901DE2001F3A21 /* phpmon-dev.rb */; };
C491997B29901DF7001F3A21 /* CaskFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997A29901DF7001F3A21 /* CaskFile.swift */; };
C491997C29901DF7001F3A21 /* CaskFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997A29901DF7001F3A21 /* CaskFile.swift */; };
C491997D29901DF7001F3A21 /* CaskFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997A29901DF7001F3A21 /* CaskFile.swift */; };
C491997E29901DF7001F3A21 /* CaskFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997A29901DF7001F3A21 /* CaskFile.swift */; };
C491998029901E0F001F3A21 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997F29901E0F001F3A21 /* AppUpdater.swift */; };
C491998129901E0F001F3A21 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997F29901E0F001F3A21 /* AppUpdater.swift */; };
C491998229901E0F001F3A21 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997F29901E0F001F3A21 /* AppUpdater.swift */; };
C491998329901E0F001F3A21 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C491997F29901E0F001F3A21 /* AppUpdater.swift */; };
C491998A29902089001F3A21 /* PHP Monitor Self-Updater.app in Resources */ = {isa = PBXBuildFile; fileRef = C491998929902089001F3A21 /* PHP Monitor Self-Updater.app */; };
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; }; C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; }; C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
@ -699,6 +711,9 @@
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; }; C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; };
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; }; C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; }; C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; };
C4FD87A529AB98720002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FD87A629AB98730002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FD87A729AB98730002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; }; C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; }; C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -801,6 +816,7 @@
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; }; C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = "<group>"; }; C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = "<group>"; };
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = "<group>"; }; C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = "<group>"; };
C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigChecker.swift; sourceTree = "<group>"; };
C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = "<group>"; }; C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = "<group>"; };
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; }; C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; }; C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
@ -815,6 +831,7 @@
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; }; C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; }; C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; }; C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCheckTest.swift; sourceTree = "<group>"; };
C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; }; C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; };
C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; }; C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; };
C451AFF52969E40F0078E617 /* HelpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpButton.swift; sourceTree = "<group>"; }; C451AFF52969E40F0078E617 /* HelpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpButton.swift; sourceTree = "<group>"; };
@ -833,8 +850,6 @@
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; }; C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; };
C469E6FD294CF7B200A82AB2 /* FakeValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeValetProxy.swift; sourceTree = "<group>"; }; C469E6FD294CF7B200A82AB2 /* FakeValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeValetProxy.swift; sourceTree = "<group>"; };
C469E702294CFDF700A82AB2 /* DomainsListTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainsListTest.swift; sourceTree = "<group>"; }; C469E702294CFDF700A82AB2 /* DomainsListTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainsListTest.swift; sourceTree = "<group>"; };
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUpdaterCheckTest.swift; sourceTree = "<group>"; };
C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellProtocol.swift; sourceTree = "<group>"; }; C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellProtocol.swift; sourceTree = "<group>"; };
C46EBC4628DB9644007ACC74 /* RealShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealShell.swift; sourceTree = "<group>"; }; C46EBC4628DB9644007ACC74 /* RealShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealShell.swift; sourceTree = "<group>"; };
C46EBC4928DB966A007ACC74 /* TestableShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableShell.swift; sourceTree = "<group>"; }; C46EBC4928DB966A007ACC74 /* TestableShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableShell.swift; sourceTree = "<group>"; };
@ -853,11 +868,17 @@
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = "<group>"; }; C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = "<group>"; };
C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = "<group>"; }; C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = "<group>"; };
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; }; C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginItemManager.swift; sourceTree = "<group>"; };
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; }; C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; }; C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; };
C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionNumber.swift; sourceTree = "<group>"; }; C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionNumber.swift; sourceTree = "<group>"; };
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; }; C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
C491997629901DD6001F3A21 /* CaskFileParserTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaskFileParserTest.swift; sourceTree = "<group>"; };
C491997829901DE2001F3A21 /* phpmon-dev.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = "phpmon-dev.rb"; sourceTree = "<group>"; };
C491997A29901DF7001F3A21 /* CaskFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaskFile.swift; sourceTree = "<group>"; };
C491997F29901E0F001F3A21 /* AppUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUpdater.swift; sourceTree = "<group>"; };
C491998929902089001F3A21 /* PHP Monitor Self-Updater.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = "PHP Monitor Self-Updater.app"; sourceTree = "<group>"; };
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; }; C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; }; C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentCheck.swift; sourceTree = "<group>"; }; C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentCheck.swift; sourceTree = "<group>"; };
@ -1140,6 +1161,7 @@
C4F5FBCC28218C93001065C5 /* .swiftlint.yml */, C4F5FBCC28218C93001065C5 /* .swiftlint.yml */,
C4E713572570151400007428 /* docs */, C4E713572570151400007428 /* docs */,
C41C1B3522B0097F00E7CF16 /* phpmon */, C41C1B3522B0097F00E7CF16 /* phpmon */,
C491998829902061001F3A21 /* phpmon-updater */,
C471E79628F9B4260021E251 /* tests */, C471E79628F9B4260021E251 /* tests */,
C41C1B3422B0097F00E7CF16 /* Products */, C41C1B3422B0097F00E7CF16 /* Products */,
C4D309E72770EF2F00958BCF /* Frameworks */, C4D309E72770EF2F00958BCF /* Frameworks */,
@ -1204,6 +1226,7 @@
C422DDAB28A2DAA100CEAC97 /* Warnings */ = { C422DDAB28A2DAA100CEAC97 /* Warnings */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C43FDBE729A9329A003D85EC /* Services */,
C47699F028A2F3150060FEB8 /* Warning.swift */, C47699F028A2F3150060FEB8 /* Warning.swift */,
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */, C47699EE28A2F2A30060FEB8 /* WarningManager.swift */,
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */, C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */,
@ -1229,6 +1252,14 @@
path = Warning; path = Warning;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C43FDBE729A9329A003D85EC /* Services */ = {
isa = PBXGroup;
children = (
C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */,
);
path = Services;
sourceTree = "<group>";
};
C44067F327E256560045BD4E /* Cells */ = { C44067F327E256560045BD4E /* Cells */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1300,6 +1331,7 @@
C459B4BF27F6094100E9B4B4 /* brew */ = { C459B4BF27F6094100E9B4B4 /* brew */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C491997829901DE2001F3A21 /* phpmon-dev.rb */,
C4E2E85228FC256B003B070C /* brew-services-normal.json */, C4E2E85228FC256B003B070C /* brew-services-normal.json */,
C4E2E85128FC256B003B070C /* brew-services-sudo.json */, C4E2E85128FC256B003B070C /* brew-services-sudo.json */,
C43A8A1F25D9D1D700591B77 /* brew-formula.json */, C43A8A1F25D9D1D700591B77 /* brew-formula.json */,
@ -1398,6 +1430,7 @@
children = ( children = (
C471E7BE28F9B90F0021E251 /* StartupTest.swift */, C471E7BE28F9B90F0021E251 /* StartupTest.swift */,
C469E702294CFDF700A82AB2 /* DomainsListTest.swift */, C469E702294CFDF700A82AB2 /* DomainsListTest.swift */,
C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */,
C4181F1028FAF9330042EA28 /* UITestCase.swift */, C4181F1028FAF9330042EA28 /* UITestCase.swift */,
); );
path = ui; path = ui;
@ -1429,6 +1462,7 @@
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */, C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
C4D3660A29113F20006BD146 /* System.swift */, C4D3660A29113F20006BD146 /* System.swift */,
C4D36614291160A1006BD146 /* WIP.swift */, C4D36614291160A1006BD146 /* WIP.swift */,
C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */,
); );
path = Helpers; path = Helpers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1444,6 +1478,14 @@
path = "PHP Version"; path = "PHP Version";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C491998829902061001F3A21 /* phpmon-updater */ = {
isa = PBXGroup;
children = (
C491998929902089001F3A21 /* PHP Monitor Self-Updater.app */,
);
path = "phpmon-updater";
sourceTree = "<group>";
};
C4AF9F6A275445C900D44ED0 /* Valet */ = { C4AF9F6A275445C900D44ED0 /* Valet */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1471,6 +1513,7 @@
C4AF9F6C275445D900D44ED0 /* Homebrew */ = { C4AF9F6C275445D900D44ED0 /* Homebrew */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C491997A29901DF7001F3A21 /* CaskFile.swift */,
C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */, C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */,
); );
path = Homebrew; path = Homebrew;
@ -1491,8 +1534,8 @@
C4EED88827A48778006D7272 /* InterAppHandler.swift */, C4EED88827A48778006D7272 /* InterAppHandler.swift */,
C4D8016522B1584700C6DA1B /* Startup.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */,
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */, C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
C40FE736282ABA4F00A302C2 /* AppVersion.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
C491997F29901E0F001F3A21 /* AppUpdater.swift */,
C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */, C4A6957528D23EE300A14CF8 /* EnvironmentManager.swift */,
); );
path = App; path = App;
@ -1580,6 +1623,7 @@
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */, C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */,
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */, C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */,
C4551656297AED18009B8466 /* ValetRcTest.swift */, C4551656297AED18009B8466 /* ValetRcTest.swift */,
C491997629901DD6001F3A21 /* CaskFileParserTest.swift */,
); );
path = Parsers; path = Parsers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1592,7 +1636,6 @@
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */, C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */,
C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */, C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */,
C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */, C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */,
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */,
); );
path = Versions; path = Versions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1903,6 +1946,7 @@
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */, C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */,
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */, C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */,
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */, C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */,
C491998A29902089001F3A21 /* PHP Monitor Self-Updater.app in Resources */,
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */, C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
C4232EE52612526500158FC6 /* Credits.html in Resources */, C4232EE52612526500158FC6 /* Credits.html in Resources */,
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */, 54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
@ -1958,6 +2002,7 @@
C455165B297AEDB5009B8466 /* valetrc.broken in Resources */, C455165B297AEDB5009B8466 /* valetrc.broken in Resources */,
54A18D40282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test in Resources */, 54A18D40282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test in Resources */,
C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */, C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */,
C491997929901DE2001F3A21 /* phpmon-dev.rb in Resources */,
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */, C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */,
C4E2E85428FC256B003B070C /* brew-services-sudo.json in Resources */, C4E2E85428FC256B003B070C /* brew-services-sudo.json in Resources */,
); );
@ -1994,6 +2039,7 @@
files = ( files = (
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */, C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */,
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */, C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
C47DF1AF299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C4D3661A291173EA006BD146 /* DictionaryExtension.swift in Sources */, C4D3661A291173EA006BD146 /* DictionaryExtension.swift in Sources */,
C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */, C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */,
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
@ -2070,6 +2116,7 @@
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */, C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */, 54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
C491997B29901DF7001F3A21 /* CaskFile.swift in Sources */,
C4297F7A28970D59004C4630 /* WarningView.swift in Sources */, C4297F7A28970D59004C4630 /* WarningView.swift in Sources */,
C4C0E8E227F88B13002D32A9 /* ValetDomainScanner.swift in Sources */, C4C0E8E227F88B13002D32A9 /* ValetDomainScanner.swift in Sources */,
C42F26732805B4B400938AC7 /* ValetListable.swift in Sources */, C42F26732805B4B400938AC7 /* ValetListable.swift in Sources */,
@ -2081,7 +2128,6 @@
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */, C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */, 03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */,
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
@ -2126,6 +2172,7 @@
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */, C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */,
C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */, C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */,
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
C451AFF62969E40F0078E617 /* HelpButton.swift in Sources */, C451AFF62969E40F0078E617 /* HelpButton.swift in Sources */,
@ -2147,6 +2194,7 @@
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */, C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */, C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */, C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */,
C491998029901E0F001F3A21 /* AppUpdater.swift in Sources */,
C4E49DEA28F7643D0026AC4E /* CommandProtocol.swift in Sources */, C4E49DEA28F7643D0026AC4E /* CommandProtocol.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -2178,13 +2226,14 @@
C471E84228F9BB650021E251 /* AppDelegate+InterApp.swift in Sources */, C471E84228F9BB650021E251 /* AppDelegate+InterApp.swift in Sources */,
C471E84328F9BB650021E251 /* App.swift in Sources */, C471E84328F9BB650021E251 /* App.swift in Sources */,
C4E2E85E28FC282B003B070C /* TestableConfiguration.swift in Sources */, C4E2E85E28FC282B003B070C /* TestableConfiguration.swift in Sources */,
C491997D29901DF7001F3A21 /* CaskFile.swift in Sources */,
C4FD87A629AB98730002D701 /* PhpConfigChecker.swift in Sources */,
C45E2A7529199248005C7CFD /* InternalSwitcherTest.swift in Sources */, C45E2A7529199248005C7CFD /* InternalSwitcherTest.swift in Sources */,
C471E84428F9BB650021E251 /* App+ActivationPolicy.swift in Sources */, C471E84428F9BB650021E251 /* App+ActivationPolicy.swift in Sources */,
C471E84528F9BB650021E251 /* App+GlobalHotkey.swift in Sources */, C471E84528F9BB650021E251 /* App+GlobalHotkey.swift in Sources */,
C471E84628F9BB650021E251 /* InterAppHandler.swift in Sources */, C471E84628F9BB650021E251 /* InterAppHandler.swift in Sources */,
C471E84728F9BB650021E251 /* Startup.swift in Sources */, C471E84728F9BB650021E251 /* Startup.swift in Sources */,
C471E84828F9BB650021E251 /* EnvironmentCheck.swift in Sources */, C471E84828F9BB650021E251 /* EnvironmentCheck.swift in Sources */,
C471E84928F9BB650021E251 /* AppUpdateChecker.swift in Sources */,
C471E84A28F9BB650021E251 /* AppVersion.swift in Sources */, C471E84A28F9BB650021E251 /* AppVersion.swift in Sources */,
C471E84B28F9BB650021E251 /* ServicesManager.swift in Sources */, C471E84B28F9BB650021E251 /* ServicesManager.swift in Sources */,
C471E84C28F9BB650021E251 /* EnvironmentManager.swift in Sources */, C471E84C28F9BB650021E251 /* EnvironmentManager.swift in Sources */,
@ -2201,6 +2250,7 @@
C4D36617291160A1006BD146 /* WIP.swift in Sources */, C4D36617291160A1006BD146 /* WIP.swift in Sources */,
C471E85728F9BB650021E251 /* DomainListTLSCell.swift in Sources */, C471E85728F9BB650021E251 /* DomainListTLSCell.swift in Sources */,
C471E85828F9BB650021E251 /* DomainListNameCell.swift in Sources */, C471E85828F9BB650021E251 /* DomainListNameCell.swift in Sources */,
C47DF1B1299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C471E85928F9BB650021E251 /* DomainListPhpCell.swift in Sources */, C471E85928F9BB650021E251 /* DomainListPhpCell.swift in Sources */,
C471E85A28F9BB650021E251 /* DomainListTypeCell.swift in Sources */, C471E85A28F9BB650021E251 /* DomainListTypeCell.swift in Sources */,
C471E85B28F9BB650021E251 /* DomainListKindCell.swift in Sources */, C471E85B28F9BB650021E251 /* DomainListKindCell.swift in Sources */,
@ -2226,6 +2276,7 @@
C471E86A28F9BB650021E251 /* PrefsVC.swift in Sources */, C471E86A28F9BB650021E251 /* PrefsVC.swift in Sources */,
C471E86B28F9BB650021E251 /* PreferenceName.swift in Sources */, C471E86B28F9BB650021E251 /* PreferenceName.swift in Sources */,
C471E86C28F9BB650021E251 /* Preferences.swift in Sources */, C471E86C28F9BB650021E251 /* Preferences.swift in Sources */,
C491998229901E0F001F3A21 /* AppUpdater.swift in Sources */,
C4D3660D29113F20006BD146 /* System.swift in Sources */, C4D3660D29113F20006BD146 /* System.swift in Sources */,
C471E86D28F9BB650021E251 /* CustomPrefs.swift in Sources */, C471E86D28F9BB650021E251 /* CustomPrefs.swift in Sources */,
C4E2E84C28FC1E70003B070C /* DataExtension.swift in Sources */, C4E2E84C28FC1E70003B070C /* DataExtension.swift in Sources */,
@ -2326,11 +2377,13 @@
C471E89028F9BB8F0021E251 /* AlertableError.swift in Sources */, C471E89028F9BB8F0021E251 /* AlertableError.swift in Sources */,
C471E89128F9BB8F0021E251 /* Errors.swift in Sources */, C471E89128F9BB8F0021E251 /* Errors.swift in Sources */,
C471E89228F9BB8F0021E251 /* Alert.swift in Sources */, C471E89228F9BB8F0021E251 /* Alert.swift in Sources */,
C4FD87A529AB98720002D701 /* PhpConfigChecker.swift in Sources */,
C471E89328F9BB8F0021E251 /* Application.swift in Sources */, C471E89328F9BB8F0021E251 /* Application.swift in Sources */,
C471E89428F9BB8F0021E251 /* LocalNotification.swift in Sources */, C471E89428F9BB8F0021E251 /* LocalNotification.swift in Sources */,
C471E89528F9BB8F0021E251 /* MenuBarImageGenerator.swift in Sources */, C471E89528F9BB8F0021E251 /* MenuBarImageGenerator.swift in Sources */,
C471E89628F9BB8F0021E251 /* PMWindowController.swift in Sources */, C471E89628F9BB8F0021E251 /* PMWindowController.swift in Sources */,
C471E89728F9BB8F0021E251 /* VersionExtractor.swift in Sources */, C471E89728F9BB8F0021E251 /* VersionExtractor.swift in Sources */,
C47DF1B2299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C4E2E86728FC2F1B003B070C /* XCPMApplication.swift in Sources */, C4E2E86728FC2F1B003B070C /* XCPMApplication.swift in Sources */,
C471E89828F9BB8F0021E251 /* ValetProxy.swift in Sources */, C471E89828F9BB8F0021E251 /* ValetProxy.swift in Sources */,
C471E89A28F9BB8F0021E251 /* DomainScanner.swift in Sources */, C471E89A28F9BB8F0021E251 /* DomainScanner.swift in Sources */,
@ -2350,7 +2403,6 @@
C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */, C471E8A928F9BB8F0021E251 /* InterAppHandler.swift in Sources */,
C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */, C471E8AA28F9BB8F0021E251 /* Startup.swift in Sources */,
C471E8AB28F9BB8F0021E251 /* EnvironmentCheck.swift in Sources */, C471E8AB28F9BB8F0021E251 /* EnvironmentCheck.swift in Sources */,
C471E8AC28F9BB8F0021E251 /* AppUpdateChecker.swift in Sources */,
C471E8AD28F9BB8F0021E251 /* AppVersion.swift in Sources */, C471E8AD28F9BB8F0021E251 /* AppVersion.swift in Sources */,
C471E8AE28F9BB8F0021E251 /* ServicesManager.swift in Sources */, C471E8AE28F9BB8F0021E251 /* ServicesManager.swift in Sources */,
C471E8AF28F9BB8F0021E251 /* EnvironmentManager.swift in Sources */, C471E8AF28F9BB8F0021E251 /* EnvironmentManager.swift in Sources */,
@ -2444,6 +2496,7 @@
C471E81028F9BAE80021E251 /* StringExtension.swift in Sources */, C471E81028F9BAE80021E251 /* StringExtension.swift in Sources */,
C471E7F828F9BACB0021E251 /* InternalSwitcher.swift in Sources */, C471E7F828F9BACB0021E251 /* InternalSwitcher.swift in Sources */,
C471E82328F9BB2E0021E251 /* ComposerJson.swift in Sources */, C471E82328F9BB2E0021E251 /* ComposerJson.swift in Sources */,
C491997E29901DF7001F3A21 /* CaskFile.swift in Sources */,
C471E82128F9BB2E0021E251 /* PhpFrameworks.swift in Sources */, C471E82128F9BB2E0021E251 /* PhpFrameworks.swift in Sources */,
C471E7EF28F9BAC30021E251 /* Actions.swift in Sources */, C471E7EF28F9BAC30021E251 /* Actions.swift in Sources */,
C471E82228F9BB2E0021E251 /* ComposerWindow.swift in Sources */, C471E82228F9BB2E0021E251 /* ComposerWindow.swift in Sources */,
@ -2454,9 +2507,11 @@
C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */, C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */,
C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */, C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */,
C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */, C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */,
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */,
C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */, C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */,
C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */, C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */,
C471E7F528F9BAC80021E251 /* PhpEnv.swift in Sources */, C471E7F528F9BAC80021E251 /* PhpEnv.swift in Sources */,
C491998329901E0F001F3A21 /* AppUpdater.swift in Sources */,
C471E7ED28F9BAC30021E251 /* Process.swift in Sources */, C471E7ED28F9BAC30021E251 /* Process.swift in Sources */,
C471E81128F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */, C471E81128F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */,
C471E7CC28F9BA5B0021E251 /* TestableShell.swift in Sources */, C471E7CC28F9BA5B0021E251 /* TestableShell.swift in Sources */,
@ -2524,6 +2579,7 @@
C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */, C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */, C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
C491997C29901DF7001F3A21 /* CaskFile.swift in Sources */,
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */, C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
C485707A28BF457800539B36 /* WarningListView.swift in Sources */, C485707A28BF457800539B36 /* WarningListView.swift in Sources */,
C4C0E8E827F88B41002D32A9 /* DomainScanner.swift in Sources */, C4C0E8E827F88B41002D32A9 /* DomainScanner.swift in Sources */,
@ -2539,6 +2595,7 @@
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */, C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
C451AFF72969E40F0078E617 /* HelpButton.swift in Sources */, C451AFF72969E40F0078E617 /* HelpButton.swift in Sources */,
C47DF1B0299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
C45B914A295607F400F4EC78 /* Service.swift in Sources */, C45B914A295607F400F4EC78 /* Service.swift in Sources */,
C4C0E8E327F88B13002D32A9 /* ValetDomainScanner.swift in Sources */, C4C0E8E327F88B13002D32A9 /* ValetDomainScanner.swift in Sources */,
@ -2570,10 +2627,12 @@
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */, C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */, C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
C4FD87A729AB98730002D701 /* PhpConfigChecker.swift in Sources */,
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */, C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */,
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */, C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */,
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
C491998129901E0F001F3A21 /* AppUpdater.swift in Sources */,
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
C4AD38B328ECD9D300FA8D83 /* TestableFileSystem.swift in Sources */, C4AD38B328ECD9D300FA8D83 /* TestableFileSystem.swift in Sources */,
@ -2600,7 +2659,6 @@
C4E2E86528FC2F1B003B070C /* XCPMApplication.swift in Sources */, C4E2E86528FC2F1B003B070C /* XCPMApplication.swift in Sources */,
C4E49DE828F764050026AC4E /* ActiveCommand.swift in Sources */, C4E49DE828F764050026AC4E /* ActiveCommand.swift in Sources */,
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */, C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */,
C485707D28BF45A200539B36 /* WarningView.swift in Sources */, C485707D28BF45A200539B36 /* WarningView.swift in Sources */,
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */, C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */, C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
@ -2623,6 +2681,7 @@
C40F505628ECA64E004AD45B /* TestableConfigurations.swift in Sources */, C40F505628ECA64E004AD45B /* TestableConfigurations.swift in Sources */,
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */, C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */,
C491997729901DD6001F3A21 /* CaskFileParserTest.swift in Sources */,
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */, C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */,
C4BF56AC2949381100379603 /* FakeValetInteractor.swift in Sources */, C4BF56AC2949381100379603 /* FakeValetInteractor.swift in Sources */,
C471E79428F9B23B0021E251 /* FileSystemProtocol.swift in Sources */, C471E79428F9B23B0021E251 /* FileSystemProtocol.swift in Sources */,
@ -2667,7 +2726,6 @@
C46EBC4B28DB966A007ACC74 /* TestableShell.swift in Sources */, C46EBC4B28DB966A007ACC74 /* TestableShell.swift in Sources */,
C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */, C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */,
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */, C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -2756,7 +2814,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@ -2813,7 +2871,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
SDKROOT = macosx; SDKROOT = macosx;
@ -2831,19 +2889,20 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1040; CURRENT_PROJECT_VERSION = 1077;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG = YES; DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist; INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.7.4; MARKETING_VERSION = 5.8;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -2860,19 +2919,20 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1040; CURRENT_PROJECT_VERSION = 1077;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG = NO; DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist; INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.7.4; MARKETING_VERSION = 5.8;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -3070,7 +3130,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
SDKROOT = macosx; SDKROOT = macosx;
@ -3082,24 +3142,25 @@
C4975D0828CD190C00FFB4E8 /* Release.Dev */ = { C4975D0828CD190C00FFB4E8 /* Release.Dev */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconEA; ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor;
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1040; CURRENT_PROJECT_VERSION = 1077;
DEBUG = NO; DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist; INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor DEV";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.7.4; MARKETING_VERSION = 5.8;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev;
PRODUCT_NAME = "$(TARGET_NAME) DEV"; PRODUCT_NAME = "$(TARGET_NAME) DEV";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -3179,7 +3240,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@ -3192,24 +3253,25 @@
C4975D0B28CD193A00FFB4E8 /* Debug.Dev */ = { C4975D0B28CD193A00FFB4E8 /* Debug.Dev */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconEA; ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor;
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements; CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1040; CURRENT_PROJECT_VERSION = 1077;
DEBUG = YES; DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist; INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor DEV";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.7.4; MARKETING_VERSION = 5.8;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -91,7 +91,7 @@
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "--cli" argument = "--cli"
isEnabled = "YES"> isEnabled = "NO">
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "--configuration:~/.phpmon_fconf_working.json" argument = "--configuration:~/.phpmon_fconf_working.json"

View File

@ -5,15 +5,13 @@
**PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up before you can use this app</u> (consult the FAQ below with info about how to set up your environment). **PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up before you can use this app</u> (consult the FAQ below with info about how to set up your environment).
<img src="./docs/screenshot.jpg#gh-light-mode-only" width="1280px" alt="phpmon screenshot (menu bar app)"/> <img src="./docs/screenshot.jpg" width="1280px" alt="phpmon screenshot (menu bar app)"/>
<img src="./docs/screenshot-dark.jpg#gh-dark-mode-only" width="1280px" alt="phpmon screenshot (menu bar app)"/>
<small><i>Screenshot: Showing the key functionality of PHP Monitor.</i></small> <small><i>Screenshot: Showing the key functionality of PHP Monitor.</i></small>
It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)! It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
<img src="./docs/notification.png#gh-light-mode-only" width="370px" alt="phpmon screenshot (notification)"/> <img src="./docs/notification.png" width="370px" alt="phpmon screenshot (notification)"/>
<img src="./docs/notification-dark.png#gh-dark-mode-only" width="370px" alt="phpmon screenshot (notification)"/>
PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more). PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
@ -24,7 +22,7 @@ You can also add new domains as links, isolate sites, manage various services, a
PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs. PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs.
* Your user account can administer your computer (required for some functionality, e.g. certificate generation) * Your user account can administer your computer (required for some functionality, e.g. certificate generation)
* macOS Monterey 12.4 or later (Ventura supported) * macOS 12.4 or later (Monterey and Ventura are supported)
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew` * Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
* Homebrew `php` formula is installed * Homebrew `php` formula is installed
* Laravel Valet (works with Valet v2, v3 and v4) * Laravel Valet (works with Valet v2, v3 and v4)
@ -43,22 +41,28 @@ valet install
valet trust valet trust
``` ```
Once that's done, you can install PHP Monitor via Homebrew (recommended), or (alternatively) you may download the latest release on GitHub. #### Manual installation (recommended, first time only)
To install via Homebrew, run: Once that's done, you can [download the latest release](https://github.com/nicoverbruggen/phpmon/releases/latest), unzip it and place it in `/Applications`.
#### Installation via Homebrew
*Prior to version 5.8, this was the recommended way of installing PHP Monitor.*
If you prefer to install the app via Homebrew, you can also run the following:
```sh ```sh
brew tap nicoverbruggen/homebrew-cask brew tap nicoverbruggen/homebrew-cask
brew install --cask phpmon brew install --cask phpmon
``` ```
To upgrade your existing installation, run: ## ⬆️ How to update
```sh The recommended method of updating the app to the latest version is to use **the built-in updater**.
brew upgrade phpmon
```
(You may need to run `brew update` or `brew update-reset` first in order to update the cask file if you ran a Homebrew operation recently.) If you have a very slow internet connection, the updater may report that the download has timed out. In that case, you may wish to manually update by [downloading the latest release](https://github.com/nicoverbruggen/phpmon/releases/latest) and placing the app in `/Applications`.
(You may also use Homebrew to update PHP Monitor, but this will require you to approve the app every time an update is installed. If you use the built-in updater, this won't be necessary.)
## ⚡️ Launchers (Alfred, Raycast) ## ⚡️ Launchers (Alfred, Raycast)
@ -273,6 +277,8 @@ This problem is usually resolved by upgrading Valet and running `valet install`
composer global update composer global update
valet install valet install
If you are seeing a 502 (Bad Gateway) error after about 30 seconds or so, your request is likely timing out. You may need to solve a performance issue with your own code.
</details> </details>

View File

@ -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 | Recommended Valet Version | | Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Recommended Valet Version |
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ---- | ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
| 5.7 | ✅ Universal binary | ✅ Yes | Monterey (12.4+)<br/>Ventura (13.0) | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.2 (w/ Valet 4.x*) | 3.0 or higher recommended<br/> 2.16.2 minimum | | 5.8 | ✅ Universal binary | ✅ Yes | Monterey (12.4+)<br/>Ventura (13.0+) | macOS 12.4 | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.2 (w/ Valet 4.x*) | 3.0 or higher recommended<br/> 2.16.2 minimum |
(*) Preliminary listing. Valet 4 hasn't been released yet and the versions of PHP Valet can work with might still change. (*) Preliminary listing. Valet 4 hasn't been released yet and the versions of PHP Valet can work with might still change.
@ -16,6 +16,7 @@ These versions of PHP Monitor are no longer supported, but if youre using an
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version | | Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ---- | ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
| 5.7 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0)* | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum |
| 5.6 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0)* | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum | | 5.6 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0)* | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum |
| 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 | | 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
| 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 | | 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 627 KiB

View File

@ -1,68 +0,0 @@
{
"images" : [
{
"filename" : "icon_16x16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "icon_16x16@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "icon_32x32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "icon_32x32@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "icon_128x128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "icon_128x128@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "icon_256x256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "icon_256x256@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "icon_512x512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "icon_512x512@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@ -16,16 +16,12 @@ public class Paths {
public static let shared = Paths() public static let shared = Paths()
internal var baseDir: Paths.HomebrewDir internal var baseDir: Paths.HomebrewDir
private var userName: String
private var userName: String! = nil
init() { init() {
baseDir = App.architecture != "x86_64" ? .opt : .usr baseDir = App.architecture != "x86_64" ? .opt : .usr
} userName = identity()
Log.info("[ID] The current username is `\(userName)`.")
public func loadUser() async {
let output = await Shell.pipe("id -un").out
userName = String(output.split(separator: "\n")[0])
} }
public func detectBinaryPaths() { public func detectBinaryPaths() {
@ -90,6 +86,11 @@ public class Paths {
return "\(shared.baseDir.rawValue)/etc" return "\(shared.baseDir.rawValue)/etc"
} }
public static var caskroomPath: String {
return "\(shared.baseDir.rawValue)/Caskroom/"
+ (App.identifier.contains(".dev") ? "phpmon-dev" : "phpmon")
}
// MARK: - Flexible Binaries // MARK: - Flexible Binaries
// (these can be in multiple locations, so we scan common places because) // (these can be in multiple locations, so we scan common places because)
// (PHP Monitor will not use the user's own PATH) // (PHP Monitor will not use the user's own PATH)

View File

@ -41,21 +41,24 @@ class RealFileSystem: FileSystemProtocol {
} }
func getShallowContentsOfDirectory(_ path: String) throws -> [String] { func getShallowContentsOfDirectory(_ path: String) throws -> [String] {
return try FileManager.default.contentsOfDirectory(atPath: path) return try FileManager.default.contentsOfDirectory(atPath: path.replacingTildeWithHomeDirectory)
} }
func getDestinationOfSymlink(_ path: String) throws -> String { func getDestinationOfSymlink(_ path: String) throws -> String {
return try FileManager.default.destinationOfSymbolicLink(atPath: path) return try FileManager.default.destinationOfSymbolicLink(atPath: path.replacingTildeWithHomeDirectory)
} }
// MARK: - Move & Delete Files // MARK: - Move & Delete Files
func move(from path: String, to newPath: String) throws { func move(from path: String, to newPath: String) throws {
try FileManager.default.moveItem(atPath: path, toPath: newPath) try FileManager.default.moveItem(
atPath: path.replacingTildeWithHomeDirectory,
toPath: newPath.replacingTildeWithHomeDirectory
)
} }
func remove(_ path: String) throws { func remove(_ path: String) throws {
try FileManager.default.removeItem(atPath: path) try FileManager.default.removeItem(atPath: path.replacingTildeWithHomeDirectory)
} }
// MARK: FS Attributes // MARK: FS Attributes

View File

@ -10,8 +10,8 @@ import UserNotifications
class LocalNotification { class LocalNotification {
@MainActor public static func send(title: String, subtitle: String, preference: PreferenceName) { @MainActor public static func send(title: String, subtitle: String, preference: PreferenceName?) {
if !Preferences.isEnabled(preference) { if preference != nil && !Preferences.isEnabled(preference!) {
return return
} }

View File

@ -0,0 +1,25 @@
//
// LoginItemManager.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 15/02/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import AppKit
import ServiceManagement
@available(macOS 13.0, *)
class LoginItemManager {
func loginItemIsEnabled() -> Bool {
return SMAppService.mainApp.status == .enabled
}
func disableLoginItem() {
try? SMAppService.mainApp.unregister()
}
func enableLoginItem() {
try? SMAppService.mainApp.register()
}
}

View File

@ -26,3 +26,31 @@ public func system(_ command: String) -> String {
return output return output
} }
/** Same as the `system` command, but does not return the output. */
public func system_quiet(_ command: String) {
_ = system(command)
}
/**
Retrieves the username for the currently signed in user via `/usr/bin/id`.
This cannot fail or the application will crash.
*/
public func identity() -> String {
let task = Process()
task.launchPath = "/usr/bin/id"
task.arguments = ["-un"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
guard let output = String(
data: pipe.fileHandleForReading.readDataToEndOfFile(),
encoding: String.Encoding.utf8
) else {
fatalError("Could not retrieve username via `id -un`!")
}
return output.trimmingCharacters(in: .whitespacesAndNewlines)
}

View File

@ -35,6 +35,7 @@ public struct PhpVersionNumberCollection: Equatable {
- Parameter strict: Whether the patch version check is strict. See more below. - Parameter strict: Whether the patch 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. The strict mode does not matter if a patch version is provided for all versions in the collection.
It also does not matter for certain comparisons (e.g. when dealing with wildcards).
Strict mode assumes that any PHP version lacking precise patch information, e.g. inferred 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, from Homebrew corresponds to the .0 patch version of that version. The default, which is imprecise,
@ -45,6 +46,7 @@ public struct PhpVersionNumberCollection: Equatable {
Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in strict mode only 8.1.? will 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). 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 (with patch precision), use When checking against actual PHP versions installed by the user (with patch precision), use
strict mode. strict mode.
@ -52,11 +54,26 @@ public struct PhpVersionNumberCollection: Equatable {
Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in non-strict mode version 8.0 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. 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). 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 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.) 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) -> [VersionNumber] { public func matching(constraint: String, strict: Bool = false) -> [VersionNumber] {
if constraint == "*" {
return self.versions
}
if let version = VersionNumber.make(from: constraint, type: .wildCardPatch) {
// Wildcard for patch (e.g. "7.4.*") must match major and minor (any patch)
return self.versions.filter { $0.hasSameMajorAndMinor(version) }
}
if let version = VersionNumber.make(from: constraint, type: .wildCardMinor) {
// Strict constraint (e.g. "7.*") -> must only match major (any patch, minor)
return self.versions.filter { $0.isSameMajorVersionAs(version) }
}
if let version = VersionNumber.make(from: constraint, type: .versionOnly) { if let version = VersionNumber.make(from: constraint, type: .versionOnly) {
// Strict constraint (e.g. "7.0") -> returns specific version // Strict constraint (e.g. "7.0") -> returns specific version
return self.versions.filter { $0.isSameAs(version, strict) } return self.versions.filter { $0.isSameAs(version, strict) }

View File

@ -39,6 +39,8 @@ public struct VersionNumber: Equatable, Hashable {
public enum MatchType: String { public enum MatchType: String {
case versionOnly = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case versionOnly = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case wildCardPatch = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\*)?\z"#
case wildCardMinor = #"^(?<major>\d+).(?<minor>\*)?\z"#
case caretVersionRange = #"^\^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case caretVersionRange = #"^\^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
@ -64,21 +66,25 @@ public struct VersionNumber: Equatable, Hashable {
range: NSRange(location: 0, length: versionString.count) range: NSRange(location: 0, length: versionString.count)
).first ).first
if match != nil { guard let match else { return nil }
let major = Int(
versionString[Range(match!.range(withName: "major"), in: versionString)!] let major = Int(versionString[Range(match.range(withName: "major"), in: versionString)!])!
)! var minor: Int = 0
let minor = Int( var patch: Int?
versionString[Range(match!.range(withName: "minor"), in: versionString)!]
)! if let minorRange = Range(match.range(withName: "minor"), in: versionString) {
var patch: Int? let value = versionString[minorRange] as String
if let minorRange = Range(match!.range(withName: "patch"), in: versionString) { // Zero is the fallback if a wildcard was used
patch = Int(versionString[minorRange]) minor = Int(value) ?? 0
}
return Self(major: major, minor: minor, patch: patch)
} }
return nil if let patchRange = Range(match.range(withName: "patch"), in: versionString) {
let value = versionString[patchRange] as String
// nil is the fallback if a wildcard was used
patch = Int(value) ?? nil
}
return Self(major: major, minor: minor, patch: patch)
} }
// MARK: Comparison Logic // MARK: Comparison Logic
@ -93,6 +99,10 @@ public struct VersionNumber: Equatable, Hashable {
&& (strict ? self.patch(strict, version) == version.patch(strict) : true) && (strict ? self.patch(strict, version) == version.patch(strict) : true)
} }
internal func hasSameMajorAndMinor(_ version: VersionNumber) -> Bool {
return self.major == version.major && self.minor == version.minor
}
internal func isNewerThan(_ version: VersionNumber, _ strict: Bool) -> Bool { internal func isNewerThan(_ version: VersionNumber, _ strict: Bool) -> Bool {
return ( return (
self.major > version.major || self.major > version.major ||

View File

@ -22,7 +22,6 @@ class InternalSwitcher: PhpSwitcher {
*/ */
func performSwitch(to version: String) async { func performSwitch(to version: String) async {
Log.info("Switching to \(version), unlinking all versions...") Log.info("Switching to \(version), unlinking all versions...")
let versions = getVersionsToBeHandled(version) let versions = getVersionsToBeHandled(version)
await withTaskGroup(of: String.self, body: { group in await withTaskGroup(of: String.self, body: { group in

View File

@ -115,14 +115,12 @@ class RealShell: ShellProtocol {
)! )!
if Log.shared.verbosity == .cli { if Log.shared.verbosity == .cli {
var args = task.arguments var args = task.arguments ?? []
let last: String = "\"" + (args?.popLast() ?? "") + "\"" let last = "\"" + (args.popLast() ?? "") + "\""
let concat = [self.launchPath] + task.arguments! + [last]
let command = concat.joined(separator: " ")
var log = """ var log = """
<~~~~~~~~~~~~~~~~~~~~~~~ <~~~~~~~~~~~~~~~~~~~~~~~
$ \(command) $ \(([self.launchPath] + args + [last]).joined(separator: " "))
[OUT]: [OUT]:
\(stdOut) \(stdOut)

View File

@ -13,6 +13,7 @@ public struct TestableConfiguration: Codable {
var filesystem: [String: FakeFile] var filesystem: [String: FakeFile]
var shellOutput: [String: BatchFakeShellOutput] var shellOutput: [String: BatchFakeShellOutput]
var commandOutput: [String: String] var commandOutput: [String: String]
var preferenceOverrides: [PreferenceName: Bool]
func apply() { func apply() {
Log.separator() Log.separator()
@ -31,6 +32,10 @@ public struct TestableConfiguration: Codable {
ServicesManager.useFake() ServicesManager.useFake()
Log.info("Applying fake Valet domain interactor...") Log.info("Applying fake Valet domain interactor...")
ValetInteractor.useFake() ValetInteractor.useFake()
Log.info("Applying temporary preference overrides...")
preferenceOverrides.forEach { (key: PreferenceName, value: Any?) in
Preferences.shared.cachedPreferences[key] = value
}
} }
func toJson(pretty: Bool = false) -> String { func toJson(pretty: Bool = false) -> String {

View File

@ -111,7 +111,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
// Make sure notifications will work // Make sure notifications will work
setupNotifications() setupNotifications()
Task { // Make sure the menu performs its initial checks Task { // Make sure the menu performs its initial checks
await paths.loadUser()
await menu.startup() await menu.startup()
} }
} }

View File

@ -1,182 +0,0 @@
//
// Updater.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/05/2022.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
import AppKit
class AppUpdateChecker {
public static var enabled: Bool = {
return Preferences.isEnabled(.automaticBackgroundUpdateCheck)
}()
public static var isDev: Bool = {
return App.version.contains("-dev")
}()
public static func retrieveVersionFromCask(
_ initiatedFromBackground: Bool = true
) async -> String {
let caskFile = App.version.contains("-dev")
? Constants.Urls.DevBuildCaskFile.absoluteString
: Constants.Urls.StableBuildCaskFile.absoluteString
var command = "curl -s"
if initiatedFromBackground {
command = "curl -s --max-time 5"
}
return await Shell.pipe(
"\(command) '\(caskFile)' | grep version"
).out
}
public static func checkIfNewerVersionIsAvailable(
initiatedFromBackground: Bool = true
) async {
if initiatedFromBackground {
if !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
Log.info("Automatic updates are disabled. No check will be performed.")
return
}
Log.info("Automatic updates are enabled, a check will be performed.")
}
let versionString = await retrieveVersionFromCask(initiatedFromBackground)
guard let onlineVersion = AppVersion.from(versionString) else {
Log.err("We couldn't check for updates!")
// Only notify about connection issues if the request to check for updates was explicit
if !initiatedFromBackground {
notifyAboutConnectionIssue()
}
return
}
let currentVersion = AppVersion.fromCurrentVersion()
handleVersionComparison(
currentVersion,
onlineVersion,
initiatedFromBackground
)
}
private static func handleVersionComparison(
_ currentVersion: AppVersion,
_ onlineVersion: AppVersion,
_ background: Bool
) {
switch onlineVersion.version.versionCompare(currentVersion.version) {
case .orderedAscending:
Log.info("You are running a newer version of PHP Monitor "
+ "(\(currentVersion.computerReadable) > \(onlineVersion.computerReadable)).")
if !background { notifyVersionDoesNotNeedUpgrade() }
case .orderedDescending:
Log.info("There is a newer version (\(onlineVersion)) available! "
+ "(\(onlineVersion.computerReadable) > \(currentVersion.computerReadable))")
notifyAboutNewerVersion(version: onlineVersion)
case .orderedSame:
if currentVersion.build != nil
&& onlineVersion.build != nil
&& buildDiffers(currentVersion, onlineVersion, background) {
return
}
Log.info("The installed version (\(currentVersion.computerReadable)) matches the latest release "
+ "(\(onlineVersion.computerReadable)).")
if !background { notifyVersionDoesNotNeedUpgrade() }
}
}
private static func buildDiffers(
_ currentVersion: AppVersion,
_ onlineVersion: AppVersion,
_ background: Bool
) -> Bool {
if Int(onlineVersion.build!)! > Int(currentVersion.build!)! {
Log.info("There is a newer build of PHP Monitor available! "
+ "(\(onlineVersion.computerReadable) > \(currentVersion.computerReadable))")
notifyAboutNewerVersion(version: onlineVersion)
return true
} else if Int(onlineVersion.build!)! < Int(currentVersion.build!)! {
Log.info("You are running a newer build of PHP Monitor "
+ "(\(currentVersion.computerReadable) > \(onlineVersion.computerReadable)).")
if !background { notifyVersionDoesNotNeedUpgrade() }
return true
}
return false
}
private static func notifyVersionDoesNotNeedUpgrade() {
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.is_latest_version.title".localized,
subtitle: "updater.alerts.is_latest_version.subtitle".localized(App.shortVersion),
description: ""
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
private static func notifyAboutNewerVersion(version: AppVersion) {
let devSuffix = isDev ? "-dev" : ""
let command = isDev ? "brew upgrade phpmon-dev" : "brew upgrade phpmon"
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.newer_version_available.title".localized(version.humanReadable),
subtitle: "updater.alerts.newer_version_available.subtitle".localized,
description: HomebrewDiagnostics.customCaskInstalled
? "updater.installation_source.brew".localized(command)
: "updater.installation_source.direct".localized
)
.withPrimary(
text: "updater.alerts.buttons.release_notes".localized,
action: { vc in
vc.close(with: .OK)
NSWorkspace.shared.open(
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.tagged)\(devSuffix)")
)
}
)
.withTertiary(text: "Dismiss", action: { vc in
vc.close(with: .OK)
})
.show()
}
}
private static func notifyAboutConnectionIssue() {
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.cannot_check_for_update.title".localized,
subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized,
description: "updater.alerts.cannot_check_for_update.description".localized(
App.version
)
)
.withTertiary(
text: "updater.alerts.buttons.releases_on_github".localized,
action: { _ in
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
}
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
}

View File

@ -0,0 +1,199 @@
//
// AppUpdater.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 04/02/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
import Cocoa
class AppUpdater {
var caskFile: CaskFile!
var latestVersionOnline: AppVersion!
var interactive: Bool = false
public func checkForUpdates(userInitiated: Bool) async {
self.interactive = userInitiated
if !interactive && !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
Log.info("Skipping automatic update check due to user preference.")
return
}
Log.info("The app will search for updates...")
let caskUrl = App.identifier.contains(".dev")
? Constants.Urls.DevBuildCaskFile
: Constants.Urls.StableBuildCaskFile
guard let caskFile = await CaskFile.from(url: caskUrl) else {
Log.err("The contents of the CaskFile at '\(caskUrl.absoluteString)' could not be retrieved.")
return presentCouldNotRetrieveUpdateIfInteractive()
}
self.caskFile = caskFile
let currentVersion = AppVersion.fromCurrentVersion()
guard let onlineVersion = AppVersion.from(caskFile.version) else {
Log.err("The version string from the CaskFile could not be read.")
return presentCouldNotRetrieveUpdateIfInteractive()
}
latestVersionOnline = onlineVersion
Log.info("The latest version read from '\(caskUrl.lastPathComponent)' is: v\(onlineVersion.computerReadable).")
if latestVersionOnline > currentVersion {
presentNewerVersionAvailableAlert()
} else if interactive {
presentNoNewerVersionAvailableAlert()
}
}
private func presentCouldNotRetrieveUpdateIfInteractive() {
if interactive {
return presentCouldNotRetrieveUpdate()
} else {
return
}
}
// MARK: - Alerts
public func presentNewerVersionAvailableAlert() {
let command = App.identifier.contains(".dev")
? "brew upgrade phpmon-dev"
: "brew upgrade phpmon"
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.newer_version_available.title"
.localized(latestVersionOnline.humanReadable),
subtitle: "updater.alerts.newer_version_available.subtitle"
.localized,
description: HomebrewDiagnostics.customCaskInstalled
? "updater.installation_source.brew".localized(command)
: "updater.installation_source.direct".localized
)
.withPrimary(
text: "updater.alerts.buttons.install".localized,
action: { vc in
self.cleanupCaskroom()
self.prepareForDownload()
vc.close(with: .OK)
}
)
.withSecondary(
text: "updater.alerts.buttons.release_notes".localized,
action: { _ in
let urlSegments = self.caskFile.url.split(separator: "/")
let tag = urlSegments[urlSegments.count - 2] // ../download/{tag}/{file.zip}
NSWorkspace.shared.open(
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/\(tag)")
)
}
)
.withTertiary(text: "updater.alerts.buttons.dismiss".localized, action: { vc in
vc.close(with: .OK)
})
.show()
}
}
public func presentNoNewerVersionAvailableAlert() {
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.is_latest_version.title".localized,
subtitle: "updater.alerts.is_latest_version.subtitle".localized(App.shortVersion),
description: ""
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
public func presentCouldNotRetrieveUpdate() {
Task { @MainActor in
BetterAlert().withInformation(
title: "updater.alerts.cannot_check_for_update.title".localized,
subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized,
description: "updater.alerts.cannot_check_for_update.description".localized(
App.version
)
)
.withTertiary(
text: "updater.alerts.buttons.releases_on_github".localized,
action: { _ in
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
}
)
.withPrimary(text: "generic.ok".localized)
.show()
}
}
// MARK: - Preparing for Self-Updater
private func prepareForDownload() {
let updater = Bundle.main.resourceURL!.path + "/PHP Monitor Self-Updater.app"
system_quiet("mkdir -p ~/.config/phpmon/updater 2> /dev/null")
let updaterDirectory = "~/.config/phpmon/updater"
.replacingOccurrences(of: "~", with: NSHomeDirectory())
system_quiet("cp -R \"\(updater)\" \"\(updaterDirectory)/PHP Monitor Self-Updater.app\"")
try! FileSystem.writeAtomicallyToFile(
"\(updaterDirectory)/update.json",
content: "{ \"url\": \"\(caskFile.url)\", \"sha256\": \"\(caskFile.sha256)\" }"
)
let updaterUrl = NSURL(fileURLWithPath: updater, isDirectory: true) as URL
let configuration = NSWorkspace.OpenConfiguration()
NSWorkspace.shared.openApplication(at: updaterUrl, configuration: configuration) { _, _ in
Log.info("The updater has been launched successfully!")
}
}
private func cleanupCaskroom() {
let path = Paths.caskroomPath
if FileSystem.directoryExists(path) {
Log.info("Removing the Caskroom directory for PHP Monitor...")
do {
try FileSystem.remove(path)
Log.info("Removed the Caskroom directory at `\(path)`.")
} catch {
Log.err("Automatically removing the Caskroom directory at `\(path)` failed.")
}
}
}
// MARK: - Checking if Self-Updater Worked
public static func checkIfUpdateWasPerformed() {
// Cleanup the upgrade.success file
if FileSystem.fileExists("~/.config/phpmon/updater/upgrade.success") {
Task { @MainActor in
LocalNotification.send(
title: "notification.phpmon_updated.title".localized,
subtitle: "notification.phpmon_updated.desc".localized(App.shortVersion),
preference: nil
)
}
Log.info("The `upgrade.success` file was found! An update was installed. Cleaning up...")
try? FileSystem.remove("~/.config/phpmon/updater/upgrade.success")
}
// Cleanup the previous updater
if FileSystem.anyExists("~/.config/phpmon/updater/PHP Monitor Self-Updater.app") {
Log.info("A remnant of the self-updater must still be removed...")
try? FileSystem.remove("~/.config/phpmon/updater/PHP Monitor Self-Updater.app")
}
}
}

View File

@ -8,14 +8,14 @@
import Foundation import Foundation
class AppVersion { class AppVersion: Comparable {
var version: String var version: String
var build: String? var build: Int?
var suffix: String? var suffix: String?
init(version: String, build: String?, suffix: String? = nil) { init(version: String, build: String?, suffix: String? = nil) {
self.version = version self.version = version
self.build = build self.build = build == nil ? nil : Int(build!)
self.suffix = suffix self.suffix = suffix
} }
@ -75,11 +75,27 @@ class AppVersion {
} }
var computerReadable: String { var computerReadable: String {
return "\(version)_\(build ?? "0")" return "\(version)_\(build ?? 0)"
} }
var humanReadable: String { var humanReadable: String {
return "\(version) (\(build ?? "???"))" return "\(version) (\(build ?? 0))"
} }
// MARK: - Comparable Protocol
static func < (lhs: AppVersion, rhs: AppVersion) -> Bool {
let comparisonResult = lhs.version.versionCompare(rhs.version)
if comparisonResult == .orderedAscending {
return true
}
return lhs.build ?? 0 < rhs.build ?? 0
}
static func == (lhs: AppVersion, rhs: AppVersion) -> Bool {
lhs.version.versionCompare(rhs.version) == .orderedSame
&& lhs.build == rhs.build
}
} }

View File

@ -0,0 +1,81 @@
//
// CaskFile.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 04/02/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
struct CaskFile {
var properties: [String: String]
var name: String {
return self.properties["name"]!
}
var url: String {
return self.properties["url"]!
}
var sha256: String {
return self.properties["sha256"]!
}
var version: String {
return self.properties["version"]!
}
public static func from(url: URL) async -> CaskFile? {
var string: String?
if url.scheme == "file" {
string = try? String(contentsOf: url)
} else {
string = await Shell.pipe("curl -s --max-time 10 '\(url.absoluteString)'").out
}
guard let string else {
Log.err("The content of the URL for the CaskFile could not be retrieved")
return nil
}
let lines = string.split(separator: "\n")
.map { line in
return line.trimmingCharacters(in: .whitespacesAndNewlines)
}
.filter { $0 != "" }
if lines.count < 4 {
Log.err("The CaskFile is <4 lines long, which is too short")
return nil
}
if !lines.first!.starts(with: "cask") || !lines.last!.starts(with: "end") {
Log.err("The CaskFile does not start with 'cask' or does not end with 'end'")
return nil
}
var props: [String: String] = [:]
let regex = try! NSRegularExpression(pattern: "(\\w+)\\s+'([^']+)'")
for line in lines {
if let match = regex.firstMatch(
in: String(line),
range: NSRange(location: 0, length: line.utf16.count)
) {
let keyRange = match.range(at: 1)
let valueRange = match.range(at: 2)
let key = (line as NSString).substring(with: keyRange)
let value = (line as NSString).substring(with: valueRange)
props[key] = value
}
}
for required in ["version", "sha256", "url", "name"] where !props.keys.contains(required) {
Log.err("Property '\(required)' expected on CaskFile, assuming CaskFile is invalid")
return nil
}
return CaskFile(properties: props)
}
}

View File

@ -32,6 +32,7 @@ class HomebrewDiagnostics {
*/ */
public static var customCaskInstalled: Bool = { public static var customCaskInstalled: Bool = {
return installedTaps.contains("nicoverbruggen/cask") return installedTaps.contains("nicoverbruggen/cask")
&& FileSystem.directoryExists(Paths.caskroomPath)
}() }()
/** /**

View File

@ -39,8 +39,8 @@ extension MainMenu {
// Determine install method // Determine install method
Log.info(HomebrewDiagnostics.customCaskInstalled Log.info(HomebrewDiagnostics.customCaskInstalled
? "[BREW] The app has probably been installed via Homebrew Cask." ? "[BREW] The app has been installed via Homebrew Cask."
: "[BREW] The app has probably been installed directly." : "[BREW] The app has been installed directly (optimal)."
) )
Log.info(HomebrewDiagnostics.usesNginxFullFormula Log.info(HomebrewDiagnostics.usesNginxFullFormula
@ -110,14 +110,17 @@ extension MainMenu {
Task { @MainActor in Task { @MainActor in
OnboardingWindowController.show() OnboardingWindowController.show()
} }
} else {
await AppUpdater().checkForUpdates(userInitiated: false)
} }
await AppUpdateChecker.checkIfNewerVersionIsAvailable()
} }
// Check if the linked version has changed between launches of phpmon // Check if the linked version has changed between launches of phpmon
Stats.evaluateLastLinkedPhpVersion() Stats.evaluateLastLinkedPhpVersion()
// Check if an update was performed earlier
AppUpdater.checkIfUpdateWasPerformed()
// We are ready! // We are ready!
Log.info("PHP Monitor is ready to serve!") Log.info("PHP Monitor is ready to serve!")
} }

View File

@ -193,7 +193,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
} }
@objc func checkForUpdates() { @objc func checkForUpdates() {
Task { await AppUpdateChecker.checkIfNewerVersionIsAvailable(initiatedFromBackground: false) } Task { await AppUpdater().checkForUpdates(userInitiated: true) }
} }
// MARK: - Menu Delegate // MARK: - Menu Delegate

View File

@ -9,6 +9,7 @@
import Foundation import Foundation
import Cocoa import Cocoa
@MainActor
class BetterAlert { class BetterAlert {
var windowController: NSWindowController! var windowController: NSWindowController!

View File

@ -42,4 +42,13 @@ class OnboardingWindowController: PMWindowController {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
override func close() {
super.close()
// Search for updates after closing the window
if Stats.successfulLaunchCount == 1 {
Task { await AppUpdater().checkForUpdates(userInitiated: false) }
}
}
} }

View File

@ -9,7 +9,7 @@
/** /**
These are the keys used for every preference in the app. These are the keys used for every preference in the app.
*/ */
enum PreferenceName: String { enum PreferenceName: String, Codable {
// FIRST-TIME LAUNCH // FIRST-TIME LAUNCH
case wasLaunchedBefore = "launched_before" case wasLaunchedBefore = "launched_before"

View File

@ -243,6 +243,10 @@ class GeneralPreferencesVC: GenericPreferenceVC {
vc.getAutomaticUpdateCheckPV() vc.getAutomaticUpdateCheckPV()
] ]
if #available(macOS 13, *) {
vc.views.append(CheckboxPreferenceView.makeLoginItemView())
}
return vc return vc
} }
} }

View File

@ -142,42 +142,50 @@ class Stats {
} }
public static func evaluateLastLinkedPhpVersion() { public static func evaluateLastLinkedPhpVersion() {
let currentVersion = PhpEnv.phpInstall.version.short let currentVersion = PhpEnv.phpInstall.version?.short ?? ""
let previousVersion = Stats.lastGlobalPhpVersion let previousVersion = Stats.lastGlobalPhpVersion
// Save the PHP version that is currently in use (only if unknown) if currentVersion == "" {
if Stats.lastGlobalPhpVersion == "" { return Log.warn("<PG> PHP Guard is unable to determine the current PHP version!")
}
Log.info("<PG> The currently linked version of PHP is: \(currentVersion).")
if previousVersion == "" {
Stats.persistCurrentGlobalPhpVersion(version: currentVersion) Stats.persistCurrentGlobalPhpVersion(version: currentVersion)
Log.info("Persisting the currently linked PHP version (first time only).") return Log.warn("<PG> PHP Guard is saving the currently linked PHP version (first time only).")
} else { }
Log.info("Previously, the globally linked PHP version was: \(previousVersion).") Log.info("<PG> Previously, the globally linked PHP version was: \(previousVersion).")
if previousVersion != currentVersion {
Log.info("Currently, that version is: \(currentVersion). This is a mismatch.") if previousVersion == currentVersion {
Task { @MainActor in return Log.info("<PG> PHP Guard did not notice any changes in the linked PHP version.")
BetterAlert() }
.withInformation(
title: "startup.version_mismatch.title".localized, // At this point, the version is *not* a match
subtitle: "startup.version_mismatch.subtitle".localized( Log.info("<PG> PHP Guard noticed a different PHP version. An alert will be displayed!")
currentVersion,
previousVersion Task { @MainActor in
), BetterAlert()
description: "startup.version_mismatch.desc".localized() .withInformation(
) title: "startup.version_mismatch.title".localized,
.withPrimary(text: "startup.version_mismatch.button_switch_back".localized( subtitle: "startup.version_mismatch.subtitle".localized(
previousVersion currentVersion,
), action: { alert in previousVersion
alert.close(with: .OK) ),
Task { MainMenu.shared.switchToAnyPhpVersion(previousVersion) } description: "startup.version_mismatch.desc".localized()
}) )
.withTertiary(text: "startup.version_mismatch.button_stay".localized( .withPrimary(text: "startup.version_mismatch.button_switch_back".localized(
currentVersion previousVersion
), action: { alert in ), action: { alert in
Stats.persistCurrentGlobalPhpVersion(version: currentVersion) alert.close(with: .OK)
alert.close(with: .OK) Task { MainMenu.shared.switchToAnyPhpVersion(previousVersion) }
}) })
.show() .withTertiary(text: "startup.version_mismatch.button_stay".localized(
} currentVersion
} ), action: { alert in
Stats.persistCurrentGlobalPhpVersion(version: currentVersion)
alert.close(with: .OK)
})
.show()
} }
} }
} }

View File

@ -10,18 +10,12 @@ import Foundation
import Cocoa import Cocoa
class CheckboxPreferenceView: NSView, XibLoadable { class CheckboxPreferenceView: NSView, XibLoadable {
@IBOutlet weak var labelSection: NSTextField! @IBOutlet weak var labelSection: NSTextField!
@IBOutlet weak var labelDescription: NSTextField! @IBOutlet weak var labelDescription: NSTextField!
@IBOutlet weak var buttonCheckbox: NSButton! @IBOutlet weak var buttonCheckbox: NSButton!
var action: (() -> Void)! var action: (() -> Void)!
var behavior: CheckboxPreferenceViewBehavior!
var preference: PreferenceName! {
didSet {
self.buttonCheckbox.state = Preferences.isEnabled(self.preference) ? .on : .off
}
}
static func make( static func make(
sectionText: String, sectionText: String,
@ -31,17 +25,75 @@ class CheckboxPreferenceView: NSView, XibLoadable {
action: @escaping () -> Void action: @escaping () -> Void
) -> NSView { ) -> NSView {
let view = Self.createFromXib()! let view = Self.createFromXib()!
view.behavior = CheckboxPreferenceBehavior(
button: view.buttonCheckbox,
preference: preference
)
view.labelSection.stringValue = sectionText view.labelSection.stringValue = sectionText
view.labelDescription.stringValue = descriptionText view.labelDescription.stringValue = descriptionText
view.buttonCheckbox.title = checkboxText view.buttonCheckbox.title = checkboxText
view.preference = preference
view.action = action view.action = action
return view return view
} }
@IBAction func toggled(_ sender: Any) { @available(macOS 13.0, *)
Preferences.update(self.preference, value: buttonCheckbox.state == .on) static func makeLoginItemView() -> NSView {
self.action() let view = Self.createFromXib()!
view.behavior = CheckboxLaunchItemBehavior(button: view.buttonCheckbox)
view.labelSection.stringValue = "prefs.startup".localized
view.labelDescription.stringValue = "prefs.auto_start_desc".localized
view.buttonCheckbox.title = "prefs.auto_start_title".localized
view.action = {}
return view
} }
@IBAction func toggled(_ sender: Any) {
self.behavior.toggled(checked: buttonCheckbox.state == .on)
self.action()
}
}
protocol CheckboxPreferenceViewBehavior {
func toggled(checked: Bool)
}
class CheckboxPreferenceBehavior: CheckboxPreferenceViewBehavior {
var button: NSButton
var preference: PreferenceName
init(button: NSButton, preference: PreferenceName) {
self.preference = preference
self.button = button
self.button.state = Preferences.isEnabled(self.preference) ? .on : .off
}
public func toggled(checked: Bool) {
Preferences.update(self.preference, value: checked)
}
}
@available(macOS 13.0, *)
class CheckboxLaunchItemBehavior: CheckboxPreferenceViewBehavior {
var manager = LoginItemManager()
var button: NSButton
init(button: NSButton) {
self.button = button
if manager.loginItemIsEnabled() {
self.button.state = .on
} else {
self.button.state = .off
}
}
public func toggled(checked: Bool) {
if checked {
self.manager.enableLoginItem()
} else {
self.manager.disableLoginItem()
}
self.button.state = self.manager.loginItemIsEnabled() ? .on : .off
}
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>

View File

@ -9,10 +9,14 @@
import SwiftUI import SwiftUI
struct WarningListView: View { struct WarningListView: View {
@State var warnings: [Warning] @ObservedObject var warningManager: WarningManager
init(empty: Bool = false) { init(empty: Bool = false) {
self.warnings = empty ? [] : WarningManager.shared.warnings if empty {
WarningManager.shared.warnings = []
}
warningManager = WarningManager.shared
} }
var body: some View { var body: some View {
@ -40,7 +44,6 @@ struct WarningListView: View {
Button("warnings.refresh.button".localizedForSwiftUI) { Button("warnings.refresh.button".localizedForSwiftUI) {
Task { // Reload warnings Task { // Reload warnings
await WarningManager.shared.checkEnvironment() await WarningManager.shared.checkEnvironment()
self.warnings = WarningManager.shared.warnings
} }
} }
Text("warnings.refresh.button.description".localizedForSwiftUI) Text("warnings.refresh.button.description".localizedForSwiftUI)
@ -51,14 +54,14 @@ struct WarningListView: View {
List { List {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
if warnings.isEmpty { if warningManager.warnings.isEmpty {
NoWarningsView() NoWarningsView()
} else { } else {
ForEach(warnings) { warning in ForEach(warningManager.warnings) { warning in
Group { Group {
WarningView( WarningView(
title: warning.title, title: warning.title,
paragraphs: warning.paragraphs, paragraphs: warning.paragraphs(),
documentationUrl: warning.url documentationUrl: warning.url
) )
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
@ -67,7 +70,8 @@ struct WarningListView: View {
}.padding(5) }.padding(5)
} }
} }
}.frame(minHeight: 0, maxHeight: .infinity).padding(5) }
.frame(minHeight: 0, maxHeight: .infinity).padding(5)
} }
.listRowInsets(EdgeInsets()) .listRowInsets(EdgeInsets())
.listStyle(.plain) .listStyle(.plain)

View File

@ -26,7 +26,7 @@ struct WarningView: View {
Text(title.localizedForSwiftUI) Text(title.localizedForSwiftUI)
.fontWeight(.bold) .fontWeight(.bold)
ForEach(paragraphs, id: \.self) { paragraph in ForEach(paragraphs, id: \.self) { paragraph in
Text(paragraph.localizedForSwiftUI) Text(paragraph)
.font(.system(size: 13)) .font(.system(size: 13))
} }
} }

View File

@ -0,0 +1,39 @@
//
// PhpConfigChecker.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 24/02/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
class PhpConfigChecker {
public static var shared = PhpConfigChecker()
var missing: [String] = []
public func check() {
missing = []
let shouldExist = [
"php.ini",
"php-fpm.conf",
"php-fpm.d/valet-fpm.conf"
]
for version in PhpEnv.shared.availablePhpVersions {
for file in shouldExist {
let fullFilePath = Paths.etcPath.appending("/php/\(version)/\(file)")
if !FileSystem.fileExists(fullFilePath) {
missing.append(fullFilePath)
}
}
}
if !missing.isEmpty {
Log.warn("The following config file(s) were missing: \(missing)")
}
}
}

View File

@ -8,19 +8,27 @@
import Foundation import Foundation
struct Warning: Identifiable { struct Warning: Identifiable, Hashable {
var id = UUID() var id = UUID()
let command: () async -> Bool let command: () async -> Bool
let name: String let name: String
let title: String let title: String
let paragraphs: [String] let paragraphs: () -> [String]
let url: String? let url: String?
/**
- Parameters:
- command: The command that, if it returns true, means that a warning applies
- name: The internal name or description for this warning
- title: The title displayed for the user
- paragraphs: The main body of text displayed for the user
- url: The URL that one can navigate to for more information (if applicable)
*/
init( init(
command: @escaping () async -> Bool, command: @escaping () async -> Bool,
name: String, name: String,
title: String, title: String,
paragraphs: [String], paragraphs: @escaping () -> [String],
url: String? url: String?
) { ) {
self.command = command self.command = command
@ -33,4 +41,12 @@ struct Warning: Identifiable {
public func applies() async -> Bool { public func applies() async -> Bool {
return await self.command() return await self.command()
} }
public static func == (lhs: Warning, rhs: Warning) -> Bool {
return lhs.hashValue == rhs.hashValue
}
public func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
}
} }

View File

@ -9,9 +9,9 @@
import Foundation import Foundation
import Cocoa import Cocoa
class WarningManager { class WarningManager: ObservableObject {
static var shared = WarningManager() static var shared: WarningManager = WarningManager()
init() { init() {
if isRunningSwiftUIPreview { if isRunningSwiftUIPreview {
@ -26,8 +26,8 @@ class WarningManager {
.trimmingCharacters(in: .whitespacesAndNewlines) == "1" .trimmingCharacters(in: .whitespacesAndNewlines) == "1"
}, },
name: "Running PHP Monitor with Rosetta on M1", name: "Running PHP Monitor with Rosetta on M1",
title: "warnings.arm_compatibility.title", title: "warnings.arm_compatibility.title".localized,
paragraphs: ["warnings.arm_compatibility.description"], paragraphs: { return ["warnings.arm_compatibility.description".localized] },
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon" url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon"
), ),
Warning( Warning(
@ -36,13 +36,27 @@ class WarningManager {
!FileSystem.isWriteableFile("/usr/local/bin/") !FileSystem.isWriteableFile("/usr/local/bin/")
}, },
name: "Helpers cannot be symlinked and not in PATH", name: "Helpers cannot be symlinked and not in PATH",
title: "warnings.helper_permissions.title", title: "warnings.helper_permissions.title".localized,
paragraphs: [ paragraphs: { return [
"warnings.helper_permissions.description", "warnings.helper_permissions.description".localized,
"warnings.helper_permissions.unavailable", "warnings.helper_permissions.unavailable".localized,
"warnings.helper_permissions.symlink" "warnings.helper_permissions.symlink".localized
], ] },
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries" url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
),
Warning(
command: {
PhpConfigChecker.shared.check()
return !PhpConfigChecker.shared.missing.isEmpty
},
name: "Your PHP installation is missing configuration files",
title: "warnings.files_missing.title".localized,
paragraphs: { return [
"warnings.files_missing.description".localized(
PhpConfigChecker.shared.missing.joined(separator: "\n")
)
] },
url: nil
) )
] ]
@ -60,11 +74,11 @@ class WarningManager {
Checks the user's environment and checks if any special warnings apply. Checks the user's environment and checks if any special warnings apply.
*/ */
func checkEnvironment() async { func checkEnvironment() async {
self.warnings = []
if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil { if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil {
// For debugging purposes, we may wish to see all possible evaluations listed // For debugging purposes, we may wish to see all possible evaluations listed
self.warnings = self.evaluations Task { @MainActor in
self.warnings = self.evaluations
}
} else { } else {
// Otherwise, loop over the actual evaluations and list the warnings // Otherwise, loop over the actual evaluations and list the warnings
await loopOverEvaluations() await loopOverEvaluations()
@ -74,9 +88,14 @@ class WarningManager {
} }
private func loopOverEvaluations() async { private func loopOverEvaluations() async {
Task { @MainActor in
self.warnings = []
}
for check in self.evaluations where await check.applies() { for check in self.evaluations where await check.applies() {
Log.info("[DOCTOR] \(check.name) (!)") Log.info("[DOCTOR] \(check.name) (!)")
self.warnings.append(check) Task { @MainActor in
self.warnings.append(check)
}
continue continue
} }
} }

View File

@ -10,6 +10,22 @@
<string>https://github.com/nicoverbruggen/phpmon</string> <string>https://github.com/nicoverbruggen/phpmon</string>
<key>Connections</key> <key>Connections</key>
<array> <array>
<dict>
<key>IsIncoming</key>
<false/>
<key>Host</key>
<string>github.com, api.github.com</string>
<key>NetworkProtocol</key>
<string>TCP</string>
<key>Port</key>
<string>443</string>
<key>Relevance</key>
<string>Essential</string>
<key>Purpose</key>
<string>PHP Monitor directly invokes Homebrew which contacts GitHub. This happens when PHP Monitor asks for more information about the PHP formula to determine which version of PHP you&apos;ve got running.</string>
<key>DenyConsequences</key>
<string>If you deny these connections, PHP Monitor might not be able to complete its preset set of instructions, causing version switching to fail.</string>
</dict>
<dict> <dict>
<key>IsIncoming</key> <key>IsIncoming</key>
<false/> <false/>
@ -46,15 +62,15 @@
<key>IsIncoming</key> <key>IsIncoming</key>
<false/> <false/>
<key>Host</key> <key>Host</key>
<string>github.com, api.github.com</string> <string>formulae.brew.sh</string>
<key>NetworkProtocol</key> <key>NetworkProtocol</key>
<string>TCP</string> <string>TCP</string>
<key>Port</key> <key>Port</key>
<string>443</string> <string>80, 443</string>
<key>Relevance</key> <key>Relevance</key>
<string>Essential</string> <string>Essential</string>
<key>Purpose</key> <key>Purpose</key>
<string>PHP Monitor directly invokes Homebrew which contacts GitHub. This happens when PHP Monitor asks for more information about the PHP formula to determine which version of PHP you&apos;ve got running.</string> <string>PHP Monitor directly invokes Homebrew which contacts the Homebrew API. This happens when PHP Monitor asks for more information about the PHP formula to determine which version of PHP you&apos;ve got running.</string>
<key>DenyConsequences</key> <key>DenyConsequences</key>
<string>If you deny these connections, PHP Monitor might not be able to complete its preset set of instructions, causing version switching to fail.</string> <string>If you deny these connections, PHP Monitor might not be able to complete its preset set of instructions, causing version switching to fail.</string>
</dict> </dict>

View File

@ -40,7 +40,7 @@
<key>LSUIElement</key> <key>LSUIElement</key>
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2019-2022 Nico Verbruggen. All rights reserved.</string> <string>Copyright © 2019-2023 Nico Verbruggen. All rights reserved.</string>
<key>NSMainStoryboardFile</key> <key>NSMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>

View File

@ -254,6 +254,10 @@ This has no effect on other terminals, only for the particular terminal session
"prefs.notifications" = "Notifications:"; "prefs.notifications" = "Notifications:";
"prefs.warnings" = "Warnings:"; "prefs.warnings" = "Warnings:";
"prefs.menu_contents" = "Features in Menu:"; "prefs.menu_contents" = "Features in Menu:";
"prefs.startup" = "Startup:";
"prefs.auto_start_desc" = "Automatically starts PHP Monitor when you log into your Mac.";
"prefs.auto_start_title" = "Start PHP Monitor at login";
"prefs.icon_options.php" = "Display PHP Icon"; "prefs.icon_options.php" = "Display PHP Icon";
"prefs.icon_options.elephant" = "Display Elephant Icon"; "prefs.icon_options.elephant" = "Display Elephant Icon";
@ -355,6 +359,9 @@ This has no effect on other terminals, only for the particular terminal session
"notification.preset_reverted_title" = "Preset reverted"; "notification.preset_reverted_title" = "Preset reverted";
"notification.preset_reverted_desc" = "The last preset you applied has been undone. Your previous configuration is now active."; "notification.preset_reverted_desc" = "The last preset you applied has been undone. Your previous configuration is now active.";
"notification.phpmon_updated.title" = "PHP Monitor has been updated!";
"notification.phpmon_updated.desc" = "You are now running PHP Monitor v%@.";
// Composer Update // Composer Update
"alert.composer_missing.title" = "Composer not found!"; "alert.composer_missing.title" = "Composer not found!";
"alert.composer_missing.subtitle" = "PHP Monitor could not find Composer. Make sure that Composer is installed and try again."; "alert.composer_missing.subtitle" = "PHP Monitor could not find Composer. Make sure that Composer is installed and try again.";
@ -617,9 +624,8 @@ COMMON TROUBLESHOOTING TIPS
"updater.alerts.newer_version_available.title" = "PHP Monitor v%@ is now available!"; "updater.alerts.newer_version_available.title" = "PHP Monitor v%@ is now available!";
"updater.alerts.newer_version_available.subtitle" = "Keeping PHP Monitor up-to-date is highly recommended, since newer versions usually fix bugs and include fixes to support the latest versions of Valet and PHP."; "updater.alerts.newer_version_available.subtitle" = "Keeping PHP Monitor up-to-date is highly recommended, since newer versions usually fix bugs and include fixes to support the latest versions of Valet and PHP.";
"updater.alerts.newer_version_available.description" = "PHP Monitor is supposed to be updated via Homebrew, so there is no built-in updater. This check is only meant to inform you of the existence of a new version, you do not need to upgrade."; "updater.installation_source.brew" = "The recommended method of installing updates to PHP Monitor is to simply press “Install Update”.\n\nSince you used Homebrew to install the app, you can also upgrade via the terminal by running `%@`, but this is not recommended.\n\n(Please note that installing via this built-in updater will remove PHP Monitor from Homebrew's Caskroom directory, to prevent duplicate updates from being downloaded and causing potential issues later.)";
"updater.installation_source.brew" = "You appear to have installed PHP Monitor via Homebrew (or have at least tapped the required Caskfile) so it is recommended that you upgrade via the terminal by running `%@`."; "updater.installation_source.direct" = "The recommended method of installing updates to PHP Monitor is to simply press “Install Update”.";
"updater.installation_source.direct" = "You do not appear to have installed PHP Monitor via Homebrew, so you will need to visit GitHub to download the latest update.";
"updater.alerts.buttons.release_notes" = "View Release Notes"; "updater.alerts.buttons.release_notes" = "View Release Notes";
"updater.alerts.is_latest_version.title" = "PHP Monitor is up-to-date!"; "updater.alerts.is_latest_version.title" = "PHP Monitor is up-to-date!";
@ -629,6 +635,8 @@ COMMON TROUBLESHOOTING TIPS
"updater.alerts.cannot_check_for_update.subtitle" = "You might not be connected to the internet, are blocking traffic or GitHub is down and won't allow you to check for updates. If you keep seeing this message, you may want to manually check the releases page."; "updater.alerts.cannot_check_for_update.subtitle" = "You might not be connected to the internet, are blocking traffic or GitHub is down and won't allow you to check for updates. If you keep seeing this message, you may want to manually check the releases page.";
"updater.alerts.cannot_check_for_update.description" = "The currently installed version is: %@. You can go to the list of the latest releases (on GitHub) by clicking on the button on the left."; "updater.alerts.cannot_check_for_update.description" = "The currently installed version is: %@. You can go to the list of the latest releases (on GitHub) by clicking on the button on the left.";
"updater.alerts.buttons.releases_on_github" = "View Releases"; "updater.alerts.buttons.releases_on_github" = "View Releases";
"updater.alerts.buttons.install" = "Install Update";
"updater.alerts.buttons.dismiss" = "Dismiss";
// WARNINGS ABOUT NON-DEFAULT TLD // WARNINGS ABOUT NON-DEFAULT TLD
@ -653,6 +661,13 @@ COMMON TROUBLESHOOTING TIPS
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta."; "warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew."; "warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
"warnings.files_missing.title" = "Your PHP installation is missing important required configuration files.";
"warnings.files_missing.description" = "The following key configuration files should exist after installing PHP:
• %@
When files like these are missing, it's recommended to reinstall the appropriate PHP version(s) via Homebrew again, which should restore the configuration files that are missing. Missing configuration files can be the reason why you get '502 Bad Gateway' errors, even after running Fix My Valet.";
"warnings.none" = "There are no recommendations available for you right now. You're all good!"; "warnings.none" = "There are no recommendations available for you right now. You're all good!";
// ONBOARDING // ONBOARDING

View File

@ -138,8 +138,22 @@ class TestableConfigurations {
: .instant(ShellStrings.shared.brewServicesAsRoot), : .instant(ShellStrings.shared.brewServicesAsRoot),
"/opt/homebrew/bin/brew services info --all --json" "/opt/homebrew/bin/brew services info --all --json"
: .instant(ShellStrings.shared.brewServicesAsUser), : .instant(ShellStrings.shared.brewServicesAsUser),
"curl -s --max-time 5 '\(Constants.Urls.StableBuildCaskFile.absoluteString)' | grep version" "curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
: .instant("version '5.6.2_976'"), : .delayed(0.5, """
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '\(App.shortVersion)_\(App.bundleVersion)'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v\(App.shortVersion)/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end
"""),
"/opt/homebrew/bin/brew unlink php" "/opt/homebrew/bin/brew unlink php"
: .delayed(0.2, "OK"), : .delayed(0.2, "OK"),
"/opt/homebrew/bin/brew unlink php@8.2" "/opt/homebrew/bin/brew unlink php@8.2"
@ -170,7 +184,8 @@ class TestableConfigurations {
: """ : """
/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini, /opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini,
""" """
] ],
preferenceOverrides: [:]
) )
} }
} }

View File

@ -16,26 +16,14 @@ final class DomainsListTest: UITestCase {
override func tearDownWithError() throws {} override func tearDownWithError() throws {}
private func openMenu() -> XCPMApplication {
let app = XCPMApplication()
app.withConfiguration(TestableConfigurations.working)
app.launch()
// Note: If this fails here, make sure the menu bar item can be displayed
// If you use Bartender or something like this, this item may be hidden and tests will fail
app.statusItems.firstMatch.click()
return app
}
final func test_can_always_open_domains_list() throws { final func test_can_always_open_domains_list() throws {
let app = openMenu() let app = launch(openMenu: true)
app.menuItems["mi_domain_list".localized].click() app.menuItems["mi_domain_list".localized].click()
} }
final func test_can_filter_domains_list() throws { final func test_can_filter_domains_list() throws {
let app = openMenu() let app = launch(openMenu: true)
app.menuItems["mi_domain_list".localized].click() app.menuItems["mi_domain_list".localized].click()
@ -58,7 +46,7 @@ final class DomainsListTest: UITestCase {
} }
final func test_can_tap_add_domain_button() throws { final func test_can_tap_add_domain_button() throws {
let app = openMenu() let app = launch(openMenu: true)
app.menuItems["mi_domain_list".localized].click() app.menuItems["mi_domain_list".localized].click()

View File

@ -10,9 +10,33 @@ import XCTest
class UITestCase: XCTestCase { class UITestCase: XCTestCase {
/** Launches the app and opens the menu. */
public func launch(
openMenu: Bool = false,
with configuration: TestableConfiguration? = nil
) -> XCPMApplication {
let app = XCPMApplication()
let config = configuration ?? TestableConfigurations.working
app.withConfiguration(config)
app.launch()
// Note: If this fails here, make sure the menu bar item can be displayed
// If you use Bartender or something like this, this item may be hidden and tests will fail
if openMenu {
app.statusItems.firstMatch.click()
}
return app
}
/** Checks if a single element exists. */ /** Checks if a single element exists. */
public func assertExists(_ element: XCUIElement, _ timeout: TimeInterval = 0.05) { public func assertExists(_ element: XCUIElement, _ timeout: TimeInterval = 0.05) {
XCTAssert(element.waitForExistence(timeout: timeout)) XCTAssertTrue(element.waitForExistence(timeout: timeout))
}
/** Checks if a single element fails to exist. */
public func assertNotExists(_ element: XCUIElement, _ timeout: TimeInterval = 0.05) {
XCTAssertFalse(element.waitForExistence(timeout: timeout))
} }
/** Checks if all elements exist. */ /** Checks if all elements exist. */

View File

@ -0,0 +1,121 @@
//
// UpdateCheckTest.swift
// UI Tests
//
// Created by Nico Verbruggen on 13/03/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
final class UpdateCheckTest: UITestCase {
override func setUpWithError() throws {
continueAfterFailure = false
}
override func tearDownWithError() throws {}
final func test_can_check_for_updates_with_no_new_update() throws {
let app = launch(openMenu: true)
app.menuItems["mi_check_for_updates".localized].click()
assertExists(app.staticTexts["updater.alerts.is_latest_version.title".localized], 1.0)
assertExists(app.buttons["generic.ok".localized])
}
final func test_will_prompt_at_launch_new_version_available() throws {
var configuration = TestableConfigurations.working
// Ensure automatic check is enabled
configuration.preferenceOverrides[.automaticBackgroundUpdateCheck] = true
// Ensure an update is available
configuration.shellOutput[
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
] = .delayed(0.5, """
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '99.0.0_9999'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v99.0/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end
""")
let app = launch(openMenu: false, with: configuration)
// Expect to see the content of the appropriate alert box
assertExists(app.staticTexts["updater.alerts.newer_version_available.title".localized("99.0.0 (9999)")], 2)
assertExists(app.buttons["updater.alerts.buttons.install".localized])
assertExists(app.buttons["updater.alerts.buttons.dismiss".localized])
}
final func test_will_require_manual_search_for_update() throws {
var configuration = TestableConfigurations.working
// Ensure automatic check is disabled
configuration.preferenceOverrides[.automaticBackgroundUpdateCheck] = false
// Ensure an update is available
configuration.shellOutput[
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
] = .delayed(0.5, """
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '99.0.0_9999'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v99.0/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end
""")
// Wait for the menu to open and search for updates
let app = launch(openMenu: false, with: configuration)
// The check should not happen if the preference is disabled
assertNotExists(app.staticTexts["updater.alerts.newer_version_available.title".localized("99.0.0 (9999)")], 2)
// Open the menu and check manually
app.statusItems.firstMatch.click()
app.menuItems["mi_check_for_updates".localized].click()
// Expect to see the content of the appropriate alert box
assertExists(app.staticTexts["updater.alerts.newer_version_available.title".localized("99.0.0 (9999)")], 2)
assertExists(app.buttons["updater.alerts.buttons.install".localized])
assertExists(app.buttons["updater.alerts.buttons.dismiss".localized])
}
final func test_could_not_parse_version() throws {
var configuration = TestableConfigurations.working
// Ensure automatic check is disabled
configuration.preferenceOverrides[.automaticBackgroundUpdateCheck] = false
// Ensure an update is available
configuration.shellOutput[
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
] = .delayed(0.5, "404 PAGE NOT FOUND")
// Wait for the menu to open and search for updates
let app = launch(openMenu: true, with: configuration)
app.menuItems["mi_check_for_updates".localized].click()
// Expect to see the content of the appropriate alert box
assertExists(app.staticTexts["updater.alerts.cannot_check_for_update.title".localized], 2)
assertExists(app.buttons["generic.ok".localized])
assertExists(app.buttons["updater.alerts.buttons.releases_on_github".localized])
}
}

View File

@ -0,0 +1,53 @@
//
// CaskFileParserTest.swift
// Unit Tests
//
// Created by Nico Verbruggen on 04/02/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
class CaskFileParserTest: XCTestCase {
// MARK: - Test Files
static var exampleFilePath: URL {
return Bundle(for: Self.self)
.url(forResource: "phpmon-dev", withExtension: "rb")!
}
func test_can_extract_fields_from_cask_file() async throws {
guard let caskFile = await CaskFile.from(url: CaskFileParserTest.exampleFilePath) else {
return XCTFail("The CaskFile could not be parsed, check the log for more info")
}
XCTAssertEqual(
caskFile.version,
"5.7.2_1035"
)
XCTAssertEqual(
caskFile.sha256,
"1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a"
)
XCTAssertEqual(
caskFile.name,
"PHP Monitor DEV"
)
XCTAssertEqual(
caskFile.url,
"https://github.com/nicoverbruggen/phpmon/releases/download/v5.7.2/phpmon-dev.zip"
)
}
func test_can_extract_fields_from_remote_cask_file() async throws {
guard let caskFile = await CaskFile.from(url: Constants.Urls.StableBuildCaskFile) else {
return XCTFail("The remote CaskFile could not be parsed, check the log for more info")
}
XCTAssertTrue(caskFile.properties.keys.contains("version"))
XCTAssertTrue(caskFile.properties.keys.contains("homepage"))
XCTAssertTrue(caskFile.properties.keys.contains("url"))
XCTAssertTrue(caskFile.properties.keys.contains("appcast"))
}
}

View File

@ -0,0 +1,14 @@
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '5.7.2_1035'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v5.7.2/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end

View File

@ -1,47 +0,0 @@
//
// AppUpdaterCheckTest.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 10/05/2022.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
class AppUpdaterCheckTest: XCTestCase {
func test_can_retrieve_version_from_cask() async {
let caskVersion = await AppUpdateChecker.retrieveVersionFromCask()
let version = VersionExtractor.from(caskVersion)
XCTAssertNotNil(version)
}
func test_tagged_release_omits_zero_patch() {
let version = AppVersion.from("3.5.0_333")!
XCTAssertEqual(version.tagged, "3.5")
XCTAssertEqual(version.version, "3.5.0")
}
func test_tagged_release_doesnt_omit_non_zero_patch() {
let version = AppVersion.from("3.5.1_333")!
XCTAssertEqual(version.tagged, "3.5.1")
XCTAssertEqual(version.version, "3.5.1")
}
func test_tag_truncation_does_not_affect_major_versions() {
var version = AppVersion.from("5.0_333")!
XCTAssertEqual(version.tagged, "5.0")
XCTAssertEqual(version.version, "5.0")
version = AppVersion.from("5.0.0_333")!
XCTAssertEqual(version.tagged, "5.0")
XCTAssertEqual(version.version, "5.0.0")
}
}

View File

@ -28,7 +28,7 @@ class AppVersionTest: XCTestCase {
XCTAssertNotNil(version) XCTAssertNotNil(version)
XCTAssertEqual("1.0.0", version?.version) XCTAssertEqual("1.0.0", version?.version)
XCTAssertEqual("600", version?.build) XCTAssertEqual(600, version?.build)
XCTAssertEqual(nil, version?.suffix) XCTAssertEqual(nil, version?.suffix)
} }
@ -46,7 +46,7 @@ class AppVersionTest: XCTestCase {
XCTAssertNotNil(version) XCTAssertNotNil(version)
XCTAssertEqual("1.0.0", version?.version) XCTAssertEqual("1.0.0", version?.version)
XCTAssertEqual("870", version?.build) XCTAssertEqual(870, version?.build)
XCTAssertEqual("dev", version?.suffix) XCTAssertEqual("dev", version?.suffix)
} }
@ -55,8 +55,48 @@ class AppVersionTest: XCTestCase {
XCTAssertNotNil(version) XCTAssertNotNil(version)
XCTAssertEqual("1.0.0", version?.version) XCTAssertEqual("1.0.0", version?.version)
XCTAssertEqual("870", version?.build) XCTAssertEqual(870, version?.build)
XCTAssertEqual("dev", version?.suffix) XCTAssertEqual("dev", version?.suffix)
} }
func test_tagged_release_omits_zero_patch() {
let version = AppVersion.from("3.5.0_333")!
XCTAssertEqual(version.tagged, "3.5")
XCTAssertEqual(version.version, "3.5.0")
}
func test_tagged_release_doesnt_omit_non_zero_patch() {
let version = AppVersion.from("3.5.1_333")!
XCTAssertEqual(version.tagged, "3.5.1")
XCTAssertEqual(version.version, "3.5.1")
}
func test_tag_truncation_does_not_affect_major_versions() {
var version = AppVersion.from("5.0_333")!
XCTAssertEqual(version.tagged, "5.0")
XCTAssertEqual(version.version, "5.0")
version = AppVersion.from("5.0.0_333")!
XCTAssertEqual(version.tagged, "5.0")
XCTAssertEqual(version.version, "5.0.0")
}
func test_can_compare_version_numbers() {
// Build is newer
XCTAssertTrue(AppVersion.from("5.0_101")! > AppVersion.from("5.0_100")!)
// Version and build is the same
XCTAssertFalse(AppVersion.from("5.0.0_100")! > AppVersion.from("5.0_100")!)
// Version is newer
XCTAssertTrue(AppVersion.from("5.1_100")! > AppVersion.from("5.0_100")!)
// Build is older
XCTAssertFalse(AppVersion.from("5.0_101")! > AppVersion.from("5.0_102")!)
}
} }

View File

@ -44,6 +44,53 @@ class PhpVersionNumberTest: XCTestCase {
} }
} }
func test_can_parse_wildcard() throws {
let version = VersionNumber.make(from: "7.*", type: .wildCardMinor)
XCTAssertNotNil(version)
XCTAssertEqual(version!.major, 7)
XCTAssertEqual(version!.minor, 0)
}
func test_can_check_wildcard_version_constraint() throws {
// Wildcard for patch only
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4.10", "7.3.10", "7.3.9"])
.matching(constraint: "7.3.*", strict: false),
PhpVersionNumberCollection
.make(from: ["7.3.10", "7.3.9"]).all
)
// Wildcard for minor
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["8.0.0", "7.4.10", "7.3.10", "7.3.9"])
.matching(constraint: "7.*", strict: false),
PhpVersionNumberCollection
.make(from: ["7.4.10", "7.3.10", "7.3.9"]).all
)
// Full wildcard
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
.matching(constraint: "*", strict: false),
PhpVersionNumberCollection
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
)
}
func test_can_check_any_version_constraint() throws {
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
.matching(constraint: "*", strict: false),
PhpVersionNumberCollection
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
)
}
func test_can_check_fixed_constraints() throws { func test_can_check_fixed_constraints() throws {
XCTAssertEqual( XCTAssertEqual(
PhpVersionNumberCollection PhpVersionNumberCollection

View File

@ -37,10 +37,4 @@ class ValetVersionExtractorTest: XCTestCase {
XCTAssertEqual(version.major, 3) XCTAssertEqual(version.major, 3)
} }
func test_can_determine_valet_version() async {
let version = await valet("--version", sudo: false)
XCTAssert(version.contains("Laravel Valet 2") || version.contains("Laravel Valet 3"))
}
} }