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/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index c37661ef..7ebbda9d 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -30,6 +30,9 @@ extension MainMenu { When the environment is all clear and the app can run, let's go. */ private func onEnvironmentPass() async { + // Load additional preferences + await container.preferences.loadCustomPreferences() + // Determine what the `php` formula is aliased to await container.phpEnvs.determinePhpAlias() diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index fa674c30..da90abed 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -29,8 +29,6 @@ class Preferences { if isRunningSwiftUIPreview { return } - - Task { await loadCustomPreferences() } } // MARK: - First Time Run 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.";