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

Compare commits

..

32 Commits

Author SHA1 Message Date
8b0aeef2e6 🚀 Version 5.8.1 2023-03-29 18:05:03 +02:00
aa406434d0 🔧 Bump build 2023-03-29 18:04:27 +02:00
d320c49092 👌 Avoid force unwrapping try (may crash) 2023-03-23 19:08:39 +01:00
966033e052 🐛 Prevent crash upon parsing invalid Valet directories
This fixes #247, which can be caused when certain folders are not
accessible for some reason. This can occur due to network reasons,
but also because the linked folder in question is iCloud Drive.
2023-03-23 18:00:45 +01:00
7c192730e1 📝 Update SECURITY 2023-03-15 20:09:32 +01:00
13ee618d5c 🚀 Version 5.8.0 2023-03-03 16:49:38 +01:00
e149a2500e 📝 Updated README 2023-03-02 16:27:26 +01:00
aef7d4021c 🔧 Bump build 2023-03-01 19:22:26 +01:00
050c489158 🐛 Prevent upgrade file removal from crashing the app 2023-03-01 19:20:13 +01:00
130bf38dd9 👌 Clean up HOMEBREW_DIR/Caskroom directory
Only when using the self-updater, of course.

Also include a description that reflects this cleanup scenario.
2023-03-01 19:17:18 +01:00
a6c3f0ceba 🍱 Use classic DEV channel icon 2023-02-27 19:16:15 +01:00
f274e9e004 👌 Updated IAP for accessing formulae.brew.sh 2023-02-27 19:08:04 +01:00
a07881a987 👌 Fix copy of missing config check 2023-02-27 19:04:50 +01:00
715b674929 Fix tests 2023-02-26 15:02:43 +01:00
cb504cc3f0 🔧 Bump build 2023-02-24 19:37:41 +01:00
7c08a2fdfe 👌 Improve PHP Doctor w/ async code
- This fixes an issue with PHP Doctor's "Scan Again" button not working.
- This also adds a new check which verifies if "php.ini", "php-fpm.conf"
  and "php-fpm.d/valet-fpm.conf" exist (all required files).
