diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index c0f5ff2..ba1f1af 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -152,10 +152,10 @@ 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 */; }; - C43BCD4429FBEF40001547BC /* HomebrewOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* HomebrewOperation.swift */; }; - C43BCD4529FBEF40001547BC /* HomebrewOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* HomebrewOperation.swift */; }; - C43BCD4629FBEF40001547BC /* HomebrewOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* HomebrewOperation.swift */; }; - C43BCD4729FBEF40001547BC /* HomebrewOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* HomebrewOperation.swift */; }; + C43BCD4429FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* InstallAndUpgradeCommand.swift */; }; + C43BCD4529FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* InstallAndUpgradeCommand.swift */; }; + C43BCD4629FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* InstallAndUpgradeCommand.swift */; }; + C43BCD4729FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43BCD4329FBEF40001547BC /* InstallAndUpgradeCommand.swift */; }; C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; }; C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; }; C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; }; @@ -629,10 +629,6 @@ C4B79EBD29CA38DB00A483EE /* BrewCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EBB29CA38DB00A483EE /* BrewCommand.swift */; }; C4B79EBE29CA38DB00A483EE /* BrewCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EBB29CA38DB00A483EE /* BrewCommand.swift */; }; C4B79EBF29CA38DB00A483EE /* BrewCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EBB29CA38DB00A483EE /* BrewCommand.swift */; }; - C4B79EC129CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */; }; - C4B79EC229CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */; }; - C4B79EC329CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */; }; - C4B79EC429CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */; }; C4B79EC629CA474200A483EE /* FakeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC529CA474200A483EE /* FakeCommand.swift */; }; C4B79EC729CA474200A483EE /* FakeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC529CA474200A483EE /* FakeCommand.swift */; }; C4B79EC829CA474200A483EE /* FakeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B79EC529CA474200A483EE /* FakeCommand.swift */; }; @@ -931,7 +927,7 @@ 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 = ""; }; - C43BCD4329FBEF40001547BC /* HomebrewOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewOperation.swift; sourceTree = ""; }; + C43BCD4329FBEF40001547BC /* InstallAndUpgradeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallAndUpgradeCommand.swift; sourceTree = ""; }; C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigChecker.swift; sourceTree = ""; }; C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = ""; }; C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = ""; }; @@ -1021,7 +1017,6 @@ C4B6091C2853AB9700C95265 /* ServicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = ""; }; C4B79EB529CA387F00A483EE /* BrewFormulaeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewFormulaeHandler.swift; sourceTree = ""; }; C4B79EBB29CA38DB00A483EE /* BrewCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewCommand.swift; sourceTree = ""; }; - C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallPhpVersionCommand.swift; sourceTree = ""; }; C4B79EC529CA474200A483EE /* FakeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeCommand.swift; sourceTree = ""; }; C4B79ECA29CA475900A483EE /* RemovePhpVersionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemovePhpVersionCommand.swift; sourceTree = ""; }; C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = ""; }; @@ -1786,9 +1781,8 @@ isa = PBXGroup; children = ( C4B79EBB29CA38DB00A483EE /* BrewCommand.swift */, - C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */, + C43BCD4329FBEF40001547BC /* InstallAndUpgradeCommand.swift */, C4B79ECA29CA475900A483EE /* RemovePhpVersionCommand.swift */, - C43BCD4329FBEF40001547BC /* HomebrewOperation.swift */, ); path = Commands; sourceTree = ""; @@ -2372,7 +2366,6 @@ C46EBC4A28DB966A007ACC74 /* TestableShell.swift in Sources */, C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */, 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */, - C4B79EC129CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */, C4C0E8E727F88B41002D32A9 /* DomainScanner.swift in Sources */, C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, @@ -2462,7 +2455,7 @@ C4B79ECB29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */, C40D725F2A018AE30054A067 /* BrewFormulaUI.swift in Sources */, C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */, - C43BCD4429FBEF40001547BC /* HomebrewOperation.swift in Sources */, + C43BCD4429FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */, C4E2E84A28FC1E70003B070C /* DataExtension.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, C42337A3281F19F000459A48 /* Xdebug.swift in Sources */, @@ -2528,7 +2521,6 @@ C471E85428F9BB650021E251 /* StatusMenu.swift in Sources */, C471E85528F9BB650021E251 /* StatusMenu+Items.swift in Sources */, C471E85628F9BB650021E251 /* DomainListCellProtocol.swift in Sources */, - C4B79EC329CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */, C4D36617291160A1006BD146 /* WIP.swift in Sources */, C471E85728F9BB650021E251 /* DomainListTLSCell.swift in Sources */, C471E85828F9BB650021E251 /* DomainListNameCell.swift in Sources */, @@ -2640,7 +2632,7 @@ C45B914B295607F400F4EC78 /* Service.swift in Sources */, C471E7D928F9BA8F0021E251 /* TestableShell.swift in Sources */, C471E81428F9BAE80021E251 /* NSWindowExtension.swift in Sources */, - C43BCD4629FBEF40001547BC /* HomebrewOperation.swift in Sources */, + C43BCD4629FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */, C471E7D328F9BA8F0021E251 /* ActiveShell.swift in Sources */, C4B79EC829CA474200A483EE /* FakeCommand.swift in Sources */, C471E7DE28F9BAA30021E251 /* CommandProtocol.swift in Sources */, @@ -2828,7 +2820,7 @@ C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */, C471E82828F9BB310021E251 /* BrewDiagnostics.swift in Sources */, C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */, - C43BCD4729FBEF40001547BC /* HomebrewOperation.swift in Sources */, + C43BCD4729FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */, C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */, C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */, C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */, @@ -2852,7 +2844,6 @@ C4AFC4B129C4F32F00BF4E0D /* BrewFormula.swift in Sources */, C471E81F28F9BB290021E251 /* NginxConfigurationFile.swift in Sources */, C471E7BF28F9B90F0021E251 /* StartupTest.swift in Sources */, - C4B79EC429CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */, C4D3661D291173EA006BD146 /* DictionaryExtension.swift in Sources */, C471E80D28F9BAE80021E251 /* ArrayExtension.swift in Sources */, C471E7CD28F9BA600021E251 /* ShellProtocol.swift in Sources */, @@ -2911,7 +2902,6 @@ C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */, C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */, C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */, - C4B79EC229CA473000A483EE /* InstallPhpVersionCommand.swift in Sources */, C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewDecodable.swift in Sources */, C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, @@ -3062,7 +3052,7 @@ C4D36602291132B7006BD146 /* ValetScanners.swift in Sources */, C40934AB298EEDA900D25014 /* CaskFileParserTest.swift in Sources */, C436B39E29F3C42500B6A64E /* PreferencesTabs.swift in Sources */, - C43BCD4529FBEF40001547BC /* HomebrewOperation.swift in Sources */, + C43BCD4529FBEF40001547BC /* InstallAndUpgradeCommand.swift in Sources */, C4551657297AED18009B8466 /* ValetRcTest.swift in Sources */, C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */, C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */, diff --git a/phpmon/Domain/Integrations/Homebrew/Brew.swift b/phpmon/Domain/Integrations/Homebrew/Brew.swift index 285426d..5d94ac8 100644 --- a/phpmon/Domain/Integrations/Homebrew/Brew.swift +++ b/phpmon/Domain/Integrations/Homebrew/Brew.swift @@ -10,6 +10,12 @@ import Foundation class BrewFormulaeObservable: ObservableObject { @Published var phpVersions: [BrewFormula] = [] + + var upgradeable: [BrewFormula] { + return phpVersions.filter { formula in + formula.hasUpgrade + } + } } class Brew { diff --git a/phpmon/Domain/Integrations/Homebrew/BrewFormula.swift b/phpmon/Domain/Integrations/Homebrew/BrewFormula.swift index c50483c..c6076a9 100644 --- a/phpmon/Domain/Integrations/Homebrew/BrewFormula.swift +++ b/phpmon/Domain/Integrations/Homebrew/BrewFormula.swift @@ -31,6 +31,7 @@ struct BrewFormula { return upgradeVersion != nil } + /// The associated Homebrew folder with this PHP formula. var homebrewFolder: String { let resolved = name .replacingOccurrences(of: "shivammathur/php/", with: "") @@ -39,6 +40,7 @@ struct BrewFormula { return "\(Paths.optPath)/\(resolved)/bin" } + /// The short version associated with this formula, if installed. var shortVersion: String? { guard let version = self.installedVersion else { return nil @@ -47,6 +49,10 @@ struct BrewFormula { return VersionNumber.make(from: version)?.short ?? nil } + /** + * Determines if this PHP installation is healthy. + * Uses the cached installation health check as basis. + */ public func isHealthy() -> Bool? { guard let shortVersion = self.shortVersion else { return nil diff --git a/phpmon/Domain/Integrations/Homebrew/Commands/HomebrewOperation.swift b/phpmon/Domain/Integrations/Homebrew/Commands/InstallAndUpgradeCommand.swift similarity index 99% rename from phpmon/Domain/Integrations/Homebrew/Commands/HomebrewOperation.swift rename to phpmon/Domain/Integrations/Homebrew/Commands/InstallAndUpgradeCommand.swift index 2535d1a..334d2ad 100644 --- a/phpmon/Domain/Integrations/Homebrew/Commands/HomebrewOperation.swift +++ b/phpmon/Domain/Integrations/Homebrew/Commands/InstallAndUpgradeCommand.swift @@ -8,7 +8,7 @@ import Foundation -class HomebrewOperation: BrewCommand { +class InstallAndUpgradeCommand: BrewCommand { let title: String let installing: [BrewFormula] diff --git a/phpmon/Domain/Integrations/Homebrew/Commands/InstallPhpVersionCommand.swift b/phpmon/Domain/Integrations/Homebrew/Commands/InstallPhpVersionCommand.swift deleted file mode 100644 index a5df34c..0000000 --- a/phpmon/Domain/Integrations/Homebrew/Commands/InstallPhpVersionCommand.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// InstallPhpVersionCommand.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 21/03/2023. -// Copyright © 2023 Nico Verbruggen. All rights reserved. -// - -import Foundation - -class InstallPhpVersionCommand: BrewCommand { - let formula: String - let version: String - - init(formula: String) { - self.version = formula - .replacingOccurrences(of: "php@", with: "") - .replacingOccurrences(of: "shivammathur/php/", with: "") - self.formula = formula - } - - func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws { - let progressTitle = "Running Homebrew operations..." - - onProgress(.create( - value: 0.2, - title: progressTitle, - description: "Please wait while Homebrew installs PHP \(version)..." - )) - - if formula.contains("shivammathur") && !BrewDiagnostics.installedTaps.contains("shivammathur/php") { - await Shell.quiet("brew tap shivammathur/php") - } - - let command = """ - export HOMEBREW_NO_INSTALL_UPGRADE=true; \ - export HOMEBREW_NO_INSTALL_CLEANUP=true; \ - \(Paths.brew) install \(formula) --force - """ - - // Keep track of the current PHP version prior to executing any operations - let phpGuard = PhpGuard() - - // Try to fix permissions - do { - try await BrewPermissionFixer().fixPermissions() - } catch { - throw BrewCommandError(error: "There was an issue fixing permissions.") - } - - let (process, _) = try! await Shell.attach( - command, - didReceiveOutput: { text, _ in - if !text.isEmpty { - Log.perf(text) - } - - if let (number, text) = self.reportInstallationProgress(text) { - onProgress(.create(value: number, title: progressTitle, description: text)) - } - }, - withTimeout: .minutes(5) - ) - - if process.terminationStatus <= 0 { - // Reload and restart PHP versions - onProgress(.create(value: 0.95, title: progressTitle, description: "Reloading PHP versions...")) - - // Check which version of PHP are now installed - await PhpEnv.detectPhpVersions() - - // Keep track of the currently installed version - await MainMenu.shared.refreshActiveInstallation() - - // If a PHP version was active prior to running the operations, attempt to restore it - if let version = phpGuard.currentVersion { - await MainMenu.shared.switchToAnyPhpVersion(version) - } - - // Let the UI know that the installation has been completed - onProgress(.create(value: 1, title: progressTitle, description: "The installation has succeeded.")) - } else { - throw BrewCommandError(error: "The command failed to run correctly.") - } - } -} diff --git a/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeView.swift b/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeView.swift index ac13164..0603e84 100644 --- a/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeView.swift +++ b/phpmon/Domain/SwiftUI/PhpManager/PhpFormulaeView.swift @@ -96,9 +96,12 @@ struct PhpFormulaeView: View { .foregroundColor(.gray) .font(.system(size: 11)) - Button("phpman.has_updates.button".localizedForSwiftUI, action: {}) - .focusable(false) - .disabled(self.status.busy) + Button("phpman.has_updates.button".localizedForSwiftUI, action: { + Task { await self.upgradeAll(self.formulae.upgradeable) } + + }) + .focusable(false) + .disabled(self.status.busy) } .padding(10) } else { @@ -134,6 +137,7 @@ struct PhpFormulaeView: View { VStack(alignment: .leading) { Text(formula.displayName).bold() + /* if formula.isHealthy() == nil { Text("Unknown health") } else { @@ -141,6 +145,7 @@ struct PhpFormulaeView: View { } Text(formula.homebrewFolder) + */ if formula.isInstalled && formula.hasUpgrade { Text("\(formula.installedVersion!) installed, \(formula.upgradeVersion!) available.") @@ -177,9 +182,7 @@ struct PhpFormulaeView: View { }.frame(width: 600, height: 600) } - public func install(_ formula: BrewFormula) async { - let command = InstallPhpVersionCommand(formula: formula.name) - + public func runCommand(_ command: InstallAndUpgradeCommand) async { do { self.setBusyStatus(true) try await command.execute { progress in @@ -198,14 +201,28 @@ struct PhpFormulaeView: View { self.setBusyStatus(false) self.presentErrorAlert( title: "phpman.failures.install.title".localized, - description: "phpman.failures.install.desc".localized( - "brew install \(formula)" - ), + description: "phpman.failures.install.desc".localized, button: "generic.ok".localized ) } } + public func upgradeAll(_ formulae: [BrewFormula]) async { + await self.runCommand(InstallAndUpgradeCommand( + title: "Installing updates...", + upgrading: formulae, + installing: [] + )) + } + + public func install(_ formula: BrewFormula) async { + await self.runCommand(InstallAndUpgradeCommand( + title: "Installing \(formula.displayName)", + upgrading: [], + installing: [formula] + )) + } + public func confirmUninstall(_ formula: BrewFormula) async { // Disallow removal of the currently active versipn if formula.installedVersion == PhpEnv.shared.currentInstall?.version.text { diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index d7fbb63..df47edf 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -120,7 +120,7 @@ You may be asked for your password during the uninstallation process if file per "phpman.warnings.removal.button" = "Uninstall"; "phpman.failures.install.title" = "Installation failed!"; -"phpman.failures.install.desc" = "Unfortunately, the automatic installation failed. You can manually try to run this command: `%@` and find out what goes wrong. Remember to restart PHP Monitor (or press the refresh button) when this is done."; +"phpman.failures.install.desc" = "Unfortunately, the automatic installation failed. PHP Monitor can only do so much and it's always possible that installations can fail for some unknown reason. At this point I'd recommend checking out the README to find out how to manually install a given PHP version via the Terminal. Remember to restart PHP Monitor (or press the refresh button) when this is done."; "phpman.uninstall_prevented.title" = "You cannot uninstall the currently active version of PHP via PHP Monitor."; "phpman.uninstall_prevented.desc" = "In order to prevent issues with PHP Monitor and further crashes, it isn't possible to uninstall the currently linked version of PHP via this UI. You can switch versions and try again, or uninstall this version manually via the terminal.\n\nPlease note that PHP Monitor may crash if you uninstall the currently linked PHP version.";