🚀 Version 5.0.1
🔀 Merge branch 'dev/5.x'
46
DEVELOPER.md
Normal file
@ -0,0 +1,46 @@
|
||||
# DEVELOPER README
|
||||
|
||||
## 🔧 Build instructions
|
||||
|
||||
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||
|
||||
If you'd like to build PHP Monitor yourself, you need:
|
||||
|
||||
* Xcode (usually the latest version)
|
||||
* The contents of this repository
|
||||
|
||||
Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you should be able to immediately build the app for your system by pressing Cmd-R. This will create a debug build. (If Xcode complains about code signing, you can turn it off.)
|
||||
|
||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||
|
||||
## 🐛 Symbolication of crashes
|
||||
|
||||
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
||||
|
||||
For example, given the following crash (from an .ips file):
|
||||
|
||||
```
|
||||
Thread 2 Crashed:: Dispatch queue: com.apple.root.user-initiated-qos
|
||||
0 libswiftDispatch.dylib 0x7ff82aa3ab8c static OS_dispatch_source.makeProcessSource(identifier:eventMask:queue:) + 28
|
||||
1 PHP Monitor 0x1096907d8 0x10965e000 + 206808
|
||||
| |
|
||||
address load address
|
||||
2 PHP Monitor 0x1096903ac 0x10965e000 + 205740
|
||||
3 PHP Monitor 0x10968f88b 0x10965e000 + 202891
|
||||
```
|
||||
|
||||
You must use the correct order for the the address and load address in the command below:
|
||||
|
||||
```
|
||||
$ atos -arch x86_64 -o '/path/to/PHP Monitor.app.dSYM/Contents/Resources/DWARF/PHP Monitor' -l 0x10965e000 0x1096907d8
|
||||
| | | |
|
||||
architecture path to DSYM load address address
|
||||
```
|
||||
|
||||
This will return the relevant information, for example:
|
||||
|
||||
```
|
||||
FSWatcher.startMonitoring(_:behaviour:) (in PHP Monitor) (PhpConfigWatcher.swift:95)
|
||||
```
|
||||
|
||||
For more information, see [Apple's documentation](https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report).
|
@ -9,13 +9,11 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||
54AB03262763858F00A29D5F /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB03252763858F00A29D5F /* Timer.swift */; };
|
||||
54AB03272763858F00A29D5F /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB03252763858F00A29D5F /* Timer.swift */; };
|
||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||
54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */; };
|
||||
54FCFD27276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */; };
|
||||
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */; };
|
||||
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */; };
|
||||
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */; };
|
||||
@ -24,37 +22,31 @@
|
||||
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; };
|
||||
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; };
|
||||
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; };
|
||||
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */; };
|
||||
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */; };
|
||||
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; };
|
||||
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; };
|
||||
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||
C40B24F327A310780018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||
C40B24F527A3108B0018C7D2 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
||||
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||
C40C7F202772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||
C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
||||
C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||
C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
||||
C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
||||
C40C7F2627721FA200DDDCDC /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
||||
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
||||
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
||||
C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
||||
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
||||
C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
||||
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; };
|
||||
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; };
|
||||
C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
||||
C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
||||
C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DE2770F34D005EF286 /* AllowedArguments.swift */; };
|
||||
C415D3E62770F540005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E52770F540005EF286 /* main.swift */; };
|
||||
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; };
|
||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; };
|
||||
C417DC74277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||
C417DC76277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
||||
@ -79,6 +71,10 @@
|
||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
||||
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
||||
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
|
||||
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
|
||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
||||
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
||||
@ -101,8 +97,9 @@
|
||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
|
||||
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
|
||||
@ -121,13 +118,10 @@
|
||||
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */; };
|
||||
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
||||
C4B585402770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
||||
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
||||
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
||||
C4B585432770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
||||
C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||
C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||
C4B585462770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
||||
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
||||
@ -142,19 +136,20 @@
|
||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; };
|
||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||
C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; };
|
||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||
C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; };
|
||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
||||
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||
C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
||||
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
||||
C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
||||
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
|
||||
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
||||
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
||||
C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
||||
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
||||
@ -170,12 +165,12 @@
|
||||
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||
C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||
C4F30B07278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
||||
C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
||||
C4F319C927B034A500AFF46F /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; };
|
||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */; };
|
||||
@ -209,39 +204,27 @@
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
C415D3D42770F341005EF286 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
||||
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||
54AB03252763858F00A29D5F /* Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = "<group>"; };
|
||||
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||
54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
|
||||
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = "<group>"; };
|
||||
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
||||
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
||||
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; };
|
||||
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
|
||||
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
|
||||
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
|
||||
C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPreferenceView.swift; sourceTree = "<group>"; };
|
||||
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = "<group>"; };
|
||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
||||
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
||||
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = "<group>"; };
|
||||
C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpFrameworks.swift; sourceTree = "<group>"; };
|
||||
C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
|
||||
C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C415D3DE2770F34D005EF286 /* AllowedArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllowedArguments.swift; sourceTree = "<group>"; };
|
||||
C415D3E52770F540005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+InterApp.swift"; sourceTree = "<group>"; };
|
||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPER.md; sourceTree = "<group>"; };
|
||||
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
||||
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -263,6 +246,8 @@
|
||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
||||
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = "<group>"; };
|
||||
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
|
||||
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
|
||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
|
||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = "<group>"; };
|
||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
|
||||
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = "<group>"; };
|
||||
@ -280,6 +265,7 @@
|
||||
C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = "<group>"; };
|
||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
||||
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
||||
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
||||
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
|
||||
C49E171E27A5736E00787921 /* PMServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMServicesView.swift; sourceTree = "<group>"; };
|
||||
@ -301,6 +287,8 @@
|
||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
||||
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
||||
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Composer.swift"; sourceTree = "<group>"; };
|
||||
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
||||
C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = "<group>"; };
|
||||
C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = "<group>"; };
|
||||
@ -333,13 +321,6 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
C415D3D32770F341005EF286 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C41C1B3022B0097F00E7CF16 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -365,6 +346,7 @@
|
||||
5420395826135DC100FB00FA /* PrefsVC.swift */,
|
||||
5420395E2613607600FB00FA /* Preferences.swift */,
|
||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */,
|
||||
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */,
|
||||
C4DEB7D327A5D60B00834718 /* Stats.swift */,
|
||||
C41CD0272628D8E20065BBED /* Keybinds */,
|
||||
54FCFD28276C88C0004CE748 /* Views */,
|
||||
@ -388,8 +370,10 @@
|
||||
54FCFD28276C88C0004CE748 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */,
|
||||
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */,
|
||||
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */,
|
||||
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */,
|
||||
C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */,
|
||||
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */,
|
||||
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */,
|
||||
);
|
||||
@ -431,26 +415,16 @@
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C415D3D72770F341005EF286 /* phpmon-cli */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C415D3E52770F540005EF286 /* main.swift */,
|
||||
C415D3DE2770F34D005EF286 /* AllowedArguments.swift */,
|
||||
);
|
||||
path = "phpmon-cli";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C41C1B2A22B0097F00E7CF16 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
||||
C4E713562570150F00007428 /* SECURITY.md */,
|
||||
C4F7807425D7F7E5000DBC97 /* RELEASE.md */,
|
||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
||||
C4E713572570151400007428 /* docs */,
|
||||
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
||||
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
||||
C415D3D72770F341005EF286 /* phpmon-cli */,
|
||||
C4B5853A2770FE2500DA4FBE /* phpmon-common */,
|
||||
C41C1B3422B0097F00E7CF16 /* Products */,
|
||||
C4D309E72770EF2F00958BCF /* Frameworks */,
|
||||
);
|
||||
@ -461,7 +435,6 @@
|
||||
children = (
|
||||
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */,
|
||||
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */,
|
||||
C415D3D62770F341005EF286 /* phpmon-cli */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -469,6 +442,7 @@
|
||||
C41C1B3522B0097F00E7CF16 /* phpmon */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4B5853A2770FE2500DA4FBE /* Common */,
|
||||
C41E181722CB61EB0072CF09 /* Domain */,
|
||||
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
||||
C4232EE42612526500158FC6 /* Credits.html */,
|
||||
@ -492,15 +466,13 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
||||
C4B13B1D25C4915000548C3A /* Core */,
|
||||
C4B13B1D25C4915000548C3A /* App */,
|
||||
C4D9ADBD27761084007277F4 /* PHP */,
|
||||
C47331A0247093AC009A0597 /* Menu */,
|
||||
C464ADAA275A7A25003FCD53 /* SiteList */,
|
||||
5420395726135DB800FB00FA /* Preferences */,
|
||||
C44C198F276E3A380072762D /* Progress */,
|
||||
C4C8E81D276F5686003AC782 /* Watcher */,
|
||||
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
||||
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
||||
C4EE55B027708BB2001DF387 /* SwiftUI */,
|
||||
);
|
||||
path = Domain;
|
||||
@ -515,6 +487,15 @@
|
||||
path = Progress;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C44CCD4327AFE93300CE40E5 /* Errors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */,
|
||||
C4927F0A27B2DFC200C55AFD /* Errors.swift */,
|
||||
);
|
||||
path = Errors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -532,7 +513,10 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */,
|
||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
||||
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */,
|
||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
||||
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */,
|
||||
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
|
||||
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
|
||||
@ -553,7 +537,6 @@
|
||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
||||
54AB03252763858F00A29D5F /* Timer.swift */,
|
||||
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
|
||||
C4EC1E6C279DF87A0010F296 /* Async.swift */,
|
||||
);
|
||||
@ -595,7 +578,7 @@
|
||||
path = Homebrew;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C4B13B1D25C4915000548C3A /* Core */ = {
|
||||
C4B13B1D25C4915000548C3A /* App */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
|
||||
@ -609,16 +592,19 @@
|
||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||
);
|
||||
path = Core;
|
||||
path = App;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C4B5853A2770FE2500DA4FBE /* phpmon-common */ = {
|
||||
C4B5853A2770FE2500DA4FBE /* Common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C40C7F2127721F7300DDDCDC /* Core */,
|
||||
54B20EDF263AA22C00D3250E /* PHP */,
|
||||
C44CCD4327AFE93300CE40E5 /* Errors */,
|
||||
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
||||
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
||||
);
|
||||
path = "phpmon-common";
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
||||
@ -715,23 +701,6 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
C415D3D52770F341005EF286 /* phpmon-cli */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C415D3DA2770F341005EF286 /* Build configuration list for PBXNativeTarget "phpmon-cli" */;
|
||||
buildPhases = (
|
||||
C415D3D22770F341005EF286 /* Sources */,
|
||||
C415D3D32770F341005EF286 /* Frameworks */,
|
||||
C415D3D42770F341005EF286 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "phpmon-cli";
|
||||
productName = "phpmon-cli";
|
||||
productReference = C415D3D62770F341005EF286 /* phpmon-cli */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
C41C1B3222B0097F00E7CF16 /* PHP Monitor */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C41C1B4322B0098000E7CF16 /* Build configuration list for PBXNativeTarget "PHP Monitor" */;
|
||||
@ -780,9 +749,6 @@
|
||||
LastUpgradeCheck = 1320;
|
||||
ORGANIZATIONNAME = "Nico Verbruggen";
|
||||
TargetAttributes = {
|
||||
C415D3D52770F341005EF286 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
};
|
||||
C41C1B3222B0097F00E7CF16 = {
|
||||
CreatedOnToolsVersion = 10.2.1;
|
||||
};
|
||||
@ -810,7 +776,6 @@
|
||||
targets = (
|
||||
C41C1B3222B0097F00E7CF16 /* PHP Monitor */,
|
||||
C4F7807825D7F84B000DBC97 /* phpmon-tests */,
|
||||
C415D3D52770F341005EF286 /* phpmon-cli */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@ -827,9 +792,10 @@
|
||||
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */,
|
||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
||||
C4232EE52612526500158FC6 /* Credits.html in Resources */,
|
||||
54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */,
|
||||
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||
C473319F2470923A009A0597 /* Localizable.strings in Resources */,
|
||||
C4F30B07278E195800755FCE /* brew-services.json in Resources */,
|
||||
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */,
|
||||
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */,
|
||||
@ -841,9 +807,10 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
54FCFD27276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */,
|
||||
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
||||
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||
C43A8A2025D9D1D700591B77 /* brew.json in Resources */,
|
||||
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */,
|
||||
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
||||
@ -854,33 +821,6 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
C415D3D22770F341005EF286 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */,
|
||||
C40C7F2627721FA200DDDCDC /* Constants.swift in Sources */,
|
||||
C4B585402770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||
C415D3E62770F540005EF286 /* main.swift in Sources */,
|
||||
C40B24F327A310780018C7D2 /* Events.swift in Sources */,
|
||||
C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */,
|
||||
C4B585432770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||
C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||
C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||
C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */,
|
||||
C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */,
|
||||
C4B585462770FE3900DA4FBE /* Command.swift in Sources */,
|
||||
C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||
C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */,
|
||||
C417DC76277614690015E6EE /* Helpers.swift in Sources */,
|
||||
C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||
C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */,
|
||||
C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */,
|
||||
C40C7F202772136000DDDCDC /* PhpEnv.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C41C1B2F22B0097F00E7CF16 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -894,6 +834,7 @@
|
||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
||||
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */,
|
||||
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||
@ -914,6 +855,7 @@
|
||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */,
|
||||
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
||||
@ -926,7 +868,7 @@
|
||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||
54AB03262763858F00A29D5F /* Timer.swift in Sources */,
|
||||
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
||||
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||
@ -934,9 +876,12 @@
|
||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
||||
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */,
|
||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
||||
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
||||
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
||||
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||
@ -944,6 +889,7 @@
|
||||
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
||||
C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */,
|
||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
||||
@ -967,7 +913,6 @@
|
||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
||||
54AB03272763858F00A29D5F /* Timer.swift in Sources */,
|
||||
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||
C415D3B82770F294005EF286 /* Actions.swift in Sources */,
|
||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
|
||||
@ -976,12 +921,14 @@
|
||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
||||
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||
@ -1000,15 +947,19 @@
|
||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
||||
C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */,
|
||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
||||
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
||||
C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */,
|
||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */,
|
||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
||||
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
||||
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
|
||||
@ -1027,6 +978,8 @@
|
||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
|
||||
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
||||
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||
@ -1065,34 +1018,6 @@
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C415D3DB2770F341005EF286 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C415D3DC2770F341005EF286 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C41C1B4122B0098000E7CF16 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -1218,7 +1143,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 560;
|
||||
CURRENT_PROJECT_VERSION = 610;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
@ -1227,7 +1152,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 5.0;
|
||||
MARKETING_VERSION = 5.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -1243,7 +1168,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 560;
|
||||
CURRENT_PROJECT_VERSION = 610;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
@ -1252,7 +1177,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 5.0;
|
||||
MARKETING_VERSION = 5.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -1305,15 +1230,6 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
C415D3DA2770F341005EF286 /* Build configuration list for PBXNativeTarget "phpmon-cli" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C415D3DB2770F341005EF286 /* Debug */,
|
||||
C415D3DC2770F341005EF286 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C41C1B2E22B0097F00E7CF16 /* Build configuration list for PBXProject "PHP Monitor" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -1,84 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
||||
BuildableName = "phpmon-cli"
|
||||
BlueprintName = "phpmon-cli"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
||||
BuildableName = "phpmon-cli"
|
||||
BlueprintName = "phpmon-cli"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "help"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
||||
BuildableName = "phpmon-cli"
|
||||
BlueprintName = "phpmon-cli"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
17
README.md
@ -320,9 +320,9 @@ You can always still ask Valet using the command line, should it be necessary. I
|
||||
<details>
|
||||
<summary><strong>After running PHP Monitor, Homebrew sometimes has issues with `brew upgrade` or `brew cleanup`!</strong></summary>
|
||||
|
||||
This is a security feature of Homebrew. When you start a service as an administrator, the root user becomes the owner of relevant binaries. You will need to manually clean up those folders yourself using `rm -rf` (or by manually removing those folders via Finder).
|
||||
You can now use **First Aid & Services > Restore Homebrew Permissions** to (temporarily) resolve this issue and allow for a clean and painless `brew upgrade` or `brew cleanup` process.
|
||||
|
||||
If you would like to know more, consult [this issue](https://github.com/nicoverbruggen/phpmon/issues/85) for more information.
|
||||
If you would like to know more, consult [this issue](https://github.com/nicoverbruggen/phpmon/issues/85) for more information about why this is needed.
|
||||
|
||||
</details>
|
||||
|
||||
@ -399,15 +399,4 @@ I have done my best to annotate as much as humanly possible, and have avoided us
|
||||
|
||||
I also have a few tests for key parts of the application that I found needed to be tested. In the future, I would like to add even more tests for some of the UI stuff, but for now the tests are more unit tests than feature tests.
|
||||
|
||||
## 🔧 Build instructions
|
||||
|
||||
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||
|
||||
If you'd like to build PHP Monitor yourself, you need:
|
||||
|
||||
* Xcode (usually the latest version)
|
||||
* The contents of this repository
|
||||
|
||||
Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you should be able to immediately build the app for your system by pressing Cmd-R. This will create a debug build. (If Xcode complains about code signing, you can turn it off.)
|
||||
|
||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||
For more detailed information for developers, please see [the documentation file for developers](./DEVS.md).
|
||||
|
BIN
assets/icons.afdesign
Normal file
@ -1,26 +0,0 @@
|
||||
//
|
||||
// AllowedArguments.swift
|
||||
// phpmon-cli
|
||||
//
|
||||
// Created by Nico Verbruggen on 20/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum AllowedArguments: String, CaseIterable {
|
||||
case use = "use"
|
||||
case performSwitch = "switch"
|
||||
case fix = "fix"
|
||||
case help = "help"
|
||||
|
||||
static func has(_ string: String) -> Bool {
|
||||
return Self.allCases.contains { arg in
|
||||
return arg.rawValue == string
|
||||
}
|
||||
}
|
||||
|
||||
static var rawValues: [String] {
|
||||
return Self.allCases.map { $0.rawValue }
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
//
|
||||
// main.swift
|
||||
// phpmon-cli
|
||||
//
|
||||
// Created by Nico Verbruggen on 20/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let toolver = "0.1 (early access)"
|
||||
|
||||
let log = Log.shared
|
||||
log.verbosity = .info
|
||||
|
||||
if CommandLine.arguments.contains("-q") || CommandLine.arguments.contains("--quiet") {
|
||||
Log.shared.verbosity = .warning
|
||||
}
|
||||
if CommandLine.arguments.contains("-p") || CommandLine.arguments.contains("--performance") {
|
||||
Log.shared.verbosity = .performance
|
||||
}
|
||||
|
||||
var argument = "help"
|
||||
if CommandLine.arguments.count > 1 {
|
||||
argument = CommandLine.arguments[1]
|
||||
}
|
||||
|
||||
if !AllowedArguments.has(argument) {
|
||||
Log.err("The supported arguments are: \(AllowedArguments.rawValues)")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let action = AllowedArguments.init(rawValue: argument)
|
||||
|
||||
switch action {
|
||||
case .use, .performSwitch:
|
||||
if !Shell.fileExists("\(Paths.binPath)/php") {
|
||||
Log.err("PHP is currently not linked. Attempting quick fix...")
|
||||
_ = Shell.user.executeSynchronously("brew link php", requiresPath: true)
|
||||
}
|
||||
|
||||
let phpenv = PhpEnv.shared
|
||||
PhpEnv.detectPhpVersions()
|
||||
|
||||
if CommandLine.arguments.count < 3 {
|
||||
Log.err("You must enter at least two additional arguments when using this command.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let version = CommandLine.arguments[2].replacingOccurrences(of: "php@", with: "")
|
||||
if phpenv.availablePhpVersions.contains(version) {
|
||||
Log.info("Switching to PHP \(version)...")
|
||||
Actions.switchToPhpVersion(
|
||||
version: version,
|
||||
availableVersions: phpenv.availablePhpVersions,
|
||||
completed: {
|
||||
Log.info("The switch has been completed.")
|
||||
exit(0)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Log.err("A PHP installation with version \(version) is not installed.")
|
||||
Log.err("The installed versions are: \(phpenv.availablePhpVersions.joined(separator: ", ")).")
|
||||
Log.err("If this version is available, you may be able to install it by using `brew install php@\(version)`.")
|
||||
exit(1)
|
||||
}
|
||||
case .fix:
|
||||
Log.info("Fixing your PHP installation...")
|
||||
Actions.fixMyPhp()
|
||||
Log.info("All operations completed. You can check which version of PHP is linked by using `php -v`.")
|
||||
exit(0)
|
||||
case .help:
|
||||
print("""
|
||||
===============================================================
|
||||
PHP MONITOR CLI \(toolver)
|
||||
by Nico Verbruggen
|
||||
===============================================================
|
||||
|
||||
Gives access to the quick version switcher from PHP Monitor,
|
||||
but without the GUI and 100% of the speed!
|
||||
|
||||
SUPPORTED COMMANDS
|
||||
|
||||
* use {version}: Switch to a specific version of PHP.
|
||||
(e.g. `phpmon-cli use 8.0`)
|
||||
* switch {version}: Alias for the `use` command.
|
||||
* fix Attempts to unlink all PHP versions,
|
||||
and link the latest version of PHP.
|
||||
* help: Show this help.
|
||||
|
||||
SUPPORTED FLAGS
|
||||
|
||||
* `-q / --quiet`: Silences all logs except for warnings and exceptions.
|
||||
* `-p / --perf`: Enables performance mode.
|
||||
|
||||
""")
|
||||
exit(0)
|
||||
case .none:
|
||||
Log.err("Action not recognized!")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
RunLoop.main.run()
|
@ -11,6 +11,10 @@ import XCTest
|
||||
class PhpVersionNumberTest: XCTestCase {
|
||||
|
||||
func testCanDeconstructPhpVersion() throws {
|
||||
XCTAssertEqual(
|
||||
try! PhpVersionNumber.parse("PHP 8.1.0RC5-dev"),
|
||||
PhpVersionNumber(major: 8, minor: 1, patch: 0)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "8.0.11"),
|
||||
PhpVersionNumber(major: 8, minor: 0, patch: 11)
|
||||
@ -29,6 +33,12 @@ class PhpVersionNumberTest: XCTestCase {
|
||||
)
|
||||
}
|
||||
|
||||
func testPhpVersionNumberParse() throws {
|
||||
XCTAssertThrowsError(try PhpVersionNumber.parse("OOF")) { error in
|
||||
XCTAssertTrue(error is VersionParseError)
|
||||
}
|
||||
}
|
||||
|
||||
func testCanCheckFixedConstraints() throws {
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
|
@ -1,11 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Linked.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "link.svg",
|
||||
"filename" : "Linked@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
|
BIN
phpmon/Assets.xcassets/IconLinked.imageset/Linked.png
vendored
Normal file
After Width: | Height: | Size: 978 B |
BIN
phpmon/Assets.xcassets/IconLinked.imageset/Linked@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"/></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,11 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Parked.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "car-alt.svg",
|
||||
"filename" : "Parked@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
|
BIN
phpmon/Assets.xcassets/IconParked.imageset/Parked.png
vendored
Normal file
After Width: | Height: | Size: 868 B |
BIN
phpmon/Assets.xcassets/IconParked.imageset/Parked@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M438.66 212.33l-11.24-28.1-19.93-49.83C390.38 91.63 349.57 64 303.5 64h-127c-46.06 0-86.88 27.63-103.99 70.4l-19.93 49.83-11.24 28.1C17.22 221.5 0 244.66 0 272v48c0 16.12 6.16 30.67 16 41.93V416c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32v-32h256v32c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32v-54.07c9.84-11.25 16-25.8 16-41.93v-48c0-27.34-17.22-50.5-41.34-59.67zm-306.73-54.16c7.29-18.22 24.94-30.17 44.57-30.17h127c19.63 0 37.28 11.95 44.57 30.17L368 208H112l19.93-49.83zM80 319.8c-19.2 0-32-12.76-32-31.9S60.8 256 80 256s48 28.71 48 47.85-28.8 15.95-48 15.95zm320 0c-19.2 0-48 3.19-48-15.95S380.8 256 400 256s32 12.76 32 31.9-12.8 31.9-32 31.9z"/></svg>
|
Before Width: | Height: | Size: 918 B |
6
phpmon/Assets.xcassets/Menu Bar Icons/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
22
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_elephant.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Menu Bar Elephant.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Menu Bar Elephant@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_elephant.imageset/Menu Bar Elephant.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_elephant.imageset/Menu Bar Elephant@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
@ -1,11 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Menu Bar.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "php@2x.png",
|
||||
"filename" : "Menu Bar@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_php.imageset/Menu Bar.png
vendored
Normal file
After Width: | Height: | Size: 658 B |
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_php.imageset/Menu Bar@2x.png
vendored
Normal file
After Width: | Height: | Size: 793 B |
@ -1,11 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ServiceLoading.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "question-circle.svg",
|
||||
"filename" : "ServiceLoading@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
|
BIN
phpmon/Assets.xcassets/ServiceLoading.imageset/ServiceLoading.png
vendored
Normal file
After Width: | Height: | Size: 854 B |
BIN
phpmon/Assets.xcassets/ServiceLoading.imageset/ServiceLoading@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 448c-110.532 0-200-89.431-200-200 0-110.495 89.472-200 200-200 110.491 0 200 89.471 200 200 0 110.53-89.431 200-200 200zm107.244-255.2c0 67.052-72.421 68.084-72.421 92.863V300c0 6.627-5.373 12-12 12h-45.647c-6.627 0-12-5.373-12-12v-8.659c0-35.745 27.1-50.034 47.579-61.516 17.561-9.845 28.324-16.541 28.324-29.579 0-17.246-21.999-28.693-39.784-28.693-23.189 0-33.894 10.977-48.942 29.969-4.057 5.12-11.46 6.071-16.666 2.124l-27.824-21.098c-5.107-3.872-6.251-11.066-2.644-16.363C184.846 131.491 214.94 112 261.794 112c49.071 0 101.45 38.304 101.45 88.8zM298 368c0 23.159-18.841 42-42 42s-42-18.841-42-42 18.841-42 42-42 42 18.841 42 42z"/></svg>
|
Before Width: | Height: | Size: 966 B |
@ -1,11 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ServiceOff.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "times-circle.svg",
|
||||
"filename" : "ServiceOff@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
|
BIN
phpmon/Assets.xcassets/ServiceOff.imageset/ServiceOff.png
vendored
Normal file
After Width: | Height: | Size: 826 B |
BIN
phpmon/Assets.xcassets/ServiceOff.imageset/ServiceOff@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z"/></svg>
|
Before Width: | Height: | Size: 685 B |
@ -1,11 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ServiceOn.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "check-circle.svg",
|
||||
"filename" : "ServiceOn@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
|
BIN
phpmon/Assets.xcassets/ServiceOn.imageset/ServiceOn.png
vendored
Normal file
After Width: | Height: | Size: 819 B |
BIN
phpmon/Assets.xcassets/ServiceOn.imageset/ServiceOn@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.532 0 200 89.451 200 200 0 110.532-89.451 200-200 200-110.532 0-200-89.451-200-200 0-110.532 89.451-200 200-200m140.204 130.267l-22.536-22.718c-4.667-4.705-12.265-4.736-16.97-.068L215.346 303.697l-59.792-60.277c-4.667-4.705-12.265-4.736-16.97-.069l-22.719 22.536c-4.705 4.667-4.736 12.265-.068 16.971l90.781 91.516c4.667 4.705 12.265 4.736 16.97.068l172.589-171.204c4.704-4.668 4.734-12.266.067-16.971z"/></svg>
|
Before Width: | Height: | Size: 718 B |
Before Width: | Height: | Size: 940 B |
@ -34,17 +34,39 @@ class Actions {
|
||||
brew("services stop dnsmasq", sudo: true)
|
||||
}
|
||||
|
||||
/**
|
||||
Kindly asks Valet to switch to a specific PHP version.
|
||||
*/
|
||||
public static func switchToPhpVersionUsingValet(
|
||||
version: String,
|
||||
availableVersions: [String],
|
||||
completed: @escaping () -> Void
|
||||
) {
|
||||
Log.info("Switching to \(version) using Valet")
|
||||
Log.info(valet("use php@\(version)"))
|
||||
completed()
|
||||
public static func fixHomebrewPermissions() throws
|
||||
{
|
||||
var servicesCommands = [
|
||||
"\(Paths.brew) services stop nginx",
|
||||
"\(Paths.brew) services stop dnsmasq",
|
||||
]
|
||||
var cellarCommands = [
|
||||
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/nginx",
|
||||
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/dnsmasq"
|
||||
]
|
||||
|
||||
PhpEnv.shared.availablePhpVersions.forEach { version in
|
||||
let formula = version == PhpEnv.brewPhpVersion
|
||||
? "php"
|
||||
: "php@\(version)"
|
||||
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
||||
cellarCommands.append("chown -R \(Paths.whoami):staff \(Paths.cellarPath)/\(formula)")
|
||||
}
|
||||
|
||||
let script =
|
||||
servicesCommands.joined(separator: " && ")
|
||||
+ " && "
|
||||
+ cellarCommands.joined(separator: " && ")
|
||||
|
||||
let appleScript = NSAppleScript(
|
||||
source: "do shell script \"\(script)\" with administrator privileges"
|
||||
)
|
||||
|
||||
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
|
||||
|
||||
if (eventResult == nil) {
|
||||
throw HomebrewPermissionError(kind: .applescriptNilError)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Finding Config Files
|
@ -27,13 +27,13 @@ class Log {
|
||||
|
||||
static func err(_ item: Any) {
|
||||
if Verbosity.error.isApplicable() {
|
||||
print(item)
|
||||
print("[ERR] \(item)")
|
||||
}
|
||||
}
|
||||
|
||||
static func warn(_ item: Any) {
|
||||
if Verbosity.warning.isApplicable() {
|
||||
print(item)
|
||||
print("[WRN] \(item)")
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class Paths {
|
||||
private var userName : String
|
||||
|
||||
init() {
|
||||
baseDir = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
|
||||
baseDir = FileManager.default.fileExists(atPath: "\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
|
||||
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||
}
|
||||
|
||||
@ -48,6 +48,10 @@ public class Paths {
|
||||
return shared.userName
|
||||
}
|
||||
|
||||
public static var cellarPath: String {
|
||||
return "\(shared.baseDir.rawValue)/Cellar"
|
||||
}
|
||||
|
||||
public static var binPath: String {
|
||||
return "\(shared.baseDir.rawValue)/bin"
|
||||
}
|
@ -105,12 +105,12 @@ public class Shell {
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if a file exists at the provided path.
|
||||
Uses `/bin/echo` instead of the `builtin` (which does not support `-n`).
|
||||
Checks if a file exists at a certain path.
|
||||
Used to be done with a shell command, now uses the native FileManager class instead.
|
||||
*/
|
||||
public static func fileExists(_ path: String) -> Bool {
|
||||
let escapedPath = path.replacingOccurrences(of: " ", with: "\\ ")
|
||||
return Shell.pipe("if [ -f \(escapedPath) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||
let fullPath = path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
||||
return FileManager.default.fileExists(atPath: fullPath)
|
||||
}
|
||||
|
||||
/**
|
13
phpmon/Common/Errors/AlertableError.swift
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Errors.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 06/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol AlertableError {
|
||||
func getErrorMessageKey() -> String
|
||||
}
|
29
phpmon/Common/Errors/Errors.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// VersionParseError.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 08/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Alertable Errors
|
||||
// These errors must be resolved by the user.
|
||||
|
||||
struct HomebrewPermissionError: Error, AlertableError {
|
||||
enum Kind: String {
|
||||
case applescriptNilError = "homebrew_permissions.applescript_returned_nil"
|
||||
}
|
||||
|
||||
let kind: Kind
|
||||
|
||||
func getErrorMessageKey() -> String {
|
||||
return "alert.errors.\(self.kind.rawValue)"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Errors that do not have an associated alert message
|
||||
// The errors must be resolved by the developer.
|
||||
|
||||
struct VersionParseError: Error {}
|
@ -51,6 +51,9 @@ class Alert {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Notify the user about something by showing an alert.
|
||||
*/
|
||||
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
|
||||
_ = present(
|
||||
messageText: message,
|
||||
@ -60,4 +63,19 @@ class Alert {
|
||||
style: style
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Notify the user about a particular error (which must be `Alertable`)
|
||||
by showing an alert.
|
||||
*/
|
||||
public static func notify(about error: Error & AlertableError) {
|
||||
let key = error.getErrorMessageKey()
|
||||
_ = present(
|
||||
messageText: "\(key).title".localized,
|
||||
informativeText: "\(key).description".localized,
|
||||
buttonTitle: "OK",
|
||||
secondButtonTitle: "",
|
||||
style: .critical
|
||||
)
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ class MenuBarImageGenerator {
|
||||
|
||||
let targetImage: NSImage = NSImage(size: image.size)
|
||||
|
||||
let rep: NSBitmapImageRep = NSBitmapImageRep(
|
||||
let representation: NSBitmapImageRep = NSBitmapImageRep(
|
||||
bitmapDataPlanes: nil,
|
||||
pixelsWide: Int(image.size.width),
|
||||
pixelsHigh: Int(image.size.height),
|
||||
@ -55,7 +55,7 @@ class MenuBarImageGenerator {
|
||||
bitsPerPixel: 0
|
||||
)!
|
||||
|
||||
targetImage.addRepresentation(rep)
|
||||
targetImage.addRepresentation(representation)
|
||||
targetImage.lockFocus()
|
||||
|
||||
image.draw(in: imageRect)
|
||||
@ -69,32 +69,69 @@ class MenuBarImageGenerator {
|
||||
The same as before, but also attempts to add an icon to the left.
|
||||
*/
|
||||
public static func textToImageWithIcon(text: String) -> NSImage {
|
||||
let textImage = self.textToImage(text: text)
|
||||
let iconImage = NSImage(named: "StatusBarPHP")!
|
||||
let iconWidthSize = iconImage.size.width
|
||||
let divider = iconWidthSize
|
||||
|
||||
// We'll start out with the image containing the text
|
||||
let textImage = self.textToImage(text: text)
|
||||
|
||||
// Then we'll fetch the image we want on the left
|
||||
var iconType = Preferences.preferences[.iconTypeToDisplay] as? String
|
||||
if iconType == nil {
|
||||
Log.warn("Invalid icon type found, using the default")
|
||||
iconType = MenuBarIcon.iconPhp.rawValue
|
||||
}
|
||||
|
||||
let iconImage = NSImage(named: "MenuBar_\(iconType!)")!
|
||||
|
||||
// We'll need to reference the width of the icon a bunch of times
|
||||
let iconWidthSize = iconImage.size.width
|
||||
|
||||
// There will also be an additional divider between the image and the text (image)
|
||||
let divider: CGFloat = 3
|
||||
|
||||
// Use a fixed size for the height of the menu bar (18pt)
|
||||
let imageRect = CGRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: textImage.size.width + divider,
|
||||
height: textImage.size.height
|
||||
width: textImage.size.width + iconWidthSize + divider,
|
||||
height: 18
|
||||
)
|
||||
|
||||
// Create a new image, we'll draw the text and our icon in there
|
||||
let image: NSImage = NSImage(size: imageRect.size)
|
||||
image.lockFocus()
|
||||
|
||||
let difference = imageRect.size.width - textImage.size.width
|
||||
// Calculate the offset between the image and the text
|
||||
let offset = imageRect.size.width - textImage.size.width
|
||||
|
||||
textImage.draw(in: imageRect, from: NSRect(
|
||||
x: -difference,
|
||||
y: 0, width: textImage.size.width + difference,
|
||||
// Draw the text with a negative x offset (so there is room on the left for the icon)
|
||||
textImage.draw(
|
||||
in: imageRect,
|
||||
from: NSRect(
|
||||
x: -offset,
|
||||
y: 0,
|
||||
width: textImage.size.width + offset,
|
||||
height: textImage.size.height
|
||||
), operation: .overlay, fraction: 1)
|
||||
),
|
||||
operation: .overlay,
|
||||
fraction: 1
|
||||
)
|
||||
|
||||
iconImage.draw(in: imageRect, from: NSRect(x: 0, y: 0, width: imageRect.size.width * 1.6, height: imageRect.size.height * 2.0), operation: .overlay, fraction: 1)
|
||||
// Draw the icon directly in the left of the imageRect (where we left space)
|
||||
iconImage.draw(
|
||||
in: imageRect,
|
||||
from: NSRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: imageRect.size.width,
|
||||
height: imageRect.size.height
|
||||
),
|
||||
operation: .overlay,
|
||||
fraction: 1
|
||||
)
|
||||
|
||||
// We're done with this image
|
||||
image.unlockFocus()
|
||||
|
||||
return image
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class VersionExtractor {
|
||||
public static func from(_ string: String) -> String? {
|
||||
do {
|
||||
let regex = try NSRegularExpression(
|
||||
pattern: #"Laravel Valet (?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
|
||||
pattern: #"(?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
|
||||
options: []
|
||||
)
|
||||
|
@ -8,11 +8,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol PhpSwitcherDelegate: AnyObject {
|
||||
func switcherDidStartSwitching()
|
||||
func switcherDidCompleteSwitch()
|
||||
}
|
||||
|
||||
class PhpEnv {
|
||||
|
||||
// MARK: - Initializer
|
||||
@ -161,7 +156,7 @@ class PhpEnv {
|
||||
*/
|
||||
public func validate(_ version: String) -> Bool {
|
||||
if self.currentInstall.version.short == version {
|
||||
print("Switching to version \(version) seems to have succeeded. Validation passed.")
|
||||
Log.info("Switching to version \(version) seems to have succeeded. Validation passed.")
|
||||
return true
|
||||
}
|
||||
|
@ -118,6 +118,14 @@ public struct PhpVersionNumber: Equatable {
|
||||
*/
|
||||
}
|
||||
|
||||
public static func parse(_ text: String) throws -> Self {
|
||||
guard let versionText = VersionExtractor.from(text) else {
|
||||
throw VersionParseError()
|
||||
}
|
||||
|
||||
return Self.make(from: versionText)!
|
||||
}
|
||||
|
||||
public static func make(from versionString: String, type: MatchType = .versionOnly) -> Self? {
|
||||
let regex = try! NSRegularExpression(pattern: type.rawValue, options: [])
|
||||
let match = regex.matches(in: versionString, options: [], range: NSMakeRange(0, versionString.count)).first
|
@ -27,9 +27,10 @@ class PhpInstallation {
|
||||
arguments: ["--version"]
|
||||
).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
self.longVersion = PhpVersionNumber.make(
|
||||
from: String(longVersionString.split(separator: "-")[0])
|
||||
)!
|
||||
// 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.
|
||||
// TODO: Alert the user that the version number could not be parsed.
|
||||
self.longVersion = try! PhpVersionNumber.parse(longVersionString)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,14 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol PhpSwitcherDelegate: AnyObject {
|
||||
|
||||
func switcherDidStartSwitching(to: String)
|
||||
|
||||
func switcherDidCompleteSwitch(to: String)
|
||||
|
||||
}
|
||||
|
||||
protocol PhpSwitcher {
|
||||
|
||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
@ -8,7 +8,7 @@
|
||||
import Cocoa
|
||||
import HotKey
|
||||
|
||||
class App: PhpSwitcherDelegate {
|
||||
class App {
|
||||
|
||||
// MARK: Static Vars
|
||||
|
||||
@ -73,20 +73,4 @@ class App: PhpSwitcherDelegate {
|
||||
The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder.
|
||||
*/
|
||||
var watcher: PhpConfigWatcher!
|
||||
|
||||
// MARK: - PhpSwitcherDelegate
|
||||
|
||||
func switcherDidStartSwitching() {
|
||||
}
|
||||
|
||||
func switcherDidCompleteSwitch() {
|
||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||
handlePhpConfigWatcher()
|
||||
|
||||
if let window = siteListWindowController {
|
||||
DispatchQueue.main.async {
|
||||
window.contentVC.reloadSites()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -45,12 +45,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
let valet: Valet
|
||||
|
||||
/**
|
||||
The PhpSwitcher singleton that handles PHP version
|
||||
The PhpEnv singleton that handles PHP version
|
||||
detection, as well as switching. It is initialized
|
||||
when the app is ready and passed all checks.
|
||||
*/
|
||||
var switcher: PhpEnv! = nil
|
||||
var phpEnvironment: PhpEnv! = nil
|
||||
|
||||
/**
|
||||
The logger is responsible for different levels of logging.
|
||||
You can tweak the verbosity in the `init` method here.
|
||||
*/
|
||||
var logger = Log.shared
|
||||
|
||||
// MARK: - Initializer
|
||||
@ -59,7 +63,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
When the application initializes, create all singletons.
|
||||
*/
|
||||
override init() {
|
||||
logger.verbosity = .performance
|
||||
logger.verbosity = .info
|
||||
Log.info("==================================")
|
||||
Log.info("PHP MONITOR by Nico Verbruggen")
|
||||
Log.info("Version \(App.version)")
|
||||
@ -73,8 +77,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
}
|
||||
|
||||
func initializeSwitcher() {
|
||||
self.switcher = PhpEnv.shared
|
||||
self.switcher.delegate = self.state
|
||||
self.phpEnvironment = PhpEnv.shared
|
||||
}
|
||||
|
||||
// MARK: - Lifecycle
|
@ -771,7 +771,7 @@ Gw
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="600" id="iRQ-sz-oyv"/>
|
||||
</constraints>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
|
||||
<rect key="frame" x="1" y="293" width="598" height="15"/>
|
||||
<rect key="frame" x="1" y="292" width="598" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
||||
@ -808,7 +808,7 @@ Gw
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Checkmark" width="512" height="512"/>
|
||||
<image name="IconLinked" width="512" height="512"/>
|
||||
<image name="IconLinked" width="25" height="25"/>
|
||||
<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"/>
|
@ -49,14 +49,6 @@ class Startup {
|
||||
breaking: true
|
||||
)
|
||||
|
||||
Valet.shared.version = VersionExtractor.from(valet("--version"))
|
||||
performEnvironmentCheck(
|
||||
Valet.shared.version == nil,
|
||||
messageText: "startup.errors.valet_version_unknown.title".localized,
|
||||
informativeText: "startup.errors.valet_version_unknown.desc".localized,
|
||||
breaking: true
|
||||
)
|
||||
|
||||
performEnvironmentCheck(
|
||||
HomebrewDiagnostics.cannotLoadService(),
|
||||
messageText: "startup.errors.services_json_error.title".localized,
|
||||
@ -81,12 +73,13 @@ class Startup {
|
||||
breaking: true
|
||||
)
|
||||
|
||||
let services = Shell.pipe("\(Paths.brew) services list | grep php")
|
||||
// Determine the Valet version only AFTER confirming the correct permission is in place
|
||||
Valet.shared.version = VersionExtractor.from(valet("--version"))
|
||||
performEnvironmentCheck(
|
||||
(services.countInstances(of: "started") > 1),
|
||||
messageText: "startup.errors.services.title".localized,
|
||||
informativeText: "startup.errors.services.desc".localized,
|
||||
breaking: false
|
||||
Valet.shared.version == nil,
|
||||
messageText: "startup.errors.valet_version_unknown.title".localized,
|
||||
informativeText: "startup.errors.valet_version_unknown.desc".localized,
|
||||
breaking: true
|
||||
)
|
||||
|
||||
if (!failed) {
|
@ -1,32 +0,0 @@
|
||||
//
|
||||
// BenchmarkTimer.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 10/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class BenchmarkTimer {
|
||||
let startTime: CFAbsoluteTime
|
||||
var endTime: CFAbsoluteTime?
|
||||
|
||||
init() {
|
||||
startTime = CFAbsoluteTimeGetCurrent()
|
||||
}
|
||||
|
||||
func stop() -> CFAbsoluteTime {
|
||||
endTime = CFAbsoluteTimeGetCurrent()
|
||||
|
||||
return duration!
|
||||
}
|
||||
|
||||
var duration: CFAbsoluteTime? {
|
||||
if let endTime = endTime {
|
||||
return endTime - startTime
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
@ -163,7 +163,7 @@ class Valet {
|
||||
|
||||
// We should also check that we can interpret the path correctly
|
||||
if URL(fileURLWithPath: siteDir).lastPathComponent == "" {
|
||||
print("Warning: could not parse the site: \(siteDir), skipping!")
|
||||
Log.warn("Could not parse the site: \(siteDir), skipping!")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -10,7 +10,7 @@
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe" customClass="HeaderView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="350" height="24"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="270" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ddg-VQ-cOT">
|
||||
@ -29,7 +29,7 @@
|
||||
<connections>
|
||||
<outlet property="textField" destination="ddg-VQ-cOT" id="aaQ-Xb-o2X"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-75" y="38"/>
|
||||
<point key="canvasLocation" x="177" y="105"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
|
96
phpmon/Domain/Menu/MainMenu+Async.swift
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// MainMenu+Async.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 06/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MainMenu {
|
||||
|
||||
// MARK: - Nicer callbacks
|
||||
|
||||
enum AsyncBehaviour {
|
||||
case setsBusyUI
|
||||
case reloadsPhpInstallation
|
||||
case updatesMenuBarContents
|
||||
case broadcastServicesUpdate
|
||||
}
|
||||
|
||||
/**
|
||||
Attempts asynchronous execution of a callback that may throw an `Error`.
|
||||
While the callback is being executed, the UI will be marked as busy.
|
||||
|
||||
(Preferably, if an `Error` is thrown, it should also be an `AlertableError`,
|
||||
which will make presenting errors easier.)
|
||||
|
||||
- Parameter execute: Required callback of the work that needs to happen.
|
||||
|
||||
- Parameter success: Optional callback that is fired when all was OK.
|
||||
- Parameter failure: Optional callback that is fired when an `Error` was thrown.
|
||||
- Parameter behaviours: Various behaviours that can be tweaked, but usually best left to the default.
|
||||
The default will set the UI to busy, reload PHP info, update the menu bar,
|
||||
and broadcast to the services view that the list has been updated.
|
||||
*/
|
||||
func asyncExecution(
|
||||
_ execute: @escaping () throws -> Void,
|
||||
success: @escaping () -> Void = {},
|
||||
failure: @escaping (Error) -> Void = { _ in },
|
||||
behaviours: [AsyncBehaviour] = [
|
||||
.setsBusyUI,
|
||||
.reloadsPhpInstallation,
|
||||
.updatesMenuBarContents,
|
||||
.broadcastServicesUpdate
|
||||
]
|
||||
) {
|
||||
if behaviours.contains(.reloadsPhpInstallation) {
|
||||
PhpEnv.shared.isBusy = true
|
||||
}
|
||||
|
||||
if behaviours.contains(.setsBusyUI) {
|
||||
setBusyImage()
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
var error: Error? = nil
|
||||
|
||||
do { try execute() } catch let e { error = e }
|
||||
|
||||
if behaviours.contains(.setsBusyUI) {
|
||||
PhpEnv.shared.isBusy = false
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [self] in
|
||||
if behaviours.contains(.reloadsPhpInstallation) {
|
||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||
}
|
||||
|
||||
if behaviours.contains(.updatesMenuBarContents) {
|
||||
updatePhpVersionInStatusBar()
|
||||
} else if behaviours.contains(.setsBusyUI) {
|
||||
refreshIcon()
|
||||
}
|
||||
|
||||
if behaviours.contains(.broadcastServicesUpdate) {
|
||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||
}
|
||||
|
||||
error == nil ? success() : failure(error!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func asyncWithBusyUI(
|
||||
_ execute: @escaping () throws -> Void,
|
||||
completion: @escaping () -> Void = {}
|
||||
) {
|
||||
asyncExecution({
|
||||
try! execute()
|
||||
}, success: {
|
||||
completion()
|
||||
}, behaviours: [.setsBusyUI])
|
||||
}
|
||||
|
||||
}
|
98
phpmon/Domain/Menu/MainMenu+Composer.swift
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// MainMenu+Composer.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 08/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MainMenu {
|
||||
|
||||
/**
|
||||
Updates the global dependencies and runs the completion callback when done.
|
||||
This method should probably be broken up into several smaller methods at some point.
|
||||
*/
|
||||
func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
|
||||
if !Shell.fileExists("/usr/local/bin/composer") {
|
||||
Alert.notify(
|
||||
message: "alert.composer_missing.title".localized,
|
||||
info: "alert.composer_missing.info".localized
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
PhpEnv.shared.isBusy = true
|
||||
setBusyImage()
|
||||
self.rebuild()
|
||||
|
||||
let noLongerBusy = {
|
||||
PhpEnv.shared.isBusy = false
|
||||
DispatchQueue.main.async { [self] in
|
||||
self.updatePhpVersionInStatusBar()
|
||||
self.rebuild()
|
||||
}
|
||||
}
|
||||
|
||||
var window: ProgressWindowController? = ProgressWindowController.display(
|
||||
title: "alert.composer_progress.title".localized,
|
||||
description: "alert.composer_progress.info".localized
|
||||
)
|
||||
window?.setType(info: true)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let task = Shell.user.createTask(
|
||||
for: "/usr/local/bin/composer global update", requiresPath: true
|
||||
)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
window?.addToConsole("/usr/local/bin/composer global update\n")
|
||||
}
|
||||
|
||||
Shell.captureOutput(
|
||||
task,
|
||||
didReceiveStdOutData: { string in
|
||||
DispatchQueue.main.async {
|
||||
window?.addToConsole(string)
|
||||
}
|
||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||
},
|
||||
didReceiveStdErrData: { string in
|
||||
DispatchQueue.main.async {
|
||||
window?.addToConsole(string)
|
||||
}
|
||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||
}
|
||||
)
|
||||
|
||||
task.launch()
|
||||
task.waitUntilExit()
|
||||
Shell.haltCapturingOutput(task)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if task.terminationStatus <= 0 {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
window?.close()
|
||||
if (notify) {
|
||||
LocalNotification.send(
|
||||
title: "alert.composer_success.title".localized,
|
||||
subtitle: "alert.composer_success.info".localized
|
||||
)
|
||||
}
|
||||
window = nil
|
||||
noLongerBusy()
|
||||
completion(true)
|
||||
}
|
||||
} else {
|
||||
window?.setType(info: false)
|
||||
window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized
|
||||
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
|
||||
window = nil
|
||||
noLongerBusy()
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
phpmon/Domain/Menu/MainMenu+Switcher.swift
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// MainMenu+PhpSwitcherDelegate.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 08/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MainMenu {
|
||||
|
||||
// MARK: - PhpSwitcherDelegate
|
||||
|
||||
func switcherDidStartSwitching(to version: String) {}
|
||||
|
||||
func switcherDidCompleteSwitch(to version: String) {
|
||||
// Update the PHP version
|
||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||
|
||||
// Ensure the config watcher gets reloaded
|
||||
App.shared.handlePhpConfigWatcher()
|
||||
|
||||
// Mark as no longer busy
|
||||
PhpEnv.shared.isBusy = false
|
||||
|
||||
// Reload the site list
|
||||
self.reloadSiteListData()
|
||||
|
||||
// Perform UI updates on main thread
|
||||
DispatchQueue.main.async { [self] in
|
||||
updatePhpVersionInStatusBar()
|
||||
rebuild()
|
||||
|
||||
if !PhpEnv.shared.validate(version) {
|
||||
self.suggestFixMyValet(failed: version)
|
||||
return
|
||||
}
|
||||
|
||||
// Run composer updates
|
||||
if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) {
|
||||
self.updateGlobalDependencies(notify: false, completion: { _ in
|
||||
self.notifyAboutVersionChange(to: version)
|
||||
})
|
||||
} else {
|
||||
self.notifyAboutVersionChange(to: version)
|
||||
}
|
||||
|
||||
// Update stats
|
||||
Stats.incrementSuccessfulSwitchCount()
|
||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
private func suggestFixMyValet(failed version: String) {
|
||||
let outcome = Alert.present(
|
||||
messageText: "alert.php_switch_failed.title".localized(version),
|
||||
informativeText: "alert.php_switch_failed.info".localized(version),
|
||||
buttonTitle: "alert.php_switch_failed.confirm".localized,
|
||||
secondButtonTitle: "alert.php_switch_failed.cancel".localized, style: .informational)
|
||||
if outcome {
|
||||
MainMenu.shared.fixMyValet()
|
||||
}
|
||||
}
|
||||
|
||||
private func reloadSiteListData() {
|
||||
if let window = App.shared.siteListWindowController {
|
||||
DispatchQueue.main.async {
|
||||
window.contentVC.reloadSites()
|
||||
}
|
||||
} else {
|
||||
Valet.shared.reloadSites()
|
||||
}
|
||||
}
|
||||
|
||||
private func notifyAboutVersionChange(to version: String) {
|
||||
LocalNotification.send(
|
||||
title: String(format: "notification.version_changed_title".localized, version),
|
||||
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
||||
)
|
||||
|
||||
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate {
|
||||
|
||||
static let shared = MainMenu()
|
||||
|
||||
@ -23,12 +23,28 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
// MARK: - UI related
|
||||
|
||||
/**
|
||||
Update the menu's contents, based on what's going on.
|
||||
This will rebuild the entire menu, so this can take a few moments.
|
||||
Rebuilds the menu (either asynchronously or synchronously).
|
||||
Defaults to rebuilding the menu asynchronously.
|
||||
*/
|
||||
func rebuild() {
|
||||
func rebuild(async: Bool = true) {
|
||||
if !async {
|
||||
self.rebuildMenu()
|
||||
return
|
||||
}
|
||||
|
||||
// Update the menu item on the main thread
|
||||
DispatchQueue.main.async { [self] in
|
||||
self.rebuildMenu()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Update the menu's contents, based on what's going on.
|
||||
This will rebuild the entire menu, so this can take a few moments.
|
||||
|
||||
Use `rebuild(async:)` to ensure the rebuilding happens in the background.
|
||||
*/
|
||||
private func rebuildMenu() {
|
||||
// Create a new menu
|
||||
let menu = StatusMenu()
|
||||
|
||||
@ -61,14 +77,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
statusItem.menu = menu
|
||||
statusItem.menu?.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the status bar image based on a version string.
|
||||
*/
|
||||
func setStatusBarImage(version: String) {
|
||||
setStatusBar(
|
||||
image: Preferences.isEnabled(.shouldDisplayPhpHintInIcon)
|
||||
image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue)
|
||||
? MenuBarImageGenerator.textToImageWithIcon(text: version)
|
||||
: MenuBarImageGenerator.textToImage(text: version)
|
||||
)
|
||||
@ -85,35 +100,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Nicer callbacks
|
||||
|
||||
/**
|
||||
Executes a specific callback and fires the completion callback,
|
||||
while updating the UI as required. As long as the completion callback
|
||||
does not fire, the app is presumed to be busy and the UI reflects this.
|
||||
|
||||
- Parameter execute: Callback of the work that needs to happen.
|
||||
- Parameter completion: Callback that is fired when the work is done.
|
||||
*/
|
||||
private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
|
||||
{
|
||||
PhpEnv.shared.isBusy = true
|
||||
setBusyImage()
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
execute()
|
||||
PhpEnv.shared.isBusy = false
|
||||
|
||||
DispatchQueue.main.async { [self] in
|
||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||
updatePhpVersionInStatusBar()
|
||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - User Interface
|
||||
|
||||
/** Reloads which PHP versions is currently active. */
|
||||
@objc func refreshActiveInstallation() {
|
||||
if !PhpEnv.shared.isBusy {
|
||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||
@ -123,12 +112,38 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the icon (refresh icon) and rebuilds the menu. */
|
||||
@objc func updatePhpVersionInStatusBar() {
|
||||
refreshIcon()
|
||||
rebuild()
|
||||
}
|
||||
|
||||
func refreshIcon() {
|
||||
/**
|
||||
Reloads the menu in the foreground.
|
||||
This mimics the exact behaviours of `asyncExecution` as set in the method below.
|
||||
*/
|
||||
@objc func reloadPhpMonitorMenuInForeground() {
|
||||
refreshActiveInstallation()
|
||||
refreshIcon()
|
||||
rebuild(async: false)
|
||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||
}
|
||||
|
||||
/** Reloads the menu in the background, using `asyncExecution`. */
|
||||
@objc func reloadPhpMonitorMenuInBackground() {
|
||||
asyncExecution({
|
||||
// This automatically reloads the menu
|
||||
Log.info("Reloading information about the PHP installation (in the background)...")
|
||||
}, behaviours: [
|
||||
.setsBusyUI,
|
||||
.reloadsPhpInstallation,
|
||||
.broadcastServicesUpdate,
|
||||
.updatesMenuBarContents
|
||||
])
|
||||
}
|
||||
|
||||
/** Refreshes the icon with the PHP version. */
|
||||
@objc func refreshIcon() {
|
||||
DispatchQueue.main.async { [self] in
|
||||
if (App.busy) {
|
||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||
@ -145,20 +160,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func reloadPhpMonitorMenuInBackground() {
|
||||
waitAndExecute {
|
||||
// This automatically reloads the menu
|
||||
Log.info("Reloading information about the PHP installation (in the background)...")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func reloadPhpMonitorMenu() {
|
||||
waitAndExecute {
|
||||
// This automatically reloads the menu
|
||||
Log.info("Reloading information about the PHP installation...")
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the icon to be displayed as busy. */
|
||||
@objc func setBusyImage() {
|
||||
DispatchQueue.main.async { [self] in
|
||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||
@ -167,18 +169,42 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func fixHomebrewPermissions() {
|
||||
if !Alert.present(
|
||||
messageText: "alert.fix_homebrew_permissions.title".localized,
|
||||
informativeText: "alert.fix_homebrew_permissions.info".localized,
|
||||
buttonTitle: "alert.fix_homebrew_permissions.ok".localized,
|
||||
secondButtonTitle: "alert.fix_homebrew_permissions.cancel".localized,
|
||||
style: .warning
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
asyncExecution {
|
||||
try Actions.fixHomebrewPermissions()
|
||||
} success: {
|
||||
Alert.notify(
|
||||
message: "alert.fix_homebrew_permissions_done.title".localized,
|
||||
info: "alert.fix_homebrew_permissions_done.info".localized,
|
||||
style: .warning
|
||||
)
|
||||
} failure: { error in
|
||||
Alert.notify(about: error as! HomebrewPermissionError)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func restartPhpFpm() {
|
||||
waitAndExecute {
|
||||
asyncExecution {
|
||||
Actions.restartPhpFpm()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func restartAllServices() {
|
||||
waitAndExecute {
|
||||
asyncExecution {
|
||||
Actions.restartDnsMasq()
|
||||
Actions.restartPhpFpm()
|
||||
Actions.restartNginx()
|
||||
} completion: {
|
||||
} success: {
|
||||
DispatchQueue.main.async {
|
||||
LocalNotification.send(
|
||||
title: "notification.services_restarted".localized,
|
||||
@ -189,9 +215,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
|
||||
@objc func stopAllServices() {
|
||||
waitAndExecute {
|
||||
asyncExecution {
|
||||
Actions.stopAllServices()
|
||||
} completion: {
|
||||
} success: {
|
||||
DispatchQueue.main.async {
|
||||
LocalNotification.send(
|
||||
title: "notification.services_stopped".localized,
|
||||
@ -202,19 +228,19 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
|
||||
@objc func restartNginx() {
|
||||
waitAndExecute {
|
||||
asyncExecution {
|
||||
Actions.restartNginx()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func restartDnsMasq() {
|
||||
waitAndExecute {
|
||||
asyncExecution {
|
||||
Actions.restartDnsMasq()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
||||
waitAndExecute {
|
||||
asyncExecution {
|
||||
sender.phpExtension?.toggle()
|
||||
|
||||
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
||||
@ -226,35 +252,33 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
@objc func openPhpInfo() {
|
||||
var url: URL? = nil
|
||||
|
||||
waitAndExecute {
|
||||
asyncWithBusyUI {
|
||||
url = Actions.createTempPhpInfoFile()
|
||||
} completion: {
|
||||
// When this has been completed, open the URL to the file in the browser
|
||||
NSWorkspace.shared.open(url!)
|
||||
if url != nil { NSWorkspace.shared.open(url!) }
|
||||
}
|
||||
}
|
||||
|
||||
@objc func fixMyValet() {
|
||||
// Tell the user the switch is about to occur
|
||||
if Alert.present(
|
||||
if !Alert.present(
|
||||
messageText: "alert.fix_my_valet.title".localized,
|
||||
informativeText: "alert.fix_my_valet.info".localized(PhpEnv.brewPhpVersion),
|
||||
buttonTitle: "alert.fix_my_valet.ok".localized,
|
||||
secondButtonTitle: "alert.fix_my_valet.cancel".localized,
|
||||
style: .warning
|
||||
) {
|
||||
// Start the fix
|
||||
waitAndExecute {
|
||||
Log.info("The user has chosen to abort Fix My Valet")
|
||||
return
|
||||
}
|
||||
|
||||
asyncExecution {
|
||||
Actions.fixMyValet()
|
||||
} completion: {
|
||||
} success: {
|
||||
Alert.notify(
|
||||
message: "alert.fix_my_valet_done.title".localized,
|
||||
info: "alert.fix_my_valet_done.info".localized
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Log.info("The user has chosen to abort Fix My Valet")
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateGlobalComposerDependencies() {
|
||||
@ -284,70 +308,26 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
self.switchToPhpVersion(sender.version)
|
||||
}
|
||||
|
||||
// TODO (5.1): Investigate if `waitAndExecute` cannot be used here
|
||||
@objc func switchToPhpVersion(_ version: String) {
|
||||
setBusyImage()
|
||||
PhpEnv.shared.isBusy = true
|
||||
PhpEnv.shared.delegate = self
|
||||
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
// Update the PHP version in the status bar
|
||||
updatePhpVersionInStatusBar()
|
||||
|
||||
// Update the menu
|
||||
rebuild()
|
||||
|
||||
let sendLocalNotification = {
|
||||
LocalNotification.send(
|
||||
title: String(format: "notification.version_changed_title".localized, version),
|
||||
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
||||
)
|
||||
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
||||
}
|
||||
|
||||
let completion = {
|
||||
// Fire off the delegate method
|
||||
PhpEnv.shared.delegate?.switcherDidCompleteSwitch()
|
||||
|
||||
// Mark as no longer busy
|
||||
PhpEnv.shared.isBusy = false
|
||||
|
||||
// Perform UI updates on main thread
|
||||
DispatchQueue.main.async { [self] in
|
||||
updatePhpVersionInStatusBar()
|
||||
rebuild()
|
||||
|
||||
if !PhpEnv.shared.validate(version) {
|
||||
let outcome = Alert.present(
|
||||
messageText: "alert.php_switch_failed.title".localized(version),
|
||||
informativeText: "alert.php_switch_failed.info".localized(version),
|
||||
buttonTitle: "alert.php_switch_failed.confirm".localized,
|
||||
secondButtonTitle: "alert.php_switch_failed.cancel".localized, style: .informational)
|
||||
if outcome {
|
||||
MainMenu.shared.fixMyValet()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Run composer updates
|
||||
if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) {
|
||||
self.updateGlobalDependencies(notify: false, completion: { _ in sendLocalNotification() })
|
||||
} else {
|
||||
sendLocalNotification()
|
||||
}
|
||||
|
||||
// Update stats
|
||||
Stats.incrementSuccessfulSwitchCount()
|
||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
PhpEnv.switcher.performSwitch(
|
||||
to: version,
|
||||
completion: completion
|
||||
completion: {
|
||||
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Menu Item Functionality
|
||||
|
||||
@objc func openAbout() {
|
||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
NSApplication.shared.orderFrontStandardAboutPanel()
|
||||
@ -380,91 +360,4 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
// When the menu is closed, allow the shortcut to work again
|
||||
App.shared.shortcutHotkey?.isPaused = false
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
|
||||
/**
|
||||
Updates the global dependencies and runs the completion callback when done.
|
||||
*/
|
||||
private func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
|
||||
if !Shell.fileExists("/usr/local/bin/composer") {
|
||||
Alert.notify(
|
||||
message: "alert.composer_missing.title".localized,
|
||||
info: "alert.composer_missing.info".localized
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
PhpEnv.shared.isBusy = true
|
||||
setBusyImage()
|
||||
self.rebuild()
|
||||
|
||||
let noLongerBusy = {
|
||||
PhpEnv.shared.isBusy = false
|
||||
DispatchQueue.main.async { [self] in
|
||||
self.updatePhpVersionInStatusBar()
|
||||
self.rebuild()
|
||||
}
|
||||
}
|
||||
|
||||
var window: ProgressWindowController? = ProgressWindowController.display(
|
||||
title: "alert.composer_progress.title".localized,
|
||||
description: "alert.composer_progress.info".localized
|
||||
)
|
||||
window?.setType(info: true)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let task = Shell.user.createTask(
|
||||
for: "/usr/local/bin/composer global update", requiresPath: true
|
||||
)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
window?.addToConsole("/usr/local/bin/composer global update\n")
|
||||
}
|
||||
|
||||
Shell.captureOutput(
|
||||
task,
|
||||
didReceiveStdOutData: { string in
|
||||
DispatchQueue.main.async {
|
||||
window?.addToConsole(string)
|
||||
}
|
||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||
},
|
||||
didReceiveStdErrData: { string in
|
||||
DispatchQueue.main.async {
|
||||
window?.addToConsole(string)
|
||||
}
|
||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||
}
|
||||
)
|
||||
|
||||
task.launch()
|
||||
task.waitUntilExit()
|
||||
Shell.haltCapturingOutput(task)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if task.terminationStatus <= 0 {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
window?.close()
|
||||
if (notify) {
|
||||
LocalNotification.send(
|
||||
title: "alert.composer_success.title".localized,
|
||||
subtitle: "alert.composer_success.info".localized
|
||||
)
|
||||
}
|
||||
window = nil
|
||||
noLongerBusy()
|
||||
completion(true)
|
||||
}
|
||||
} else {
|
||||
window?.setType(info: false)
|
||||
window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized
|
||||
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
|
||||
window = nil
|
||||
noLongerBusy()
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,18 +9,18 @@
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe" customClass="ServicesView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="340" height="46"/>
|
||||
<customView wantsLayer="YES" id="c22-O7-iKe" customClass="ServicesView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="330" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
||||
<rect key="frame" x="30" y="3" width="280" height="40"/>
|
||||
<rect key="frame" x="30" y="3" width="270" height="40"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
||||
<rect key="frame" x="0.0" y="4" width="80" height="32"/>
|
||||
<rect key="frame" x="0.0" y="4" width="77" height="32"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="At1-ch-qv2">
|
||||
<rect key="frame" x="25" y="18" width="31" height="14"/>
|
||||
<rect key="frame" x="23" y="18" width="31" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="PHP" id="LKe-C4-jxo">
|
||||
<font key="font" metaFont="systemMedium" size="11"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -28,7 +28,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tko-cP-XSz">
|
||||
<rect key="frame" x="28" y="0.0" width="24" height="16"/>
|
||||
<rect key="frame" x="26" y="0.0" width="24" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="Fxu-6h-A2h"/>
|
||||
<constraint firstAttribute="width" constant="24" id="hOc-Ur-dmA"/>
|
||||
@ -47,10 +47,10 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
||||
<rect key="frame" x="100" y="4" width="80" height="32"/>
|
||||
<rect key="frame" x="97" y="4" width="76" height="32"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
||||
<rect key="frame" x="20" y="18" width="40" height="14"/>
|
||||
<rect key="frame" x="18" y="18" width="40" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="NGINX" id="Qfq-Bl-yuh">
|
||||
<font key="font" metaFont="systemMedium" size="11"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -58,7 +58,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZqW-6d-vpe">
|
||||
<rect key="frame" x="32" y="0.0" width="16" height="16"/>
|
||||
<rect key="frame" x="30" y="0.0" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="EPG-jm-7Xs"/>
|
||||
<constraint firstAttribute="width" constant="16" id="iif-kT-phn"/>
|
||||
@ -77,10 +77,10 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
||||
<rect key="frame" x="200" y="4" width="80" height="32"/>
|
||||
<rect key="frame" x="193" y="4" width="77" height="32"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
||||
<rect key="frame" x="9" y="18" width="62" height="14"/>
|
||||
<rect key="frame" x="8" y="18" width="62" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="DNSMASQ" id="lGh-MT-TgI">
|
||||
<font key="font" metaFont="systemMedium" size="11"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -88,7 +88,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DcG-x3-lvy">
|
||||
<rect key="frame" x="32" y="0.0" width="16" height="16"/>
|
||||
<rect key="frame" x="31" y="0.0" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="16" id="AKl-Gq-RtM"/>
|
||||
<constraint firstAttribute="height" constant="16" id="q2g-Ua-eIJ"/>
|
||||
@ -145,6 +145,6 @@
|
||||
</customView>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ServiceLoading" width="512" height="512"/>
|
||||
<image name="ServiceLoading" width="17" height="16"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -10,11 +10,11 @@
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe" customClass="StatsView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="340" height="55"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="330" height="55"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
||||
<rect key="frame" x="30" y="6" width="280" height="43"/>
|
||||
<rect key="frame" x="30" y="6" width="270" height="43"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
||||
<rect key="frame" x="0.0" y="4" width="87" height="35"/>
|
||||
@ -46,10 +46,10 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
||||
<rect key="frame" x="107" y="4" width="77" height="35"/>
|
||||
<rect key="frame" x="107" y="4" width="68" height="35"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
||||
<rect key="frame" x="7" y="21" width="63" height="14"/>
|
||||
<rect key="frame" x="3" y="21" width="63" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="MAX POST" id="Qfq-Bl-yuh">
|
||||
<font key="font" metaFont="systemMedium" size="11"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -57,7 +57,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vyu-AO-8SH">
|
||||
<rect key="frame" x="11" y="0.0" width="55" height="19"/>
|
||||
<rect key="frame" x="7" y="0.0" width="55" height="19"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="1024M" id="uH4-Zy-43x">
|
||||
<font key="font" metaFont="systemMedium" size="16"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -75,7 +75,7 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
||||
<rect key="frame" x="204" y="4" width="76" height="35"/>
|
||||
<rect key="frame" x="195" y="4" width="75" height="35"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
||||
<rect key="frame" x="-2" y="21" width="79" height="14"/>
|
||||
|
@ -8,6 +8,7 @@
|
||||
import Cocoa
|
||||
|
||||
class StatusMenu : NSMenu {
|
||||
|
||||
func addPhpVersionMenuItems() {
|
||||
if PhpEnv.phpInstall.version.error {
|
||||
for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] {
|
||||
@ -37,47 +38,6 @@ class StatusMenu : NSMenu {
|
||||
self.addItem(NSMenuItem.separator())
|
||||
}
|
||||
|
||||
func addOtherMenuItems() {
|
||||
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
||||
let servicesMenu = NSMenu()
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: ""))
|
||||
|
||||
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
||||
servicesMenu.addItem(NSMenuItem(
|
||||
title: "mi_fix_my_valet_unavailable".localized(PhpEnv.brewPhpVersion),
|
||||
action: nil, keyEquivalent: "f"
|
||||
))
|
||||
} else {
|
||||
servicesMenu.addItem(NSMenuItem(
|
||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: "f"))
|
||||
}
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_services".localized, action: nil, keyEquivalent: ""))
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||
|
||||
servicesMenu.addItem(
|
||||
NSMenuItem(title: "mi_stop_all_services".localized, action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"),
|
||||
withKeyModifier: [.command, .shift])
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s"))
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_manual_actions".localized, action: nil, keyEquivalent: ""))
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenu), keyEquivalent: "r"))
|
||||
|
||||
for item in servicesMenu.items {
|
||||
item.target = MainMenu.shared
|
||||
}
|
||||
|
||||
self.setSubmenu(servicesMenu, for: services)
|
||||
self.addItem(services)
|
||||
}
|
||||
|
||||
func addValetMenuItems() {
|
||||
self.addItem(HeaderView.asMenuItem(text: "mi_valet".localized))
|
||||
self.addItem(NSMenuItem(title: "mi_valet_config".localized, action: #selector(MainMenu.openValetConfigFolder), keyEquivalent: "v"))
|
||||
@ -131,9 +91,59 @@ class StatusMenu : NSMenu {
|
||||
|
||||
// Other
|
||||
self.addItem(NSMenuItem.separator())
|
||||
self.addOtherMenuItems()
|
||||
self.addFirstAidAndServicesMenuItems()
|
||||
}
|
||||
|
||||
func addFirstAidAndServicesMenuItems() {
|
||||
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
||||
let servicesMenu = NSMenu()
|
||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
|
||||
|
||||
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
||||
servicesMenu.addItem(NSMenuItem(
|
||||
title: "mi_fix_my_valet_unavailable".localized(PhpEnv.brewPhpVersion),
|
||||
action: nil, keyEquivalent: "f")
|
||||
)
|
||||
} else {
|
||||
servicesMenu.addItem(NSMenuItem(
|
||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: "")
|
||||
)
|
||||
}
|
||||
|
||||
/* (disabled until v5.1 and further tweaking)
|
||||
servicesMenu.addItem(NSMenuItem(
|
||||
title: "mi_fix_brew_permissions".localized(),
|
||||
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: "")
|
||||
)
|
||||
*/
|
||||
|
||||
servicesMenu.addItem(NSMenuItem.separator())
|
||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s"))
|
||||
servicesMenu.addItem(
|
||||
NSMenuItem(title: "mi_stop_all_services".localized, action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"),
|
||||
withKeyModifier: [.command, .shift])
|
||||
|
||||
servicesMenu.addItem(NSMenuItem.separator())
|
||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_manual_actions".localized))
|
||||
|
||||
servicesMenu.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenuInForeground), keyEquivalent: "r"))
|
||||
|
||||
for item in servicesMenu.items {
|
||||
item.target = MainMenu.shared
|
||||
}
|
||||
|
||||
self.setSubmenu(servicesMenu, for: services)
|
||||
self.addItem(services)
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS
|
||||
|
||||
private func addSwitchToPhpMenuItems() {
|
||||
var shortcutKey = 1
|
||||
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
|
||||
|
16
phpmon/Domain/Preferences/MenuBarIcons.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// MenuBarIcons.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 06/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
enum MenuBarIcon: String, CaseIterable {
|
||||
case iconPhp = "php"
|
||||
case iconElephant = "elephant"
|
||||
case noIcon = "none"
|
||||
}
|
@ -14,7 +14,7 @@ import Foundation
|
||||
enum PreferenceName: String {
|
||||
case wasLaunchedBefore = "launched_before"
|
||||
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
||||
case shouldDisplayPhpHintInIcon = "add_php_to_icon"
|
||||
case iconTypeToDisplay = "icon_type_to_display"
|
||||
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
|
||||
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
|
||||
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
|
||||
@ -22,6 +22,13 @@ enum PreferenceName: String {
|
||||
case globalHotkey = "global_hotkey"
|
||||
}
|
||||
|
||||
/**
|
||||
These are retired preferences that, if present, should be migrated.
|
||||
*/
|
||||
enum RetiredPreferenceName: String {
|
||||
case shouldDisplayPhpHintInIcon = "add_php_to_icon"
|
||||
}
|
||||
|
||||
/**
|
||||
These are internal stats. They NEVER get shared.
|
||||
*/
|
||||
@ -64,7 +71,7 @@ class Preferences {
|
||||
UserDefaults.standard.register(defaults: [
|
||||
/// Preferences
|
||||
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
|
||||
PreferenceName.shouldDisplayPhpHintInIcon.rawValue: true,
|
||||
PreferenceName.iconTypeToDisplay.rawValue: MenuBarIcon.iconPhp.rawValue,
|
||||
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
|
||||
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true,
|
||||
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
|
||||
@ -76,6 +83,7 @@ class Preferences {
|
||||
])
|
||||
|
||||
if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) {
|
||||
handleMigration()
|
||||
return
|
||||
}
|
||||
|
||||
@ -84,6 +92,23 @@ class Preferences {
|
||||
UserDefaults.standard.synchronize()
|
||||
}
|
||||
|
||||
/**
|
||||
Sometimes preferences will change, and a migration is required to take the user's previous preference
|
||||
and migrate it over to the new type. For example, the choice to disable the icon next to the version
|
||||
number was once a boolean (do you want the icon? yes / no) but has now become a multi-faceted option.
|
||||
*/
|
||||
static func handleMigration() {
|
||||
// If the user chose the "no icon" option, migrate it over
|
||||
if (
|
||||
UserDefaults.standard.value(forKey: RetiredPreferenceName.shouldDisplayPhpHintInIcon.rawValue) != nil &&
|
||||
UserDefaults.standard.bool(forKey: RetiredPreferenceName.shouldDisplayPhpHintInIcon.rawValue) == false
|
||||
) {
|
||||
Log.info("The preference where the user chose no icon has been migrated over.")
|
||||
UserDefaults.standard.set(MenuBarIcon.noIcon.rawValue, forKey: PreferenceName.iconTypeToDisplay.rawValue)
|
||||
UserDefaults.standard.removeObject(forKey: RetiredPreferenceName.shouldDisplayPhpHintInIcon.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
static var preferences: [PreferenceName: Any?] {
|
||||
@ -112,7 +137,6 @@ class Preferences {
|
||||
return [
|
||||
// Part 1: Always Booleans
|
||||
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
||||
.shouldDisplayPhpHintInIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayPhpHintInIcon.rawValue) as Any,
|
||||
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
|
||||
.autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any,
|
||||
.autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any,
|
||||
@ -120,6 +144,7 @@ class Preferences {
|
||||
|
||||
// Part 2: Always Strings
|
||||
.globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any,
|
||||
.iconTypeToDisplay: UserDefaults.standard.string(forKey: PreferenceName.iconTypeToDisplay.rawValue) as Any,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -57,11 +57,12 @@ class PrefsVC: NSViewController {
|
||||
MainMenu.shared.refreshIcon()
|
||||
}
|
||||
),
|
||||
CheckboxPreferenceView.make(
|
||||
SelectPreferenceView.make(
|
||||
sectionText: "",
|
||||
descriptionText: "prefs.icon_hint_desc".localized,
|
||||
checkboxText: "prefs.icon_hint_title".localized,
|
||||
preference: .shouldDisplayPhpHintInIcon,
|
||||
descriptionText: "prefs.icon_options_desc".localized,
|
||||
options: MenuBarIcon.allCases.map({ return $0.rawValue }),
|
||||
localizationPrefix: "prefs.icon_options",
|
||||
preference: .iconTypeToDisplay,
|
||||
action: {
|
||||
MainMenu.shared.refreshIcon()
|
||||
}
|
||||
|
@ -61,7 +61,7 @@
|
||||
<outlet property="labelDescription" destination="Bcg-X1-qca" id="T23-ag-AUf"/>
|
||||
<outlet property="labelSection" destination="B8f-nb-Y0A" id="i61-ls-yM0"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="149" y="-114.5"/>
|
||||
<point key="canvasLocation" x="149" y="-115"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
|
82
phpmon/Domain/Preferences/Views/SelectPreferenceView.swift
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// SelectPreferenceView.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 06/02/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class SelectPreferenceView: NSView, XibLoadable {
|
||||
|
||||
@IBOutlet weak var labelSection: NSTextField!
|
||||
@IBOutlet weak var labelDescription: NSTextField!
|
||||
@IBOutlet weak var popupButton: NSPopUpButton!
|
||||
|
||||
var localizationPrefix: String = ""
|
||||
var imagePrefix: String? = nil
|
||||
|
||||
var options: [String] = [] {
|
||||
didSet {
|
||||
self.popupButton.removeAllItems()
|
||||
self.options.forEach { value in
|
||||
self.popupButton.addItem(
|
||||
withTitle: "\(localizationPrefix).\(value)".localized
|
||||
)
|
||||
}
|
||||
|
||||
if imagePrefix == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self.popupButton.itemArray.enumerated().forEach { item in
|
||||
item.element.image = NSImage(named: "\(imagePrefix!)_\(self.options[item.offset])")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var action: (() -> Void)!
|
||||
|
||||
var preference: PreferenceName! {
|
||||
didSet {
|
||||
let value = Preferences.preferences[preference] as! String
|
||||
self.options.enumerated().forEach { option in
|
||||
if option.element == value {
|
||||
self.popupButton.selectItem(at: option.offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func make(
|
||||
sectionText: String,
|
||||
descriptionText: String,
|
||||
options: [String],
|
||||
localizationPrefix: String,
|
||||
imagePrefix: String? = nil,
|
||||
preference: PreferenceName,
|
||||
action: @escaping () -> Void) -> NSView
|
||||
{
|
||||
let view = Self.createFromXib()!
|
||||
|
||||
view.labelSection.stringValue = sectionText
|
||||
view.labelDescription.stringValue = descriptionText
|
||||
|
||||
view.localizationPrefix = localizationPrefix
|
||||
view.imagePrefix = imagePrefix
|
||||
view.options = options
|
||||
view.preference = preference
|
||||
view.action = action
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@IBAction func valueChanged(_ sender: Any) {
|
||||
let index = self.popupButton.indexOfSelectedItem
|
||||
Preferences.update(.iconTypeToDisplay, value: self.options[index])
|
||||
self.action()
|
||||
}
|
||||
|
||||
}
|
72
phpmon/Domain/Preferences/Views/SelectPreferenceView.xib
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe" customClass="SelectPreferenceView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="596" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Bcg-X1-qca">
|
||||
<rect key="frame" x="168" y="5" width="410" height="14"/>
|
||||
<textFieldCell key="cell" title="DESCRIPTION" id="9fH-up-Sob">
|
||||
<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>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="B8f-nb-Y0A">
|
||||
<rect key="frame" x="-2" y="29" width="154" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="150" id="euj-t0-xv4"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="SECTION" id="46w-Sv-y21">
|
||||
<font key="font" metaFont="systemMedium" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YaB-Tg-Ir3">
|
||||
<rect key="frame" x="167" y="23" width="110" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Icon Option" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="SaA-mm-HBo" id="Su6-C4-aGo">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="WjV-7P-3WA">
|
||||
<items>
|
||||
<menuItem title="Icon Option" state="on" id="SaA-mm-HBo"/>
|
||||
<menuItem title="Item 2" id="I5Z-Ka-zo6"/>
|
||||
<menuItem title="Item 3" id="qUM-nV-HdU"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="valueChanged:" target="c22-O7-iKe" id="M95-aC-xtz"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="YaB-Tg-Ir3" firstAttribute="centerY" secondItem="B8f-nb-Y0A" secondAttribute="centerY" id="1t0-bW-LpQ"/>
|
||||
<constraint firstItem="B8f-nb-Y0A" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="5" id="2Zu-h3-qb0"/>
|
||||
<constraint firstItem="Bcg-X1-qca" firstAttribute="leading" secondItem="YaB-Tg-Ir3" secondAttribute="leading" id="5IQ-Nb-Q3V"/>
|
||||
<constraint firstItem="YaB-Tg-Ir3" firstAttribute="leading" secondItem="B8f-nb-Y0A" secondAttribute="trailing" constant="20" id="6sr-bU-xvG"/>
|
||||
<constraint firstItem="Bcg-X1-qca" firstAttribute="top" secondItem="YaB-Tg-Ir3" secondAttribute="bottom" constant="8" symbolic="YES" id="Mji-pe-CNl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Bcg-X1-qca" secondAttribute="trailing" constant="20" symbolic="YES" id="UPo-Il-l81"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="YaB-Tg-Ir3" secondAttribute="trailing" constant="20" symbolic="YES" id="Zlg-jj-uKY"/>
|
||||
<constraint firstItem="B8f-nb-Y0A" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="Ztd-uk-4aw"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Bcg-X1-qca" secondAttribute="bottom" constant="5" id="hNE-mU-jcu"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="labelDescription" destination="Bcg-X1-qca" id="T23-ag-AUf"/>
|
||||
<outlet property="labelSection" destination="B8f-nb-Y0A" id="i61-ls-yM0"/>
|
||||
<outlet property="popupButton" destination="YaB-Tg-Ir3" id="sAB-BL-qUQ"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="149" y="-115"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
@ -47,7 +47,6 @@ class SiteListCell: NSTableCellView
|
||||
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
||||
|
||||
// Show the green or red lock based on whether the site was secured
|
||||
// imageViewLock.image = NSImage(named: site.secured ? "Lock" : "LockUnlocked")
|
||||
imageViewLock.contentTintColor = site.secured ?
|
||||
NSColor(named: "IconColorGreen") // green
|
||||
: NSColor(named: "IconColorRed")
|
||||
@ -103,7 +102,7 @@ class SiteListCell: NSTableCellView
|
||||
if response.rawValue > NSApplication.ModalResponse.alertFirstButtonReturn.rawValue {
|
||||
if map.keys.contains(response.rawValue) {
|
||||
let version = map[response.rawValue]!
|
||||
print("Pressed button to switch to \(version)")
|
||||
Log.info("Pressed button to switch to \(version)")
|
||||
MainMenu.shared.switchToPhpVersion(version)
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
return sites[tableView.selectedRow]
|
||||
}
|
||||
|
||||
var timer: Timer? = nil
|
||||
|
||||
// MARK: - Display
|
||||
|
||||
public static func create(delegate: NSWindowDelegate?) {
|
||||
@ -90,7 +92,11 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
Also shows a spinner to indicate that we're busy.
|
||||
*/
|
||||
private func setUIBusy() {
|
||||
progressIndicator.startAnimation(nil)
|
||||
// If it takes more than 0.5s to set the UI to not busy, show a spinner
|
||||
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in
|
||||
self.progressIndicator.startAnimation(true)
|
||||
})
|
||||
|
||||
tableView.alphaValue = 0.3
|
||||
tableView.isEnabled = false
|
||||
tableView.selectRowIndexes([], byExtendingSelection: true)
|
||||
@ -100,6 +106,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
Re-enables the UI so the user can interact with it.
|
||||
*/
|
||||
private func setUINotBusy() {
|
||||
timer?.invalidate()
|
||||
progressIndicator.stopAnimation(nil)
|
||||
tableView.alphaValue = 1.0
|
||||
tableView.isEnabled = true
|
||||
@ -118,7 +125,9 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
setUIBusy()
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
execute()
|
||||
DispatchQueue.main.async { [self] in
|
||||
|
||||
// For a smoother animation, expect at least a 0.2 second delay
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in
|
||||
completion()
|
||||
setUINotBusy()
|
||||
}
|
||||
|
@ -30,9 +30,13 @@ extension App {
|
||||
func handlePhpConfigWatcher(forceReload: Bool = false) {
|
||||
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(PhpEnv.phpInstall.version.short)")
|
||||
|
||||
// Check whether the watcher exists and schedule on the main thread
|
||||
// if we don't consistently do this, the app will create duplicate watchers
|
||||
// due to timing issues, which creates retain cycles.
|
||||
DispatchQueue.main.async {
|
||||
// Watcher needs to be created
|
||||
if self.watcher == nil {
|
||||
startWatcher(url)
|
||||
self.startWatcher(url)
|
||||
}
|
||||
|
||||
// Watcher needs to be updated
|
||||
@ -40,7 +44,8 @@ extension App {
|
||||
self.watcher.disable()
|
||||
self.watcher = nil
|
||||
Log.info("Watcher has stopped watching files. Starting new one...")
|
||||
startWatcher(url)
|
||||
self.startWatcher(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,19 +40,24 @@ class PhpConfigWatcher {
|
||||
self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write)
|
||||
}
|
||||
|
||||
Log.info("A watcher exists for the following config paths:")
|
||||
Log.info(self.watchers.map({ watcher in
|
||||
Log.perf("A watcher exists for the following config paths:")
|
||||
Log.perf(self.watchers.map({ watcher in
|
||||
return watcher.url.relativePath
|
||||
}))
|
||||
}
|
||||
|
||||
func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour = .reloadsMenu) {
|
||||
if !Filesystem.fileExists(url.path) {
|
||||
Log.warn("No watcher was created for \(url.path) because the requested file does not exist.")
|
||||
return
|
||||
}
|
||||
|
||||
let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self, behaviour: behaviour)
|
||||
self.watchers.append(watcher)
|
||||
}
|
||||
|
||||
func disable() {
|
||||
Log.info("Turning off existing watchers...")
|
||||
Log.perf("Turning off all individual existing watchers...")
|
||||
self.watchers.forEach { (watcher) in
|
||||
watcher.stopMonitoring()
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
"mi_stop_all_services" = "Stop All Services";
|
||||
"mi_fix_my_valet" = "Fix My Valet (PHP & Services)";
|
||||
"mi_fix_my_valet_unavailable" = "Fix My Valet Unavailable";
|
||||
"mi_fix_brew_permissions" = "Restore Homebrew Permissions";
|
||||
"mi_php_refresh" = "Refresh Information";
|
||||
|
||||
"mi_configuration" = "PHP Configuration";
|
||||
@ -36,11 +37,11 @@
|
||||
"mi_upload_max_filesize" = "Max Upload";
|
||||
"mi_manual_actions" = "Manual Actions";
|
||||
"mi_services" = "Services";
|
||||
"mi_help" = "First Aid";
|
||||
"mi_other" = "First Aid & Services";
|
||||
"mi_first_aid" = "First Aid";
|
||||
|
||||
"mi_composer" = "Composer";
|
||||
"mi_valet_config" = "Locate Valet folder (.config/valet)";
|
||||
"mi_valet_config" = "Locate Valet Folder (.config/valet)";
|
||||
"mi_php_config" = "Locate PHP Configuration File (php.ini)";
|
||||
"mi_global_composer" = "Locate Global Composer File (.composer)";
|
||||
"mi_phpinfo" = "Show Current Configuration (phpinfo)";
|
||||
@ -132,22 +133,26 @@
|
||||
"prefs.subtitle" = "Preferences";
|
||||
"prefs.close" = "Close";
|
||||
|
||||
"prefs.global_shortcut" = "Global shortcut:";
|
||||
"prefs.dynamic_icon" = "Icon customization:";
|
||||
"prefs.info_density" = "Info density:";
|
||||
"prefs.global_shortcut" = "Global Shortcut:";
|
||||
"prefs.dynamic_icon" = "Dynamic Icon:";
|
||||
"prefs.dynamic_icon" = "Icon Type:";
|
||||
"prefs.info_density" = "Info Density:";
|
||||
"prefs.services" = "Services:";
|
||||
"prefs.switcher" = "Switcher:";
|
||||
"prefs.integrations" = "Integrations:";
|
||||
|
||||
"prefs.icon_options.php" = "Display PHP Icon";
|
||||
"prefs.icon_options.elephant" = "Display Elephant Icon";
|
||||
"prefs.icon_options.none" = "Do Not Display Icon";
|
||||
|
||||
"prefs.icon_options_desc" = "This option decides which icon will be displayed next to the version number of the currently linked PHP version. If the Dynamic Icon option has been disabled, this will have no effect.";
|
||||
|
||||
"prefs.auto_restart_services_title" = "Auto-restart PHP-FPM";
|
||||
"prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when you check or uncheck an extension. Slightly slower when enabled, but this applies the extension change immediately for all sites you're serving, no need to restart PHP-FPM manually.";
|
||||
|
||||
"prefs.dynamic_icon_title" = "Display dynamic icon in menu bar";
|
||||
"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible. If checked, it will display the major version number of the currently linked PHP version.";
|
||||
|
||||
"prefs.icon_hint_title" = "Display PHP hint next to version number";
|
||||
"prefs.icon_hint_desc" = "If you uncheck this box, the icon in the menu bar will only show the version number and not the PHP hint. This preference does not do anything if the dynamic icon has been disabled.";
|
||||
|
||||
"prefs.display_full_php_version" = "Display full PHP version everywhere";
|
||||
"prefs.display_full_php_version_desc" = "Display the full version instead of the major version displayed in the menu bar and the dropdown menu. (This may be undesirable on smaller displays, so this is disabled by default.)";
|
||||
|
||||
@ -180,7 +185,7 @@
|
||||
|
||||
// Composer Update
|
||||
"alert.composer_missing.title" = "Composer not found!";
|
||||
"alert.composer_missing.info" = "Make sure you have Composer available in `/usr/local/bin/composer`. If Composer is located somewhere else, please create a symlink, like so (make sure to use the correct path):\n\n`ln -s /path/to/composer /user/local/bin`.";
|
||||
"alert.composer_missing.info" = "Make sure you have Composer available in `/usr/local/bin/composer`. If Composer is located somewhere else, please create a symlink, like so (make sure to use the correct path):\n\n`ln -s /path/to/composer /usr/local/bin`.";
|
||||
|
||||
"alert.composer_progress.title" = "Updating global dependencies...";
|
||||
"alert.composer_progress.info" = "You can see the progress in the terminal output below.";
|
||||
@ -213,6 +218,15 @@ problem manually, using your own Terminal app (this just shows you the output)."
|
||||
"alert.fix_my_valet_done.title" = "Fix My Valet has completed its operations.";
|
||||
"alert.fix_my_valet_done.info" = "All appropriate services have been stopped and the correct ones restarted, and the latest version of PHP should now be active. You can now try switching to another version of PHP.\n\nIf visiting sites still does not work, you may try running `valet install` again, this can fix a 502 issue (Bad Gateway).\n\nIf Valet is broken and you cannot run `valet install`, you may need to run `composer global update`. Please consult the FAQ on GitHub if you have further issues.";
|
||||
|
||||
// Restore Homebrew Permissions
|
||||
"alert.fix_homebrew_permissions.title" = "About \"Restore Homebrew Permissions\"";
|
||||
"alert.fix_homebrew_permissions.info" = "This feature was created so you can run `brew upgrade` or `brew cleanup` without permission issues.\n\nThis will require administrative privileges, because PHP Monitor will restore your ownership of the files and folders that are currently owned by the `root` user, due to Valet services running as root.\n\n(You will be notified when this fix has been applied.)";
|
||||
"alert.fix_homebrew_permissions.ok" = "Restore Permissions";
|
||||
"alert.fix_homebrew_permissions.cancel" = "Cancel";
|
||||
|
||||
"alert.fix_homebrew_permissions_done.title" = "Permissions Fixed!";
|
||||
"alert.fix_homebrew_permissions_done.info" = "When you are done with Homebrew, you should open PHP Monitor and restart all services if you want Valet to work again. It is also recommended to restart PHP Monitor after running `brew upgrade`.";
|
||||
|
||||
// PHP FPM Broken
|
||||
"alert.php_fpm_broken.title" = "PHP-FPM configuration is incorrect";
|
||||
"alert.php_fpm_broken.info" = "PHP Monitor has determined that there are issues with your PHP-FPM config: it's not pointing to the Valet socket. This will result in 502 Bad Gateway if you visit websites linked via Valet.\n\nYou can usually fix this by running\n`valet install`, which updates your\n PHP-FPM configuration.";
|
||||
@ -278,3 +292,8 @@ You can do this by running `composer global update` in your terminal. After that
|
||||
"startup.sponsor_encouragement.desc" = "If you have already donated, then YOU are the reason why the app was able to get all these new features. In that case, this is a THANK YOU message to you.\n\nTo be 100% transparent: I plan to keep PHP Monitor open source and free. Your support makes this decision very easy.\n\n(You will only see this prompt once.)";
|
||||
"startup.sponsor_encouragement.accept" = "Yes, I would like to sponsor";
|
||||
"startup.sponsor_encouragement.skip" = "Nevermind";
|
||||
|
||||
// ERROR MESSAGES (based on AlertableError)
|
||||
|
||||
"alert.errors.homebrew_permissions.applescript_returned_nil.title" = "Restore Homebrew Permissions has been cancelled.";
|
||||
"alert.errors.homebrew_permissions.applescript_returned_nil.description" = "The outcome of the script that is executed to adjust the permissions returned nil, which usually means that you did not grant administrative permissions to PHP Monitor.\n\nIf you clicked on Cancel during the authentication prompt, this is normal. If you did actually authenticate and you are still seeing this message, something probably went wrong.";
|
||||
|