From dc91d0e00c2cc70a4d7f250633999c4fbc853dfa Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 11 Feb 2022 18:58:07 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20Path=20based=20on=20architecture?= =?UTF-8?q?=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++- phpmon/Common/Core/Paths.swift | 6 ++-- phpmon/Domain/App/App.swift | 14 +++++++++ phpmon/Domain/App/Startup.swift | 38 ++++++++--------------- phpmon/Domain/Menu/MainMenu+Startup.swift | 19 ++++++++++++ phpmon/Localizable.strings | 4 +++ 6 files changed, 61 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 8e4025e..6ff35be 100644 --- a/README.md +++ b/README.md @@ -231,8 +231,15 @@ Since v3.4 all of the loaded .ini files are sourced to determine which extension
I've got two Homebrew installations on my Apple Silicon Mac, can I choose which installation to use with PHP Monitor? -Not at this time, no. PHP Monitor will prefer the `/opt/homebrew` installation over the classic installation directory. +If you are using PHP Monitor on an Intel machine or on an Apple Silicon machine with Rosetta enabled, PHP Monitor expects the main Homebrew binary in `/usr/local/bin/brew`. +If you are using PHP Monitor on Apple Silicon without Rosetta, PHP Monitor expects the main Homebrew binary in `/opt/homebrew/bin/brew`. + +If there's an issue here, you'll get an alert at launch. + +Make sure that the version of Homebrew that you are running normally is the same as the one that PHP Monitor expects. If you are on M1 hardware for example, but still using Rosetta for Homebrew, you'll need to run PHP Monitor under Rosetta as well. + +PHP Monitor is a universal app and supports both architectures, so [find out here](https://support.apple.com/en-us/HT211861) how to enable Rosetta with PHP Monitor.
diff --git a/phpmon/Common/Core/Paths.swift b/phpmon/Common/Core/Paths.swift index a227cc3..9176650 100644 --- a/phpmon/Common/Core/Paths.swift +++ b/phpmon/Common/Core/Paths.swift @@ -15,15 +15,15 @@ public class Paths { public static let shared = Paths() - private var baseDir : Paths.HomebrewDir + internal var baseDir : Paths.HomebrewDir private var userName : String init() { - baseDir = FileManager.default.fileExists(atPath: "\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr + baseDir = App.architecture != "x86_64" ? .opt : .usr userName = String(Shell.pipe("whoami").split(separator: "\n")[0]) } - + // - MARK: Binaries public static var valet: String { diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 8e1edf3..db971bb 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -22,6 +22,20 @@ class App { return "\(version) (\(build))" } + static var architecture: String { + var systeminfo = utsname() + uname(&systeminfo) + let machine = withUnsafeBytes(of: &systeminfo.machine) {bufPtr->String in + let data = Data(bufPtr) + if let lastIndex = data.lastIndex(where: {$0 != 0}) { + return String(data: data[0...lastIndex], encoding: .isoLatin1)! + } else { + return String(data: data, encoding: .isoLatin1)! + } + } + return machine + } + /** Whether the app is busy doing something. Used to determine what UI to display. */ // TODO: Remove this, and always use PhpEnv.shared.isBusy static var busy: Bool { diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 5f4bbdf..ea01198 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -10,7 +10,7 @@ import AppKit class Startup { - public var failed : Bool = false + public var failed: Bool = false public var failureCallback = {} /** @@ -27,40 +27,34 @@ class Startup { performEnvironmentCheck( !Shell.fileExists("\(Paths.binPath)/php"), messageText: "startup.errors.php_binary.title".localized, - informativeText: "startup.errors.php_binary.desc".localized, - breaking: true + informativeText: "startup.errors.php_binary.desc".localized ) performEnvironmentCheck( !Shell.pipe("ls \(Paths.optPath) | grep php").contains("php"), messageText: "startup.errors.php_opt.title".localized, - informativeText: "startup.errors.php_opt.desc".localized, - breaking: true + informativeText: "startup.errors.php_opt.desc".localized ) performEnvironmentCheck( // Check for Valet; it can be symlinked or in .composer/vendor/bin - !(Shell.fileExists("/usr/local/bin/valet") - || Shell.fileExists("/opt/homebrew/bin/valet") + !(Shell.fileExists("\(Paths.binPath))/valet") || Shell.fileExists("~/.composer/vendor/bin/valet") ), messageText: "startup.errors.valet_executable.title".localized, - informativeText: "startup.errors.valet_executable.desc".localized, - breaking: true + informativeText: "startup.errors.valet_executable.desc".localized ) performEnvironmentCheck( HomebrewDiagnostics.cannotLoadService(), messageText: "startup.errors.services_json_error.title".localized, - informativeText: "startup.errors.services_json_error.desc".localized, - breaking: true + informativeText: "startup.errors.services_json_error.desc".localized ) performEnvironmentCheck( !Shell.pipe("cat /private/etc/sudoers.d/brew").contains("\(Paths.binPath)/brew"), messageText: "startup.errors.sudoers_brew.title".localized, - informativeText: "startup.errors.sudoers_brew.desc".localized, - breaking: true + informativeText: "startup.errors.sudoers_brew.desc".localized ) performEnvironmentCheck( @@ -69,8 +63,7 @@ class Startup { || Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/opt/homebrew/bin/valet") ), messageText: "startup.errors.sudoers_valet.title".localized, - informativeText: "startup.errors.sudoers_valet.desc".localized, - breaking: true + informativeText: "startup.errors.sudoers_valet.desc".localized ) // Determine the Valet version only AFTER confirming the correct permission is in place @@ -78,8 +71,7 @@ class Startup { performEnvironmentCheck( Valet.shared.version == nil, messageText: "startup.errors.valet_version_unknown.title".localized, - informativeText: "startup.errors.valet_version_unknown.desc".localized, - breaking: true + informativeText: "startup.errors.valet_version_unknown.desc".localized ) if (!failed) { @@ -101,32 +93,28 @@ class Startup { } /** - Perform an environment check. Will cause the application to terminate, if `breaking` is set to true. + Perform an environment check. - Parameter condition: Fail condition to check for; if this returns `true`, the alert will be shown - Parameter messageText: Short description of what is wrong - Parameter informativeText: Expanded description of the environment check that failed - - Parameter breaking: If the application should terminate afterwards */ private func performEnvironmentCheck( _ condition: Bool, messageText: String, - informativeText: String, - breaking: Bool + informativeText: String ) { if (!condition) { return } - - failed = breaking DispatchQueue.main.async { [self] in // Present the information to the user Alert.notify( message: messageText, info: informativeText, - style: breaking ? .critical : .warning + style: .critical ) // Only breaking issues will throw the extra retry modal - breaking ? failureCallback() : () + failureCallback() } } diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 66ab435..68df58a 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -16,6 +16,25 @@ extension MainMenu { // Start with the icon setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!) + // Do the important system setup checks + Log.info("The user is running PHP Monitor with the architecture: \(App.architecture)") + + // Make sure Homebrew is installed + if !FileManager.default.fileExists(atPath: Paths.brew) { + _ = Alert.present( + messageText: "alert.homebrew_missing.title".localized, + informativeText: "alert.homebrew_missing.info".localized( + App.architecture + .replacingOccurrences(of: "x86_64", with: "Intel") + .replacingOccurrences(of: "arm64", with: "Apple Silicon"), + Paths.brew + ), + buttonTitle: "alert.homebrew_missing.quit".localized, + style: .critical + ) + exit(1) + } + // Perform environment boot checks DispatchQueue.global(qos: .userInitiated).async { [unowned self] in Startup().checkEnvironment(success: { onEnvironmentPass() }, diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 682a08a..7f00195 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -183,6 +183,10 @@ // ALERTS +"alert.homebrew_missing.title" = "PHP Monitor cannot start"; +"alert.homebrew_missing.info" = "You are running PHP Monitor with the following architecture: %@.\n\nA working Homebrew binary is expected in `%@`, but was not found, so PHP Monitor cannot work.\n\nIf you have not installed Homebrew yet, please do so. (If you are on Apple Silicon, make sure your Homebrew and PHP Monitor use the same architecture, by enabling or disabling Rosetta where needed.)\n\nPHP Monitor will now quit, please restart the app after fixing this issue."; +"alert.homebrew_missing.quit" = "Quit"; + // Composer Update "alert.composer_missing.title" = "Composer not found!"; "alert.composer_missing.info" = "Make sure you have Composer available in `/usr/local/bin/composer`. If Composer is located somewhere else, please create a symlink, like so (make sure to use the correct path):\n\n`ln -s /path/to/composer /usr/local/bin`.";