2023-02-24 19:36:12 +01:00
3f14754177 📝 Update README to reflect #239 2023-02-21 18:18:23 +01:00
2f47610c85 🔧 Use macOS 12.4 deployment target 2023-02-21 02:27:03 +01:00
cc6324b692 ♻️ PHP Guard changes 2023-02-20 18:15:12 +01:00
15d75a7f98 🔧 Bump build 2023-02-17 17:21:13 +01:00
c7c5311ff9 🐛 Ensure checkbox shows correct initial state 2023-02-17 17:19:56 +01:00
c93f047909 🔧 Bump build 2023-02-15 20:32:56 +01:00
f82a2120f7 🔧 Add display name 2023-02-15 20:32:37 +01:00
857cba9f45 On macOS 13 and newer, add "Start at login" (#210) 2023-02-15 20:24:01 +01:00
b0de0c04c6 🚀 Version 5.7.4 2023-02-14 18:53:32 +01:00
ec49257bcc 🔧 Bump build 2023-02-13 17:43:52 +01:00
9c6a21008a 🐛 Adjusted for new Homebrew JSON output (#235) 2023-02-13 17:38:09 +01:00
f27e07fc78 🔧 Bump build 2023-02-13 17:30:37 +01:00
9fceab3e2e 🐛 Adjusted for new Homebrew JSON output (#235) 2023-02-13 17:30:01 +01:00
5dffbf57d1 👌 Do not load identity asynchronously 2023-02-11 20:46:05 +01:00
03fdf23f0a 🚀 Version 5.7.3 2023-02-06 19:15:25 +01:00
412b7bad5c 🐛 Fix generated script (#231) 2023-02-06 19:13:49 +01:00
53 changed files with 681 additions and 300 deletions

View File

@ -112,6 +112,7 @@
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; };
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; };
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
@ -140,6 +141,7 @@
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
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 */; };
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */; };
C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; };
C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
@ -481,6 +483,10 @@
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C47DF1AF299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B0299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B1299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B2299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
@ -705,6 +711,9 @@
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; };
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; };
C4FD87A529AB98720002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FD87A629AB98730002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FD87A729AB98730002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
/* End PBXBuildFile section */
@ -807,6 +816,7 @@
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = "<group>"; };
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = "<group>"; };
C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigChecker.swift; sourceTree = "<group>"; };
C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = "<group>"; };
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
@ -821,6 +831,7 @@
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>"; };
C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCheckTest.swift; sourceTree = "<group>"; };
C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; };
C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; };
C451AFF52969E40F0078E617 /* HelpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpButton.swift; sourceTree = "<group>"; };
@ -857,6 +868,7 @@
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = "<group>"; };
C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = "<group>"; };
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginItemManager.swift; sourceTree = "<group>"; };
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; };
@ -1214,6 +1226,7 @@
C422DDAB28A2DAA100CEAC97 /* Warnings */ = {
isa = PBXGroup;
children = (
C43FDBE729A9329A003D85EC /* Services */,
C47699F028A2F3150060FEB8 /* Warning.swift */,
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */,
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */,
@ -1239,6 +1252,14 @@
path = Warning;
sourceTree = "<group>";
};
C43FDBE729A9329A003D85EC /* Services */ = {
isa = PBXGroup;
children = (
C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */,
);
path = Services;
sourceTree = "<group>";
};
C44067F327E256560045BD4E /* Cells */ = {
isa = PBXGroup;
children = (
@ -1409,6 +1430,7 @@
children = (
C471E7BE28F9B90F0021E251 /* StartupTest.swift */,
C469E702294CFDF700A82AB2 /* DomainsListTest.swift */,
C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */,
C4181F1028FAF9330042EA28 /* UITestCase.swift */,
);
path = ui;
@ -1440,6 +1462,7 @@
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
C4D3660A29113F20006BD146 /* System.swift */,
C4D36614291160A1006BD146 /* WIP.swift */,
C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -2016,6 +2039,7 @@
files = (
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */,
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
C47DF1AF299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C4D3661A291173EA006BD146 /* DictionaryExtension.swift in Sources */,
C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */,
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
@ -2148,6 +2172,7 @@
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */,
C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */,
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
C451AFF62969E40F0078E617 /* HelpButton.swift in Sources */,
@ -2202,6 +2227,7 @@
C471E84328F9BB650021E251 /* App.swift in Sources */,
C4E2E85E28FC282B003B070C /* TestableConfiguration.swift in Sources */,
C491997D29901DF7001F3A21 /* CaskFile.swift in Sources */,
C4FD87A629AB98730002D701 /* PhpConfigChecker.swift in Sources */,
C45E2A7529199248005C7CFD /* InternalSwitcherTest.swift in Sources */,
C471E84428F9BB650021E251 /* App+ActivationPolicy.swift in Sources */,
C471E84528F9BB650021E251 /* App+GlobalHotkey.swift in Sources */,
@ -2224,6 +2250,7 @@
C4D36617291160A1006BD146 /* WIP.swift in Sources */,
C471E85728F9BB650021E251 /* DomainListTLSCell.swift in Sources */,
C471E85828F9BB650021E251 /* DomainListNameCell.swift in Sources */,
C47DF1B1299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C471E85928F9BB650021E251 /* DomainListPhpCell.swift in Sources */,
C471E85A28F9BB650021E251 /* DomainListTypeCell.swift in Sources */,
C471E85B28F9BB650021E251 /* DomainListKindCell.swift in Sources */,
@ -2350,11 +2377,13 @@
C471E89028F9BB8F0021E251 /* AlertableError.swift in Sources */,
C471E89128F9BB8F0021E251 /* Errors.swift in Sources */,
C471E89228F9BB8F0021E251 /* Alert.swift in Sources */,
C4FD87A529AB98720002D701 /* PhpConfigChecker.swift in Sources */,
C471E89328F9BB8F0021E251 /* Application.swift in Sources */,
C471E89428F9BB8F0021E251 /* LocalNotification.swift in Sources */,
C471E89528F9BB8F0021E251 /* MenuBarImageGenerator.swift in Sources */,
C471E89628F9BB8F0021E251 /* PMWindowController.swift in Sources */,
C471E89728F9BB8F0021E251 /* VersionExtractor.swift in Sources */,
C47DF1B2299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C4E2E86728FC2F1B003B070C /* XCPMApplication.swift in Sources */,
C471E89828F9BB8F0021E251 /* ValetProxy.swift in Sources */,
C471E89A28F9BB8F0021E251 /* DomainScanner.swift in Sources */,
@ -2478,6 +2507,7 @@
C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */,
C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */,
C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */,
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */,
C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */,
C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */,
C471E7F528F9BAC80021E251 /* PhpEnv.swift in Sources */,
@ -2565,6 +2595,7 @@
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
C451AFF72969E40F0078E617 /* HelpButton.swift in Sources */,
C47DF1B0299D5A3B0007055D /* LoginItemManager.swift in Sources */,
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
C45B914A295607F400F4EC78 /* Service.swift in Sources */,
C4C0E8E327F88B13002D32A9 /* ValetDomainScanner.swift in Sources */,
@ -2596,6 +2627,7 @@
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
C4FD87A729AB98730002D701 /* PhpConfigChecker.swift in Sources */,
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */,
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */,
@ -2782,7 +2814,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@ -2839,7 +2871,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
@ -2857,19 +2889,20 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1064;
CURRENT_PROJECT_VERSION = 1079;
DEAD_CODE_STRIPPING = YES;
DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.8;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2886,19 +2919,20 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1064;
CURRENT_PROJECT_VERSION = 1079;
DEAD_CODE_STRIPPING = YES;
DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.8;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3096,7 +3130,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
@ -3108,24 +3142,25 @@
C4975D0828CD190C00FFB4E8 /* Release.Dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconEA;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor;
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1064;
CURRENT_PROJECT_VERSION = 1079;
DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor DEV";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.8;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev;
PRODUCT_NAME = "$(TARGET_NAME) DEV";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3205,7 +3240,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@ -3218,24 +3253,25 @@
C4975D0B28CD193A00FFB4E8 /* Debug.Dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconEA;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor;
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1064;
CURRENT_PROJECT_VERSION = 1079;
DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "PHP Monitor DEV";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.8;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 627 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

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

View File

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

View File

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

View File

@ -8,8 +8,6 @@
import Foundation
struct HomebrewPackage: Decodable {
let name: String
let full_name: String
let aliases: [String]
let installed: [HomebrewInstalled]

View File

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

View File

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

View File

@ -14,10 +14,10 @@ class AppUpdater {
var latestVersionOnline: AppVersion!
var interactive: Bool = false
public func checkForUpdates(interactive: Bool) async {
self.interactive = interactive
public func checkForUpdates(userInitiated: Bool) async {
self.interactive = userInitiated
if interactive && !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
if !interactive && !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
Log.info("Skipping automatic update check due to user preference.")
return
}
@ -80,6 +80,7 @@ class AppUpdater {
.withPrimary(
text: "updater.alerts.buttons.install".localized,
action: { vc in
self.cleanupCaskroom()
self.prepareForDownload()
vc.close(with: .OK)
}
@ -158,6 +159,20 @@ class AppUpdater {
}
}
private func cleanupCaskroom() {
let path = Paths.caskroomPath
if FileSystem.directoryExists(path) {
Log.info("Removing the Caskroom directory for PHP Monitor...")
do {
try FileSystem.remove(path)
Log.info("Removed the Caskroom directory at `\(path)`.")
} catch {
Log.err("Automatically removing the Caskroom directory at `\(path)` failed.")
}
}
}
// MARK: - Checking if Self-Updater Worked
public static func checkIfUpdateWasPerformed() {
@ -172,7 +187,7 @@ class AppUpdater {
}
Log.info("The `upgrade.success` file was found! An update was installed. Cleaning up...")
try! FileSystem.remove("~/.config/phpmon/updater/upgrade.success")
try? FileSystem.remove("~/.config/phpmon/updater/upgrade.success")
}
// Cleanup the previous updater

View File

@ -15,7 +15,7 @@ class AppVersion: Comparable {
init(version: String, build: String?, suffix: String? = nil) {
self.version = version
self.build = Int(build ?? "0")
self.build = build == nil ? nil : Int(build!)
self.suffix = suffix
}

View File

@ -17,7 +17,7 @@ public class EnvironmentManager {
// Failure condition #1: does not contain Laravel Valet
if !output.contains("Laravel Valet") {
return true
return false
}
// Extract the version number
@ -25,7 +25,6 @@ public class EnvironmentManager {
// Get the actual version
return Valet.shared.version == nil
}() // returns true if none of the failure conditions are met
}
}

View File

@ -39,6 +39,9 @@ struct CaskFile {
}
let lines = string.split(separator: "\n")
.map { line in
return line.trimmingCharacters(in: .whitespacesAndNewlines)
}
.filter { $0 != "" }
if lines.count < 4 {

View File

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

View File

@ -14,14 +14,18 @@ class ValetDomainScanner: DomainScanner {
func resolveSiteCount(paths: [String]) -> Int {
return paths.map { path in
do {
let entries = try FileSystem
.getShallowContentsOfDirectory(path)
let entries = try! FileSystem
.getShallowContentsOfDirectory(path)
return entries
.map { self.isSite($0, forPath: path) }
.filter { $0 == true}
.count
return entries
.map { self.isSite($0, forPath: path) }
.filter { $0 == true}
.count
} catch {
Log.err("Unexpected error getting contents of \(path): \(error).")
return 0
}
}.reduce(0, +)
}
@ -30,13 +34,17 @@ class ValetDomainScanner: DomainScanner {
var sites: [ValetSite] = []
paths.forEach { path in
let entries = try! FileSystem
.getShallowContentsOfDirectory(path)
do {
let entries = try FileSystem
.getShallowContentsOfDirectory(path)
return entries.forEach {
if let site = self.resolveSite(path: "\(path)/\($0)") {
sites.append(site)
return entries.forEach {
if let site = self.resolveSite(path: "\(path)/\($0)") {
sites.append(site)
}
}
} catch {
Log.err("Unexpected error getting contents of \(path): \(error).")
}
}

View File

@ -39,8 +39,8 @@ extension MainMenu {
// Determine install method
Log.info(HomebrewDiagnostics.customCaskInstalled
? "[BREW] The app has probably been installed via Homebrew Cask."
: "[BREW] The app has probably been installed directly."
? "[BREW] The app has been installed via Homebrew Cask."
: "[BREW] The app has been installed directly (optimal)."
)
Log.info(HomebrewDiagnostics.usesNginxFullFormula
@ -111,7 +111,7 @@ extension MainMenu {
OnboardingWindowController.show()
}
} else {
await AppUpdater().checkForUpdates(interactive: false)
await AppUpdater().checkForUpdates(userInitiated: false)
}
}

View File

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

View File

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

View File

@ -48,7 +48,7 @@ class OnboardingWindowController: PMWindowController {
// Search for updates after closing the window
if Stats.successfulLaunchCount == 1 {
Task { await AppUpdater().checkForUpdates(interactive: false) }
Task { await AppUpdater().checkForUpdates(userInitiated: false) }
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -254,6 +254,10 @@ This has no effect on other terminals, only for the particular terminal session
"prefs.notifications" = "Notifications:";
"prefs.warnings" = "Warnings:";
"prefs.menu_contents" = "Features in Menu:";
"prefs.startup" = "Startup:";
"prefs.auto_start_desc" = "Automatically starts PHP Monitor when you log into your Mac.";
"prefs.auto_start_title" = "Start PHP Monitor at login";
"prefs.icon_options.php" = "Display PHP Icon";
"prefs.icon_options.elephant" = "Display Elephant Icon";
@ -620,8 +624,8 @@ COMMON TROUBLESHOOTING TIPS
"updater.alerts.newer_version_available.title" = "PHP Monitor v%@ is now available!";
"updater.alerts.newer_version_available.subtitle" = "Keeping PHP Monitor up-to-date is highly recommended, since newer versions usually fix bugs and include fixes to support the latest versions of Valet and PHP.";
"updater.installation_source.brew" = "The recommended method of installing updates to PHP Monitor is to simply press 'Install Update'.\n\n(You may also upgrade via the terminal by running `%@`, but this is not recommended.)";
"updater.installation_source.direct" = "The recommended method of installing updates to PHP Monitor is to simply press 'Install Update'.";
"updater.installation_source.brew" = "The recommended method of installing updates to PHP Monitor is to simply press Install Update.\n\nSince you used Homebrew to install the app, you can also upgrade via the terminal by running `%@`, but this is not recommended.\n\n(Please note that installing via this built-in updater will remove PHP Monitor from Homebrew's Caskroom directory, to prevent duplicate updates from being downloaded and causing potential issues later.)";
"updater.installation_source.direct" = "The recommended method of installing updates to PHP Monitor is to simply press Install Update.";
"updater.alerts.buttons.release_notes" = "View Release Notes";
"updater.alerts.is_latest_version.title" = "PHP Monitor is up-to-date!";
@ -657,6 +661,13 @@ COMMON TROUBLESHOOTING TIPS
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
"warnings.files_missing.title" = "Your PHP installation is missing important required configuration files.";
"warnings.files_missing.description" = "The following key configuration files should exist after installing PHP:
• %@
When files like these are missing, it's recommended to reinstall the appropriate PHP version(s) via Homebrew again, which should restore the configuration files that are missing. Missing configuration files can be the reason why you get '502 Bad Gateway' errors, even after running Fix My Valet.";
"warnings.none" = "There are no recommendations available for you right now. You're all good!";
// ONBOARDING

View File

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

View File

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

View File

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

View File

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

View File

@ -23,11 +23,10 @@ class HomebrewPackageTest: XCTestCase {
[HomebrewPackage].self, from: json.data(using: .utf8)!
).first!
XCTAssertEqual(package.name, "php")
XCTAssertEqual(package.full_name, "php")
XCTAssertEqual(package.aliases.first!, "php@8.1")
XCTAssertEqual(package.aliases.first!, "php@8.2")
XCTAssertEqual(package.installed.contains(where: { installed in
installed.version.starts(with: "8.1")
installed.version.starts(with: "8.2")
}), true)
}

View File

@ -1,69 +1,77 @@
[
{
"name": "php",
"full_name": "php",
"tap": "homebrew/core",
"oldname": null,
"aliases": [
"php@8.1"
"php@8.2"
],
"versioned_formulae": [
"php@8.1",
"php@8.0",
"php@7.4",
"php@7.3",
"php@7.2"
"php@7.4"
],
"desc": "General-purpose scripting language",
"license": "PHP-3.01",
"homepage": "https://www.php.net/",
"versions": {
"stable": "8.1.10",
"stable": "8.2.2",
"head": "HEAD",
"bottle": true
},
"urls": {
"stable": {
"url": "https://www.php.net/distributions/php-8.1.10.tar.xz",
"url": "https://www.php.net/distributions/php-8.2.2.tar.xz",
"tag": null,
"revision": null
"revision": null,
"checksum": "bdc4aa38e652bac86039601840bae01c0c3653972eaa6f9f93d5f71953a7ee33"
},
"head": {
"url": "https://github.com/php/php-src.git",
"branch": "master"
}
},
"revision": 1,
"revision": 0,
"version_scheme": 0,
"bottle": {
"stable": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_ventura": {
"cellar": "/opt/homebrew/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:ad2e6a6f1cdc65c22b39bd607cbb7305958951cf58ee87d5060717be5a8b5a45",
"sha256": "ad2e6a6f1cdc65c22b39bd607cbb7305958951cf58ee87d5060717be5a8b5a45"
},
"arm64_monterey": {
"cellar": "/opt/homebrew/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:dcee33c9f445db3026a7e867805eb8f6d82e9e5599599b8c6cd8645475f7961c",
"sha256": "dcee33c9f445db3026a7e867805eb8f6d82e9e5599599b8c6cd8645475f7961c"
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:27069c973e63f38a3cb4fad1c7a2e17853bcffe318c8a957ff96a1026dff0cac",
"sha256": "27069c973e63f38a3cb4fad1c7a2e17853bcffe318c8a957ff96a1026dff0cac"
},
"arm64_big_sur": {
"cellar": "/opt/homebrew/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:e0590064cd32f2baa4102fa49c80056f3886a0a89aec0589d0134ecbf0e7923e",
"sha256": "e0590064cd32f2baa4102fa49c80056f3886a0a89aec0589d0134ecbf0e7923e"
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:ceef280bcd57e5f794ae59cc75e83d407c9704aa3d238b282bda52cbc644d0dd",
"sha256": "ceef280bcd57e5f794ae59cc75e83d407c9704aa3d238b282bda52cbc644d0dd"
},
"ventura": {
"cellar": "/usr/local/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:22f733b7b0b0ed95cd6b0a1534b9eca4cf63fe54647394c3f7e7ac019eb019ff",
"sha256": "22f733b7b0b0ed95cd6b0a1534b9eca4cf63fe54647394c3f7e7ac019eb019ff"
},
"monterey": {
"cellar": "/usr/local/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:62481320613b19c6ff310bf6ed50c7d2a2253cdbf403af12ec97bccd8a97a84c",
"sha256": "62481320613b19c6ff310bf6ed50c7d2a2253cdbf403af12ec97bccd8a97a84c"
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:9ff8f5e1df5e849567cdb2ddea6d3c2a2b9cae024842c9ac65b35a01657bfc37",
"sha256": "9ff8f5e1df5e849567cdb2ddea6d3c2a2b9cae024842c9ac65b35a01657bfc37"
},
"big_sur": {
"cellar": "/usr/local/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:b34d96f7aad3c580a7cbdaadb8054fb9b6872111a5eec8e1bcb4a529970c8e03",
"sha256": "b34d96f7aad3c580a7cbdaadb8054fb9b6872111a5eec8e1bcb4a529970c8e03"
},
"catalina": {
"cellar": "/usr/local/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:cc0b85dcfdd60e1d8d7fa74c9f53be5d249d068835dbc7a81edacb7a076b6c76",
"sha256": "cc0b85dcfdd60e1d8d7fa74c9f53be5d249d068835dbc7a81edacb7a076b6c76"
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:11fd1ea6da8ef728b7cacd4da8a51ed125069595abf4e37ae1552d418560c5fb",
"sha256": "11fd1ea6da8ef728b7cacd4da8a51ed125069595abf4e37ae1552d418560c5fb"
},
"x86_64_linux": {
"cellar": "/home/linuxbrew/.linuxbrew/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:b934a5a4ad2d29b629f83962b57f638a654801d1ba21ba659a42da2e5afe3fae",
"sha256": "b934a5a4ad2d29b629f83962b57f638a654801d1ba21ba659a42da2e5afe3fae"
"url": "https://ghcr.io/v2/homebrew/core/php/blobs/sha256:baaa41e60f9e8125fe8f549d4813a8476a8947a1f10d7817a2ee36d8baa625f3",
"sha256": "baaa41e60f9e8125fe8f549d4813a8476a8947a1f10d7817a2ee36d8baa625f3"
}
}
}
@ -127,34 +135,35 @@
"conflicts_with": [
],
"caveats": "To enable PHP in Apache add the following to httpd.conf and restart Apache:\n LoadModule php_module $(brew --prefix)/opt/php/lib/httpd/modules/libphp.so\n\n <FilesMatch \\.php$>\n SetHandler application/x-httpd-php\n </FilesMatch>\n\nFinally, check DirectoryIndex includes index.php\n DirectoryIndex index.php index.html\n\nThe php.ini and php-fpm.ini file can be found in:\n $(brew --prefix)/etc/php/8.1/\n",
"caveats": "To enable PHP in Apache add the following to httpd.conf and restart Apache:\n LoadModule php_module $(brew --prefix)/opt/php/lib/httpd/modules/libphp.so\n\n <FilesMatch \\.php$>\n SetHandler application/x-httpd-php\n </FilesMatch>\n\nFinally, check DirectoryIndex includes index.php\n DirectoryIndex index.php index.html\n\nThe php.ini and php-fpm.ini file can be found in:\n $(brew --prefix)/etc/php/8.2/\n",
"installed": [
{
"version": "8.1.10_1",
"version": "8.2.2",
"used_options": [
],
"built_as_bottle": true,
"poured_from_bottle": true,
"time": 1675654665,
"runtime_dependencies": [
{
"full_name": "apr",
"version": "1.7.0",
"version": "1.7.2",
"declared_directly": true
},
{
"full_name": "ca-certificates",
"version": "2022-07-19",
"version": "2023-01-10",
"declared_directly": false
},
{
"full_name": "openssl@1.1",
"version": "1.1.1q",
"version": "1.1.1s",
"declared_directly": true
},
{
"full_name": "apr-util",
"version": "1.6.1",
"version": "1.6.3",
"declared_directly": true
},
{
@ -182,24 +191,24 @@
"version": "1.0.9",
"declared_directly": false
},
{
"full_name": "gettext",
"version": "0.21",
"declared_directly": true
},
{
"full_name": "libunistring",
"version": "1.0",
"version": "1.1",
"declared_directly": false
},
{
"full_name": "gettext",
"version": "0.21.1",
"declared_directly": true
},
{
"full_name": "libidn2",
"version": "2.3.3",
"version": "2.3.4",
"declared_directly": false
},
{
"full_name": "libnghttp2",
"version": "1.49.0",
"version": "1.51.0",
"declared_directly": false
},
{
@ -224,7 +233,7 @@
},
{
"full_name": "xz",
"version": "5.2.6",
"version": "5.4.1",
"declared_directly": false
},
{
@ -234,7 +243,7 @@
},
{
"full_name": "curl",
"version": "7.85.0",
"version": "7.87.0",
"declared_directly": true
},
{
@ -249,12 +258,12 @@
},
{
"full_name": "freetds",
"version": "1.3.13",
"version": "1.3.17",
"declared_directly": true
},
{
"full_name": "libpng",
"version": "1.6.37",
"version": "1.6.39",
"declared_directly": false
},
{
@ -264,12 +273,12 @@
},
{
"full_name": "fontconfig",
"version": "2.14.0",
"version": "2.14.2",
"declared_directly": false
},
{
"full_name": "jpeg-turbo",
"version": "2.1.4",
"version": "2.1.5",
"declared_directly": false
},
{
@ -278,13 +287,13 @@
"declared_directly": false
},
{
"full_name": "imath",
"version": "3.1.5",
"full_name": "highway",
"version": "1.0.3",
"declared_directly": false
},
{
"full_name": "openexr",
"version": "3.1.5",
"full_name": "imath",
"version": "3.1.6",
"declared_directly": false
},
{
@ -292,14 +301,24 @@
"version": "4.4.0",
"declared_directly": false
},
{
"full_name": "little-cms2",
"version": "2.14",
"declared_directly": false
},
{
"full_name": "openexr",
"version": "3.1.5",
"declared_directly": false
},
{
"full_name": "webp",
"version": "1.2.4",
"version": "1.3.0",
"declared_directly": false
},
{
"full_name": "jpeg-xl",
"version": "0.6.1",
"version": "0.8.1",
"declared_directly": false
},
{
@ -309,12 +328,12 @@
},
{
"full_name": "aom",
"version": "3.4.0",
"version": "3.5.0",
"declared_directly": false
},
{
"full_name": "libavif",
"version": "0.10.1",
"version": "0.11.1",
"declared_directly": false
},
{
@ -329,17 +348,17 @@
},
{
"full_name": "icu4c",
"version": "71.1",
"version": "72.1",
"declared_directly": true
},
{
"full_name": "krb5",
"version": "1.20",
"version": "1.20.1",
"declared_directly": true
},
{
"full_name": "libpq",
"version": "14.5",
"version": "15.1",
"declared_directly": true
},
{
@ -359,17 +378,17 @@
},
{
"full_name": "pcre2",
"version": "10.40",
"version": "10.42",
"declared_directly": true
},
{
"full_name": "readline",
"version": "8.1.2",
"version": "8.2.1",
"declared_directly": false
},
{
"full_name": "sqlite",
"version": "3.39.2",
"version": "3.40.1",
"declared_directly": true
},
{
@ -382,7 +401,7 @@
"installed_on_request": true
}
],
"linked_keg": "8.1.10_1",
"linked_keg": "8.2.2",
"pinned": false,
"outdated": false,
"deprecated": false,
@ -390,6 +409,8 @@
"deprecation_reason": null,
"disabled": false,
"disable_date": null,
"disable_reason": null
"disable_reason": null,
"tap_git_head": "0bbb89420e74756a5a5c145ed7efa4a32f7e7e7c"
}
]
]

View File

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