1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-11-07 05:10:06 +01:00

Homebrew version validation, UI workshopping

This commit is contained in:
2023-03-17 17:40:30 +01:00
parent 78c24555f7
commit 9dae03a04e
12 changed files with 241 additions and 66 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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
)

View File

@@ -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

View File

@@ -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,

View File

@@ -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.")
}
}
}

View File

@@ -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.
*/

View File

@@ -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()

View File

@@ -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])
])
}
}