From 9dae03a04e17014124b0fde9605dfc825497927d Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 17 Mar 2023 17:40:30 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Homebrew=20version=20validation,=20?= =?UTF-8?q?UI=20workshopping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 48 ++++++-- phpmon/Common/Core/Actions.swift | 26 ++--- phpmon/Common/Core/Homebrew.swift | 36 +++--- phpmon/Common/PHP/PhpVersionInstaller.swift | 26 +++-- phpmon/Domain/App/AppDelegate.swift | 7 ++ phpmon/Domain/App/AppUpdater.swift | 2 +- .../Domain/App/Services/ServicesManager.swift | 6 +- phpmon/Domain/App/Startup.swift | 4 +- .../Domain/Integrations/Homebrew/Brew.swift | 31 +++++ ...iagnostics.swift => BrewDiagnostics.swift} | 4 +- phpmon/Domain/Menu/MainMenu+Startup.swift | 11 +- .../SwiftUI/PhpManager/PhpManager.swift | 106 ++++++++++++++++++ 12 files changed, 241 insertions(+), 66 deletions(-) create mode 100644 phpmon/Domain/Integrations/Homebrew/Brew.swift rename phpmon/Domain/Integrations/Homebrew/{HomebrewDiagnostics.swift => BrewDiagnostics.swift} (99%) create mode 100644 phpmon/Domain/SwiftUI/PhpManager/PhpManager.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 2ac6517..b8f1bf5 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -129,6 +129,14 @@ C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; }; C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; }; C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; }; + C43931C529C4BD610069165B /* PhpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C429C4BD610069165B /* PhpManager.swift */; }; + C43931C629C4BD610069165B /* PhpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C429C4BD610069165B /* PhpManager.swift */; }; + C43931C729C4BD610069165B /* PhpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C429C4BD610069165B /* PhpManager.swift */; }; + C43931C829C4BD610069165B /* PhpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C429C4BD610069165B /* PhpManager.swift */; }; + C43931CA29C4C03F0069165B /* Brew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C929C4C03F0069165B /* Brew.swift */; }; + C43931CB29C4C03F0069165B /* Brew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C929C4C03F0069165B /* Brew.swift */; }; + C43931CC29C4C03F0069165B /* Brew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C929C4C03F0069165B /* Brew.swift */; }; + C43931CD29C4C03F0069165B /* Brew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43931C929C4C03F0069165B /* Brew.swift */; }; C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; }; C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; }; C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; }; @@ -313,8 +321,8 @@ C471E82428F9BB2E0021E251 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; }; C471E82528F9BB2E0021E251 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; C471E82628F9BB2E0021E251 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; }; - C471E82728F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; - C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; + C471E82728F9BB310021E251 /* BrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* BrewDiagnostics.swift */; }; + C471E82828F9BB310021E251 /* BrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* BrewDiagnostics.swift */; }; C471E82928F9BB330021E251 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; C471E82A28F9BB330021E251 /* ValetListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* ValetListable.swift */; }; C471E82B28F9BB340021E251 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; @@ -709,8 +717,8 @@ C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; }; C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; }; - C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; - C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; + C4F2E4372752F0870020E974 /* BrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* BrewDiagnostics.swift */; }; + C4F2E4382752F08D0020E974 /* BrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* BrewDiagnostics.swift */; }; C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; }; @@ -861,6 +869,8 @@ C42F26722805B4B400938AC7 /* ValetListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetListable.swift; sourceTree = ""; }; C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = ""; }; C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = ""; }; + C43931C429C4BD610069165B /* PhpManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpManager.swift; sourceTree = ""; }; + C43931C929C4C03F0069165B /* Brew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Brew.swift; sourceTree = ""; }; C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; }; C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = ""; }; C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = ""; }; @@ -1004,7 +1014,7 @@ C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; C4EED88827A48778006D7272 /* InterAppHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterAppHandler.swift; sourceTree = ""; }; - C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewDiagnostics.swift; sourceTree = ""; }; + C4F2E4362752F0870020E974 /* BrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewDiagnostics.swift; sourceTree = ""; }; C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = ""; }; C4F30B02278E16BA00755FCE /* HomebrewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewService.swift; sourceTree = ""; }; C4F30B06278E195800755FCE /* brew-services.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "brew-services.json"; sourceTree = ""; }; @@ -1326,6 +1336,14 @@ path = Warning; sourceTree = ""; }; + C43931C329C4BD510069165B /* PhpManager */ = { + isa = PBXGroup; + children = ( + C43931C429C4BD610069165B /* PhpManager.swift */, + ); + path = PhpManager; + sourceTree = ""; + }; C43FDBE729A9329A003D85EC /* Services */ = { isa = PBXGroup; children = ( @@ -1591,7 +1609,8 @@ C4AF9F6C275445D900D44ED0 /* Homebrew */ = { isa = PBXGroup; children = ( - C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */, + C43931C929C4C03F0069165B /* Brew.swift */, + C4F2E4362752F0870020E974 /* BrewDiagnostics.swift */, C40934A1298EEB2C00D25014 /* CaskFile.swift */, ); path = Homebrew; @@ -1812,6 +1831,7 @@ C4EE55B027708BB2001DF387 /* SwiftUI */ = { isa = PBXGroup; children = ( + C43931C329C4BD510069165B /* PhpManager */, C490E3A329BC92E6006D2DE6 /* Progress */, C4297F7828970D4E004C4630 /* Warning */, C4E9D2BE2878B32D008FFDAD /* Onboarding */, @@ -2172,6 +2192,7 @@ C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */, C409349D298EE8E900D25014 /* AppUpdater.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, + C43931CA29C4C03F0069165B /* Brew.swift in Sources */, C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */, C48D6C70279CD2AC00F26D7E /* VersionNumber.swift in Sources */, C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */, @@ -2211,7 +2232,7 @@ C4463FCC29804BCB007B93D5 /* RCFile.swift in Sources */, C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */, C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */, - C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, + C4F2E4372752F0870020E974 /* BrewDiagnostics.swift in Sources */, C4AD38B228ECD9D300FA8D83 /* TestableFileSystem.swift in Sources */, C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */, C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, @@ -2229,6 +2250,7 @@ C4C3643928AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, C4CDA893288F1A71007CE25F /* Keys.swift in Sources */, + C43931C529C4BD610069165B /* PhpManager.swift in Sources */, C40175B82903108900763A68 /* ValetInteractor.swift in Sources */, C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */, C46EBC4A28DB966A007ACC74 /* TestableShell.swift in Sources */, @@ -2401,6 +2423,7 @@ C471E86328F9BB650021E251 /* PMTableView.swift in Sources */, C471E86428F9BB650021E251 /* Warning.swift in Sources */, C40175BA2903108900763A68 /* ValetInteractor.swift in Sources */, + C43931C729C4BD610069165B /* PhpManager.swift in Sources */, C4463FCE29804BCB007B93D5 /* RCFile.swift in Sources */, C45B9150295608E300F4EC78 /* ValetServicesManager.swift in Sources */, C471E86528F9BB650021E251 /* WarningManager.swift in Sources */, @@ -2444,6 +2467,7 @@ C471E88B28F9BB650021E251 /* HotKey.swift in Sources */, C471E88C28F9BB650021E251 /* HotKeysController.swift in Sources */, C41ADCEA2970CCC700120423 /* FSNotifier.swift in Sources */, + C43931CC29C4C03F0069165B /* Brew.swift in Sources */, C471E88D28F9BB650021E251 /* Key.swift in Sources */, C471E88E28F9BB650021E251 /* KeyCombo.swift in Sources */, C471E88F28F9BB650021E251 /* ModifierFlagsExtension.swift in Sources */, @@ -2472,7 +2496,7 @@ C471E7F928F9BACB0021E251 /* PhpSwitcher.swift in Sources */, C471E82A28F9BB330021E251 /* ValetListable.swift in Sources */, C4F8765029BFAF00006BBE26 /* PhpVersionInstaller.swift in Sources */, - C471E82728F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */, + C471E82728F9BB310021E251 /* BrewDiagnostics.swift in Sources */, C471E81C28F9BB250021E251 /* BetterAlert.swift in Sources */, C471E7DB28F9BA8F0021E251 /* RealShell.swift in Sources */, C490E3B929BCA368006D2DE6 /* App+BrewWatch.swift in Sources */, @@ -2531,6 +2555,7 @@ C471E89F28F9BB8F0021E251 /* ValetDomainScanner.swift in Sources */, C471E8A028F9BB8F0021E251 /* FakeDomainScanner.swift in Sources */, C471E8A228F9BB8F0021E251 /* AppDelegate.swift in Sources */, + C43931CD29C4C03F0069165B /* Brew.swift in Sources */, C451AFF92969E40F0078E617 /* HelpButton.swift in Sources */, C471E8A328F9BB8F0021E251 /* AppDelegate+MenuOutlets.swift in Sources */, C471E8A428F9BB8F0021E251 /* AppDelegate+Notifications.swift in Sources */, @@ -2649,10 +2674,11 @@ C471E82228F9BB2E0021E251 /* ComposerWindow.swift in Sources */, C4D3660E29113F20006BD146 /* System.swift in Sources */, C471E80428F9BAD40021E251 /* PhpExtension.swift in Sources */, + C43931C829C4BD610069165B /* PhpManager.swift in Sources */, C471E7F728F9BACB0021E251 /* PhpSwitcher.swift in Sources */, C4463FCF29804BCB007B93D5 /* RCFile.swift in Sources */, C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */, - C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */, + C471E82828F9BB310021E251 /* BrewDiagnostics.swift in Sources */, C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */, C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */, C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */, @@ -2748,6 +2774,7 @@ C47DF1B0299D5A3B0007055D /* LoginItemManager.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C45B914A295607F400F4EC78 /* Service.swift in Sources */, + C43931C629C4BD610069165B /* PhpManager.swift in Sources */, C4C0E8E327F88B13002D32A9 /* ValetDomainScanner.swift in Sources */, C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */, C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */, @@ -2755,7 +2782,7 @@ C46FA98C2822F08F00D78807 /* PhpConfigurationTest.swift in Sources */, C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */, C4E49DEB28F7643D0026AC4E /* CommandProtocol.swift in Sources */, - C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */, + C4F2E4382752F08D0020E974 /* BrewDiagnostics.swift in Sources */, C485707428BF454E00539B36 /* ServicesView.swift in Sources */, C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */, C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, @@ -2859,6 +2886,7 @@ C48D6C71279CD2AC00F26D7E /* VersionNumber.swift in Sources */, C485706F28BF452300539B36 /* WarningsWindowController.swift in Sources */, C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */, + C43931CB29C4C03F0069165B /* Brew.swift in Sources */, C41C02AB27E61CB3009F26CB /* FakeValetSite.swift in Sources */, C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */, C4D9F24C280B69E100DCD39A /* AddProxyVC.swift in Sources */, diff --git a/phpmon/Common/Core/Actions.swift b/phpmon/Common/Core/Actions.swift index 38d33f3..1579a82 100644 --- a/phpmon/Common/Core/Actions.swift +++ b/phpmon/Common/Core/Actions.swift @@ -19,32 +19,32 @@ class Actions { } public static func restartPhpFpm() async { - await brew("services restart \(Homebrew.Formulae.php)", sudo: Homebrew.Formulae.php.elevated) + await brew("services restart \(HomebrewFormulae.php)", sudo: HomebrewFormulae.php.elevated) } public static func restartNginx() async { - await brew("services restart \(Homebrew.Formulae.nginx)", sudo: Homebrew.Formulae.nginx.elevated) + await brew("services restart \(HomebrewFormulae.nginx)", sudo: HomebrewFormulae.nginx.elevated) } public static func restartDnsMasq() async { - await brew("services restart \(Homebrew.Formulae.dnsmasq)", sudo: Homebrew.Formulae.dnsmasq.elevated) + await brew("services restart \(HomebrewFormulae.dnsmasq)", sudo: HomebrewFormulae.dnsmasq.elevated) } public static func stopValetServices() async { - await brew("services stop \(Homebrew.Formulae.php)", sudo: Homebrew.Formulae.php.elevated) - await brew("services stop \(Homebrew.Formulae.nginx)", sudo: Homebrew.Formulae.nginx.elevated) - await brew("services stop \(Homebrew.Formulae.dnsmasq)", sudo: Homebrew.Formulae.dnsmasq.elevated) + await brew("services stop \(HomebrewFormulae.php)", sudo: HomebrewFormulae.php.elevated) + await brew("services stop \(HomebrewFormulae.nginx)", sudo: HomebrewFormulae.nginx.elevated) + await brew("services stop \(HomebrewFormulae.dnsmasq)", sudo: HomebrewFormulae.dnsmasq.elevated) } public static func fixHomebrewPermissions() throws { var servicesCommands = [ - "\(Paths.brew) services stop \(Homebrew.Formulae.nginx)", - "\(Paths.brew) services stop \(Homebrew.Formulae.dnsmasq)" + "\(Paths.brew) services stop \(HomebrewFormulae.nginx)", + "\(Paths.brew) services stop \(HomebrewFormulae.dnsmasq)" ] var cellarCommands = [ - "chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(Homebrew.Formulae.nginx)", - "chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(Homebrew.Formulae.dnsmasq)" + "chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(HomebrewFormulae.nginx)", + "chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(HomebrewFormulae.dnsmasq)" ] PhpEnv.shared.availablePhpVersions.forEach { version in @@ -126,8 +126,8 @@ class Actions { */ public static func fixMyValet() async { await InternalSwitcher().performSwitch(to: PhpEnv.brewPhpAlias) - await brew("services restart \(Homebrew.Formulae.dnsmasq)", sudo: Homebrew.Formulae.dnsmasq.elevated) - await brew("services restart \(Homebrew.Formulae.php)", sudo: Homebrew.Formulae.php.elevated) - await brew("services restart \(Homebrew.Formulae.nginx)", sudo: Homebrew.Formulae.nginx.elevated) + await brew("services restart \(HomebrewFormulae.dnsmasq)", sudo: HomebrewFormulae.dnsmasq.elevated) + await brew("services restart \(HomebrewFormulae.php)", sudo: HomebrewFormulae.php.elevated) + await brew("services restart \(HomebrewFormulae.nginx)", sudo: HomebrewFormulae.nginx.elevated) } } diff --git a/phpmon/Common/Core/Homebrew.swift b/phpmon/Common/Core/Homebrew.swift index 19173a4..7f2e35b 100644 --- a/phpmon/Common/Core/Homebrew.swift +++ b/phpmon/Common/Core/Homebrew.swift @@ -8,29 +8,27 @@ import Foundation -class Homebrew { - struct Formulae { - static var php: HomebrewFormula { - if PhpEnv.shared.homebrewPackage == nil { - return HomebrewFormula("php", elevated: true) - } - - guard let install = PhpEnv.phpInstall else { - return HomebrewFormula("php", elevated: true) - } - - return HomebrewFormula(install.formula, elevated: true) +struct HomebrewFormulae { + static var php: HomebrewFormula { + if PhpEnv.shared.homebrewPackage == nil { + return HomebrewFormula("php", elevated: true) } - static var nginx: HomebrewFormula { - return HomebrewDiagnostics.usesNginxFullFormula - ? HomebrewFormula("nginx-full", elevated: true) - : HomebrewFormula("nginx", elevated: true) + guard let install = PhpEnv.phpInstall else { + return HomebrewFormula("php", elevated: true) } - static var dnsmasq: HomebrewFormula { - return HomebrewFormula("dnsmasq", elevated: true) - } + return HomebrewFormula(install.formula, elevated: true) + } + + static var nginx: HomebrewFormula { + return BrewDiagnostics.usesNginxFullFormula + ? HomebrewFormula("nginx-full", elevated: true) + : HomebrewFormula("nginx", elevated: true) + } + + static var dnsmasq: HomebrewFormula { + return HomebrewFormula("dnsmasq", elevated: true) } } diff --git a/phpmon/Common/PHP/PhpVersionInstaller.swift b/phpmon/Common/PHP/PhpVersionInstaller.swift index e0f39ab..20b7470 100644 --- a/phpmon/Common/PHP/PhpVersionInstaller.swift +++ b/phpmon/Common/PHP/PhpVersionInstaller.swift @@ -9,6 +9,13 @@ import Foundation import Cocoa +public enum PhpInstallAction { + case install + case remove + case upgrade + case purge +} + public class PhpVersionInstaller { public static var installables = [ // "8.2": "php", @@ -21,12 +28,6 @@ public class PhpVersionInstaller { "7.0": "shivammathur/php/php@7.0" ] - public enum PhpInstallAction { - case install - case remove - case purge - } - // swiftlint:disable cyclomatic_complexity function_body_length /** Performs the desired action on the provided PHP version. @@ -36,6 +37,8 @@ public class PhpVersionInstaller { switch action { case .install: return "Installing PHP \(version)" + case .upgrade: + return "Upgrading to PHP \(version)" case .remove: return "Removing PHP \(version)" case .purge: @@ -47,6 +50,8 @@ public class PhpVersionInstaller { switch action { case .install: return "Please wait while Homebrew installs PHP \(version)..." + case .upgrade: + return "Please wait while Homebrew upgrades PHP \(version)..." case .remove: return "Please wait while Homebrew uninstalls PHP \(version)..." case .purge: @@ -71,7 +76,7 @@ public class PhpVersionInstaller { var command: String! if action == .install { - if formula.contains("shivammathur") && !HomebrewDiagnostics.installedTaps.contains("shivammathur/php") { + if formula.contains("shivammathur") && !BrewDiagnostics.installedTaps.contains("shivammathur/php") { await Shell.quiet("brew tap shivammathur/php") } @@ -82,12 +87,9 @@ public class PhpVersionInstaller { """ } - // TODO: Ensure that a PHP version can also be updated - /* - if action == .update { - + if action == .upgrade { + fatalError("This is not supported yet.") } - */ if action == .purge || action == .remove { // Removal always requires permission diff --git a/phpmon/Domain/App/AppDelegate.swift b/phpmon/Domain/App/AppDelegate.swift index f0cf1d3..5bba9eb 100644 --- a/phpmon/Domain/App/AppDelegate.swift +++ b/phpmon/Domain/App/AppDelegate.swift @@ -37,6 +37,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele */ let valet: Valet + /** + The Brew singleton that contains all information about Homebrew + and its configuration on your system. + */ + let brew: Brew + /** The PhpEnv singleton that handles PHP version detection, as well as switching. It is initialized @@ -87,6 +93,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele self.menu = MainMenu.shared self.paths = Paths.shared self.valet = Valet.shared + self.brew = Brew.shared super.init() } diff --git a/phpmon/Domain/App/AppUpdater.swift b/phpmon/Domain/App/AppUpdater.swift index 26b9d7c..bbddad0 100644 --- a/phpmon/Domain/App/AppUpdater.swift +++ b/phpmon/Domain/App/AppUpdater.swift @@ -73,7 +73,7 @@ class AppUpdater { .localized(latestVersionOnline.humanReadable), subtitle: "updater.alerts.newer_version_available.subtitle" .localized, - description: HomebrewDiagnostics.customCaskInstalled + description: BrewDiagnostics.customCaskInstalled ? "updater.installation_source.brew".localized(command) : "updater.installation_source.direct".localized ) diff --git a/phpmon/Domain/App/Services/ServicesManager.swift b/phpmon/Domain/App/Services/ServicesManager.swift index d2d51f4..fb8017f 100644 --- a/phpmon/Domain/App/Services/ServicesManager.swift +++ b/phpmon/Domain/App/Services/ServicesManager.swift @@ -107,9 +107,9 @@ class ServicesManager: ObservableObject { var formulae: [HomebrewFormula] { var formulae = [ - Homebrew.Formulae.php, - Homebrew.Formulae.nginx, - Homebrew.Formulae.dnsmasq + HomebrewFormulae.php, + HomebrewFormulae.nginx, + HomebrewFormulae.dnsmasq ] let additionalFormulae = (Preferences.custom.services ?? []).map({ item in diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 47f3bb5..81941da 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -202,8 +202,8 @@ class Startup { // ================================================================================= EnvironmentCheck( command: { - await HomebrewDiagnostics.loadInstalledTaps() - return await HomebrewDiagnostics.cannotLoadService("dnsmasq") + await BrewDiagnostics.loadInstalledTaps() + return await BrewDiagnostics.cannotLoadService("dnsmasq") }, name: "`sudo \(Paths.brew) services info` JSON loaded", titleText: "startup.errors.services_json_error.title".localized, diff --git a/phpmon/Domain/Integrations/Homebrew/Brew.swift b/phpmon/Domain/Integrations/Homebrew/Brew.swift new file mode 100644 index 0000000..0b46c3c --- /dev/null +++ b/phpmon/Domain/Integrations/Homebrew/Brew.swift @@ -0,0 +1,31 @@ +// +// Homebrew.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 17/03/2023. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class Brew { + static let shared = Brew() + + /// The version of Homebrew that was detected. + var version: VersionNumber? + + public func determineVersion() async { + let output = await Shell.pipe("\(Paths.brew) --version") + self.version = try? VersionNumber.parse(output.out) + + if let version = version { + Log.info("The user has Homebrew \(version.text) installed.") + + if version.major < 4 { + Log.warn("Managing PHP versions is only supported with Homebrew 4 or newer!") + } + } else { + Log.warn("The Homebrew version could not be determined.") + } + } +} diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/BrewDiagnostics.swift similarity index 99% rename from phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift rename to phpmon/Domain/Integrations/Homebrew/BrewDiagnostics.swift index 3a31a1c..b842105 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/BrewDiagnostics.swift @@ -1,5 +1,5 @@ // -// AliasConflict.swift +// BrewDiagnostics.swift // PHP Monitor // // Created by Nico Verbruggen on 28/11/2021. @@ -8,7 +8,7 @@ import Foundation -class HomebrewDiagnostics { +class BrewDiagnostics { /** Determines the Homebrew taps the user has installed. */ diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index b82f28b..4bf5b76 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -38,12 +38,12 @@ extension MainMenu { _ = Preferences.shared // Determine install method - Log.info(HomebrewDiagnostics.customCaskInstalled + Log.info(BrewDiagnostics.customCaskInstalled ? "[BREW] The app has been installed via Homebrew Cask." : "[BREW] The app has been installed directly (optimal)." ) - Log.info(HomebrewDiagnostics.usesNginxFullFormula + Log.info(BrewDiagnostics.usesNginxFullFormula ? "[BREW] The app will be using the `nginx-full` formula." : "[BREW] The app will be using the `nginx` formula." ) @@ -56,11 +56,14 @@ extension MainMenu { // Validate the version (this will enforce which versions of PHP are supported) Valet.shared.validateVersion() + // Validate the Homebrew version (determines install/upgrade functionality) + await Brew.shared.determineVersion() + // Actually detect the PHP versions await PhpEnv.detectPhpVersions() // Check for an alias conflict - await HomebrewDiagnostics.checkForCaskConflict() + await BrewDiagnostics.checkForCaskConflict() // Update the icon updatePhpVersionInStatusBar() @@ -92,7 +95,7 @@ extension MainMenu { await Valet.shared.startPreloadingSites() // After preloading sites, check for PHP-FPM pool conflicts - await HomebrewDiagnostics.checkForValetMisconfiguration() + await BrewDiagnostics.checkForValetMisconfiguration() // Check if PHP-FPM is broken (should be fixed automatically if phpmon >= 6.0) await Valet.shared.notifyAboutBrokenPhpFpm() diff --git a/phpmon/Domain/SwiftUI/PhpManager/PhpManager.swift b/phpmon/Domain/SwiftUI/PhpManager/PhpManager.swift new file mode 100644 index 0000000..e4a3d12 --- /dev/null +++ b/phpmon/Domain/SwiftUI/PhpManager/PhpManager.swift @@ -0,0 +1,106 @@ +// +// PhpManager.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 17/03/2023. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation +import SwiftUI + +struct PhpInstallable { + var name: String + var installed: String? + var latest: String + var actions: [PhpInstallAction] + + var icon: String { + if actions.contains(.upgrade) { + return "arrow.up.square.fill" + } + if actions.contains(.remove) || installed != nil { + return "checkmark.square.fill" + } + return "square.dashed" + } + + var iconColor: Color { + if actions.contains(.upgrade) { + return .blue + } else if actions.contains(.remove) || installed != nil { + return .green + } + return Color.gray.opacity(0.3) + } +} + +struct ContentView: View { + @State var phpVersions: [PhpInstallable] + + var body: some View { + List(phpVersions, id: \.name) { version in + HStack { + Image(systemName: version.icon) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 16, height: 16) + .foregroundColor(version.iconColor) + .padding(.horizontal, 5) + VStack(alignment: .leading) { + Text(version.name).bold() + + if version.actions.contains(.upgrade) { + Text("\(version.installed!) installed, \(version.latest) available.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } else if version.installed != nil { + Text("Latest version is currently installed.").font(.system(size: 11)) + .foregroundColor(.gray) + } + + if version.actions.contains(.install) { + Text("This version can be installed.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + if version.actions.contains(.install) { + Button("Install") { + // handle install action here + } + } + if version.actions.contains(.upgrade) { + Button("Upgrade") { + // handle uninstall action here + } + } + if version.actions.contains(.remove) { + Button("Uninstall") { + // handle uninstall action here + } + } + if version.actions.isEmpty { + Button("Unavailable") { + // handle uninstall action here + }.disabled(true) + } + }.padding(.vertical, 10) + } + .listStyle(.bordered(alternatesRowBackgrounds: true)) + .frame(width: 400, height: 300) + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(phpVersions: [ + PhpInstallable(name: "PHP 8.2", installed: "8.2.3", latest: "8.2.3", actions: []), + PhpInstallable(name: "PHP 8.1", installed: "8.1.0", latest: "8.1.5", actions: [.upgrade, .remove]), + PhpInstallable(name: "PHP 8.0", installed: "8.0.14", latest: "8.0.14", actions: [.remove]), + PhpInstallable(name: "PHP 7.4", installed: nil, latest: "", actions: [.install]), + PhpInstallable(name: "PHP 7.3", installed: nil, latest: "", actions: [.install]) + ]) + } +}