From 2a27989a96cdd36b4b06244a2aec2926fc1312f3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 27 Jun 2025 20:59:26 +0200 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20Detect=20Tempest=20framework?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/Integrations/Composer/ProjectTypeDetection.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phpmon/Domain/Integrations/Composer/ProjectTypeDetection.swift b/phpmon/Domain/Integrations/Composer/ProjectTypeDetection.swift index 7cad37f..a9eb5ed 100644 --- a/phpmon/Domain/Integrations/Composer/ProjectTypeDetection.swift +++ b/phpmon/Domain/Integrations/Composer/ProjectTypeDetection.swift @@ -15,7 +15,8 @@ struct ProjectTypeDetection { public static let CommonDependencyList = [ "laravel/framework": "Laravel", "symfony/symfony": "Symfony", - "laravel/lumen": "Lumen" + "laravel/lumen": "Lumen", + "tempest/framework": "Tempest" ] /** From 769779970b19cff07e3976865dc12dc8456750c6 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 27 Jun 2025 21:05:19 +0200 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=94=A7=20Bump=20build=20for=20EAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 8a492db..4660297 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -3692,7 +3692,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1555; + CURRENT_PROJECT_VERSION = 1556; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -3722,7 +3722,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1555; + CURRENT_PROJECT_VERSION = 1556; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -3955,7 +3955,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1555; + CURRENT_PROJECT_VERSION = 1556; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -4071,7 +4071,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1555; + CURRENT_PROJECT_VERSION = 1556; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -4187,7 +4187,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1555; + CURRENT_PROJECT_VERSION = 1556; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -4366,7 +4366,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1555; + CURRENT_PROJECT_VERSION = 1556; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; From e94377ebb1dfae6dab8217ba6e2339fce79df8f3 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 23 Jul 2025 16:48:21 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=90=9B=20Prevent=20timeout=20message?= =?UTF-8?q?=20from=20showing=20incorrectly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 34 ++++++++++++++-------- phpmon/Common/Core/Constants.swift | 10 +++++++ phpmon/Domain/App/Startup+Timers.swift | 32 +++++++++++++++++++++ phpmon/Domain/App/Startup.swift | 15 ---------- phpmon/Domain/Menu/MainMenu+Startup.swift | 35 ++++++++++++----------- phpmon/Domain/PHP/PhpGuard.swift | 3 +- 6 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 phpmon/Domain/App/Startup+Timers.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 4660297..29a43cb 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -21,6 +21,10 @@ 033D45A02B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */; }; 033D45A12B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */; }; 033D45A32B0D531D00070080 /* PhpExtensionManagerView+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 033D45A22B0D531D00070080 /* PhpExtensionManagerView+Actions.swift */; }; + 03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; + 03BFF5282E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; + 03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; + 03BFF52A2E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; 03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; }; 03E36FE828D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; }; 5420395926135DC100FB00FA /* PreferencesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PreferencesVC.swift */; }; @@ -920,6 +924,7 @@ 033D45972B0D4EC600070080 /* InstallPhpExtensionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallPhpExtensionCommand.swift; sourceTree = ""; }; 033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemovePhpExtensionCommand.swift; sourceTree = ""; }; 033D45A22B0D531D00070080 /* PhpExtensionManagerView+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PhpExtensionManagerView+Actions.swift"; sourceTree = ""; }; + 03BFF5262E312C39007F96FA /* Startup+Timers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Startup+Timers.swift"; sourceTree = ""; }; 03E36FE628D9219000636F7F /* ActiveShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveShell.swift; sourceTree = ""; }; 5420395826135DC100FB00FA /* PreferencesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesVC.swift; sourceTree = ""; }; 5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; @@ -1938,6 +1943,7 @@ C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, C4EED88827A48778006D7272 /* InterAppHandler.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */, + 03BFF5262E312C39007F96FA /* Startup+Timers.swift */, C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */, C409349C298EE8E900D25014 /* AppUpdater.swift */, @@ -2589,6 +2595,7 @@ C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */, C4EA3C472BA4F947007B0BA7 /* CustomButtonStyles.swift in Sources */, C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, + 03BFF5282E312C3D007F96FA /* Startup+Timers.swift in Sources */, C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C456A0C62AA614BD0080144F /* PhpPreference.swift in Sources */, @@ -2892,6 +2899,7 @@ C490E3B929BCA368006D2DE6 /* App+BrewWatch.swift in Sources */, C471E7FF28F9BAD10021E251 /* Xdebug.swift in Sources */, C409349F298EE8E900D25014 /* AppUpdater.swift in Sources */, + 03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */, C471E7F228F9BAC70021E251 /* PhpEnvironments.swift in Sources */, C471E7E628F9BAC20021E251 /* Process.swift in Sources */, C471E81928F9BAE80021E251 /* NSMenuItemExtension.swift in Sources */, @@ -3078,6 +3086,7 @@ C4611E5A2AEAD2E20010BE24 /* ConfigManagerWindowController.swift in Sources */, C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */, C490E3BA29BCA368006D2DE6 /* App+BrewWatch.swift in Sources */, + 03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */, C471E7D028F9BA630021E251 /* FileSystemProtocol.swift in Sources */, C471E81228F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */, C471E7DF28F9BAAB0021E251 /* RealCommand.swift in Sources */, @@ -3248,6 +3257,7 @@ C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */, C481F79726164A78004FBCFF /* PreferencesVC.swift in Sources */, C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */, + 03BFF52A2E312C3D007F96FA /* Startup+Timers.swift in Sources */, C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, @@ -3692,7 +3702,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1556; + CURRENT_PROJECT_VERSION = 1560; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -3704,7 +3714,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.06; + MARKETING_VERSION = 25.07; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3722,7 +3732,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1556; + CURRENT_PROJECT_VERSION = 1560; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -3734,7 +3744,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.06; + MARKETING_VERSION = 25.07; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3955,7 +3965,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1556; + CURRENT_PROJECT_VERSION = 1560; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -3967,7 +3977,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.06; + MARKETING_VERSION = 25.07; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME) DEV"; @@ -4071,7 +4081,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1556; + CURRENT_PROJECT_VERSION = 1560; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -4083,7 +4093,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.06; + MARKETING_VERSION = 25.07; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.dev; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME) DEV"; @@ -4187,7 +4197,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1556; + CURRENT_PROJECT_VERSION = 1560; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -4199,7 +4209,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.06; + MARKETING_VERSION = 25.07; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.eap; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME) EAP"; @@ -4366,7 +4376,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1556; + CURRENT_PROJECT_VERSION = 1560; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -4378,7 +4388,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.06; + MARKETING_VERSION = 25.07; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.eap; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME) EAP"; diff --git a/phpmon/Common/Core/Constants.swift b/phpmon/Common/Core/Constants.swift index c6acd0c..af1b941 100644 --- a/phpmon/Common/Core/Constants.swift +++ b/phpmon/Common/Core/Constants.swift @@ -18,6 +18,16 @@ struct Constants { */ static let MinimumRecommendedValetVersion = "2.16.2" + /** + The amount of seconds that is considered the threshold for + PHP Monitor to mark any given launch as a "slow" launch. + + If the startup procedure was slow (or hangs), this message should + be displayed. This is based on an appropriate launch time on a + basic M1 Apple chip, with some margin for slower Intel chips. + */ + static let SlowBootThresholdInterval: TimeInterval = 30.0 + /** PHP Monitor supplies a hardcoded list of PHP packages in its own PHP Version Manager. diff --git a/phpmon/Domain/App/Startup+Timers.swift b/phpmon/Domain/App/Startup+Timers.swift new file mode 100644 index 0000000..4e2bc03 --- /dev/null +++ b/phpmon/Domain/App/Startup+Timers.swift @@ -0,0 +1,32 @@ +// +// Startup+Timers.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 23/07/2025. +// Copyright © 2025 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension Startup { + @MainActor static var startupTimer: Timer? + @MainActor static var launchTime: Date? + + @MainActor func startTimeoutTimer() { + Self.launchTime = Date() + Self.startupTimer = Timer.scheduledTimer( + timeInterval: Constants.SlowBootThresholdInterval, target: self, + selector: #selector(startupTimeout), userInfo: nil, repeats: false + ) + } + + @MainActor static func invalidateTimeoutTimer() { + let elapsedTime = Date().timeIntervalSince(Self.launchTime!) + let printableTime = String(format: "%.2f", elapsedTime) + + Log.info("PHP Monitor launched quickly enough!") + Log.info("PHP Monitor boot time: \(printableTime) sec") + Self.startupTimer?.invalidate() + Self.startupTimer = nil + } +} diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 4f486aa..7e4617e 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -10,21 +10,6 @@ import AppKit import NVAlert class Startup { - - @MainActor static var startupTimer: Timer? - - @MainActor func startTimeoutTimer() { - Self.startupTimer = Timer.scheduledTimer( - timeInterval: 30.0, target: self, selector: #selector(startupTimeout), - userInfo: nil, repeats: false - ) - } - - @MainActor static func invalidateTimeoutTimer() { - Self.startupTimer?.invalidate() - Self.startupTimer = nil - } - /** Checks the user's environment and checks if PHP Monitor can be used properly. This checks if PHP is installed, Valet is running, the appropriate permissions are set, and more. diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 80ed97e..c7d5e69 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -103,12 +103,6 @@ extension MainMenu { // Find out which services are active Log.info("The services manager knows about \(ServicesManager.shared.services.count) services.") - // Post-launch stats and update check, but only if not running tests - await performPostLaunchActions() - - // Check if the linked version has changed between launches of phpmon - PhpGuard().compareToLastGlobalVersion() - // We are ready! PhpEnvironments.shared.isBusy = false @@ -120,6 +114,9 @@ extension MainMenu { // Check if we upgraded from a previous version AppUpdater.checkIfUpdateWasPerformed() + + // Post-launch stats and update check, but only if not running tests + await performPostLaunchActions() } /** @@ -127,18 +124,24 @@ extension MainMenu { (This code is skipped when running SwiftUI previews.) */ private func performPostLaunchActions() async { - if !isRunningSwiftUIPreview { - Stats.incrementSuccessfulLaunchCount() - Stats.evaluateSponsorMessageShouldBeDisplayed() + if isRunningSwiftUIPreview { + return + } - if Stats.successfulLaunchCount == 1 { - Log.info("Should present the first launch screen!") - Task { @MainActor in - OnboardingWindowController.show() - } - } else { - await AppUpdater().checkForUpdates(userInitiated: false) + Stats.incrementSuccessfulLaunchCount() + Stats.evaluateSponsorMessageShouldBeDisplayed() + + if Stats.successfulLaunchCount == 1 { + Log.info("Should present the first launch screen!") + Task { @MainActor in + OnboardingWindowController.show() } + } else { + // Check for updates + await AppUpdater().checkForUpdates(userInitiated: false) + + // Check if the linked version has changed between launches of phpmon + await PhpGuard().compareToLastGlobalVersion() } } diff --git a/phpmon/Domain/PHP/PhpGuard.swift b/phpmon/Domain/PHP/PhpGuard.swift index 55af936..420e061 100644 --- a/phpmon/Domain/PHP/PhpGuard.swift +++ b/phpmon/Domain/PHP/PhpGuard.swift @@ -23,7 +23,7 @@ class PhpGuard { Log.info("The currently linked version of PHP is: \(linked.version.short).") } - public func compareToLastGlobalVersion() { + public func compareToLastGlobalVersion() async { guard let currentVersion else { return } @@ -34,6 +34,7 @@ class PhpGuard { Stats.persistCurrentGlobalPhpVersion(version: currentVersion) return Log.warn("PHP Guard is saving the currently linked PHP version (first time only).") } + Log.info("Previously, the globally linked PHP version was: \(previousVersion).") if previousVersion == currentVersion { From 729c1e8f2ff7477259b01c88df045f4fb0cfca37 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Wed, 23 Jul 2025 17:24:54 +0200 Subject: [PATCH 4/6] =?UTF-8?q?=E2=9C=A8=20Add=20driver=20to=20main=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 10 +++++++ .../ValetDriverIcon.imageset/Contents.json | 24 +++++++++++++++ .../ValetDriverIcon@2x.png | Bin 0 -> 831 bytes phpmon/Domain/Menu/StatusMenu+Driver.swift | 28 ++++++++++++++++++ phpmon/Domain/Menu/StatusMenu+Items.swift | 10 ++----- phpmon/Domain/Menu/StatusMenu.swift | 6 +++- phpmon/en.lproj/Localizable.strings | 1 + 7 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 phpmon/Assets.xcassets/ValetDriverIcon.imageset/Contents.json create mode 100644 phpmon/Assets.xcassets/ValetDriverIcon.imageset/ValetDriverIcon@2x.png create mode 100644 phpmon/Domain/Menu/StatusMenu+Driver.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 29a43cb..e127e47 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -25,6 +25,10 @@ 03BFF5282E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; 03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; 03BFF52A2E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; }; + 03BFF52C2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; }; + 03BFF52D2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; }; + 03BFF52E2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; }; + 03BFF52F2E313244007F96FA /* StatusMenu+Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */; }; 03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; }; 03E36FE828D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; }; 5420395926135DC100FB00FA /* PreferencesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PreferencesVC.swift */; }; @@ -925,6 +929,7 @@ 033D459D2B0D513900070080 /* RemovePhpExtensionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemovePhpExtensionCommand.swift; sourceTree = ""; }; 033D45A22B0D531D00070080 /* PhpExtensionManagerView+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PhpExtensionManagerView+Actions.swift"; sourceTree = ""; }; 03BFF5262E312C39007F96FA /* Startup+Timers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Startup+Timers.swift"; sourceTree = ""; }; + 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Driver.swift"; sourceTree = ""; }; 03E36FE628D9219000636F7F /* ActiveShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveShell.swift; sourceTree = ""; }; 5420395826135DC100FB00FA /* PreferencesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesVC.swift; sourceTree = ""; }; 5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; @@ -1855,6 +1860,7 @@ C4F361602836BFD9003598CC /* MainMenu+Actions.swift */, C47331A1247093B7009A0597 /* StatusMenu.swift */, C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */, + 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */, C4821C592C2DEDE200357A68 /* AppMenu.swift */, ); path = Menu; @@ -2648,6 +2654,7 @@ C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewDecodable.swift in Sources */, + 03BFF52E2E313244007F96FA /* StatusMenu+Driver.swift in Sources */, 03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */, C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */, C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */, @@ -2869,6 +2876,7 @@ C471E7FE28F9BACE0021E251 /* HomebrewDecodable.swift in Sources */, C4415E8F2B0287E90035F520 /* BrewFormulaeObservable.swift in Sources */, C471E7D828F9BA8F0021E251 /* FileSystemProtocol.swift in Sources */, + 03BFF52F2E313244007F96FA /* StatusMenu+Driver.swift in Sources */, C471E7F328F9BAC70021E251 /* PhpHelper.swift in Sources */, C46DC7A62C7B5BC900F19D17 /* Favorites.swift in Sources */, C471E7E728F9BAC20021E251 /* Constants.swift in Sources */, @@ -3098,6 +3106,7 @@ C471E80228F9BAD40021E251 /* PhpInstallation.swift in Sources */, C471E81028F9BAE80021E251 /* StringExtension.swift in Sources */, C48DDD1029C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */, + 03BFF52C2E313244007F96FA /* StatusMenu+Driver.swift in Sources */, C471E7F828F9BACB0021E251 /* InternalSwitcher.swift in Sources */, C471E82328F9BB2E0021E251 /* ComposerJson.swift in Sources */, C471E82128F9BB2E0021E251 /* ProjectTypeDetection.swift in Sources */, @@ -3246,6 +3255,7 @@ C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */, C42759682627662800093CAE /* NSMenuExtension.swift in Sources */, + 03BFF52D2E313244007F96FA /* StatusMenu+Driver.swift in Sources */, C4AFC4B429C4F43300BF4E0D /* HomebrewUpgradableTest.swift in Sources */, C4E2E84828FC1D93003B070C /* TestableConfigurationTest.swift in Sources */, C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */, diff --git a/phpmon/Assets.xcassets/ValetDriverIcon.imageset/Contents.json b/phpmon/Assets.xcassets/ValetDriverIcon.imageset/Contents.json new file mode 100644 index 0000000..39e31f6 --- /dev/null +++ b/phpmon/Assets.xcassets/ValetDriverIcon.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ValetDriverIcon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/phpmon/Assets.xcassets/ValetDriverIcon.imageset/ValetDriverIcon@2x.png b/phpmon/Assets.xcassets/ValetDriverIcon.imageset/ValetDriverIcon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e63a8ef63b0492a0775800f47bf6bbb0f441001d GIT binary patch literal 831 zcmV-F1Hk-=P)9wf8pteElASj9;TGzIyP08(C6^zI;oH^h8X3k;e3|K=+C8gJR zA(hT(!AYNMeqqsNf5lE4PU;y9E4n%s2>7XLpZ8eIc-v#vs($WYYg~!y3RvodM-^4e z2u}(7*E6cgJHo9>BD^AeChXDVg2H#E9x3sQ$?+0@o2-R`<6vzTcEx{jSX(kwFh(1PS9-@bSdSykGd~Y#-hBe7mWLC+c zu}Yi-89bt9rFv9iO!TOXBssJHfBUM(2Kr0Y9X_aiHjMW!ux~>lZx~-khEX^M+Y?-- zEBR}OF#I8UzM{Dgz_|(MR~0R?3m03^{-}nva8a^}TVpYJJB6kNXt{xgrBctz`~Th2R|e*kB(XXc~<-V6W$010qNS#tmY3ljhU3ljkVnw%H_00Co3L_t(Y ziM7@}NE}fV#qr<97!(o&5rPO7nr$|P5U{YY5z=Zcq_Ik6DF`;&grp2eN(~`kXJMNn zAzFx?F55^zut=(H;wL0JDaL_8Cc86_nFALd@4@*W?p)qHsN*ZP&``a`@e#Y2fDJ@< zb&W;TDgf88fQuAw8@{JDlG?#giSPk&hwpQmu$OTV;U{KFfKTD%Ka5tV01kIYW3EK-eZ9o56mJWjil0bt@77go5Nr61(SIN+C~KJ`a>D=s002ov JPDHLkV1o3~X Date: Thu, 24 Jul 2025 12:32:22 +0200 Subject: [PATCH 5/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Clean=20up=20startup?= =?UTF-8?q?=20timers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpmon/Domain/App/Startup+Timers.swift | 52 +++++++++++++++++++++++--- phpmon/Domain/App/Startup.swift | 25 +------------ 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/phpmon/Domain/App/Startup+Timers.swift b/phpmon/Domain/App/Startup+Timers.swift index 4e2bc03..3d3fcbc 100644 --- a/phpmon/Domain/App/Startup+Timers.swift +++ b/phpmon/Domain/App/Startup+Timers.swift @@ -7,12 +7,20 @@ // import Foundation +import AppKit +import NVAlert extension Startup { @MainActor static var startupTimer: Timer? @MainActor static var launchTime: Date? - @MainActor func startTimeoutTimer() { + /** Returns a human-readable version to indicate how many seconds elapsed since boot. */ + @MainActor static var humanReadableSinceBootTime: String { + return String(format: "%.2f", Date().timeIntervalSince(Self.launchTime!)) + } + + /** Starts the timeout timer that keeps track of how long the app takes to boot. */ + @MainActor func startStartupTimer() { Self.launchTime = Date() Self.startupTimer = Timer.scheduledTimer( timeInterval: Constants.SlowBootThresholdInterval, target: self, @@ -20,13 +28,47 @@ extension Startup { ) } + /** + Invalidates and stops the startup timer. + This is only called if the slow boot threshold is not exceeded. + */ @MainActor static func invalidateTimeoutTimer() { - let elapsedTime = Date().timeIntervalSince(Self.launchTime!) - let printableTime = String(format: "%.2f", elapsedTime) + if Self.startupTimer == nil { + return + } - Log.info("PHP Monitor launched quickly enough!") - Log.info("PHP Monitor boot time: \(printableTime) sec") + Log.info("PHP Monitor was quick; elapsed time: \(Self.humanReadableSinceBootTime) sec.") Self.startupTimer?.invalidate() Self.startupTimer = nil } + + /** + Displays an alert for when the application startup process takes too long. + */ + @MainActor @objc func startupTimeout() { + Log.info("PHP Monitor was slow; elapsed time: \(Self.humanReadableSinceBootTime) sec.") + + // Invalidate the timer + Self.startupTimer?.invalidate() + Self.startupTimer = nil + + // Present an alert that lets the user know about the slow start + NVAlert() + .withInformation( + title: "startup.timeout.title".localized, + subtitle: "startup.timeout.subtitle".localized, + description: "startup.timeout.description".localized + ) + .withPrimary(text: "alert.cannot_start.close".localized, action: { vc in + vc.close(with: .alertFirstButtonReturn) + exit(1) + }) + .withSecondary(text: "startup.timeout.ignore".localized, action: { vc in + vc.close(with: .alertSecondButtonReturn) + }) + .withTertiary(text: "", action: { _ in + NSWorkspace.shared.open(URL(string: "https://github.com/nicoverbruggen/phpmon/issues/294")!) + }) + .show() + } } diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 7e4617e..ebc6c0c 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -23,7 +23,7 @@ class Startup { // Set up a "background" timer on the main thread Task { @MainActor in - startTimeoutTimer() + startStartupTimer() } for group in self.groups { @@ -54,29 +54,6 @@ class Startup { return true } - /** - Displays an alert for when the application startup process takes too long. - */ - @MainActor @objc func startupTimeout() { - NVAlert() - .withInformation( - title: "startup.timeout.title".localized, - subtitle: "startup.timeout.subtitle".localized, - description: "startup.timeout.description".localized - ) - .withPrimary(text: "alert.cannot_start.close".localized, action: { vc in - vc.close(with: .alertFirstButtonReturn) - exit(1) - }) - .withSecondary(text: "startup.timeout.ignore".localized, action: { vc in - vc.close(with: .alertSecondButtonReturn) - }) - .withTertiary(text: "", action: { _ in - NSWorkspace.shared.open(URL(string: "https://github.com/nicoverbruggen/phpmon/issues/294")!) - }) - .show() - } - /** Displays an alert for a particular check. There are two types of alerts: - ones that require an app restart, which prompt the user to exit the app From ca65fca77dff52445bb0414b6bba18921151f9c5 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 28 Jul 2025 11:22:23 +0200 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issue=20with=20securin?= =?UTF-8?q?g=20domains?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you serve a single folder locally multiple times, e.g. as `cdn.mydomain.test` and `mydomain.test`, securing would fail for domain that came alphabetically last. This has been resolved if you are running Valet 3 or newer by leveraging the `valet secure $domain` syntax. --- PHP Monitor.xcodeproj/project.pbxproj | 12 ++++++------ .../Integrations/Valet/Domains/ValetInteractor.swift | 10 +++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index e127e47..90a9ad7 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -3712,7 +3712,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1560; + CURRENT_PROJECT_VERSION = 1565; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -3742,7 +3742,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1560; + CURRENT_PROJECT_VERSION = 1565; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -3975,7 +3975,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1560; + CURRENT_PROJECT_VERSION = 1565; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; @@ -4091,7 +4091,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1560; + CURRENT_PROJECT_VERSION = 1565; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -4207,7 +4207,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1560; + CURRENT_PROJECT_VERSION = 1565; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_HARDENED_RUNTIME = YES; @@ -4386,7 +4386,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1560; + CURRENT_PROJECT_VERSION = 1565; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_HARDENED_RUNTIME = YES; diff --git a/phpmon/Domain/Integrations/Valet/Domains/ValetInteractor.swift b/phpmon/Domain/Integrations/Valet/Domains/ValetInteractor.swift index 7cc2c26..b51bc33 100644 --- a/phpmon/Domain/Integrations/Valet/Domains/ValetInteractor.swift +++ b/phpmon/Domain/Integrations/Valet/Domains/ValetInteractor.swift @@ -51,7 +51,15 @@ class ValetInteractor { // Keep track of the command we wish to run let action = site.secured ? "unsecure" : "secure" - let command = "cd '\(site.absolutePath)' && sudo \(Paths.valet) \(action) && exit;" + + // Use modernized version of command using domain name + // This will allow us to secure multiple domains that use the same path + var command = "sudo \(Paths.valet) \(action) '\(site.name)' && exit;" + + // For Valet 2, use the old syntax; this has a known issue so Valet 3+ is preferred + if !Valet.enabled(feature: .isolatedSites) { + command = "cd '\(site.absolutePath)' && sudo \(Paths.valet) \(action) && exit;" + } // Run the command await Shell.quiet(command)