Compare commits
64 Commits
Author | SHA1 | Date | |
---|---|---|---|
80bcbd085e | |||
ca65fca77d | |||
96975f8e57 | |||
729c1e8f2f | |||
e94377ebb1 | |||
769779970b | |||
2a27989a96 | |||
79430f7581 | |||
76f585a50e | |||
1bbc77967b | |||
1e4e2afe68 | |||
3caf90b0cd | |||
445acffa4c | |||
e90c068e95 | |||
c9428c300c | |||
807d9c55a4 | |||
ca167537cf | |||
8ae2031ba5 | |||
ec0ad13ad0 | |||
9988b775c9 | |||
8e24851014 | |||
c1d90cb909 | |||
4827c4a44b | |||
201554b237 | |||
e4bf6a9655 | |||
f9ea654ffc | |||
8677628850 | |||
e09b0156df | |||
41a83b1d91 | |||
a9e97b0bc9 | |||
e4e12799ef | |||
4e25fffa7d | |||
3e319cd50f | |||
595dc8c028 | |||
f7b1679e97 | |||
9f1761d68e | |||
871480d70c | |||
2b1c1c12f8 | |||
a22346ed35 | |||
e3fa34d4f9 | |||
3d225ea79f | |||
d2cd387c18 | |||
48bb782e33 | |||
9710ffa8da | |||
46408f5ee5 | |||
2c39f1db8b | |||
f20286cbd9 | |||
f1fe42e563 | |||
94abfe4b49 | |||
9778fd5c7b | |||
dba2ce5bf3 | |||
4644c1ada4 | |||
cef19243ee | |||
b319ecab59 | |||
a47b139d92 | |||
e026ecf60d | |||
3c0a4a6142 | |||
87ebb20284 | |||
d60c26c9b2 | |||
5c9c51f580 | |||
0c320074da | |||
e3ea712a99 | |||
4db478ca64 | |||
3064a07d69 |
4
.gitignore
vendored
@ -1,6 +1,4 @@
|
||||
phpmon.xcodeproj/project.xcworkspace
|
||||
phpmon.xcodeproj/xcuserdata
|
||||
PHP Monitor.xcodeproj/project.xcworkspace
|
||||
PHP Monitor.xcodeproj/xcuserdata
|
||||
phpmon-updater/PHP Monitor Self-Updater.app/
|
||||
|
||||
.DS_Store
|
||||
|
17
DEVELOPER.md
@ -14,6 +14,17 @@ It also automatically runs when you try to build the project. You'll get a warni
|
||||
swiftlint --fix
|
||||
```
|
||||
|
||||
## 📦 Swift Packages
|
||||
|
||||
Starting from PHP Monitor 7.1, the app now uses various first-party package dependencies.
|
||||
|
||||
The following package dependencies are in use:
|
||||
|
||||
* [`NVAppUpdater`](https://github.com/nicoverbruggen/NVAppUpdater)
|
||||
* [`NVAlert`](https://github.com/nicoverbruggen/NVAlert)
|
||||
|
||||
You may need an internet connection to download these dependencies, or you can also clone the dependencies and include them manually.
|
||||
|
||||
## ⚙️ Preferences
|
||||
|
||||
You can find the persisted configuration file in `~/Library/Preferences/com.nicoverbruggen.phpmon.plist`
|
||||
@ -40,6 +51,12 @@ Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you
|
||||
|
||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||
|
||||
## ✅ Testing
|
||||
|
||||
In order to properly test everything, you will want to use the _PHP Monitor DEV_ target. There are unit and UI tests both.
|
||||
|
||||
You may sporadically see failures in UI tests due to the following error: `Invalid parameter not satisfying: point.x != INFINITY && point.y != INFINITY`. This seems to be an issue with Xcode that Apple may need to resolve? You can retry the tests in question and they should eventually pass.
|
||||
|
||||
## 🚀 Release procedure
|
||||
|
||||
1. Merge into `main`
|
||||
|
@ -21,6 +21,14 @@
|
||||
033D45A02B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */; };
|
||||
033D45A12B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */; };
|
||||
033D45A32B0D531D00070080 /* PhpExtensionManagerView+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033D45A22B0D531D00070080 /* PhpExtensionManagerView+Actions.swift */; };
|
||||
03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
03BFF5282E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
03BFF52A2E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
03BFF52C2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; };
|
||||
03BFF52D2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; };
|
||||
03BFF52E2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; };
|
||||
03BFF52F2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; };
|
||||
03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; };
|
||||
03E36FE828D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; };
|
||||
5420395926135DC100FB00FA /* PreferencesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PreferencesVC.swift */; };
|
||||
@ -64,11 +72,6 @@
|
||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||
C406A5F3298AD2CE00B5B85A /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C406A5F2298AD2CE00B5B85A /* main.swift */; };
|
||||
C406A5F7298AD2CF00B5B85A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C406A5F6298AD2CF00B5B85A /* Assets.xcassets */; };
|
||||
C406A602298AD50D00B5B85A /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C406A601298AD50D00B5B85A /* Updater.swift */; };
|
||||
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||
C409349D298EE8E900D25014 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C409349C298EE8E900D25014 /* AppUpdater.swift */; };
|
||||
C409349E298EE8E900D25014 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C409349C298EE8E900D25014 /* AppUpdater.swift */; };
|
||||
C409349F298EE8E900D25014 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C409349C298EE8E900D25014 /* AppUpdater.swift */; };
|
||||
@ -295,6 +298,10 @@
|
||||
C469E700294CF7B200A82AB2 /* 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 */; };
|
||||
C46DC7A42C7B55DC00F19D17 /* Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46DC7A32C7B55DC00F19D17 /* Favorites.swift */; };
|
||||
C46DC7A52C7B5BC900F19D17 /* Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46DC7A32C7B55DC00F19D17 /* Favorites.swift */; };
|
||||
C46DC7A62C7B5BC900F19D17 /* Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46DC7A32C7B55DC00F19D17 /* Favorites.swift */; };
|
||||
C46DC7A72C7B5BCA00F19D17 /* Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46DC7A32C7B55DC00F19D17 /* Favorites.swift */; };
|
||||
C46EBC4428DB95F0007ACC74 /* 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 */; };
|
||||
@ -305,6 +312,15 @@
|
||||
C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */; };
|
||||
C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */; };
|
||||
C46FA98C2822F08F00D78807 /* PhpConfigurationFileTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA98A2822F08F00D78807 /* PhpConfigurationFileTest.swift */; };
|
||||
C47014FC2C46D31B0069AAE7 /* NVAppUpdater in Frameworks */ = {isa = PBXBuildFile; productRef = C47014FB2C46D31B0069AAE7 /* NVAppUpdater */; };
|
||||
C47014FF2C46D57C0069AAE7 /* NVAlert in Frameworks */ = {isa = PBXBuildFile; productRef = C47014FE2C46D57C0069AAE7 /* NVAlert */; };
|
||||
C47015022C46D6910069AAE7 /* NVAlertExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47015012C46D6910069AAE7 /* NVAlertExtension.swift */; };
|
||||
C47015032C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47015012C46D6910069AAE7 /* NVAlertExtension.swift */; };
|
||||
C47015042C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47015012C46D6910069AAE7 /* NVAlertExtension.swift */; };
|
||||
C47015052C46D7F10069AAE7 /* NVAlertExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47015012C46D6910069AAE7 /* NVAlertExtension.swift */; };
|
||||
C47015072C46D8180069AAE7 /* NVAlert in Frameworks */ = {isa = PBXBuildFile; productRef = C47015062C46D8180069AAE7 /* NVAlert */; };
|
||||
C470150B2C46D81E0069AAE7 /* NVAlert in Frameworks */ = {isa = PBXBuildFile; productRef = C470150A2C46D81E0069AAE7 /* NVAlert */; };
|
||||
C470150D2C46D83E0069AAE7 /* NVAlert in Frameworks */ = {isa = PBXBuildFile; productRef = C470150C2C46D83E0069AAE7 /* NVAlert */; };
|
||||
C4709CA228524B3400088BB8 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4709CA128524B3400088BB8 /* StatsView.swift */; };
|
||||
C471E79328F9B21F0021E251 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; };
|
||||
C471E79428F9B23B0021E251 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
||||
@ -392,10 +408,6 @@
|
||||
C471E81828F9BAE80021E251 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
||||
C471E81928F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */; };
|
||||
C471E81A28F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B3A4528E5C70100718CB1 /* TimeIntervalExtension.swift */; };
|
||||
C471E81B28F9BB250021E251 /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||
C471E81C28F9BB250021E251 /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||
C471E81D28F9BB260021E251 /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||
C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||
C471E81F28F9BB290021E251 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; };
|
||||
C471E82028F9BB290021E251 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; };
|
||||
C471E82128F9BB2E0021E251 /* ProjectTypeDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* ProjectTypeDetection.swift */; };
|
||||
@ -720,8 +732,6 @@
|
||||
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */; };
|
||||
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||
C4C75F5A298C2D5700DFD82E /* LaunchControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C75F59298C2D5700DFD82E /* LaunchControl.swift */; };
|
||||
C4C75F5C298C31C000DFD82E /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C75F5B298C31C000DFD82E /* Utility.swift */; };
|
||||
C4C8900328F0E28800CE5E97 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
||||
C4C8900528F0E3D100CE5E97 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
||||
C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; };
|
||||
@ -918,6 +928,8 @@
|
||||
033D45972B0D4EC600070080 /* InstallPhpExtensionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallPhpExtensionCommand.swift; sourceTree = "<group>"; };
|
||||
033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemovePhpExtensionCommand.swift; sourceTree = "<group>"; };
|
||||
033D45A22B0D531D00070080 /* PhpExtensionManagerView+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PhpExtensionManagerView+Actions.swift"; sourceTree = "<group>"; };
|
||||
03BFF5262E312C39007F96FA /* Startup+Timers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Startup+Timers.swift"; sourceTree = "<group>"; };
|
||||
03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Driver.swift"; sourceTree = "<group>"; };
|
||||
03E36FE628D9219000636F7F /* ActiveShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveShell.swift; sourceTree = "<group>"; };
|
||||
5420395826135DC100FB00FA /* PreferencesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesVC.swift; sourceTree = "<group>"; };
|
||||
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||
@ -946,9 +958,6 @@
|
||||
C406A5F2298AD2CE00B5B85A /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
C406A5F6298AD2CF00B5B85A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
C406A5FB298AD2CF00B5B85A /* phpmon-updater.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "phpmon-updater.entitlements"; sourceTree = "<group>"; };
|
||||
C406A601298AD50D00B5B85A /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = "<group>"; };
|
||||
C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlert.swift; sourceTree = "<group>"; };
|
||||
C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlertVC.swift; sourceTree = "<group>"; };
|
||||
C409349C298EE8E900D25014 /* AppUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdater.swift; sourceTree = "<group>"; };
|
||||
C40934A1298EEB2C00D25014 /* CaskFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaskFile.swift; sourceTree = "<group>"; };
|
||||
C40934A6298EEB8700D25014 /* phpmon-dev.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = "phpmon-dev.rb"; sourceTree = "<group>"; };
|
||||
@ -1054,12 +1063,14 @@
|
||||
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>"; };
|
||||
C469E702294CFDF700A82AB2 /* DomainsListTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainsListTest.swift; sourceTree = "<group>"; };
|
||||
C46DC7A32C7B55DC00F19D17 /* Favorites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Favorites.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>"; };
|
||||
C46EBC4928DB966A007ACC74 /* TestableShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableShell.swift; sourceTree = "<group>"; };
|
||||
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
|
||||
C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigurationFile.swift; sourceTree = "<group>"; };
|
||||
C46FA98A2822F08F00D78807 /* PhpConfigurationFileTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigurationFileTest.swift; sourceTree = "<group>"; };
|
||||
C47015012C46D6910069AAE7 /* NVAlertExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVAlertExtension.swift; sourceTree = "<group>"; };
|
||||
C4709CA128524B3400088BB8 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
||||
C471E79228F9B1D30021E251 /* PHP Monitor.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PHP Monitor.xctestplan"; sourceTree = "<group>"; };
|
||||
C471E7AD28F9B4940021E251 /* Feature Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Feature Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -1084,6 +1095,8 @@
|
||||
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>"; };
|
||||
C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||
C49DA9BC2D67AC49006F9CF4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C49DA9BD2D67B298006F9CF4 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C49EAA5129B12A5A00AB28FC /* Measurements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Measurements.swift; sourceTree = "<group>"; };
|
||||
C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+BrewWatch.swift"; sourceTree = "<group>"; };
|
||||
C4A81CA328C67101008DD9D1 /* PMTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMTableView.swift; sourceTree = "<group>"; };
|
||||
@ -1120,8 +1133,6 @@
|
||||
C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Items.swift"; sourceTree = "<group>"; };
|
||||
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.swift"; sourceTree = "<group>"; };
|
||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
|
||||
C4C75F59298C2D5700DFD82E /* LaunchControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchControl.swift; sourceTree = "<group>"; };
|
||||
C4C75F5B298C31C000DFD82E /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
||||
C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemProtocol.swift; sourceTree = "<group>"; };
|
||||
C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealFileSystem.swift; sourceTree = "<group>"; };
|
||||
C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveFileSystem.swift; sourceTree = "<group>"; };
|
||||
@ -1198,6 +1209,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C47014FC2C46D31B0069AAE7 /* NVAppUpdater in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1205,6 +1217,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C47014FF2C46D57C0069AAE7 /* NVAlert in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1212,6 +1225,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C470150B2C46D81E0069AAE7 /* NVAlert in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1219,6 +1233,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C470150D2C46D83E0069AAE7 /* NVAlert in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1226,6 +1241,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C47015072C46D8180069AAE7 /* NVAlert in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1348,24 +1364,12 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C406A5F2298AD2CE00B5B85A /* main.swift */,
|
||||
C406A601298AD50D00B5B85A /* Updater.swift */,
|
||||
C4C75F5B298C31C000DFD82E /* Utility.swift */,
|
||||
C4C75F59298C2D5700DFD82E /* LaunchControl.swift */,
|
||||
C406A5F6298AD2CF00B5B85A /* Assets.xcassets */,
|
||||
C406A5FB298AD2CF00B5B85A /* phpmon-updater.entitlements */,
|
||||
);
|
||||
path = "phpmon-updater";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C4080FF827BD955900BF2C6B /* Notice */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */,
|
||||
C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */,
|
||||
);
|
||||
path = Notice;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C40C5C9E2846A42D00E28255 /* Presets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1468,7 +1472,6 @@
|
||||
C41E181722CB61EB0072CF09 /* Domain */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4080FF827BD955900BF2C6B /* Notice */,
|
||||
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
||||
C4B13B1D25C4915000548C3A /* App */,
|
||||
C4D9ADBD27761084007277F4 /* PHP */,
|
||||
@ -1789,6 +1792,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C44DFA852A67090A00B98ED5 /* UI */,
|
||||
C46DC7A32C7B55DC00F19D17 /* Favorites.swift */,
|
||||
);
|
||||
path = "Domain List";
|
||||
sourceTree = "<group>";
|
||||
@ -1856,6 +1860,7 @@
|
||||
C4F361602836BFD9003598CC /* MainMenu+Actions.swift */,
|
||||
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
||||
C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */,
|
||||
03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */,
|
||||
C4821C592C2DEDE200357A68 /* AppMenu.swift */,
|
||||
);
|
||||
path = Menu;
|
||||
@ -1944,6 +1949,7 @@
|
||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||
03BFF5262E312C39007F96FA /* Startup+Timers.swift */,
|
||||
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
|
||||
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
|
||||
C409349C298EE8E900D25014 /* AppUpdater.swift */,
|
||||
@ -2235,6 +2241,7 @@
|
||||
C44B3A4528E5C70100718CB1 /* TimeIntervalExtension.swift */,
|
||||
C4E2E84928FC1E70003B070C /* DataExtension.swift */,
|
||||
C4D36619291173EA006BD146 /* DictionaryExtension.swift */,
|
||||
C47015012C46D6910069AAE7 /* NVAlertExtension.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -2255,6 +2262,9 @@
|
||||
dependencies = (
|
||||
);
|
||||
name = "PHP Monitor Self-Updater";
|
||||
packageProductDependencies = (
|
||||
C47014FB2C46D31B0069AAE7 /* NVAppUpdater */,
|
||||
);
|
||||
productName = "PHP Monitor Updater";
|
||||
productReference = C406A5F0298AD2CE00B5B85A /* PHP Monitor Self-Updater.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
@ -2275,6 +2285,7 @@
|
||||
);
|
||||
name = "PHP Monitor";
|
||||
packageProductDependencies = (
|
||||
C47014FE2C46D57C0069AAE7 /* NVAlert */,
|
||||
);
|
||||
productName = phpmon;
|
||||
productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */;
|
||||
@ -2294,6 +2305,9 @@
|
||||
C471E7B228F9B4940021E251 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Feature Tests";
|
||||
packageProductDependencies = (
|
||||
C470150A2C46D81E0069AAE7 /* NVAlert */,
|
||||
);
|
||||
productName = "Feature Tests";
|
||||
productReference = C471E7AD28F9B4940021E251 /* Feature Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
@ -2312,6 +2326,9 @@
|
||||
C471E7C328F9B90F0021E251 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "UI Tests";
|
||||
packageProductDependencies = (
|
||||
C470150C2C46D83E0069AAE7 /* NVAlert */,
|
||||
);
|
||||
productName = "UI Tests";
|
||||
productReference = C471E7BC28F9B90F0021E251 /* UI Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
@ -2331,6 +2348,7 @@
|
||||
);
|
||||
name = "Unit Tests";
|
||||
packageProductDependencies = (
|
||||
C47015062C46D8180069AAE7 /* NVAlert */,
|
||||
);
|
||||
productName = "phpmon-tests";
|
||||
productReference = C4F7807925D7F84B000DBC97 /* Unit Tests.xctest */;
|
||||
@ -2344,7 +2362,7 @@
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 1420;
|
||||
LastUpgradeCheck = 1530;
|
||||
LastUpgradeCheck = 1640;
|
||||
ORGANIZATIONNAME = "Nico Verbruggen";
|
||||
TargetAttributes = {
|
||||
C406A5EF298AD2CE00B5B85A = {
|
||||
@ -2378,9 +2396,13 @@
|
||||
Base,
|
||||
fr,
|
||||
"zh-Hans",
|
||||
es,
|
||||
id,
|
||||
);
|
||||
mainGroup = C41C1B2A22B0097F00E7CF16;
|
||||
packageReferences = (
|
||||
C47014FA2C46D31B0069AAE7 /* XCRemoteSwiftPackageReference "NVAppUpdater" */,
|
||||
C47014FD2C46D57C0069AAE7 /* XCRemoteSwiftPackageReference "NVAlert" */,
|
||||
);
|
||||
productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -2508,10 +2530,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C4C75F5C298C31C000DFD82E /* Utility.swift in Sources */,
|
||||
C490E3BC29BCA375006D2DE6 /* Measurements.swift in Sources */,
|
||||
C406A602298AD50D00B5B85A /* Updater.swift in Sources */,
|
||||
C4C75F5A298C2D5700DFD82E /* LaunchControl.swift in Sources */,
|
||||
C41F3D08298AED0D0042ACBF /* System.swift in Sources */,
|
||||
C406A5F3298AD2CE00B5B85A /* main.swift in Sources */,
|
||||
);
|
||||
@ -2552,7 +2571,6 @@
|
||||
C48DDD0D29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */,
|
||||
C45B91532956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
|
||||
C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */,
|
||||
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
||||
0309E6672B0D4B2F002AC007 /* BrewExtensionsObservable.swift in Sources */,
|
||||
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||
@ -2583,6 +2601,7 @@
|
||||
C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */,
|
||||
C4EA3C472BA4F947007B0BA7 /* CustomButtonStyles.swift in Sources */,
|
||||
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
||||
03BFF5282E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||
C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */,
|
||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||
C456A0C62AA614BD0080144F /* PhpPreference.swift in Sources */,
|
||||
@ -2635,6 +2654,7 @@
|
||||
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
||||
C412E5FC25700D5300A1FB67 /* HomebrewDecodable.swift in Sources */,
|
||||
03BFF52E2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||
03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */,
|
||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */,
|
||||
@ -2686,11 +2706,11 @@
|
||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
|
||||
C4415E8D2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */,
|
||||
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */,
|
||||
C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */,
|
||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||
C451AFF62969E40F0078E617 /* HelpButton.swift in Sources */,
|
||||
C46DC7A42C7B55DC00F19D17 /* Favorites.swift in Sources */,
|
||||
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||
C4AFC4AE29C4F32F00BF4E0D /* BrewPhpFormula.swift in Sources */,
|
||||
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||
@ -2715,6 +2735,7 @@
|
||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */,
|
||||
C47015022C46D6910069AAE7 /* NVAlertExtension.swift in Sources */,
|
||||
C4E49DEA28F7643D0026AC4E /* CommandProtocol.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -2788,6 +2809,7 @@
|
||||
C4E2E86628FC2F1B003B070C /* XCPMApplication.swift in Sources */,
|
||||
C471E85F28F9BB650021E251 /* DomainListVC+Actions.swift in Sources */,
|
||||
C4D5576629C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */,
|
||||
C47015042C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */,
|
||||
C4ACE9E329F84EDD00110766 /* PhpGuard.swift in Sources */,
|
||||
C471E86028F9BB650021E251 /* SelectionVC.swift in Sources */,
|
||||
C471E86128F9BB650021E251 /* AddSiteVC.swift in Sources */,
|
||||
@ -2854,7 +2876,9 @@
|
||||
C471E7FE28F9BACE0021E251 /* HomebrewDecodable.swift in Sources */,
|
||||
C4415E8F2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */,
|
||||
C471E7D828F9BA8F0021E251 /* FileSystemProtocol.swift in Sources */,
|
||||
03BFF52F2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||
C471E7F328F9BAC70021E251 /* PhpHelper.swift in Sources */,
|
||||
C46DC7A62C7B5BC900F19D17 /* Favorites.swift in Sources */,
|
||||
C471E7E728F9BAC20021E251 /* Constants.swift in Sources */,
|
||||
C471E81628F9BAE80021E251 /* DateExtension.swift in Sources */,
|
||||
C469E700294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */,
|
||||
@ -2879,11 +2903,11 @@
|
||||
C471E82A28F9BB330021E251 /* ValetListable.swift in Sources */,
|
||||
031E2B6B2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */,
|
||||
C471E82728F9BB310021E251 /* BrewDiagnostics.swift in Sources */,
|
||||
C471E81C28F9BB250021E251 /* BetterAlert.swift in Sources */,
|
||||
C471E7DB28F9BA8F0021E251 /* RealShell.swift in Sources */,
|
||||
C490E3B929BCA368006D2DE6 /* App+BrewWatch.swift in Sources */,
|
||||
C471E7FF28F9BAD10021E251 /* Xdebug.swift in Sources */,
|
||||
C409349F298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
||||
03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||
C471E7F228F9BAC70021E251 /* PhpEnvironments.swift in Sources */,
|
||||
C471E7E628F9BAC20021E251 /* Process.swift in Sources */,
|
||||
C471E81928F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */,
|
||||
@ -2896,7 +2920,6 @@
|
||||
C42106682AFA9FF400DF3732 /* PhpVersionManagerView+Actions.swift in Sources */,
|
||||
C4B79EC829CA474200A483EE /* FakeCommand.swift in Sources */,
|
||||
C471E7DE28F9BAA30021E251 /* CommandProtocol.swift in Sources */,
|
||||
C471E81B28F9BB250021E251 /* BetterAlertVC.swift in Sources */,
|
||||
C471E82928F9BB330021E251 /* Valet.swift in Sources */,
|
||||
C471E80728F9BAD40021E251 /* PhpConfigurationFile.swift in Sources */,
|
||||
C471E7D528F9BA8F0021E251 /* TestableConfigurations.swift in Sources */,
|
||||
@ -3004,6 +3027,7 @@
|
||||
C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */,
|
||||
C471E8C728F9BB8F0021E251 /* Warning.swift in Sources */,
|
||||
C471E8C828F9BB8F0021E251 /* WarningManager.swift in Sources */,
|
||||
C46DC7A72C7B5BCA00F19D17 /* Favorites.swift in Sources */,
|
||||
C471E8C928F9BB8F0021E251 /* PhpDoctorWindowController.swift in Sources */,
|
||||
C41ADCEB2970CCC700120423 /* FSNotifier.swift in Sources */,
|
||||
C471E8CA28F9BB8F0021E251 /* OnboardingWindowController.swift in Sources */,
|
||||
@ -3052,6 +3076,7 @@
|
||||
C456A0CE2AA6166F0080144F /* BytePhpPreference.swift in Sources */,
|
||||
C4FD87A829AB9ABD0002D701 /* PhpConfigChecker.swift in Sources */,
|
||||
C45B9151295608E300F4EC78 /* ValetServicesManager.swift in Sources */,
|
||||
C47015032C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */,
|
||||
C471E8EC28F9BB8F0021E251 /* SwiftUIHelper.swift in Sources */,
|
||||
C471E8EE28F9BB8F0021E251 /* HotKey.swift in Sources */,
|
||||
C471E8EF28F9BB8F0021E251 /* HotKeysController.swift in Sources */,
|
||||
@ -3069,6 +3094,7 @@
|
||||
C4611E5A2AEAD2E20010BE24 /* ConfigManagerWindowController.swift in Sources */,
|
||||
C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */,
|
||||
C490E3BA29BCA368006D2DE6 /* App+BrewWatch.swift in Sources */,
|
||||
03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||
C471E7D028F9BA630021E251 /* FileSystemProtocol.swift in Sources */,
|
||||
C471E81228F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */,
|
||||
C471E7DF28F9BAAB0021E251 /* RealCommand.swift in Sources */,
|
||||
@ -3080,6 +3106,7 @@
|
||||
C471E80228F9BAD40021E251 /* PhpInstallation.swift in Sources */,
|
||||
C471E81028F9BAE80021E251 /* StringExtension.swift in Sources */,
|
||||
C48DDD1029C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */,
|
||||
03BFF52C2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||
C471E7F828F9BACB0021E251 /* InternalSwitcher.swift in Sources */,
|
||||
C471E82328F9BB2E0021E251 /* ComposerJson.swift in Sources */,
|
||||
C471E82128F9BB2E0021E251 /* ProjectTypeDetection.swift in Sources */,
|
||||
@ -3092,7 +3119,6 @@
|
||||
C4463FCF29804BCB007B93D5 /* RCFile.swift in Sources */,
|
||||
C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */,
|
||||
C471E82828F9BB310021E251 /* BrewDiagnostics.swift in Sources */,
|
||||
C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */,
|
||||
C43BCD4729FBEF40001547BC /* ModifyPhpVersionCommand.swift in Sources */,
|
||||
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */,
|
||||
C4513F8E2B13E2E5001AD760 /* PhpExtensionManagerWindowController.swift in Sources */,
|
||||
@ -3108,7 +3134,6 @@
|
||||
C471E7DD28F9BAA30021E251 /* CommandProtocol.swift in Sources */,
|
||||
C471E7D128F9BA630021E251 /* RealFileSystem.swift in Sources */,
|
||||
033D459B2B0D4EC600070080 /* InstallPhpExtensionCommand.swift in Sources */,
|
||||
C471E81D28F9BB260021E251 /* BetterAlertVC.swift in Sources */,
|
||||
C471E82B28F9BB340021E251 /* Valet.swift in Sources */,
|
||||
C471E80328F9BAD40021E251 /* PhpConfigurationFile.swift in Sources */,
|
||||
C471E7C928F9BA2F0021E251 /* TestableConfigurations.swift in Sources */,
|
||||
@ -3165,7 +3190,6 @@
|
||||
C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */,
|
||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||
C485707528BF454F00539B36 /* StatsView.swift in Sources */,
|
||||
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
||||
C485707328BF454300539B36 /* OnboardingView.swift in Sources */,
|
||||
@ -3213,6 +3237,7 @@
|
||||
C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */,
|
||||
C456A0C72AA614BD0080144F /* PhpPreference.swift in Sources */,
|
||||
C42106672AFA9FF400DF3732 /* PhpVersionManagerView+Actions.swift in Sources */,
|
||||
C46DC7A52C7B5BC900F19D17 /* Favorites.swift in Sources */,
|
||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */,
|
||||
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||
@ -3230,6 +3255,7 @@
|
||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,
|
||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||
03BFF52D2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||
C4AFC4B429C4F43300BF4E0D /* HomebrewUpgradableTest.swift in Sources */,
|
||||
C4E2E84828FC1D93003B070C /* TestableConfigurationTest.swift in Sources */,
|
||||
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
|
||||
@ -3241,6 +3267,7 @@
|
||||
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */,
|
||||
C481F79726164A78004FBCFF /* PreferencesVC.swift in Sources */,
|
||||
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */,
|
||||
03BFF52A2E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||
@ -3258,7 +3285,6 @@
|
||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */,
|
||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
||||
C469E6FF294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */,
|
||||
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||
5489625928313231004F647A /* CreatedFromFile.swift in Sources */,
|
||||
C4513F932B13E2FB001AD760 /* PhpExtensionManagerView.swift in Sources */,
|
||||
@ -3316,6 +3342,7 @@
|
||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
||||
C4C0E8E027F88AEB002D32A9 /* FakeDomainScanner.swift in Sources */,
|
||||
C4463FCD29804BCB007B93D5 /* RCFile.swift in Sources */,
|
||||
C47015052C46D7F10069AAE7 /* NVAlertExtension.swift in Sources */,
|
||||
C409349E298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
||||
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */,
|
||||
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */,
|
||||
@ -3406,6 +3433,8 @@
|
||||
C4622F572A7593CB0016F8FB /* pt-PT */,
|
||||
0336CAAF2B0D0CDA009A1034 /* fr */,
|
||||
C453874C2BE37FD6002B9C65 /* zh-Hans */,
|
||||
C49DA9BC2D67AC49006F9CF4 /* es */,
|
||||
C49DA9BD2D67B298006F9CF4 /* id */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
@ -3427,19 +3456,18 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor Self-Updater";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023-2025 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.2;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-updater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -3462,19 +3490,18 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor Self-Updater";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023-2025 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.2;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-updater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -3497,19 +3524,18 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor Self-Updater";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023-2025 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.2;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-updater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -3532,19 +3558,18 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor Self-Updater";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023-2025 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.2;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-updater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -3592,6 +3617,7 @@
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -3657,6 +3683,7 @@
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -3685,10 +3712,9 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1490;
|
||||
CURRENT_PROJECT_VERSION = 1565;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor";
|
||||
@ -3697,8 +3723,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 7.0.4;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.5;
|
||||
MARKETING_VERSION = 25.07;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||
PRODUCT_MODULE_NAME = PHP_Monitor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@ -3716,10 +3742,9 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1490;
|
||||
CURRENT_PROJECT_VERSION = 1565;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG = NO;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor";
|
||||
@ -3728,8 +3753,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 7.0.4;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.5;
|
||||
MARKETING_VERSION = 25.07;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||
PRODUCT_MODULE_NAME = PHP_Monitor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@ -3745,7 +3770,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
@ -3764,7 +3788,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
@ -3783,7 +3806,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
@ -3802,7 +3824,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
@ -3821,7 +3842,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@ -3840,7 +3860,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@ -3859,7 +3878,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@ -3878,7 +3896,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@ -3929,6 +3946,7 @@
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -3957,10 +3975,9 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1490;
|
||||
CURRENT_PROJECT_VERSION = 1565;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG = NO;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor DEV";
|
||||
@ -3969,8 +3986,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 7.0.4;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.5;
|
||||
MARKETING_VERSION = 25.07;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev;
|
||||
PRODUCT_MODULE_NAME = PHP_Monitor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME) DEV";
|
||||
@ -3985,7 +4002,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -4039,6 +4055,7 @@
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -4074,10 +4091,9 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1490;
|
||||
CURRENT_PROJECT_VERSION = 1565;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor DEV";
|
||||
@ -4086,8 +4102,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 7.0.4;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.5;
|
||||
MARKETING_VERSION = 25.07;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev;
|
||||
PRODUCT_MODULE_NAME = PHP_Monitor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME) DEV";
|
||||
@ -4102,7 +4118,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -4156,6 +4171,7 @@
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -4191,10 +4207,9 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1490;
|
||||
CURRENT_PROJECT_VERSION = 1565;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor EAP";
|
||||
@ -4203,8 +4218,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 7.0.4;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.5;
|
||||
MARKETING_VERSION = 25.07;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.eap;
|
||||
PRODUCT_MODULE_NAME = PHP_Monitor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME) EAP";
|
||||
@ -4227,19 +4242,18 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor Self-Updater";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023-2025 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.2;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-updater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -4254,7 +4268,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -4276,7 +4289,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
@ -4295,7 +4307,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@ -4346,6 +4357,7 @@
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@ -4374,10 +4386,9 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1490;
|
||||
CURRENT_PROJECT_VERSION = 1565;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG = NO;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor EAP";
|
||||
@ -4386,8 +4397,8 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 7.0.4;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.5;
|
||||
MARKETING_VERSION = 25.07;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.eap;
|
||||
PRODUCT_MODULE_NAME = PHP_Monitor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME) EAP";
|
||||
@ -4410,19 +4421,18 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor Self-Updater";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023-2025 Nico Verbruggen. All rights reserved.";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.2;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-updater";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -4437,7 +4447,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -4459,7 +4468,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
@ -4478,7 +4486,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.4;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@ -4496,7 +4503,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -4517,7 +4523,6 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -4614,6 +4619,53 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
C47014FA2C46D31B0069AAE7 /* XCRemoteSwiftPackageReference "NVAppUpdater" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/nicoverbruggen/NVAppUpdater";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
C47014FD2C46D57C0069AAE7 /* XCRemoteSwiftPackageReference "NVAlert" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/nicoverbruggen/NVAlert";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
C47014FB2C46D31B0069AAE7 /* NVAppUpdater */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C47014FA2C46D31B0069AAE7 /* XCRemoteSwiftPackageReference "NVAppUpdater" */;
|
||||
productName = NVAppUpdater;
|
||||
};
|
||||
C47014FE2C46D57C0069AAE7 /* NVAlert */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C47014FD2C46D57C0069AAE7 /* XCRemoteSwiftPackageReference "NVAlert" */;
|
||||
productName = NVAlert;
|
||||
};
|
||||
C47015062C46D8180069AAE7 /* NVAlert */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C47014FD2C46D57C0069AAE7 /* XCRemoteSwiftPackageReference "NVAlert" */;
|
||||
productName = NVAlert;
|
||||
};
|
||||
C470150A2C46D81E0069AAE7 /* NVAlert */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C47014FD2C46D57C0069AAE7 /* XCRemoteSwiftPackageReference "NVAlert" */;
|
||||
productName = NVAlert;
|
||||
};
|
||||
C470150C2C46D83E0069AAE7 /* NVAlert */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C47014FD2C46D57C0069AAE7 /* XCRemoteSwiftPackageReference "NVAlert" */;
|
||||
productName = NVAlert;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1540"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
14
README.md
@ -1,7 +1,7 @@
|
||||
> **Note**
|
||||
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider [sponsoring](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
|
||||
|
||||
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
|
||||
|
||||
<p align="center"><img src="./docs/logo.svg" alt="PHP Monitor Logo" width="500px" /></p>
|
||||
|
||||
**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 if you want to use all of the functionality of the app</u> (consult the FAQ below with info about how to set up your environment).
|
||||
|
||||
@ -22,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.
|
||||
|
||||
* Your user account can administer your computer (required for some functionality, e.g. certificate generation)
|
||||
* macOS 12.4 or later (Monterey, Ventura and Sonoma are supported)
|
||||
* macOS 13.5 or later
|
||||
* Homebrew is installed in the default location (`/usr/local/homebrew` or `/opt/homebrew`)
|
||||
* Homebrew `php` formula is installed
|
||||
* Optional but recommended: Laravel Valet
|
||||
@ -84,9 +84,13 @@ Initially, I had an Alfred workflow for this — but it has now been replaced wi
|
||||
|
||||
## 🐘 Why not use Laravel Herd?
|
||||
|
||||
If you don't need to customize your local PHP setup and just want an easy and ready-to-go environment to start coding, [Laravel Herd](https://herd.laravel.com) is probably more than sufficient for many use cases.
|
||||
_**Disclaimer**: The author is not affiliated with Laravel or the Laravel team, nor Beyond Code, who maintain Laravel Herd. PHP Monitor is an independent project._
|
||||
|
||||
If you need more customization and flexibility I encourage you to consider PHP Monitor in combination with Laravel Valet or some other solution like Docker (with Laravel Sail, for example).
|
||||
If you don't need to customize your local PHP setup and just want an easy and ready-to-go environment to start coding, [Laravel Herd](https://herd.laravel.com) is probably more than sufficient for many use cases. They also offer paid features that may be useful to you or your team.
|
||||
|
||||
At this point, many people enjoy using Herd. However, Herd may not be for everyone, which is why other solutions to run PHP locally exist. If you need more customization and flexibility I encourage you to consider PHP Monitor in combination with Laravel Valet.
|
||||
|
||||
If you want to get as close as you can to a real server environment your best bet is probably to use a Docker container. I _highly_ recommend that you try different setups, and use what you like best.
|
||||
|
||||
## 🤬 The app won't start?!
|
||||
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
## Supported versions
|
||||
|
||||
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up):
|
||||
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up).
|
||||
|
||||
| Version | Apple Silicon | Supported | Supported macOS | Minimum Deployment | Detected PHP Versions | Recommended Valet Version |
|
||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||
| 7.0 | ✅ Universal binary | ✅ Yes | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0) | macOS 12.4+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.4 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 25 | ✅ Universal binary | ✅ Yes | Ventura (13.5+)<br/>Sonoma (14.0+)<br/>Sequoia (15.0+)<br/>Tahoe (26.0+)* | macOS 13.5+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.5 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
|
||||
(*) Denotes preliminary supported based on the app being built with the latest version of the SDK prior to the release of the latest release of macOS. Please check out the pinned issue for more information.
|
||||
|
||||
## Legacy versions
|
||||
|
||||
@ -14,6 +16,8 @@ These versions of PHP Monitor are no longer supported, but if you’re using an
|
||||
|
||||
| Version | Apple Silicon | Supported | Supported macOS | Minimum Deployment | Detected PHP Versions | Minimum Required Valet Version |
|
||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||
| 7.1 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0+)<br/>Sequoia (15.0+) | macOS 12.4+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.5 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 7.0 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0) | macOS 12.4+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.4 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 6.2 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0) | macOS 12.4+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.4 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 6.1 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0) | macOS 12.4+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.4 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 6.0 | ✅ Universal binary | ❌ | 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 |
|
||||
|
BIN
assets/affinity/icon-unified.afdesign
Normal file
BIN
assets/affinity/logo.afdesign
Normal file
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
1
assets/icon-2025.svg
Normal file
After Width: | Height: | Size: 24 KiB |
10
assets/xcode-icon-composer/icon.icon/Assets/phpmon.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.25033,-0.175723,0.175723,1.25033,14.4412,107.226)">
|
||||
<path d="M133.3,83.75L120.4,83.75L120.4,54.437C120.4,52.134 118.465,50.25 116.1,50.25L107.5,50.25C105.135,50.25 103.2,52.134 103.2,54.438L103.2,96.312C103.2,98.616 105.135,100.5 107.5,100.5L133.3,100.5C135.665,100.5 137.6,98.616 137.6,96.312L137.6,87.938C137.6,85.634 135.665,83.75 133.3,83.75ZM335.4,184.25L326.8,184.25L326.8,127.666C326.8,121.019 324.059,114.633 319.221,109.922L265.525,57.63C260.688,52.92 254.13,50.25 247.304,50.25L223.6,50.25L223.6,25.125C223.6,11.254 212.044,0 197.8,0L25.8,0C11.556,0 0,11.254 0,25.125L0,192.625C0,206.496 11.556,217.75 25.8,217.75L34.4,217.75C34.4,245.492 57.513,268 86,268C114.487,268 137.6,245.492 137.6,217.75L206.4,217.75C206.4,245.492 229.512,268 258,268C286.488,268 309.6,245.492 309.6,217.75L335.4,217.75C340.13,217.75 344,213.981 344,209.375L344,192.625C344,188.019 340.13,184.25 335.4,184.25ZM86,242.875C71.756,242.875 60.2,231.621 60.2,217.75C60.2,203.879 71.756,192.625 86,192.625C100.244,192.625 111.8,203.879 111.8,217.75C111.8,231.621 100.244,242.875 86,242.875ZM111.8,150.75C78.529,150.75 51.6,124.526 51.6,92.125C51.6,59.725 78.529,33.5 111.8,33.5C145.071,33.5 172,59.724 172,92.125C172,124.525 145.071,150.75 111.8,150.75ZM258,242.875C243.756,242.875 232.2,231.621 232.2,217.75C232.2,203.879 243.756,192.625 258,192.625C272.244,192.625 283.8,203.879 283.8,217.75C283.8,231.621 272.244,242.875 258,242.875ZM301,134L223.6,134L223.6,75.375L247.304,75.375L301,127.666L301,134Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.96882e-14,321.533,-321.533,1.96882e-14,172.079,-41.4918)"><stop offset="0" style="stop-color:rgb(81,194,251);stop-opacity:1"/><stop offset="0" style="stop-color:rgb(81,194,251);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(28,145,254);stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
48
assets/xcode-icon-composer/icon.icon/icon.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"fill" : {
|
||||
"linear-gradient" : [
|
||||
"srgb:0.27800,0.58000,0.98800,1.00000",
|
||||
"srgb:0.27800,0.58000,0.98800,1.00000"
|
||||
]
|
||||
},
|
||||
"groups" : [
|
||||
{
|
||||
"blend-mode" : "screen",
|
||||
"blur-material" : null,
|
||||
"layers" : [
|
||||
{
|
||||
"blend-mode" : "normal",
|
||||
"fill" : {
|
||||
"solid" : "srgb:1.00000,0.99038,0.96423,1.00000"
|
||||
},
|
||||
"glass" : true,
|
||||
"image-name" : "phpmon.svg",
|
||||
"name" : "phpmon",
|
||||
"position" : {
|
||||
"scale" : 1.85,
|
||||
"translation-in-points" : [
|
||||
10.0234375,
|
||||
8.21875
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"lighting" : "individual",
|
||||
"shadow" : {
|
||||
"kind" : "neutral",
|
||||
"opacity" : 0.5
|
||||
},
|
||||
"specular" : true,
|
||||
"translucency" : {
|
||||
"enabled" : true,
|
||||
"value" : 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"supported-platforms" : {
|
||||
"circles" : [
|
||||
"watchOS"
|
||||
],
|
||||
"squares" : "shared"
|
||||
}
|
||||
}
|
BIN
docs/logo.png
Before Width: | Height: | Size: 51 KiB |
57
docs/logo.svg
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 811 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 450 KiB |
@ -1,46 +0,0 @@
|
||||
//
|
||||
// LaunchControl.swift
|
||||
// PHP Monitor Self-Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 02/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class LaunchControl {
|
||||
public static func smartRestart(priority: [String]) async {
|
||||
for appPath in priority {
|
||||
if FileManager.default.fileExists(atPath: appPath) {
|
||||
let app = await LaunchControl.startApplication(at: appPath)
|
||||
if app != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func terminateApplications(bundleIds: [String]) async {
|
||||
let runningApplications = NSWorkspace.shared.runningApplications
|
||||
|
||||
// Terminate all instances found
|
||||
for id in bundleIds {
|
||||
if let phpmon = runningApplications.first(where: {
|
||||
(application) in return application.bundleIdentifier == id
|
||||
}) {
|
||||
phpmon.terminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func startApplication(at path: String) async -> NSRunningApplication? {
|
||||
await withCheckedContinuation { continuation in
|
||||
let url = NSURL(fileURLWithPath: path, isDirectory: true) as URL
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
NSWorkspace.shared.openApplication(at: url, configuration: configuration) { phpmon, error in
|
||||
continuation.resume(returning: phpmon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
//
|
||||
// Updater.swift
|
||||
// PHP Monitor Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 01/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class Updater: NSObject, NSApplicationDelegate {
|
||||
|
||||
var updaterDirectory: String = ""
|
||||
var manifestPath: String = ""
|
||||
var manifest: ReleaseManifest! = nil
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
Task { await self.installUpdate() }
|
||||
}
|
||||
|
||||
func installUpdate() async {
|
||||
print("PHP MONITOR SELF-UPDATER by Nico Verbruggen")
|
||||
print("===========================================")
|
||||
|
||||
self.updaterDirectory = "~/.config/phpmon/updater"
|
||||
.replacingOccurrences(of: "~", with: NSHomeDirectory())
|
||||
|
||||
print("Updater directory set to: \(self.updaterDirectory)")
|
||||
|
||||
self.manifestPath = "\(updaterDirectory)/update.json"
|
||||
|
||||
// Fetch the manifest on the local filesystem
|
||||
let manifest = await parseManifest()!
|
||||
|
||||
// Download the latest file
|
||||
let zipPath = await download(manifest)
|
||||
|
||||
// Terminate all instances of PHP Monitor first
|
||||
await LaunchControl.terminateApplications(bundleIds: [
|
||||
"com.nicoverbruggen.phpmon.eap",
|
||||
"com.nicoverbruggen.phpmon.dev",
|
||||
"com.nicoverbruggen.phpmon"
|
||||
])
|
||||
|
||||
// Install the app based on the zip
|
||||
let appPath = await extractAndInstall(zipPath: zipPath)
|
||||
|
||||
// Restart PHP Monitor, this will also close the updater
|
||||
_ = await LaunchControl.startApplication(at: appPath)
|
||||
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
private func parseManifest() async -> ReleaseManifest? {
|
||||
// Read out the correct information from the manifest JSON
|
||||
print("Checking manifest file at \(manifestPath)...")
|
||||
|
||||
do {
|
||||
let manifestText = try String(contentsOfFile: manifestPath)
|
||||
manifest = try JSONDecoder().decode(ReleaseManifest.self, from: manifestText.data(using: .utf8)!)
|
||||
return manifest
|
||||
} catch {
|
||||
print("Parsing the manifest failed (or the manifest file doesn't exist)!")
|
||||
await Alert.show(description: "The manifest file for a potential update was not found. Please try searching for updates again in PHP Monitor.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private func download(_ manifest: ReleaseManifest) async -> String {
|
||||
// Remove all zips
|
||||
system_quiet("rm -rf \(updaterDirectory)/*.zip")
|
||||
|
||||
// Download the file (and follow redirects + no output on failure)
|
||||
system_quiet("cd \"\(updaterDirectory)\" && curl \(manifest.url) -fLO --max-time 20")
|
||||
|
||||
// Identify the downloaded file
|
||||
let filename = system("cd \"\(updaterDirectory)\" && ls | grep .zip")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Ensure the zip exists
|
||||
if filename.isEmpty {
|
||||
print("The update has not been downloaded. Sadly, that means that PHP Monitor cannot not updated!")
|
||||
await Alert.show(description: "The update could not be downloaded, or the file was not correctly written to disk. \n\nPlease try again. \n\n(Note that the download will time-out after 20 seconds, so for slow connections it is recommended to manually download the update.)")
|
||||
}
|
||||
|
||||
// Calculate the checksum for the downloaded file
|
||||
let checksum = system("openssl dgst -sha256 \"\(updaterDirectory)/\(filename)\" | awk '{print $NF}'")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Compare the checksums
|
||||
print("""
|
||||
Comparing checksums...
|
||||
Expected SHA256: \(manifest.sha256)
|
||||
Actual SHA256: \(checksum)
|
||||
""")
|
||||
|
||||
// Make sure the checksum matches before we do anything with the file
|
||||
if checksum != manifest.sha256 {
|
||||
print("The checksums failed to match. Cancelling!")
|
||||
await Alert.show(description: "The downloaded update failed checksum validation. Please try again. If this issue persists, there may be an issue with the server and I do not recommend upgrading.")
|
||||
}
|
||||
|
||||
// Return the path to the zip
|
||||
return "\(updaterDirectory)/\(filename)"
|
||||
}
|
||||
|
||||
private func extractAndInstall(zipPath: String) async -> String {
|
||||
// Remove the directory that will contain the extracted update
|
||||
system_quiet("rm -rf \"\(updaterDirectory)/extracted\"")
|
||||
|
||||
// Recreate the directory where we will unzip the .app file
|
||||
system_quiet("mkdir -p \"\(updaterDirectory)/extracted\"")
|
||||
|
||||
// Make sure the updater directory exists
|
||||
var isDirectory: ObjCBool = true
|
||||
if !FileManager.default.fileExists(atPath: "\(updaterDirectory)/extracted", isDirectory: &isDirectory) {
|
||||
await Alert.show(description: "The updater directory is missing. The automatic updater will quit. Make sure that ` ~/.config/phpmon/updater` is writeable.")
|
||||
}
|
||||
|
||||
// Unzip the file
|
||||
system_quiet("unzip \"\(zipPath)\" -d \"\(updaterDirectory)/extracted\"")
|
||||
|
||||
// Find the .app file
|
||||
let app = system("ls \"\(updaterDirectory)/extracted\" | grep .app")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
print("Finished extracting: \(updaterDirectory)/extracted/\(app)")
|
||||
|
||||
// Make sure the file was extracted
|
||||
if app.isEmpty {
|
||||
await Alert.show(description: "The downloaded file could not be extracted. The automatic updater will quit. Make sure that ` ~/.config/phpmon/updater` is writeable.")
|
||||
}
|
||||
|
||||
// Remove the original app
|
||||
print("Removing \(app) before replacing...")
|
||||
system_quiet("rm -rf \"/Applications/\(app)\"")
|
||||
|
||||
// Move the new app in place
|
||||
system_quiet("mv \"\(updaterDirectory)/extracted/\(app)\" \"/Applications/\(app)\"")
|
||||
|
||||
// Remove the zip
|
||||
system_quiet("rm \"\(zipPath)\"")
|
||||
|
||||
// Remove the manifest
|
||||
system_quiet("rm \"\(manifestPath)\"")
|
||||
|
||||
// Write a file that is only written when we upgraded successfully
|
||||
system_quiet("touch \"\(updaterDirectory)/upgrade.success\"")
|
||||
|
||||
// Return the new location of the app
|
||||
return "/Applications/\(app)"
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
//
|
||||
// Utility.swift
|
||||
// PHP Monitor Self-Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 02/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class Alert {
|
||||
public static func show(description: String, shouldExit: Bool = true) async {
|
||||
await withUnsafeContinuation { continuation in
|
||||
DispatchQueue.main.async {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "The app could not be updated."
|
||||
alert.informativeText = description
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.alertStyle = .critical
|
||||
alert.runModal()
|
||||
if shouldExit {
|
||||
exit(0)
|
||||
}
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ReleaseManifest: Codable {
|
||||
let url: String
|
||||
let sha256: String
|
||||
}
|
@ -7,8 +7,17 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import NVAppUpdater
|
||||
|
||||
let app = NSApplication.shared
|
||||
let delegate = Updater()
|
||||
app.delegate = delegate
|
||||
let delegate = SelfUpdater(
|
||||
appName: "PHP Monitor",
|
||||
bundleIdentifiers: [
|
||||
"com.nicoverbruggen.phpmon.eap",
|
||||
"com.nicoverbruggen.phpmon.dev",
|
||||
"com.nicoverbruggen.phpmon"
|
||||
],
|
||||
selfUpdaterPath: "~/.config/phpmon/updater"
|
||||
)
|
||||
|
||||
NSApplication.shared.delegate = delegate
|
||||
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 783 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 457 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 648 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 819 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 499 KiB |
24
phpmon/Assets.xcassets/ValetDriverIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "ValetDriverIcon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
BIN
phpmon/Assets.xcassets/ValetDriverIcon.imageset/ValetDriverIcon@2x.png
vendored
Normal file
After Width: | Height: | Size: 831 B |
@ -18,19 +18,29 @@ struct Constants {
|
||||
*/
|
||||
static let MinimumRecommendedValetVersion = "2.16.2"
|
||||
|
||||
/**
|
||||
The amount of seconds that is considered the threshold for
|
||||
PHP Monitor to mark any given launch as a "slow" launch.
|
||||
|
||||
If the startup procedure was slow (or hangs), this message should
|
||||
be displayed. This is based on an appropriate launch time on a
|
||||
basic M1 Apple chip, with some margin for slower Intel chips.
|
||||
*/
|
||||
static let SlowBootThresholdInterval: TimeInterval = 30.0
|
||||
|
||||
/**
|
||||
PHP Monitor supplies a hardcoded list of PHP packages in its own
|
||||
PHP Version Manager.
|
||||
|
||||
This hardcoded list will expire and will need to be modified when
|
||||
the cutoff date occurs, which is when the `php` formula will
|
||||
become PHP 8.4, and a new build will need to be made.
|
||||
become PHP 8.5, and a new build will need to be made.
|
||||
|
||||
If users launch an older version of the app, then a warning
|
||||
will be displayed to let them know that certain operations
|
||||
will not work correctly and that they need to update their app.
|
||||
*/
|
||||
static let PhpFormulaeCutoffDate = "2024-11-01" // YYYY-MM-DD
|
||||
static let PhpFormulaeCutoffDate = "2025-11-30" // YYYY-MM-DD
|
||||
|
||||
/**
|
||||
* The PHP versions that are considered pre-release versions.
|
||||
@ -39,7 +49,8 @@ struct Constants {
|
||||
*/
|
||||
static var ExperimentalPhpVersions: Set<String> {
|
||||
let releaseDates = [
|
||||
"8.4": Date.fromString("2024-12-01") // PLACEHOLDER DATE
|
||||
"8.5": Date.fromString(Self.PhpFormulaeCutoffDate),
|
||||
"8.4": Date.fromString("2024-11-22")
|
||||
]
|
||||
|
||||
return Set(releaseDates
|
||||
@ -72,8 +83,8 @@ struct Constants {
|
||||
static let DetectedPhpVersions: Set = [
|
||||
"5.6",
|
||||
"7.0", "7.1", "7.2", "7.3", "7.4",
|
||||
"8.0", "8.1", "8.2", "8.3",
|
||||
"8.4"
|
||||
"8.0", "8.1", "8.2", "8.3", "8.4",
|
||||
"8.5" // DEV
|
||||
]
|
||||
|
||||
/**
|
||||
@ -89,14 +100,13 @@ struct Constants {
|
||||
3: // Valet v3 dropped support for v5.6
|
||||
[
|
||||
"7.0", "7.1", "7.2", "7.3", "7.4",
|
||||
"8.0", "8.1", "8.2", "8.3",
|
||||
"8.4" // dev
|
||||
"8.0", "8.1", "8.2", "8.3", "8.4"
|
||||
],
|
||||
4: // Valet v4 dropped support for v7.0
|
||||
[
|
||||
"7.1", "7.2", "7.3", "7.4",
|
||||
"8.0", "8.1", "8.2", "8.3",
|
||||
"8.4" // dev
|
||||
"8.0", "8.1", "8.2", "8.3", "8.4",
|
||||
"8.5" // DEV
|
||||
]
|
||||
]
|
||||
|
||||
@ -112,6 +122,14 @@ struct Constants {
|
||||
string: "https://phpmon.app/faq"
|
||||
)!
|
||||
|
||||
static let WikiPhpUnavailable = URL(
|
||||
string: "https://phpmon.app/php-unavailable"
|
||||
)!
|
||||
|
||||
static let WikiPhpUpgrade = URL(
|
||||
string: "https://phpmon.app/php-upgrade"
|
||||
)!
|
||||
|
||||
static let DonationPayment = URL(
|
||||
string: "https://phpmon.app/sponsor/now"
|
||||
)!
|
||||
|
@ -45,7 +45,6 @@ func grepContains(file: String, query: String) async -> Bool {
|
||||
|
||||
/**
|
||||
Attempts to introduce sleep for a particular duration. Use with caution.
|
||||
Only intended for testing purposes.
|
||||
*/
|
||||
func delay(seconds: Double) async {
|
||||
try! await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
|
||||
|
@ -8,6 +8,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol AlertableError {
|
||||
public protocol AlertableError {
|
||||
func getErrorMessageKey() -> String
|
||||
}
|
||||
|
@ -9,6 +9,24 @@
|
||||
import Cocoa
|
||||
|
||||
extension NSMenuItem {
|
||||
convenience init(
|
||||
title: String,
|
||||
action: Selector? = nil,
|
||||
keyEquivalent: String = "",
|
||||
keyModifier: NSEvent.ModifierFlags = [],
|
||||
systemImage: String? = nil,
|
||||
customImage: String? = nil,
|
||||
) {
|
||||
self.init(title: title, action: action, keyEquivalent: keyEquivalent)
|
||||
self.keyEquivalentModifierMask = keyModifier
|
||||
if systemImage != nil {
|
||||
self.image = NSImage(systemSymbolName: systemImage!, accessibilityDescription: "")
|
||||
}
|
||||
if customImage != nil {
|
||||
self.image = NSImage(named: customImage!)
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(
|
||||
title: String,
|
||||
action: Selector? = nil,
|
||||
@ -26,12 +44,20 @@ extension NSMenuItem {
|
||||
keyEquivalent: String = "",
|
||||
keyModifier: NSEvent.ModifierFlags = [],
|
||||
toolTip: String? = nil,
|
||||
systemImage: String? = nil,
|
||||
customImage: String? = nil,
|
||||
submenu: [NSMenuItem],
|
||||
target: NSObject? = nil
|
||||
) {
|
||||
self.init(title: title, action: nil, keyEquivalent: keyEquivalent)
|
||||
self.keyEquivalentModifierMask = keyModifier
|
||||
self.toolTip = toolTip
|
||||
if systemImage != nil {
|
||||
self.image = NSImage(systemSymbolName: systemImage!, accessibilityDescription: "")
|
||||
}
|
||||
if customImage != nil {
|
||||
self.image = NSImage(named: customImage!)
|
||||
}
|
||||
self.submenu = NSMenu(items: submenu, target: target)
|
||||
}
|
||||
}
|
||||
|
23
phpmon/Common/Extensions/NVAlertExtension.swift
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// NVAlertExtension.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 16/07/2024.
|
||||
// Copyright © 2024 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
|
||||
extension NVAlert {
|
||||
/**
|
||||
Shows the modal for a particular error.
|
||||
*/
|
||||
@MainActor public static func show(for error: Error & AlertableError) {
|
||||
let key = error.getErrorMessageKey()
|
||||
return NVAlert().withInformation(
|
||||
title: "\(key).title".localized,
|
||||
subtitle: "\(key).description".localized
|
||||
).withPrimary(text: "generic.ok".localized).show()
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ class PhpInstallation {
|
||||
|
||||
var iniFiles: [PhpConfigurationFile] = []
|
||||
|
||||
var isPreRelease: Bool = false
|
||||
|
||||
var isMissingBinary: Bool = false
|
||||
|
||||
var isHealthy: Bool = true
|
||||
@ -59,6 +61,10 @@ class PhpInstallation {
|
||||
trimNewlines: false
|
||||
).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if longVersionString.contains("-dev") {
|
||||
isPreRelease = true
|
||||
}
|
||||
|
||||
// The parser should always work, or the string has to be very unusual.
|
||||
// If so, the app SHOULD crash, so that the users report what's up.
|
||||
versionNumber = try! VersionNumber.parse(longVersionString)
|
||||
|
@ -8,9 +8,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Process: @unchecked Sendable {}
|
||||
extension Timer: @unchecked Sendable {}
|
||||
|
||||
class RealShell: ShellProtocol {
|
||||
/**
|
||||
The launch path of the terminal in question that is used.
|
||||
@ -184,25 +181,26 @@ class RealShell: ShellProtocol {
|
||||
}
|
||||
|
||||
return try await withCheckedThrowingContinuation({ continuation in
|
||||
let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { _ in
|
||||
let task = Task {
|
||||
try await Task.sleep(nanoseconds: timeout.nanoseconds)
|
||||
// Only terminate if the process is still running
|
||||
if process.isRunning {
|
||||
process.terminationHandler = nil
|
||||
process.terminate()
|
||||
return continuation.resume(throwing: ShellError.timedOut)
|
||||
continuation.resume(throwing: ShellError.timedOut)
|
||||
}
|
||||
}
|
||||
|
||||
process.terminationHandler = { [timer, output] process in
|
||||
timer.invalidate()
|
||||
process.terminationHandler = { [output] process in
|
||||
task.cancel()
|
||||
|
||||
process.haltListening()
|
||||
|
||||
if !output.err.isEmpty {
|
||||
return continuation.resume(returning: (process, .err(output.err)))
|
||||
continuation.resume(returning: (process, .err(output.err)))
|
||||
} else {
|
||||
continuation.resume(returning: (process, .out(output.out)))
|
||||
}
|
||||
|
||||
return continuation.resume(returning: (process, .out(output.out)))
|
||||
}
|
||||
|
||||
process.launch()
|
||||
@ -210,3 +208,9 @@ class RealShell: ShellProtocol {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension TimeInterval {
|
||||
var nanoseconds: UInt64 {
|
||||
return UInt64(self * 1_000_000_000)
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ class TestableFileSystem: FileSystemProtocol {
|
||||
self.files = files
|
||||
|
||||
// Ensure that each of the ~ characters are replaced with the home directory path
|
||||
for key in self.files.keys where key.contains("~") {
|
||||
self.files.renameKey(
|
||||
fromKey: key,
|
||||
toKey: key.replacingOccurrences(of: "~", with: self.homeDirectory)
|
||||
)
|
||||
accessQueue.sync {
|
||||
for (key, value) in files {
|
||||
let adjustedKey = key.contains("~") ? key.replacingOccurrences(of: "~", with: self.homeDirectory) : key
|
||||
self.files[adjustedKey] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that intermediate directories are created
|
||||
@ -46,38 +46,49 @@ class TestableFileSystem: FileSystemProtocol {
|
||||
*/
|
||||
private(set) var homeDirectory = "/Users/fake"
|
||||
|
||||
/**
|
||||
Serial dispatch queue for ensuring thread-safe access to the `files` dictionary.
|
||||
*/
|
||||
private let accessQueue = DispatchQueue(label: "com.testablefilesystem.accessQueue")
|
||||
|
||||
// MARK: - Basics
|
||||
|
||||
func createDirectory(_ path: String, withIntermediateDirectories: Bool) throws {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
if files[path] != nil {
|
||||
throw TestableFileSystemError.alreadyExists
|
||||
try accessQueue.sync {
|
||||
if files[path] != nil {
|
||||
throw TestableFileSystemError.alreadyExists
|
||||
}
|
||||
|
||||
self.createIntermediateDirectories(path)
|
||||
|
||||
self.files[path] = .fake(.directory)
|
||||
}
|
||||
|
||||
self.createIntermediateDirectories(path)
|
||||
|
||||
self.files[path] = .fake(.directory)
|
||||
}
|
||||
|
||||
func writeAtomicallyToFile(_ path: String, content: String) throws {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
if files[path] != nil {
|
||||
throw TestableFileSystemError.alreadyExists
|
||||
}
|
||||
try accessQueue.sync {
|
||||
if files[path] != nil {
|
||||
throw TestableFileSystemError.alreadyExists
|
||||
}
|
||||
|
||||
self.files[path] = .fake(.text, content)
|
||||
self.files[path] = .fake(.text, content)
|
||||
}
|
||||
}
|
||||
|
||||
func getStringFromFile(_ path: String) throws -> String {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
throw TestableFileSystemError.fileMissing
|
||||
}
|
||||
return try accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
throw TestableFileSystemError.fileMissing
|
||||
}
|
||||
|
||||
return file.content ?? ""
|
||||
return file.content ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
func getShallowContentsOfDirectory(_ path: String) throws -> [String] {
|
||||
@ -88,32 +99,36 @@ class TestableFileSystem: FileSystemProtocol {
|
||||
seek = "\(seek)/"
|
||||
}
|
||||
|
||||
return self.files.keys
|
||||
.filter { $0.hasPrefix(seek) }
|
||||
.map { $0.replacingOccurrences(of: seek, with: "") }
|
||||
.filter { !$0.contains("/") }
|
||||
return accessQueue.sync {
|
||||
self.files.keys
|
||||
.filter { $0.hasPrefix(seek) }
|
||||
.map { $0.replacingOccurrences(of: seek, with: "") }
|
||||
.filter { !$0.contains("/") }
|
||||
}
|
||||
}
|
||||
|
||||
func getDestinationOfSymlink(_ path: String) throws -> String {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
throw TestableFileSystemError.fileMissing
|
||||
}
|
||||
return try accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
throw TestableFileSystemError.fileMissing
|
||||
}
|
||||
|
||||
if file.type != .symlink {
|
||||
throw TestableFileSystemError.notSymlink
|
||||
}
|
||||
if file.type != .symlink {
|
||||
throw TestableFileSystemError.notSymlink
|
||||
}
|
||||
|
||||
guard let pathToSymlink = file.content else {
|
||||
throw TestableFileSystemError.invalidSymlink
|
||||
}
|
||||
guard let pathToSymlink = file.content else {
|
||||
throw TestableFileSystemError.invalidSymlink
|
||||
}
|
||||
|
||||
if !files.keys.contains(pathToSymlink) {
|
||||
throw TestableFileSystemError.invalidSymlink
|
||||
}
|
||||
if !files.keys.contains(pathToSymlink) {
|
||||
throw TestableFileSystemError.invalidSymlink
|
||||
}
|
||||
|
||||
return pathToSymlink
|
||||
return pathToSymlink
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Move & Delete Files
|
||||
@ -122,27 +137,31 @@ class TestableFileSystem: FileSystemProtocol {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
let newPath = newPath.replacingTildeWithHomeDirectory
|
||||
|
||||
self.files.keys.forEach { key in
|
||||
if key.hasPrefix(path) {
|
||||
self.files.renameKey(
|
||||
fromKey: key,
|
||||
toKey: key.replacingOccurrences(of: path, with: newPath)
|
||||
)
|
||||
accessQueue.sync {
|
||||
self.files.keys.forEach { key in
|
||||
if key.hasPrefix(path) {
|
||||
self.files.renameKey(
|
||||
fromKey: key,
|
||||
toKey: key.replacingOccurrences(of: path, with: newPath)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.files.renameKey(fromKey: path, toKey: newPath)
|
||||
self.files.renameKey(fromKey: path, toKey: newPath)
|
||||
}
|
||||
}
|
||||
|
||||
func remove(_ path: String) throws {
|
||||
// Remove recursively
|
||||
self.files.keys.forEach { key in
|
||||
if key.hasPrefix(path) {
|
||||
self.files.removeValue(forKey: key)
|
||||
accessQueue.sync {
|
||||
// Remove recursively
|
||||
self.files.keys.forEach { key in
|
||||
if key.hasPrefix(path) {
|
||||
self.files.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.files.removeValue(forKey: path)
|
||||
self.files.removeValue(forKey: path)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: — Attributes
|
||||
@ -150,11 +169,13 @@ class TestableFileSystem: FileSystemProtocol {
|
||||
func makeExecutable(_ path: String) throws {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
throw TestableFileSystemError.fileMissing
|
||||
}
|
||||
try accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
throw TestableFileSystemError.fileMissing
|
||||
}
|
||||
|
||||
file.type = .binary
|
||||
file.type = .binary
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Checks
|
||||
@ -162,93 +183,107 @@ class TestableFileSystem: FileSystemProtocol {
|
||||
func isExecutableFile(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path.replacingTildeWithHomeDirectory] else {
|
||||
return false
|
||||
}
|
||||
return accessQueue.sync {
|
||||
guard let file = files[path.replacingTildeWithHomeDirectory] else {
|
||||
return false
|
||||
}
|
||||
|
||||
return file.type == .binary
|
||||
return file.type == .binary
|
||||
}
|
||||
}
|
||||
|
||||
func isWriteableFile(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path.replacingTildeWithHomeDirectory] else {
|
||||
return false
|
||||
}
|
||||
return accessQueue.sync {
|
||||
guard let file = files[path.replacingTildeWithHomeDirectory] else {
|
||||
return false
|
||||
}
|
||||
|
||||
return !file.readOnly
|
||||
return !file.readOnly
|
||||
}
|
||||
}
|
||||
|
||||
func anyExists(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
return files.keys.contains(path)
|
||||
return accessQueue.sync {
|
||||
files.keys.contains(path)
|
||||
}
|
||||
}
|
||||
|
||||
func fileExists(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
return accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
|
||||
return [.binary, .symlink, .text].contains(file.type)
|
||||
return [.binary, .symlink, .text].contains(file.type)
|
||||
}
|
||||
}
|
||||
|
||||
func directoryExists(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
return accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
|
||||
return [.directory].contains(file.type)
|
||||
return [.directory].contains(file.type)
|
||||
}
|
||||
}
|
||||
|
||||
func isSymlink(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
return accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
|
||||
return file.type == .symlink
|
||||
return file.type == .symlink
|
||||
}
|
||||
}
|
||||
|
||||
func isDirectory(_ path: String) -> Bool {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
return accessQueue.sync {
|
||||
guard let file = files[path] else {
|
||||
return false
|
||||
}
|
||||
|
||||
return file.type == .directory
|
||||
return file.type == .directory
|
||||
}
|
||||
}
|
||||
|
||||
public func printContents() {
|
||||
for key in self.files.keys.sorted() {
|
||||
print("\(key) -> \(self.files[key]!.type)")
|
||||
accessQueue.sync {
|
||||
for key in self.files.keys.sorted() {
|
||||
print("\(key) -> \(self.files[key]!.type)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func createIntermediateDirectories(_ path: String) {
|
||||
let path = path.replacingTildeWithHomeDirectory
|
||||
|
||||
let items = path.components(separatedBy: "/")
|
||||
|
||||
var preceding = ""
|
||||
|
||||
var directoriesToCreate: [String] = []
|
||||
|
||||
for item in items {
|
||||
let key = preceding == "/"
|
||||
? "/\(item)"
|
||||
: "\(preceding)/\(item)"
|
||||
|
||||
if !self.files.keys.contains(key) {
|
||||
self.files[key] = .fake(.directory)
|
||||
}
|
||||
|
||||
let key = preceding == "/" ? "/\(item)" : "\(preceding)/\(item)"
|
||||
directoriesToCreate.append(key)
|
||||
preceding = key
|
||||
}
|
||||
|
||||
for key in directoriesToCreate where !self.files.keys.contains(key) {
|
||||
self.files[key] = .fake(.directory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
<p><b>Do you enjoy using the app? Is it helping you save time?</b> Leave a <a href="https://phpmon.app/github">star on GitHub</a>!</p>
|
||||
<p><b>Having issues?</b> Consult the <a href="https://phpmon.app/faq">FAQ</a> section, I did my best to ensure everything is documented.</p>
|
||||
<p><b>Want to support further development of PHP Monitor?</b> You can <a href="https://phpmon.app/sponsor">financially support</a> the continued development of this app.</p>
|
||||
<p><b>Get the latest on Twitter or Mastodon.</b> Give me a <a href="https://twitter.com/nicoverbruggen">follow on Twitter</a> or <a href="https://phpc.social/@nicoverbruggen">Mastodon</a> to learn about what's brewing and when new updates drop.</p>
|
||||
<p><b>Get the latest on Bluesky or Mastodon.</b> Give me a <a href="https://bsky.app/profile/nicoverbruggen.be">follow on Bluesky</a> or <a href="https://phpc.social/@nicoverbruggen">Mastodon</a> to learn about what's brewing and when new updates drop.</p>
|
||||
<p><b>Special thanks</b> to all current and past <a href="https://github.com/sponsors/nicoverbruggen#sponsors"><b>sponsors</b></a> of PHP Monitor, who have helped to make further development of the app possible.</p>
|
||||
<p><b>Made possible by these GitHub Sponsors</b>: @abdusfauzi, @abicons, @adrolli, @andresayej, @andyunleashed, @anzacorp, @argirisp, @AshPowell, @aurawindsurfing, @awsmug, @barrycarton, @BertvanHoekelen, @calebporzio, @caseyalee, @cgreuling, @cjcox17, @Diewy, @drfraker, @driftingly, @duellsy, @edalzell, @EYOND, @faithfm, @frankmichel, @gwleuverink, @hopkins385, @intrepidws, @jacksleight, @JacobBennett, @jasonvarga, @jeromegamez, @jimmyaldape, @jimmysawczuk, @joetannenbaum, @jolora, @joshuablum, @jpeinelt, @jreviews, @JustSteveKing, @Kajvdh, @KFoobar, @Laravel-Backpack, @leganz, @martinleveille, @mathiasonea, @matthewmnewman, @mcastillo1030, @megabubbletea, @mennen-online, @mike-healy, @mostafakram, @mpociot, @MrMicky-FR, @MrMooky, @murdercode, @nckrtl, @nhedger, @ninjaparade, @ozanuzer, @pepatel, @philbraun, @pickuse2013, @pk-informatics, @Plytas, @rderimay, @rickyjohnston, @rico, @RobertBoes, @runofthemill, @SahinU88, @sdebacker, @sdevore, @shadracnicholas, @simonhamp, @SRWieZ, @stefanbauer, @StriveMedia, @swilla, @Tailcode-Studio, @theutz, @ThomasEnssner, @tillkruss, @timothyrowan, @ttnppedr, @vincent-tarrit, @WheresMarco, @xPand4B, @xuandung38, @yeslandi89, @zackkatz, @zacksmash, @zaherg.<br/>(Some names have been omitted due to their sponsorships being private. Thank you all!)</p>
|
||||
<p><b>Made possible by these GitHub Sponsors</b>: @abdusfauzi, @abicons, @adibnoh, @adrolli, @andresayej, @andyunleashed, @anzacorp, @argirisp, @ash-jc-allen, @AshPowell, @aurawindsurfing, @awsmug, @barrycarton, @BertvanHoekelen, @calebporzio, @casenxu, @caseyalee, @cgreuling, @cjcox17, @clescuyer, @codelinde, @designhammer, @Diewy, @drfraker, @driftingly, @duellsy, @e9li, @edalzell, @EYOND, @faithfm, @frankmichel, @gekich, @gpluess, @gwleuverink, @hopkins385, @incon, @intrepidws, @israaraujo, @jacksleight, @JacobBennett, @jasonvarga, @jeromegamez, @jimmyaldape, @jimmysawczuk, @joetannenbaum, @jolora, @jorisnoo, @joshuablum, @jpeinelt, @jreviews, @JustSteveKing, @Kajvdh, @KFoobar, @kholisabdullah, @Laravel-Backpack, @leganz, @lucianvacaroiu,@martinleveille, @mathiasonea, @matthewmnewman, @mcastillo1030, @megabubbletea, @megabubbleteam, @mennen-online, @mike-healy, @mostafakram, @mpociot, @MrMicky-FR, @MrMooky, @murdercode, @nckrtl, @nhedger, @ninjaparade, @ozanuzer, @pepatel, @philbraun, @pickuse2013, @pk-informatics, @Plytas, @rastitkac, @rderimay, @renecum, @richardhulbert, @richardtape, @rickyjohnston, @rico, @RobertBoes, @runofthemill, @SahinU88, @sdebacker, @sdevore, @shadracnicholas, @simonhamp, @slaFFik, @spatie, @SRWieZ, @stefanbauer, @stefanzweifel, @StriveMedia, @swilla, @Tailcode-Studio, @theutz, @ThomasEnssner, @tillkruss, @timothyrowan, @ttnppedr, @vincent-tarrit, @vintagesucks, @WheresMarco, @xPand4B, @xuandung38, @yeslandi89, @zackkatz, @zacksmash, @zaherg.<br/>(This is a historical list of sponsors, not current sponsors. Some names have been omitted due to their sponsorships being private. Thank you all!)</p>
|
||||
<p><b>Localization credits:</b></br>
|
||||
‐ English, Dutch</b> by @nicoverbruggen</br>
|
||||
‐ Vietnamese</b> by @xuandung38</br>
|
||||
@ -26,6 +26,8 @@
|
||||
‐ Portuguese</b> by @joseborges</br>
|
||||
‐ French</b> by @nhedger, @tplesnar</br>
|
||||
‐ Chinese</b> by @guanguans</br>
|
||||
</br>
|
||||
Other languages are considered experimental, and were generated via a local LLM. If you have feedback or concerns, please don't hesitate to get in touch.
|
||||
</p>
|
||||
<br/>
|
||||
</body>
|
||||
|
@ -89,6 +89,9 @@ class App {
|
||||
/** List of detected (installed) applications that PHP Monitor can work with. */
|
||||
var detectedApplications: [Application] = []
|
||||
|
||||
/** Favorites storage, which keeps track of favorited domains. */
|
||||
var favorites = Favorites.shared
|
||||
|
||||
/** The warning manager, responsible for keeping track of warnings. */
|
||||
var warnings = WarningManager.shared
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
import NVAlert
|
||||
|
||||
class AppUpdater {
|
||||
var caskFile: CaskFile!
|
||||
@ -72,7 +73,7 @@ class AppUpdater {
|
||||
: "brew upgrade phpmon"
|
||||
|
||||
Task { @MainActor in
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "updater.alerts.newer_version_available.title"
|
||||
.localized(latestVersionOnline.humanReadable),
|
||||
subtitle: "updater.alerts.newer_version_available.subtitle"
|
||||
@ -112,7 +113,7 @@ class AppUpdater {
|
||||
|
||||
public func presentNoNewerVersionAvailableAlert() {
|
||||
Task { @MainActor in
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "updater.alerts.is_latest_version.title".localized,
|
||||
subtitle: "updater.alerts.is_latest_version.subtitle".localized(App.shortVersion),
|
||||
description: ""
|
||||
@ -124,7 +125,7 @@ class AppUpdater {
|
||||
|
||||
public func presentCouldNotRetrieveUpdate() {
|
||||
Task { @MainActor in
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().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(
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
|
||||
<capability name="Image references" minToolsVersion="12.0"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Search Toolbar Item" minToolsVersion="12.0" minSystemVersion="11.0"/>
|
||||
@ -515,10 +515,10 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-374" y="2267"/>
|
||||
</scene>
|
||||
<!--Better AlertVC-->
|
||||
<!--AlertVC-->
|
||||
<scene sceneID="y9E-bB-wIG">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="noticeVC" id="hkw-9V-NxP" customClass="BetterAlertVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController storyboardIdentifier="noticeVC" id="hkw-9V-NxP" customClass="NVAlertVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="UPH-hV-Naz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="500" height="212"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@ -923,6 +923,50 @@ Gw
|
||||
<outlet property="labelSiteName" destination="XJL-Uw-frD" id="f0t-vd-W68"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="domainListNameCellFavorited" wantsLayer="YES" id="Byb-te-u65" customClass="DomainListNameCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||
<rect key="frame" x="69" y="54" width="200" height="54"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="aot-FJ-HIk">
|
||||
<rect key="frame" x="33" y="26" width="145" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="my-domain-name.test" id="LHu-UF-QlC">
|
||||
<font key="font" metaFont="systemSemibold" size="13"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="GNH-l8-oki">
|
||||
<rect key="frame" x="33" y="12" width="75" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="~/path/to/site" id="LNw-Ju-0Ot">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="3Wp-DX-An9">
|
||||
<rect key="frame" x="5" y="4" width="20" height="47"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="Q76-fI-lkW">
|
||||
<imageReference key="image" image="star.circle.fill" catalog="system" symbolScale="large"/>
|
||||
</imageCell>
|
||||
<color key="contentTintColor" name="AccentColor"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3Wp-DX-An9" firstAttribute="leading" secondItem="Byb-te-u65" secondAttribute="leading" constant="5" id="CTd-ON-loK"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="aot-FJ-HIk" secondAttribute="trailing" constant="20" symbolic="YES" id="Csc-Dy-H4K"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="GNH-l8-oki" secondAttribute="trailing" constant="20" symbolic="YES" id="H10-MG-hCG"/>
|
||||
<constraint firstItem="GNH-l8-oki" firstAttribute="leading" secondItem="aot-FJ-HIk" secondAttribute="leading" id="Hk0-x3-RyN"/>
|
||||
<constraint firstItem="3Wp-DX-An9" firstAttribute="top" secondItem="Byb-te-u65" secondAttribute="top" constant="9" id="erH-dR-K7S"/>
|
||||
<constraint firstItem="aot-FJ-HIk" firstAttribute="top" secondItem="Byb-te-u65" secondAttribute="top" constant="12" id="ktI-fg-qaX"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Wp-DX-An9" secondAttribute="bottom" constant="9" id="uyc-26-gZb"/>
|
||||
<constraint firstItem="aot-FJ-HIk" firstAttribute="leading" secondItem="Byb-te-u65" secondAttribute="leading" constant="35" id="vXE-jj-lLF"/>
|
||||
<constraint firstItem="GNH-l8-oki" firstAttribute="top" secondItem="aot-FJ-HIk" secondAttribute="bottom" id="wSX-fR-O7a"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="labelPathName" destination="GNH-l8-oki" id="GC1-TA-lIk"/>
|
||||
<outlet property="labelSiteName" destination="aot-FJ-HIk" id="HdZ-Rh-ua6"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="ENVIRONMENT" width="100" minWidth="100" maxWidth="150" id="hzb-XI-Out">
|
||||
@ -1092,13 +1136,6 @@ Gw
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableHeaderView>
|
||||
</scrollView>
|
||||
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="ZiS-Gq-TLQ">
|
||||
<rect key="frame" x="298" y="150" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="XK3-AR-Oc0"/>
|
||||
<constraint firstAttribute="height" constant="30" id="lfW-dB-Eu3"/>
|
||||
</constraints>
|
||||
</progressIndicator>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="wcV-ed-8Bv">
|
||||
<rect key="frame" x="113" y="5" width="400" height="300"/>
|
||||
<constraints>
|
||||
@ -1106,27 +1143,57 @@ Gw
|
||||
<constraint firstAttribute="height" constant="300" id="Xpi-Rl-xmb"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
<visualEffectView hidden="YES" blendingMode="behindWindow" material="popover" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="r8h-6t-ZNm">
|
||||
<rect key="frame" x="263" y="125" width="100" height="80"/>
|
||||
<subviews>
|
||||
<progressIndicator wantsLayer="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="ZiS-Gq-TLQ">
|
||||
<rect key="frame" x="35" y="35" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="XK3-AR-Oc0"/>
|
||||
<constraint firstAttribute="height" constant="30" id="lfW-dB-Eu3"/>
|
||||
</constraints>
|
||||
</progressIndicator>
|
||||
<textField wantsLayer="YES" focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xoy-5Y-WDT">
|
||||
<rect key="frame" x="15" y="14" width="71" height="13"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="PLEASE WAIT" id="tMX-Ky-caT">
|
||||
<font key="font" metaFont="system" size="10"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="xoy-5Y-WDT" firstAttribute="top" secondItem="ZiS-Gq-TLQ" secondAttribute="bottom" constant="8" symbolic="YES" id="4pN-Xn-po4"/>
|
||||
<constraint firstAttribute="width" constant="100" id="Fo3-MY-e5e"/>
|
||||
<constraint firstItem="xoy-5Y-WDT" firstAttribute="centerX" secondItem="r8h-6t-ZNm" secondAttribute="centerX" id="JPe-3T-uYg"/>
|
||||
<constraint firstAttribute="height" constant="80" id="hcO-TE-dKr"/>
|
||||
<constraint firstItem="ZiS-Gq-TLQ" firstAttribute="centerX" secondItem="r8h-6t-ZNm" secondAttribute="centerX" id="sbD-l6-6kk"/>
|
||||
<constraint firstItem="ZiS-Gq-TLQ" firstAttribute="centerY" secondItem="r8h-6t-ZNm" secondAttribute="centerY" constant="-10" id="x7S-hb-YV1"/>
|
||||
</constraints>
|
||||
</visualEffectView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="p0j-eB-I2i" firstAttribute="leading" secondItem="rIZ-4U-bhj" secondAttribute="leading" id="2Tx-yb-xrv"/>
|
||||
<constraint firstItem="wcV-ed-8Bv" firstAttribute="centerX" secondItem="rIZ-4U-bhj" secondAttribute="centerX" id="DPz-kQ-aP0"/>
|
||||
<constraint firstItem="wcV-ed-8Bv" firstAttribute="centerY" secondItem="rIZ-4U-bhj" secondAttribute="centerY" id="HCW-zJ-gSY"/>
|
||||
<constraint firstItem="r8h-6t-ZNm" firstAttribute="centerX" secondItem="rIZ-4U-bhj" secondAttribute="centerX" id="JF1-EN-aWm"/>
|
||||
<constraint firstItem="p0j-eB-I2i" firstAttribute="top" secondItem="rIZ-4U-bhj" secondAttribute="top" id="Pst-5A-dI0"/>
|
||||
<constraint firstAttribute="bottom" secondItem="p0j-eB-I2i" secondAttribute="bottom" id="QEw-5m-u1s"/>
|
||||
<constraint firstItem="ZiS-Gq-TLQ" firstAttribute="centerY" secondItem="rIZ-4U-bhj" secondAttribute="centerY" constant="-10" id="XqX-Tf-8ck"/>
|
||||
<constraint firstItem="ZiS-Gq-TLQ" firstAttribute="centerX" secondItem="rIZ-4U-bhj" secondAttribute="centerX" id="eD8-TV-7dF"/>
|
||||
<constraint firstItem="r8h-6t-ZNm" firstAttribute="centerY" secondItem="rIZ-4U-bhj" secondAttribute="centerY" constant="-10" id="dkm-LB-eCY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="p0j-eB-I2i" secondAttribute="trailing" id="zWH-TD-RZv"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="labelProgressIndicator" destination="xoy-5Y-WDT" id="Wfj-oK-Bni"/>
|
||||
<outlet property="noResultsView" destination="wcV-ed-8Bv" id="K3s-fb-1aN"/>
|
||||
<outlet property="progressIndicator" destination="ZiS-Gq-TLQ" id="Ylb-Vk-uub"/>
|
||||
<outlet property="progressIndicatorContainer" destination="r8h-6t-ZNm" id="x0d-1g-Kzw"/>
|
||||
<outlet property="tableView" destination="cp3-34-pQj" id="sdw-Ac-27X"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="HgD-aB-bQb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="323" y="723"/>
|
||||
<point key="canvasLocation" x="323" y="722.5"/>
|
||||
</scene>
|
||||
<!--Add ProxyVC-->
|
||||
<scene sceneID="g8z-pE-RL9">
|
||||
@ -1355,7 +1422,7 @@ Gw
|
||||
<visualEffectView blendingMode="behindWindow" material="toolTip" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="F37-zt-gM3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="540" height="177"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FhN-AM-SkI">
|
||||
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FhN-AM-SkI">
|
||||
<rect key="frame" x="13" y="13" width="114" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="[i18n] Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="LxP-t4-H2W">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -1374,7 +1441,7 @@ Gw
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pYe-Qu-qnK">
|
||||
<rect key="frame" x="187" y="20" width="333" height="20"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="L5n-Gw-J27">
|
||||
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="L5n-Gw-J27">
|
||||
<rect key="frame" x="-7" y="-7" width="172" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="[i18n] Create a Link" bezelStyle="rounded" image="IconLinked" imagePosition="left" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="8UP-Sw-TP6">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -1385,7 +1452,7 @@ Gw
|
||||
<action selector="pressedCreateLink:" target="gOD-Gu-zDG" id="77M-Ip-GMi"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="01Z-IV-hv1">
|
||||
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="01Z-IV-hv1">
|
||||
<rect key="frame" x="159" y="-7" width="181" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="[i18n] Create a Proxy" bezelStyle="rounded" image="IconProxy" imagePosition="left" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="bJ4-q8-1Ej">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -1503,6 +1570,10 @@ Gw
|
||||
<image name="Lock" width="30" height="30"/>
|
||||
<image name="arrow.clockwise" catalog="system" width="14" height="16"/>
|
||||
<image name="plus" catalog="system" width="14" height="13"/>
|
||||
<image name="star.circle.fill" catalog="system" width="20" height="20"/>
|
||||
<namedColor name="AccentColor">
|
||||
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<namedColor name="IconColorGreen">
|
||||
<color red="0.24699999392032623" green="0.69700002670288086" blue="0.50099998712539673" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
import NVAlert
|
||||
|
||||
class ValetServicesManager: ServicesManager {
|
||||
override init() {
|
||||
@ -131,7 +132,7 @@ class ValetServicesManager: ServicesManager {
|
||||
Log.err("The service '\(named)' is now reporting an error.")
|
||||
|
||||
guard let errorLogPath = after.error_log_path else {
|
||||
return BetterAlert().withInformation(
|
||||
return NVAlert().withInformation(
|
||||
title: "alert.service_error.title".localized(named),
|
||||
subtitle: "alert.service_error.subtitle.no_error_log".localized(named),
|
||||
description: "alert.service_error.extra".localized
|
||||
@ -140,7 +141,7 @@ class ValetServicesManager: ServicesManager {
|
||||
.show()
|
||||
}
|
||||
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "alert.service_error.title".localized(named),
|
||||
subtitle: "alert.service_error.subtitle.error_log".localized(named),
|
||||
description: "alert.service_error.extra".localized
|
||||
|
74
phpmon/Domain/App/Startup+Timers.swift
Normal file
@ -0,0 +1,74 @@
|
||||
//
|
||||
// Startup+Timers.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 23/07/2025.
|
||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
import NVAlert
|
||||
|
||||
extension Startup {
|
||||
@MainActor static var startupTimer: Timer?
|
||||
@MainActor static var launchTime: Date?
|
||||
|
||||
/** Returns a human-readable version to indicate how many seconds elapsed since boot. */
|
||||
@MainActor static var humanReadableSinceBootTime: String {
|
||||
return String(format: "%.2f", Date().timeIntervalSince(Self.launchTime!))
|
||||
}
|
||||
|
||||
/** Starts the timeout timer that keeps track of how long the app takes to boot. */
|
||||
@MainActor func startStartupTimer() {
|
||||
Self.launchTime = Date()
|
||||
Self.startupTimer = Timer.scheduledTimer(
|
||||
timeInterval: Constants.SlowBootThresholdInterval, target: self,
|
||||
selector: #selector(startupTimeout), userInfo: nil, repeats: false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Invalidates and stops the startup timer.
|
||||
This is only called if the slow boot threshold is not exceeded.
|
||||
*/
|
||||
@MainActor static func invalidateTimeoutTimer() {
|
||||
if Self.startupTimer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
Log.info("PHP Monitor was quick; elapsed time: \(Self.humanReadableSinceBootTime) sec.")
|
||||
Self.startupTimer?.invalidate()
|
||||
Self.startupTimer = nil
|
||||
}
|
||||
|
||||
/**
|
||||
Displays an alert for when the application startup process takes too long.
|
||||
*/
|
||||
@MainActor @objc func startupTimeout() {
|
||||
Log.info("PHP Monitor was slow; elapsed time: \(Self.humanReadableSinceBootTime) sec.")
|
||||
|
||||
// Invalidate the timer
|
||||
Self.startupTimer?.invalidate()
|
||||
Self.startupTimer = nil
|
||||
|
||||
// Present an alert that lets the user know about the slow start
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "startup.timeout.title".localized,
|
||||
subtitle: "startup.timeout.subtitle".localized,
|
||||
description: "startup.timeout.description".localized
|
||||
)
|
||||
.withPrimary(text: "alert.cannot_start.close".localized, action: { vc in
|
||||
vc.close(with: .alertFirstButtonReturn)
|
||||
exit(1)
|
||||
})
|
||||
.withSecondary(text: "startup.timeout.ignore".localized, action: { vc in
|
||||
vc.close(with: .alertSecondButtonReturn)
|
||||
})
|
||||
.withTertiary(text: "", action: { _ in
|
||||
NSWorkspace.shared.open(URL(string: "https://github.com/nicoverbruggen/phpmon/issues/294")!)
|
||||
})
|
||||
.show()
|
||||
}
|
||||
}
|
@ -7,9 +7,9 @@
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
import NVAlert
|
||||
|
||||
class Startup {
|
||||
|
||||
/**
|
||||
Checks the user's environment and checks if PHP Monitor can be used properly.
|
||||
This checks if PHP is installed, Valet is running, the appropriate permissions are set, and more.
|
||||
@ -21,6 +21,11 @@ class Startup {
|
||||
// Do the important system setup checks
|
||||
Log.info("The user is running PHP Monitor with the architecture: \(App.architecture)")
|
||||
|
||||
// Set up a "background" timer on the main thread
|
||||
Task { @MainActor in
|
||||
startStartupTimer()
|
||||
}
|
||||
|
||||
for group in self.groups {
|
||||
if group.condition() {
|
||||
Log.info("Now running \(group.checks.count) \(group.name) checks!")
|
||||
@ -44,6 +49,7 @@ class Startup {
|
||||
// If we get here, nothing has gone wrong. That's what we want!
|
||||
initializeSwitcher()
|
||||
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
||||
|
||||
Log.separator(as: .info)
|
||||
return true
|
||||
}
|
||||
@ -55,7 +61,7 @@ class Startup {
|
||||
*/
|
||||
@MainActor private func showAlert(for check: EnvironmentCheck) {
|
||||
if check.requiresAppRestart {
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: check.titleText,
|
||||
subtitle: check.subtitleText,
|
||||
@ -66,7 +72,7 @@ class Startup {
|
||||
}).show()
|
||||
}
|
||||
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: check.titleText,
|
||||
subtitle: check.subtitleText,
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
|
||||
@MainActor class ComposerWindow {
|
||||
private var shouldNotify: Bool! = nil
|
||||
@ -109,7 +110,7 @@ import Foundation
|
||||
// MARK: Alert
|
||||
|
||||
private func presentMissingAlert() {
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.composer_missing.title".localized,
|
||||
subtitle: "alert.composer_missing.subtitle".localized,
|
||||
|
@ -15,7 +15,8 @@ struct ProjectTypeDetection {
|
||||
public static let CommonDependencyList = [
|
||||
"laravel/framework": "Laravel",
|
||||
"symfony/symfony": "Symfony",
|
||||
"laravel/lumen": "Lumen"
|
||||
"laravel/lumen": "Lumen",
|
||||
"tempest/framework": "Tempest"
|
||||
]
|
||||
|
||||
/**
|
||||
|
@ -35,10 +35,11 @@ class Brew {
|
||||
|
||||
/// Each formula for each PHP version that can be installed.
|
||||
public static let phpVersionFormulae = [
|
||||
"8.5": "shivammathur/php/php@8.5",
|
||||
"8.4": "shivammathur/php/php@8.4",
|
||||
"8.3": "php@8.3",
|
||||
"8.2": "php@8.2",
|
||||
"8.1": "php@8.1",
|
||||
"8.3": "shivammathur/php/php@8.3",
|
||||
"8.2": "shivammathur/php/php@8.2",
|
||||
"8.1": "shivammathur/php/php@8.1",
|
||||
"8.0": "shivammathur/php/php@8.0",
|
||||
"7.4": "shivammathur/php/php@7.4",
|
||||
"7.3": "shivammathur/php/php@7.3",
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
|
||||
class BrewDiagnostics {
|
||||
/**
|
||||
@ -183,7 +184,7 @@ class BrewDiagnostics {
|
||||
*/
|
||||
private static func presentAlertAboutConflict() {
|
||||
Task { @MainActor in
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.php_alias_conflict.title".localized,
|
||||
subtitle: "alert.php_alias_conflict.info".localized
|
||||
|
@ -21,11 +21,12 @@ struct BrewPhpFormula: Equatable {
|
||||
/// The upgrade that is currently available, if it exists.
|
||||
let upgradeVersion: String?
|
||||
|
||||
// TODO: A rebuild attribute could be checked, to check if a Tap update exists for a pre-release version
|
||||
|
||||
/// Whether this formula is a stable version of PHP.
|
||||
let prerelease: Bool
|
||||
|
||||
/// Whether this formula's associated Homebrew file exists.
|
||||
var hasFormulaFile: Bool = false
|
||||
|
||||
/// Whether the formula is currently installed.
|
||||
var isInstalled: Bool {
|
||||
return installedVersion != nil
|
||||
@ -43,6 +44,7 @@ struct BrewPhpFormula: Equatable {
|
||||
self.installedVersion = installedVersion
|
||||
self.upgradeVersion = upgradeVersion
|
||||
self.prerelease = prerelease
|
||||
self.hasFormulaFile = checkFormulaFile()
|
||||
}
|
||||
|
||||
/// Whether the formula can be upgraded.
|
||||
@ -93,6 +95,21 @@ struct BrewPhpFormula: Equatable {
|
||||
return isHealthy() ?? true
|
||||
}
|
||||
|
||||
/**
|
||||
Verify whether the formula file exists (sourced via `shivammathur/homebrew-php`).
|
||||
If it does not exist, the formula cannot be installed.
|
||||
*/
|
||||
private func checkFormulaFile() -> Bool {
|
||||
guard let version = shortVersion else {
|
||||
return false
|
||||
}
|
||||
|
||||
return FileSystem.fileExists(
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-php/Formula/php@\(version).rb"
|
||||
.replacingOccurrences(of: "php@" + PhpEnvironments.brewPhpAlias, with: "php")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this PHP installation is healthy.
|
||||
* Uses the cached installation health check as basis.
|
||||
|
@ -39,19 +39,25 @@ class BrewPhpFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
OutdatedFormulae.self,
|
||||
from: rawJsonText
|
||||
).formulae.filter({ formula in
|
||||
formula.name.starts(with: "php")
|
||||
formula.name.starts(with: "shivammathur/php/php") || formula.name.starts(with: "php")
|
||||
})
|
||||
}
|
||||
|
||||
return Brew.phpVersionFormulae.map { (version, formula) in
|
||||
var fullVersion: String?
|
||||
var upgradeVersion: String?
|
||||
var isPrerelease: Bool = Constants.ExperimentalPhpVersions.contains(version)
|
||||
|
||||
if let install = PhpEnvironments.shared.cachedPhpInstallations[version] {
|
||||
fullVersion = install.versionNumber.text
|
||||
fullVersion = install.isPreRelease ? "\(fullVersion!)-dev" : fullVersion
|
||||
|
||||
upgradeVersion = outdated?.first(where: { formula in
|
||||
return formula.name == install.formulaName
|
||||
return formula.name.replacingOccurrences(of: "shivammathur/php/", with: "")
|
||||
== install.formulaName.replacingOccurrences(of: "shivammathur/php/", with: "")
|
||||
})?.current_version
|
||||
|
||||
isPrerelease = install.isPreRelease
|
||||
}
|
||||
|
||||
return BrewPhpFormula(
|
||||
@ -59,7 +65,7 @@ class BrewPhpFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
displayName: "PHP \(version)",
|
||||
installedVersion: fullVersion,
|
||||
upgradeVersion: upgradeVersion,
|
||||
prerelease: Constants.ExperimentalPhpVersions.contains(version)
|
||||
prerelease: isPrerelease
|
||||
)
|
||||
}.sorted { $0.displayName > $1.displayName }
|
||||
}
|
||||
|
@ -16,21 +16,65 @@ protocol BrewCommand {
|
||||
|
||||
extension BrewCommand {
|
||||
internal func reportInstallationProgress(_ text: String) -> (Double, String)? {
|
||||
if text.contains("Fetching") {
|
||||
// Special cases: downloading a manifest is effectively fetching metadata
|
||||
if text.contains("==> Downloading") && text.contains("/manifests/") {
|
||||
return (0.1, "phpman.steps.fetching".localized)
|
||||
}
|
||||
if text.contains("Downloading") {
|
||||
return (0.25, "phpman.steps.downloading".localized)
|
||||
}
|
||||
if text.contains("Installing") {
|
||||
return (0.60, "phpman.steps.installing".localized)
|
||||
}
|
||||
if text.contains("Pouring") {
|
||||
return (0.80, "phpman.steps.pouring".localized)
|
||||
}
|
||||
if text.contains("Summary") {
|
||||
|
||||
// Logical progress evaluation (reverse order for accuracy)
|
||||
if text.contains("==> Summary") {
|
||||
return (0.90, "phpman.steps.summary".localized)
|
||||
}
|
||||
if text.contains("==> Pouring") {
|
||||
if let subject = extractContext(from: text) {
|
||||
return (0.80, "phpman.steps.pouring".localized + "\n(\(subject))")
|
||||
}
|
||||
return (0.80, "phpman.steps.pouring".localized)
|
||||
}
|
||||
if text.contains("==> Installing") {
|
||||
if let subject = extractContext(from: text) {
|
||||
return (0.60, "phpman.steps.installing".localized + "\n(\(subject))")
|
||||
}
|
||||
return (0.60, "phpman.steps.installing".localized)
|
||||
}
|
||||
if text.contains("==> Downloading") {
|
||||
if let subject = extractContext(from: text) {
|
||||
return (0.25, "phpman.steps.downloading".localized + "\n(\(subject))")
|
||||
}
|
||||
return (0.25, "phpman.steps.downloading".localized)
|
||||
}
|
||||
if text.contains("==> Fetching") {
|
||||
return (0.1, "phpman.steps.fetching".localized)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
internal func extractContext(from text: String) -> String? {
|
||||
var pattern = #""#
|
||||
if text.contains("==> Fetching") {
|
||||
pattern = #"==> Fetching (\S+)"#
|
||||
}
|
||||
if text.contains("==> Downloading") {
|
||||
pattern = #"==> Downloading (\S+)"#
|
||||
}
|
||||
if text.contains("==> Installing") {
|
||||
pattern = #"==> Installing (\S+)"#
|
||||
}
|
||||
if text.contains("==> Pouring") {
|
||||
pattern = #"==> Pouring (\S+)"#
|
||||
}
|
||||
|
||||
guard let regex = try? NSRegularExpression(pattern: pattern) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let range = NSRange(text.startIndex..<text.endIndex, in: text)
|
||||
if let match = regex.firstMatch(in: text, options: [], range: range) {
|
||||
if let formulaRange = Range(match.range(at: 1), in: text) {
|
||||
return String(text[formulaRange])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,15 @@ class ValetInteractor {
|
||||
|
||||
// Keep track of the command we wish to run
|
||||
let action = site.secured ? "unsecure" : "secure"
|
||||
let command = "cd '\(site.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
|
||||
|
||||
// Use modernized version of command using domain name
|
||||
// This will allow us to secure multiple domains that use the same path
|
||||
var command = "sudo \(Paths.valet) \(action) '\(site.name)' && exit;"
|
||||
|
||||
// For Valet 2, use the old syntax; this has a known issue so Valet 3+ is preferred
|
||||
if !Valet.enabled(feature: .isolatedSites) {
|
||||
command = "cd '\(site.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
|
||||
}
|
||||
|
||||
// Run the command
|
||||
await Shell.quiet(command)
|
||||
|
@ -24,4 +24,6 @@ protocol ValetListable {
|
||||
|
||||
func getListableUrl() -> URL?
|
||||
|
||||
func getListableFavorited() -> Bool
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ class ValetProxy: ValetListable {
|
||||
var target: String
|
||||
var secured: Bool = false
|
||||
|
||||
var favorited: Bool = false
|
||||
var favoriteSignature: String {
|
||||
"proxy:domain:\(domain).\(tld)|target:\(target)"
|
||||
}
|
||||
|
||||
init(domain: String, target: String, secure: Bool, tld: String) {
|
||||
self.domain = domain
|
||||
self.tld = tld
|
||||
@ -28,6 +33,8 @@ class ValetProxy: ValetListable {
|
||||
secure: false,
|
||||
tld: configuration.tld
|
||||
)
|
||||
|
||||
self.favorited = Favorites.shared.contains(domain: self.domain)
|
||||
self.determineSecured()
|
||||
}
|
||||
|
||||
@ -61,12 +68,21 @@ class ValetProxy: ValetListable {
|
||||
return URL(string: "\(self.secured ? "https://" : "http://")\(self.domain).\(self.tld)")
|
||||
}
|
||||
|
||||
func getListableFavorited() -> Bool {
|
||||
return self.favorited
|
||||
}
|
||||
|
||||
// MARK: - Interactions
|
||||
|
||||
func determineSecured() {
|
||||
self.secured = FileSystem.fileExists("~/.config/valet/Certificates/\(self.domain).\(self.tld).key")
|
||||
}
|
||||
|
||||
func toggleFavorite() {
|
||||
self.favorited.toggle()
|
||||
Favorites.shared.toggle(domain: self.favoriteSignature)
|
||||
}
|
||||
|
||||
func toggleSecure() async throws {
|
||||
try await ValetInteractor.shared.toggleSecure(proxy: self)
|
||||
}
|
||||
|
@ -61,6 +61,11 @@ class ValetSite: ValetListable {
|
||||
?? "???"
|
||||
}
|
||||
|
||||
var favorited: Bool = false
|
||||
var favoriteSignature: String {
|
||||
"site:domain:\(name).\(tld)|path:\(absolutePath)"
|
||||
}
|
||||
|
||||
init(
|
||||
name: String,
|
||||
tld: String,
|
||||
@ -75,6 +80,7 @@ class ValetSite: ValetListable {
|
||||
self.secured = false
|
||||
|
||||
if makeDeterminations {
|
||||
self.favorited = Favorites.shared.contains(domain: favoriteSignature)
|
||||
determineSecured()
|
||||
determineIsolated()
|
||||
determineComposerPhpVersion()
|
||||
@ -305,12 +311,21 @@ class ValetSite: ValetListable {
|
||||
return URL(string: "\(self.secured ? "https://" : "http://")\(self.name).\(Valet.shared.config.tld)")
|
||||
}
|
||||
|
||||
func getListableFavorited() -> Bool {
|
||||
return self.favorited
|
||||
}
|
||||
|
||||
// MARK: - Interactions
|
||||
|
||||
func toggleSecure() async throws {
|
||||
try await ValetInteractor.shared.toggleSecure(site: self)
|
||||
}
|
||||
|
||||
func toggleFavorite() {
|
||||
self.favorited.toggle()
|
||||
Favorites.shared.toggle(domain: self.favoriteSignature)
|
||||
}
|
||||
|
||||
func isolate(version: String) async throws {
|
||||
try await ValetInteractor.shared.isolate(site: self, version: version)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
|
||||
extension Valet {
|
||||
|
||||
@ -16,7 +17,7 @@ extension Valet {
|
||||
public func notifyAboutUnsupportedTLD() {
|
||||
if Valet.shared.config.tld != "test" && Preferences.isEnabled(.warnAboutNonStandardTLD) {
|
||||
Task { @MainActor in
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "alert.warnings.tld_issue.title".localized,
|
||||
subtitle: "alert.warnings.tld_issue.subtitle".localized,
|
||||
description: "alert.warnings.tld_issue.description".localized
|
||||
@ -33,7 +34,7 @@ extension Valet {
|
||||
|
||||
public func notifyAboutOutdatedValetVersion(_ version: VersionNumber) {
|
||||
Task { @MainActor in
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.min_valet_version.title".localized,
|
||||
subtitle: "alert.min_valet_version.info".localized(
|
||||
@ -60,7 +61,7 @@ extension Valet {
|
||||
}
|
||||
|
||||
Task { @MainActor in
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.php_fpm_broken.title".localized,
|
||||
subtitle: "alert.php_fpm_broken.info".localized,
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import NVAlert
|
||||
|
||||
extension MainMenu {
|
||||
|
||||
@ -20,7 +21,7 @@ extension MainMenu {
|
||||
|
||||
@MainActor @objc func displayUnlinkedInfo() {
|
||||
Task { @MainActor in
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "phpman.unlinked.title".localized,
|
||||
subtitle: "phpman.unlinked.desc".localized,
|
||||
@ -32,7 +33,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor @objc func fixHomebrewPermissions() {
|
||||
if !BetterAlert()
|
||||
if !NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.fix_homebrew_permissions.title".localized,
|
||||
subtitle: "alert.fix_homebrew_permissions.subtitle".localized,
|
||||
@ -47,7 +48,7 @@ extension MainMenu {
|
||||
asyncExecution {
|
||||
try Actions.fixHomebrewPermissions()
|
||||
} success: {
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.fix_homebrew_permissions_done.title".localized,
|
||||
subtitle: "alert.fix_homebrew_permissions_done.subtitle".localized,
|
||||
@ -56,7 +57,7 @@ extension MainMenu {
|
||||
.withPrimary(text: "generic.ok".localized)
|
||||
.show()
|
||||
} failure: { error in
|
||||
BetterAlert.show(for: error as! HomebrewPermissionError)
|
||||
NVAlert.show(for: error as! HomebrewPermissionError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,7 +176,7 @@ extension MainMenu {
|
||||
return
|
||||
}
|
||||
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "alert.revert_description.title".localized,
|
||||
subtitle: "alert.revert_description.subtitle".localized(
|
||||
preset.textDescription
|
||||
@ -196,7 +197,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor @objc func showPresetHelp() {
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "preset_help_title".localized,
|
||||
subtitle: "preset_help_info".localized,
|
||||
description: "preset_help_desc".localized
|
||||
@ -263,7 +264,7 @@ extension MainMenu {
|
||||
Task { MainMenu.shared.switchToPhpVersion(version) }
|
||||
} else {
|
||||
Task {
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "alert.php_switch_unavailable.title".localized,
|
||||
subtitle: "alert.php_switch_unavailable.subtitle".localized(version)
|
||||
).withPrimary(
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
import NVAlert
|
||||
|
||||
extension MainMenu {
|
||||
|
||||
@ -19,7 +20,7 @@ extension MainMenu {
|
||||
return
|
||||
}
|
||||
|
||||
if !BetterAlert()
|
||||
if !NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.fix_my_valet.title".localized,
|
||||
subtitle: "alert.fix_my_valet.info".localized(PhpEnvironments.brewPhpAlias)
|
||||
@ -43,7 +44,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor private func presentAlertForMissingFormula() {
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.php_formula_missing.title".localized,
|
||||
subtitle: "alert.php_formula_missing.info".localized
|
||||
@ -53,7 +54,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor private func presentAlertForSameVersion() {
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.fix_my_valet_done.title".localized,
|
||||
subtitle: "alert.fix_my_valet_done.subtitle".localized,
|
||||
@ -64,7 +65,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor private func presentAlertForDifferentVersion(version: String) {
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.fix_my_valet_done.title".localized,
|
||||
subtitle: "alert.fix_my_valet_done.subtitle".localized,
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import NVAlert
|
||||
|
||||
extension MainMenu {
|
||||
/**
|
||||
@ -102,20 +103,20 @@ extension MainMenu {
|
||||
// Find out which services are active
|
||||
Log.info("The services manager knows about \(ServicesManager.shared.services.count) services.")
|
||||
|
||||
// Post-launch stats and update check, but only if not running tests
|
||||
await performPostLaunchActions()
|
||||
|
||||
// Check if the linked version has changed between launches of phpmon
|
||||
PhpGuard().compareToLastGlobalVersion()
|
||||
|
||||
// We are ready!
|
||||
PhpEnvironments.shared.isBusy = false
|
||||
|
||||
// Finally!
|
||||
Log.info("PHP Monitor is ready to serve!")
|
||||
|
||||
// Avoid showing the "startup timeout" alert
|
||||
Startup.invalidateTimeoutTimer()
|
||||
|
||||
// Check if we upgraded from a previous version
|
||||
AppUpdater.checkIfUpdateWasPerformed()
|
||||
|
||||
// Post-launch stats and update check, but only if not running tests
|
||||
await performPostLaunchActions()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,18 +124,24 @@ extension MainMenu {
|
||||
(This code is skipped when running SwiftUI previews.)
|
||||
*/
|
||||
private func performPostLaunchActions() async {
|
||||
if !isRunningSwiftUIPreview {
|
||||
Stats.incrementSuccessfulLaunchCount()
|
||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||
if isRunningSwiftUIPreview {
|
||||
return
|
||||
}
|
||||
|
||||
if Stats.successfulLaunchCount == 1 {
|
||||
Log.info("Should present the first launch screen!")
|
||||
Task { @MainActor in
|
||||
OnboardingWindowController.show()
|
||||
}
|
||||
} else {
|
||||
await AppUpdater().checkForUpdates(userInitiated: false)
|
||||
Stats.incrementSuccessfulLaunchCount()
|
||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||
|
||||
if Stats.successfulLaunchCount == 1 {
|
||||
Log.info("Should present the first launch screen!")
|
||||
Task { @MainActor in
|
||||
OnboardingWindowController.show()
|
||||
}
|
||||
} else {
|
||||
// Check for updates
|
||||
await AppUpdater().checkForUpdates(userInitiated: false)
|
||||
|
||||
// Check if the linked version has changed between launches of phpmon
|
||||
await PhpGuard().compareToLastGlobalVersion()
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +150,7 @@ extension MainMenu {
|
||||
*/
|
||||
private func onEnvironmentFail() async {
|
||||
Task { @MainActor [self] in
|
||||
BetterAlert()
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.cannot_start.title".localized,
|
||||
subtitle: "alert.cannot_start.subtitle".localized,
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
|
||||
extension MainMenu {
|
||||
|
||||
@ -75,7 +76,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor private func suggestFixMyValet(failed version: String) {
|
||||
let outcome = BetterAlert()
|
||||
let outcome = NVAlert()
|
||||
.withInformation(
|
||||
title: "alert.php_switch_failed.title".localized(version),
|
||||
subtitle: "alert.php_switch_failed.info".localized(version),
|
||||
@ -90,7 +91,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
@MainActor private func suggestFixMyComposer() {
|
||||
BetterAlert().withInformation(
|
||||
NVAlert().withInformation(
|
||||
title: "alert.global_composer_platform_issues.title".localized,
|
||||
subtitle: "alert.global_composer_platform_issues.subtitle".localized,
|
||||
description: "alert.global_composer_platform_issues.desc".localized
|
||||
|