From 1994e1fa84211005527076a6e3f0a59d7729cf04 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 10 Nov 2025 12:36:22 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Improve=20robustness=20of=20boot?= =?UTF-8?q?=20procedure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added extra check for valid `brew info php --json` output - Moved PHP binary check and PHP installation not broken checks to `core` - Workaround for Homebrew logs appearing in JSON output --- PHP Monitor.xcodeproj/project.pbxproj | 16 ++++----- .../PHP/PHP Version/PhpEnvironments.swift | 35 +++++++++++++++---- phpmon/Domain/App/Startup.swift | 19 ++++++++-- phpmon/en.lproj/Localizable.strings | 5 +++ 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index e02e4e6f..6db4e48b 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -3920,7 +3920,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1690; + CURRENT_PROJECT_VERSION = 1695; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_APP_SANDBOX = NO; @@ -3939,7 +3939,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.10.1; + MARKETING_VERSION = 25.10.2; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3964,7 +3964,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1690; + CURRENT_PROJECT_VERSION = 1695; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_APP_SANDBOX = NO; @@ -3983,7 +3983,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.10.1; + MARKETING_VERSION = 25.10.2; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -4146,7 +4146,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1690; + CURRENT_PROJECT_VERSION = 1695; DEAD_CODE_STRIPPING = YES; DEBUG = YES; ENABLE_APP_SANDBOX = NO; @@ -4165,7 +4165,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.10.1; + MARKETING_VERSION = 25.10.2; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.eap; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME) EAP"; @@ -4339,7 +4339,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1690; + CURRENT_PROJECT_VERSION = 1695; DEAD_CODE_STRIPPING = YES; DEBUG = NO; ENABLE_APP_SANDBOX = NO; @@ -4358,7 +4358,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.5; - MARKETING_VERSION = 25.10.1; + MARKETING_VERSION = 25.10.2; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.eap; PRODUCT_MODULE_NAME = PHP_Monitor; PRODUCT_NAME = "$(TARGET_NAME) EAP"; diff --git a/phpmon/Common/PHP/PHP Version/PhpEnvironments.swift b/phpmon/Common/PHP/PHP Version/PhpEnvironments.swift index ba328314..21bbe250 100644 --- a/phpmon/Common/PHP/PHP Version/PhpEnvironments.swift +++ b/phpmon/Common/PHP/PHP Version/PhpEnvironments.swift @@ -22,16 +22,39 @@ class PhpEnvironments { } /** - Determine which PHP version the `php` formula is aliased to. + Loads the valid HomebrewPackage information. + If invalid, this will prevent PHP Monitor from starting correctly. */ - @MainActor func determinePhpAlias() async { + func getHomebrewInformation() async { let brewPhpAlias = await container.shell.pipe("\(container.paths.brew) info php --json").out - self.homebrewPackage = try! JSONDecoder().decode( - [HomebrewPackage].self, - from: brewPhpAlias.data(using: .utf8)! - ).first! + // Remove any non-JSON output (progress indicators, etc.) before the actual JSON array + // This is a workaround for https://github.com/homebrew/brew/issues/20978 + // Since users may not upgrade Homebrew frequently, this fix will remain + let jsonString = brewPhpAlias + .components(separatedBy: .newlines) + .drop(while: { !$0.trimmingCharacters(in: .whitespaces).hasPrefix("[") }) + .joined(separator: "\n") + // Get all packages + let packages = try? JSONDecoder().decode( + [HomebrewPackage].self, + from: jsonString.data(using: .utf8)! + ) + + // But we only need the first one! + guard let package = packages?.first else { + Log.err("Could not determine PHP version due to malformed output.") + return + } + + self.homebrewPackage = package + } + + /** + Determine which PHP version the `php` formula is aliased to. + */ + func determinePhpAlias() async { PhpEnvironments.brewPhpAlias = self.homebrewPackage.version Log.info("[BREW] On your system, the `php` formula means version \(homebrewPackage.version).") diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 1596b489..dd5584fd 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -118,9 +118,7 @@ class Startup { App.shared.container.paths.optPath ), descriptionText: "startup.errors.php_opt.desc".localized - ) - ]), - EnvironmentCheckGroup(name: "valet", condition: { return Valet.installed }, checks: [ + ), // ================================================================================= // The PHP binary must exist. // ================================================================================= @@ -148,6 +146,21 @@ class Startup { ), descriptionText: "startup.errors.dyld_library.desc".localized ), + // ================================================================================= + // Make sure we can get valid output from Homebrew's info command. + // ================================================================================= + EnvironmentCheck( + command: { container in + await container.phpEnvs.getHomebrewInformation() + return container.phpEnvs.homebrewPackage == nil + }, + name: "`brew info php --json` could parse valid JSON", + titleText: "startup.errors.php_brew_info_invalid.title".localized, + subtitleText: "startup.errors.php_brew_info_invalid.subtitle".localized, + descriptionText: "startup.errors.php_brew_info_invalid.desc".localized + ) + ]), + EnvironmentCheckGroup(name: "valet", condition: { return Valet.installed }, checks: [ // ================================================================================= // The Valet binary must exist. // ================================================================================= diff --git a/phpmon/en.lproj/Localizable.strings b/phpmon/en.lproj/Localizable.strings index 72d3ca26..f1489dac 100644 --- a/phpmon/en.lproj/Localizable.strings +++ b/phpmon/en.lproj/Localizable.strings @@ -671,6 +671,11 @@ You can do this by running `composer global update` in your terminal. After that "startup.errors.php_binary.subtitle" = "You must install PHP via Homebrew. The app will not work correctly until you resolve this issue."; "startup.errors.php_binary.desc" = "Usually running `brew link php` in your Terminal will resolve this issue.\n\nTo diagnose what is wrong, you can try running `which php` in your Terminal, it should return `%@`."; +// Invalid brew info php output +"startup.errors.php_brew_info_invalid.title" = "Homebrew returned invalid output for `brew info php --json` which requires valid JSON as output."; +"startup.errors.php_brew_info_invalid.subtitle" = "This will prevent PHP Monitor from starting correctly. It's possible that Homebrew is currently in a broken state or some additional logging crept into the output of this command. This is a known issue."; +"startup.errors.php_brew_info_invalid.desc" = "Simply retrying may fix the issue, but you may want to run the command yourself if the issue persists and validate if it's valid JSON. Press OK, and select Retry to try again."; + // PHP not found in /usr/local/opt or /opt/homebrew/opt "startup.errors.php_opt.title" = "PHP is not correctly installed"; "startup.errors.php_opt.subtitle" = "The PHP alias was not found in `%@`. The app will not work correctly until you resolve this issue.";