From 94f086881a4fb6b4b57c049032abe921fc27facf Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 1 Jan 2021 22:54:03 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20Homebrew=20in?= =?UTF-8?q?=20/opt/homebrew=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++ phpmon/AppDelegate.swift | 7 +++ phpmon/Classes/Commands/Actions.swift | 48 ++++++++--------- phpmon/Classes/Commands/Startup.swift | 10 ++-- phpmon/Classes/Helpers/PhpVersion.swift | 5 +- phpmon/Localizable.strings | 6 +-- phpmon/Singletons/MainMenu.swift | 2 +- phpmon/Singletons/Paths.swift | 70 +++++++++++++++++++++++++ phpmon/Singletons/Shell.swift | 6 +++ 9 files changed, 124 insertions(+), 34 deletions(-) create mode 100644 phpmon/Singletons/Paths.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 5328a9c..21af174 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; C486EFFC2586931100A02B2C /* PhpMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C486EFFB2586931100A02B2C /* PhpMenuItem.swift */; }; + C49EAB46259FC305007F6C3B /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; @@ -54,6 +55,7 @@ C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = ""; }; C486EFFB2586931100A02B2C /* PhpMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpMenuItem.swift; sourceTree = ""; }; + C49EAB45259FC305007F6C3B /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; @@ -149,6 +151,7 @@ C4811D2622D70CEF00B5F6B3 /* Singletons */ = { isa = PBXGroup; children = ( + C49EAB45259FC305007F6C3B /* Paths.swift */, C41C1B4622B009A400E7CF16 /* Shell.swift */, C42295DC2358D02000E263B2 /* Command.swift */, C4811D2322D70A4700B5F6B3 /* App.swift */, @@ -272,6 +275,7 @@ C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */, C486EFFC2586931100A02B2C /* PhpMenuItem.swift in Sources */, + C49EAB46259FC305007F6C3B /* Paths.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, diff --git a/phpmon/AppDelegate.swift b/phpmon/AppDelegate.swift index 3bdecee..107ce8a 100644 --- a/phpmon/AppDelegate.swift +++ b/phpmon/AppDelegate.swift @@ -33,6 +33,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele */ let menu : MainMenu + /** + The paths singleton that determines where Homebrew is installed, + and where to look for binaries. + */ + let paths : Paths + // MARK: - Initializer /** @@ -42,6 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele self.sharedShell = Shell.user self.state = App.shared self.menu = MainMenu.shared + self.paths = Paths.shared super.init() } diff --git a/phpmon/Classes/Commands/Actions.swift b/phpmon/Classes/Commands/Actions.swift index cdeca58..e4c5f76 100644 --- a/phpmon/Classes/Commands/Actions.swift +++ b/phpmon/Classes/Commands/Actions.swift @@ -12,7 +12,7 @@ import AppKit class Actions { public static func detectPhpVersions() -> [String] { - let files = Shell.user.pipe("ls /usr/local/opt | grep php@") + let files = Shell.user.pipe("ls \(Paths.optPath()) | grep php@") var versions = files.components(separatedBy: "\n") // Remove all empty strings @@ -40,15 +40,15 @@ class Actions { public static func restartPhpFpm() { let version = App.shared.currentVersion!.short if (version == App.shared.brewPhpVersion) { - Shell.user.run("sudo brew services restart php") + Shell.user.run("sudo \(Paths.brew()) services restart php") } else { - Shell.user.run("sudo brew services restart php@\(version)") + Shell.user.run("sudo \(Paths.brew()) services restart php@\(version)") } } public static func restartNginx() { - Shell.user.run("sudo brew services restart nginx") + Shell.user.run("sudo \(Paths.brew()) services restart nginx") } /** @@ -63,22 +63,22 @@ class Actions { public static func switchToPhpVersion(version: String, availableVersions: [String]) { availableVersions.forEach { (available) in let formula = (available == App.shared.brewPhpVersion) ? "php" : "php@\(available)" - Shell.user.run("brew unlink \(formula)") - Shell.user.run("sudo brew services stop \(formula)") + Shell.user.run("\(Paths.brew()) unlink \(formula)") + Shell.user.run("sudo \(Paths.brew()) services stop \(formula)") } let formula = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)" - Shell.user.run("brew link \(formula) --overwrite --force") - Shell.user.run("sudo brew services start \(formula)") + Shell.user.run("\(Paths.brew()) link \(formula) --overwrite --force") + Shell.user.run("sudo \(Paths.brew()) services start \(formula)") } public static func openGenericPhpConfigFolder() { - let files = [NSURL(fileURLWithPath: "/usr/local/etc/php")]; + let files = [NSURL(fileURLWithPath: "\(Paths.etcPath())/php")]; NSWorkspace.shared.activateFileViewerSelecting(files as [URL]) } public static func openPhpConfigFolder(version: String) { - let files = [NSURL(fileURLWithPath: "/usr/local/etc/php/\(version)/php.ini")]; + let files = [NSURL(fileURLWithPath: "\(Paths.etcPath())/php/\(version)/php.ini")]; NSWorkspace.shared.activateFileViewerSelecting(files as [URL]) } @@ -89,7 +89,7 @@ class Actions { public static func didFindXdebug(_ version: String) -> Bool { let command = """ - grep -q 'zend_extension="xdebug.so"' /usr/local/etc/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO" + grep -q 'zend_extension="xdebug.so"' \(Paths.etcPath())/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO" """ let output = Shell.user.pipe(command).trimmingCharacters(in: .whitespacesAndNewlines) return (output == "YES") @@ -97,7 +97,7 @@ class Actions { public static func didEnableXdebug(_ version: String) -> Bool { let command = """ - grep -q '; zend_extension="xdebug.so"' /usr/local/etc/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO" + grep -q '; zend_extension="xdebug.so"' \(Paths.etcPath())/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO" """ let output = Shell.user.pipe(command).trimmingCharacters(in: .whitespacesAndNewlines) return (output == "NO") @@ -106,11 +106,11 @@ class Actions { public static func toggleXdebug() { let version = App.shared.currentVersion?.short var command = """ - sed -i '' 's/; zend_extension="xdebug.so"/zend_extension="xdebug.so"/g' /usr/local/etc/php/\(version!)/php.ini + sed -i '' 's/; zend_extension="xdebug.so"/zend_extension="xdebug.so"/g' \(Paths.etcPath())/php/\(version!)/php.ini """ if (self.didEnableXdebug(version!)) { command = """ - sed -i '' 's/zend_extension="xdebug.so"/; zend_extension="xdebug.so"/g' /usr/local/etc/php/\(version!)/php.ini + sed -i '' 's/zend_extension="xdebug.so"/; zend_extension="xdebug.so"/g' \(Paths.etcPath())/php/\(version!)/php.ini """ } Shell.user.run(command) @@ -124,19 +124,19 @@ class Actions { public static func fixMyPhp() { let versions = self.detectPhpVersions() versions.forEach { (version) in - Shell.user.run("brew unlink php@\(version)") + Shell.user.run("\(Paths.brew()) unlink php@\(version)") if (version == App.shared.brewPhpVersion) { - Shell.user.run("brew services stop php") - Shell.user.run("sudo brew services stop php") + Shell.user.run("\(Paths.brew()) services stop php") + Shell.user.run("sudo \(Paths.brew()) services stop php") } else { - Shell.user.run("brew services stop php@\(version)") - Shell.user.run("sudo brew services stop php@\(version)") + Shell.user.run("\(Paths.brew()) services stop php@\(version)") + Shell.user.run("sudo \(Paths.brew()) services stop php@\(version)") } } - Shell.user.run("brew services stop php") - Shell.user.run("brew services stop nginx") - Shell.user.run("brew link php") - Shell.user.run("sudo brew services restart php") - Shell.user.run("sudo brew services restart nginx") + Shell.user.run("\(Paths.brew()) services stop php") + Shell.user.run("\(Paths.brew()) services stop nginx") + Shell.user.run("\(Paths.brew()) link php") + Shell.user.run("sudo \(Paths.brew()) services restart php") + Shell.user.run("sudo \(Paths.brew()) services restart nginx") } } diff --git a/phpmon/Classes/Commands/Startup.swift b/phpmon/Classes/Commands/Startup.swift index 53e65b1..d458471 100644 --- a/phpmon/Classes/Commands/Startup.swift +++ b/phpmon/Classes/Commands/Startup.swift @@ -25,14 +25,14 @@ class Startup { self.failureCallback = failure self.performEnvironmentCheck( - !Shell.user.pipe("which php").contains("/usr/local/bin/php"), + !Shell.fileExists("\(Paths.binPath())/php"), messageText: "startup.errors.php_binary.title".localized, informativeText: "startup.errors.php_binary_desc".localized, breaking: true ) self.performEnvironmentCheck( - !Shell.user.pipe("ls /usr/local/opt | grep php").contains("php"), + !Shell.user.pipe("ls \(Paths.optPath()) | grep php").contains("php"), messageText: "startup.errors.php_opt.title".localized, informativeText: "startup.errors.php_opt.desc".localized, breaking: true @@ -46,7 +46,7 @@ class Startup { ) self.performEnvironmentCheck( - !Shell.user.pipe("cat /private/etc/sudoers.d/brew").contains("/usr/local/bin/brew"), + !Shell.user.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 @@ -59,7 +59,7 @@ class Startup { breaking: true ) - let services = Shell.user.pipe("brew services list | grep php") + let services = Shell.user.pipe("\(Paths.brew()) services list | grep php") self.performEnvironmentCheck( (services.countInstances(of: "started") > 1), messageText: "startup.errors.services.title".localized, @@ -82,7 +82,7 @@ class Startup { print("PHP Monitor has determined the application has successfully passed all checks.") print("Determining which version of PHP is aliased to `php` via Homebrew...") - let brewPhpAlias = Shell.user.pipe("brew info php --json"); + let brewPhpAlias = Shell.user.pipe("\(Paths.brew()) info php --json"); App.shared.brewPhpPackage = try! JSONDecoder().decode( [HomebrewPackage].self, diff --git a/phpmon/Classes/Helpers/PhpVersion.swift b/phpmon/Classes/Helpers/PhpVersion.swift index ec9ed8b..b2f4c36 100644 --- a/phpmon/Classes/Helpers/PhpVersion.swift +++ b/phpmon/Classes/Helpers/PhpVersion.swift @@ -19,7 +19,10 @@ class PhpVersion { var error : Bool = false init() { - let version = Command.execute(path: "/usr/local/bin/php", arguments: ["-r", "print phpversion();"]) + let version = Command.execute( + path: Paths.php(), + arguments: ["-r", "print phpversion();"] + ) if (version == "" || version.contains("Warning")) { self.short = "💩 BROKEN" diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 1f64371..ff9d974 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -59,11 +59,11 @@ /// 1. PHP binary not found "startup.errors.php_binary.title" = "PHP is not correctly installed"; -"startup.errors.php_binary_desc" = "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php`. The app will not work correctly until you resolve this issue. (Usually `brew link php` resolves this issue.)"; +"startup.errors.php_binary_desc" = "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php` (or `/opt/homebrew/bin/php`). The app will not work correctly until you resolve this issue. (Usually `brew link php` resolves this issue.)"; -/// 2. PHP not found in /usr/local/opt +/// 2. 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.desc" = "PHP alias was not found in `/usr/local/opt`. The app will not work correctly until you resolve this issue. If you already have the `php` formula installed, you may need to run `brew install php` in order for PHP Monitor to detect this installation."; +"startup.errors.php_opt.desc" = "PHP alias was not found in `/usr/local/opt` (or `/opt/homebrew/opt`). The app will not work correctly until you resolve this issue. If you already have the `php` formula installed, you may need to run `brew install php` in order for PHP Monitor to detect this installation."; /// 3. Valet not installed "startup.errors.valet_executable.title" = "Laravel Valet is not correctly installed"; diff --git a/phpmon/Singletons/MainMenu.swift b/phpmon/Singletons/MainMenu.swift index ec2d547..e17a717 100644 --- a/phpmon/Singletons/MainMenu.swift +++ b/phpmon/Singletons/MainMenu.swift @@ -201,7 +201,7 @@ class MainMenu: NSObject, NSWindowDelegate { @objc public func openPhpInfo() { self.waitAndExecute({ try! " /tmp/phpmon_phpinfo.html") + Shell.user.run("\(Paths.binPath())/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html") }, { NSWorkspace.shared.open(URL(string: "file:///private/tmp/phpmon_phpinfo.html")!) }) diff --git a/phpmon/Singletons/Paths.swift b/phpmon/Singletons/Paths.swift new file mode 100644 index 0000000..4356c6b --- /dev/null +++ b/phpmon/Singletons/Paths.swift @@ -0,0 +1,70 @@ +// +// Paths.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 01/01/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +enum HomebrewDirectory: String { + case opt = "/opt/homebrew/bin" + case usr = "/usr/local/bin" +} + +class Paths { + + static let shared = Paths() + var baseDirectory : HomebrewDirectory + + init() { + let optBrewFound = Shell.fileExists("\(HomebrewDirectory.opt.rawValue)/brew") + let usrBrewFound = Shell.fileExists("\(HomebrewDirectory.usr.rawValue)/brew") + + if (optBrewFound) { + self.baseDirectory = .opt + } else if (usrBrewFound) { + self.baseDirectory = .usr + } else { + // Falling back to Intel + print("Seems like we couldn't determine the architecture.") + print("This usually means we're in trouble... (no Homebrew?)") + self.baseDirectory = .usr + } + + print("Homebrew directory: \(self.baseDirectory)") + } + + public static func brew() -> String { + return "\(self.binPath())/brew" + } + + public static func php() -> String { + return "\(self.binPath())/php" + } + + public static func binPath() -> String { + return self.shared.baseDirectory.rawValue + } + + public static func optPath() -> String { + switch self.shared.baseDirectory { + case .opt: + return "/opt/homebrew/opt" + case .usr: + return "/usr/local/opt" + } + } + + public static func etcPath() -> String { + switch self.shared.baseDirectory { + case .opt: + return "/opt/homebrew/etc" + case .usr: + return "/usr/local/etc" + } + } +} + + diff --git a/phpmon/Singletons/Shell.swift b/phpmon/Singletons/Shell.swift index f08b6f2..733a1b8 100644 --- a/phpmon/Singletons/Shell.swift +++ b/phpmon/Singletons/Shell.swift @@ -46,4 +46,10 @@ class Shell { encoding: .utf8 )! } + + public static func fileExists(_ filePath: String) -> Bool { + return Shell.user.pipe( + "if [ -f \(filePath) ]; then echo \"Y\"; fi" + ).contains("Y") + } }