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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
31
phpmon/Domain/Integrations/Homebrew/Brew.swift
Normal file
31
phpmon/Domain/Integrations/Homebrew/Brew.swift
Normal 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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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()
|
||||
|
||||
106
phpmon/Domain/SwiftUI/PhpManager/PhpManager.swift
Normal file
106
phpmon/Domain/SwiftUI/PhpManager/PhpManager.swift
Normal 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])
|
||||
])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user