mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-04-07 03:20:09 +02:00
♻️ Even more refactoring
This commit is contained in:
@@ -15,6 +15,7 @@ public struct ContainerAccessMacro: MemberMacro {
|
||||
("shell", "ShellProtocol"),
|
||||
("filesystem", "FileSystemProtocol"),
|
||||
("command", "CommandProtocol"),
|
||||
("paths", "Paths"),
|
||||
("favorites", "Favorites"),
|
||||
("warningManager", "WarningManager")
|
||||
]
|
||||
|
||||
@@ -7,62 +7,56 @@
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class Actions {
|
||||
var container: Container
|
||||
|
||||
init(
|
||||
container: Container = App.shared.container,
|
||||
) {
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: - Services
|
||||
|
||||
public static func linkPhp() async {
|
||||
public func linkPhp() async {
|
||||
await brew("link php --overwrite --force")
|
||||
}
|
||||
|
||||
public static func restartPhpFpm() async {
|
||||
public func restartPhpFpm() async {
|
||||
await brew("services restart \(HomebrewFormulae.php)", sudo: HomebrewFormulae.php.elevated)
|
||||
}
|
||||
|
||||
public static func restartPhpFpm(version: String) async {
|
||||
public func restartPhpFpm(version: String) async {
|
||||
let formula = (version == PhpEnvironments.brewPhpAlias) ? "php" : "php@\(version)"
|
||||
await brew("services restart \(formula)", sudo: HomebrewFormulae.php.elevated)
|
||||
}
|
||||
|
||||
public static func restartNginx() async {
|
||||
public func restartNginx() async {
|
||||
await brew("services restart \(HomebrewFormulae.nginx)", sudo: HomebrewFormulae.nginx.elevated)
|
||||
}
|
||||
|
||||
public static func restartDnsMasq() async {
|
||||
public func restartDnsMasq() async {
|
||||
await brew("services restart \(HomebrewFormulae.dnsmasq)", sudo: HomebrewFormulae.dnsmasq.elevated)
|
||||
}
|
||||
|
||||
public static func stopValetServices() async {
|
||||
public func stopValetServices() async {
|
||||
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 {
|
||||
public func fixHomebrewPermissions() throws {
|
||||
var servicesCommands = [
|
||||
"\(Paths.brew) services stop \(HomebrewFormulae.nginx)",
|
||||
"\(Paths.brew) services stop \(HomebrewFormulae.dnsmasq)"
|
||||
"\(paths.brew) services stop \(HomebrewFormulae.nginx)",
|
||||
"\(paths.brew) services stop \(HomebrewFormulae.dnsmasq)"
|
||||
]
|
||||
|
||||
var cellarCommands = [
|
||||
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(HomebrewFormulae.nginx)",
|
||||
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(HomebrewFormulae.dnsmasq)"
|
||||
"chown -R \(paths.whoami):admin \(paths.cellarPath)/\(HomebrewFormulae.nginx)",
|
||||
"chown -R \(paths.whoami):admin \(paths.cellarPath)/\(HomebrewFormulae.dnsmasq)"
|
||||
]
|
||||
|
||||
PhpEnvironments.shared.availablePhpVersions.forEach { version in
|
||||
let formula = version == PhpEnvironments.brewPhpAlias
|
||||
? "php"
|
||||
: "php@\(version)"
|
||||
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
||||
cellarCommands.append("chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(formula)")
|
||||
servicesCommands.append("\(paths.brew) services stop \(formula)")
|
||||
cellarCommands.append("chown -R \(paths.whoami):admin \(paths.cellarPath)/\(formula)")
|
||||
}
|
||||
|
||||
let script =
|
||||
@@ -84,27 +78,27 @@ class Actions {
|
||||
|
||||
// MARK: - Finding Config Files
|
||||
|
||||
public static func openGenericPhpConfigFolder() {
|
||||
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php")]
|
||||
public func openGenericPhpConfigFolder() {
|
||||
let files = [NSURL(fileURLWithPath: "\(paths.etcPath)/php")]
|
||||
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
||||
}
|
||||
|
||||
public static func openPhpConfigFolder(version: String) {
|
||||
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php/\(version)/php.ini")]
|
||||
public func openPhpConfigFolder(version: String) {
|
||||
let files = [NSURL(fileURLWithPath: "\(paths.etcPath)/php/\(version)/php.ini")]
|
||||
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
||||
}
|
||||
|
||||
public static func openGlobalComposerFolder() {
|
||||
public func openGlobalComposerFolder() {
|
||||
let file = URL(string: "file://~/.composer/composer.json".replacingTildeWithHomeDirectory)!
|
||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||
}
|
||||
|
||||
public static func openValetConfigFolder() {
|
||||
public func openValetConfigFolder() {
|
||||
let file = URL(string: "file://~/.config/valet".replacingTildeWithHomeDirectory)!
|
||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||
}
|
||||
|
||||
public static func openPhpMonitorConfigFile() {
|
||||
public func openPhpMonitorConfigFile() {
|
||||
let file = URL(string: "file://~/.config/phpmon".replacingTildeWithHomeDirectory)!
|
||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||
}
|
||||
@@ -112,10 +106,10 @@ class Actions {
|
||||
// MARK: - Other Actions
|
||||
|
||||
public func createTempPhpInfoFile() async -> URL {
|
||||
try! container.filesystem.writeAtomicallyToFile("/tmp/phpmon_phpinfo.php", content: "<?php phpinfo();")
|
||||
try! filesystem.writeAtomicallyToFile("/tmp/phpmon_phpinfo.php", content: "<?php phpinfo();")
|
||||
|
||||
// Tell php-cgi to run the PHP and output as an .html file
|
||||
await container.shell.quiet("\(Paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
|
||||
await shell.quiet("\(paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
|
||||
|
||||
return URL(string: "file:///private/tmp/phpmon_phpinfo.html")!
|
||||
}
|
||||
@@ -134,7 +128,7 @@ class Actions {
|
||||
If this does not solve the issue, the user may need to install additional
|
||||
extensions and/or run `composer global update`.
|
||||
*/
|
||||
public static func fixMyValet() async {
|
||||
public func fixMyValet() async {
|
||||
await InternalSwitcher().performSwitch(to: PhpEnvironments.brewPhpAlias)
|
||||
await brew("services restart \(HomebrewFormulae.dnsmasq)", sudo: HomebrewFormulae.dnsmasq.elevated)
|
||||
await brew("services restart \(HomebrewFormulae.php)", sudo: HomebrewFormulae.php.elevated)
|
||||
|
||||
@@ -13,8 +13,12 @@ import Foundation
|
||||
/**
|
||||
Runs a `brew` command. Can run as superuser.
|
||||
*/
|
||||
func brew(_ command: String, sudo: Bool = false, shell: ShellProtocol = App.shared.container.shell) async {
|
||||
await shell.quiet("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)")
|
||||
func brew(
|
||||
_ command: String,
|
||||
sudo: Bool = false,
|
||||
container: Container = App.shared.container,
|
||||
) async {
|
||||
await container.shell.quiet("\(sudo ? "sudo " : "")" + "\(container.paths.brew) \(command)")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,7 +29,8 @@ func sed(
|
||||
original: String,
|
||||
replacement: String,
|
||||
filesystem: FileSystemProtocol = App.shared.container.filesystem,
|
||||
shell: ShellProtocol = App.shared.container.shell
|
||||
shell: ShellProtocol = App.shared.container.shell,
|
||||
paths: Paths = App.shared.container.paths,
|
||||
) async {
|
||||
// Escape slashes (or `sed` won't work)
|
||||
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
||||
@@ -33,8 +38,8 @@ func sed(
|
||||
|
||||
// Check if gsed exists; it is able to follow symlinks,
|
||||
// which we want to do to toggle the extension
|
||||
if filesystem.fileExists("\(Paths.binPath)/gsed") {
|
||||
await shell.quiet("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
if filesystem.fileExists("\(paths.binPath)/gsed") {
|
||||
await shell.quiet("\(paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
} else {
|
||||
await shell.quiet("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
}
|
||||
|
||||
@@ -12,21 +12,19 @@ import Foundation
|
||||
The path to the Homebrew directory and the user's name are fetched only once, at boot.
|
||||
*/
|
||||
public class Paths {
|
||||
|
||||
public static let shared = Paths()
|
||||
|
||||
internal let container: Container
|
||||
internal var baseDir: Paths.HomebrewDir
|
||||
private var userName: String
|
||||
private var preferredShell: String
|
||||
|
||||
init() {
|
||||
init(container: Container = App.shared.container) {
|
||||
// Assume the default directory is correct
|
||||
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
||||
|
||||
// Ensure that if a different location is used, it takes precendence
|
||||
if baseDir == .usr
|
||||
&& App.shared.container.filesystem.directoryExists("/usr/local/homebrew")
|
||||
&& !App.shared.container.filesystem.directoryExists("/usr/local/Cellar") {
|
||||
&& container.filesystem.directoryExists("/usr/local/homebrew")
|
||||
&& !container.filesystem.directoryExists("/usr/local/Cellar") {
|
||||
Log.warn("Using /usr/local/homebrew as base directory!")
|
||||
baseDir = .usr_hb
|
||||
}
|
||||
@@ -38,6 +36,8 @@ public class Paths {
|
||||
Log.info("The current username is `\(userName)`.")
|
||||
Log.info("The user's shell is `\(preferredShell)`.")
|
||||
}
|
||||
|
||||
self.container = container
|
||||
}
|
||||
|
||||
public func detectBinaryPaths() {
|
||||
@@ -46,76 +46,76 @@ public class Paths {
|
||||
|
||||
// - MARK: Binaries
|
||||
|
||||
public static var valet: String {
|
||||
public var valet: String {
|
||||
return "\(binPath)/valet"
|
||||
}
|
||||
|
||||
public static var brew: String {
|
||||
public var brew: String {
|
||||
return "\(binPath)/brew"
|
||||
}
|
||||
|
||||
public static var php: String {
|
||||
public var php: String {
|
||||
return "\(binPath)/php"
|
||||
}
|
||||
|
||||
public static var phpConfig: String {
|
||||
public var phpConfig: String {
|
||||
return "\(binPath)/php-config"
|
||||
}
|
||||
|
||||
// - MARK: Detected Binaries
|
||||
|
||||
/** The path to the Composer binary. Can be in multiple locations, so is detected instead. */
|
||||
public static var composer: String?
|
||||
public var composer: String?
|
||||
|
||||
// - MARK: Paths
|
||||
|
||||
public static var whoami: String {
|
||||
return shared.userName
|
||||
public var whoami: String {
|
||||
return userName
|
||||
}
|
||||
|
||||
public static var homePath: String {
|
||||
if App.shared.container.filesystem is RealFileSystem {
|
||||
public var homePath: String {
|
||||
if container.filesystem is RealFileSystem {
|
||||
return NSHomeDirectory()
|
||||
}
|
||||
|
||||
if App.shared.container.filesystem is TestableFileSystem {
|
||||
let fs = App.shared.container.filesystem as! TestableFileSystem
|
||||
if container.filesystem is TestableFileSystem {
|
||||
let fs = container.filesystem as! TestableFileSystem
|
||||
return fs.homeDirectory
|
||||
}
|
||||
|
||||
fatalError("A valid FileSystem must be allowed to return the home path")
|
||||
}
|
||||
|
||||
public static var cellarPath: String {
|
||||
return "\(shared.baseDir.rawValue)/Cellar"
|
||||
public var cellarPath: String {
|
||||
return "\(baseDir.rawValue)/Cellar"
|
||||
}
|
||||
|
||||
public static var binPath: String {
|
||||
return "\(shared.baseDir.rawValue)/bin"
|
||||
public var binPath: String {
|
||||
return "\(baseDir.rawValue)/bin"
|
||||
}
|
||||
|
||||
public static var optPath: String {
|
||||
return "\(shared.baseDir.rawValue)/opt"
|
||||
public var optPath: String {
|
||||
return "\(baseDir.rawValue)/opt"
|
||||
}
|
||||
|
||||
public static var etcPath: String {
|
||||
return "\(shared.baseDir.rawValue)/etc"
|
||||
public var etcPath: String {
|
||||
return "\(baseDir.rawValue)/etc"
|
||||
}
|
||||
|
||||
public static var tapPath: String {
|
||||
if shared.baseDir == .usr {
|
||||
return "\(shared.baseDir.rawValue)/homebrew/Library/Taps"
|
||||
public var tapPath: String {
|
||||
if baseDir == .usr {
|
||||
return "\(baseDir.rawValue)/homebrew/Library/Taps"
|
||||
}
|
||||
|
||||
return "\(shared.baseDir.rawValue)/Library/Taps"
|
||||
return "\(baseDir.rawValue)/Library/Taps"
|
||||
}
|
||||
|
||||
public static var caskroomPath: String {
|
||||
return "\(shared.baseDir.rawValue)/Caskroom/phpmon"
|
||||
public var caskroomPath: String {
|
||||
return "\(baseDir.rawValue)/Caskroom/phpmon"
|
||||
}
|
||||
|
||||
public static var shell: String {
|
||||
return shared.preferredShell
|
||||
public var shell: String {
|
||||
return preferredShell
|
||||
}
|
||||
|
||||
// MARK: - Flexible Binaries
|
||||
@@ -123,14 +123,14 @@ public class Paths {
|
||||
// (PHP Monitor will not use the user's own PATH)
|
||||
|
||||
private func detectComposerBinary() {
|
||||
if App.shared.container.filesystem.fileExists("/usr/local/bin/composer") {
|
||||
Paths.composer = "/usr/local/bin/composer"
|
||||
} else if App.shared.container.filesystem.fileExists("/opt/homebrew/bin/composer") {
|
||||
Paths.composer = "/opt/homebrew/bin/composer"
|
||||
} else if App.shared.container.filesystem.fileExists("/usr/local/homebrew/bin/composer") {
|
||||
Paths.composer = "/usr/local/homebrew/bin/composer"
|
||||
if container.filesystem.fileExists("/usr/local/bin/composer") {
|
||||
composer = "/usr/local/bin/composer"
|
||||
} else if container.filesystem.fileExists("/opt/homebrew/bin/composer") {
|
||||
composer = "/opt/homebrew/bin/composer"
|
||||
} else if container.filesystem.fileExists("/usr/local/homebrew/bin/composer") {
|
||||
composer = "/usr/local/homebrew/bin/composer"
|
||||
} else {
|
||||
Paths.composer = nil
|
||||
composer = nil
|
||||
Log.warn("Composer was not found.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import Foundation
|
||||
|
||||
extension String {
|
||||
var replacingTildeWithHomeDirectory: String {
|
||||
return self.replacingOccurrences(of: "~", with: Paths.homePath)
|
||||
return self.replacingOccurrences(of: "~", with: App.shared.container.paths.homePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,9 @@ class ActivePhpInstallation {
|
||||
// MARK: - Initializer
|
||||
|
||||
public static func load() -> ActivePhpInstallation? {
|
||||
if !App.shared.container.filesystem.fileExists(Paths.phpConfig) {
|
||||
let container = App.shared.container
|
||||
|
||||
if !container.filesystem.fileExists(container.paths.phpConfig) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -75,7 +77,7 @@ class ActivePhpInstallation {
|
||||
)
|
||||
|
||||
let paths = shell
|
||||
.sync("\(Paths.php) --ini | grep -E -o '(/[^ ]+\\.ini)'").out
|
||||
.sync("\(container.paths.php) --ini | grep -E -o '(/[^ ]+\\.ini)'").out
|
||||
.split(separator: "\n")
|
||||
.map { String($0) }
|
||||
|
||||
@@ -92,7 +94,7 @@ class ActivePhpInstallation {
|
||||
_or_ if the output contains the word "Warning" or "Error". In normal situations this should not be the case.
|
||||
*/
|
||||
private func determineVersion() throws {
|
||||
let output = command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
||||
let output = command.execute(path: container.paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
||||
|
||||
self.hasErrorState = (output == "" || output.contains("Warning") || output.contains("Error"))
|
||||
|
||||
@@ -115,7 +117,7 @@ class ActivePhpInstallation {
|
||||
- Parameter key: The key of the `ini` value that needs to be retrieved. For example, you can use `memory_limit`.
|
||||
*/
|
||||
private func getByteCount(key: String) -> String {
|
||||
let value = command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"], trimNewlines: false)
|
||||
let value = command.execute(path: container.paths.php, arguments: ["-r", "echo ini_get('\(key)');"], trimNewlines: false)
|
||||
|
||||
// Check if the value is unlimited
|
||||
if value == "-1" {
|
||||
|
||||
@@ -32,7 +32,7 @@ class PhpEnvironments {
|
||||
Determine which PHP version the `php` formula is aliased to.
|
||||
*/
|
||||
@MainActor func determinePhpAlias() async {
|
||||
let brewPhpAlias = await container.shell.pipe("\(Paths.brew) info php --json").out
|
||||
let brewPhpAlias = await container.shell.pipe("\(container.paths.brew) info php --json").out
|
||||
|
||||
self.homebrewPackage = try! JSONDecoder().decode(
|
||||
[HomebrewPackage].self,
|
||||
@@ -43,7 +43,7 @@ class PhpEnvironments {
|
||||
Log.info("[BREW] On your system, the `php` formula means version \(homebrewPackage.version).")
|
||||
|
||||
// Check if that version actually corresponds to an older version
|
||||
let phpConfigExecutablePath = "\(Paths.optPath)/php/bin/php-config"
|
||||
let phpConfigExecutablePath = "\(container.paths.optPath)/php/bin/php-config"
|
||||
if container.filesystem.fileExists(phpConfigExecutablePath) {
|
||||
let longVersionString = container.command.execute(
|
||||
path: phpConfigExecutablePath,
|
||||
@@ -164,7 +164,7 @@ class PhpEnvironments {
|
||||
Returns a `Set<String>` of installations that are considered valid.
|
||||
*/
|
||||
public func detectPhpVersions() async -> Set<String> {
|
||||
let files = await container.shell.pipe("ls \(Paths.optPath) | grep php@").out
|
||||
let files = await container.shell.pipe("ls \(container.paths.optPath) | grep php@").out
|
||||
|
||||
let versions = await extractPhpVersions(from: files.components(separatedBy: "\n"))
|
||||
|
||||
@@ -184,7 +184,7 @@ class PhpEnvironments {
|
||||
let phpAlias = homebrewPackage.version
|
||||
|
||||
// Avoid inserting a duplicate
|
||||
if !supportedVersions.contains(phpAlias) && container.filesystem.fileExists("\(Paths.optPath)/php/bin/php") {
|
||||
if !supportedVersions.contains(phpAlias) && container.filesystem.fileExists("\(container.paths.optPath)/php/bin/php") {
|
||||
let phpAliasInstall = PhpInstallation(phpAlias)
|
||||
// Before inserting, ensure that the actual output matches the alias
|
||||
// if that isn't the case, our formula remains out-of-date
|
||||
@@ -237,7 +237,7 @@ class PhpEnvironments {
|
||||
// is supported and where the binary exists (avoids broken installs)
|
||||
if !output.contains(version)
|
||||
&& supported.contains(version)
|
||||
&& (checkBinaries ? container.filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true) {
|
||||
&& (checkBinaries ? container.filesystem.fileExists("\(container.paths.optPath)/php@\(version)/bin/php") : true) {
|
||||
output.insert(version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,22 +11,21 @@ import Foundation
|
||||
class PhpHelper {
|
||||
static let keyPhrase = "This file was automatically generated by PHP Monitor."
|
||||
|
||||
static var container: Container {
|
||||
return App.shared.container
|
||||
}
|
||||
|
||||
public static func generate(for version: String) async {
|
||||
public static func generate(
|
||||
for version: String,
|
||||
container: Container = App.shared.container
|
||||
) async {
|
||||
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
||||
let dotless = version.replacingOccurrences(of: ".", with: "")
|
||||
|
||||
// Determine the dotless name for this PHP version
|
||||
let destination = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||
let destination = "\(container.paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||
|
||||
// Check if the ~/.config/phpmon/bin directory is in the PATH
|
||||
let inPath = container.shell.PATH.contains("\(Paths.homePath)/.config/phpmon/bin")
|
||||
let inPath = container.shell.PATH.contains("\(container.paths.homePath)/.config/phpmon/bin")
|
||||
|
||||
// Check if we can create symlinks (`/usr/local/bin` must be writable)
|
||||
let canWriteSymlinks = App.shared.container.filesystem.isWriteableFile("/usr/local/bin/")
|
||||
let canWriteSymlinks = container.filesystem.isWriteableFile("/usr/local/bin/")
|
||||
|
||||
Task { // Create the appropriate folders and check if the files exist
|
||||
do {
|
||||
@@ -49,11 +48,11 @@ class PhpHelper {
|
||||
}
|
||||
|
||||
// Let's follow the symlink to the PHP binary folder
|
||||
let path = URL(fileURLWithPath: "\(Paths.optPath)/php@\(version)/bin")
|
||||
let path = URL(fileURLWithPath: "\(container.paths.optPath)/php@\(version)/bin")
|
||||
.resolvingSymlinksInPath().path
|
||||
|
||||
// Check if the user uses Fish
|
||||
let script = Paths.shell.contains("/fish")
|
||||
let script = container.paths.shell.contains("/fish")
|
||||
? fishScript(path, keyPhrase, version, dotless)
|
||||
: zshScript(path, keyPhrase, version, dotless)
|
||||
|
||||
@@ -105,10 +104,11 @@ class PhpHelper {
|
||||
_ path: String,
|
||||
_ keyPhrase: String,
|
||||
_ version: String,
|
||||
_ dotless: String
|
||||
_ dotless: String,
|
||||
container: Container = App.shared.container
|
||||
) -> String {
|
||||
return """
|
||||
#!\(Paths.binPath)/fish
|
||||
#!\(container.paths.binPath)/fish
|
||||
# \(keyPhrase)
|
||||
# It reflects the location of PHP \(version)'s binaries on your system.
|
||||
# Usage: . pm\(dotless)
|
||||
@@ -117,11 +117,14 @@ class PhpHelper {
|
||||
"""
|
||||
}
|
||||
|
||||
private static func createSymlink(_ dotless: String) async {
|
||||
let source = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||
private static func createSymlink(
|
||||
_ dotless: String,
|
||||
container: Container = App.shared.container
|
||||
) async {
|
||||
let source = "\(container.paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||
let destination = "/usr/local/bin/pm\(dotless)"
|
||||
|
||||
if !App.shared.container.filesystem.fileExists(destination) {
|
||||
if !container.filesystem.fileExists(destination) {
|
||||
Log.info("Creating new symlink: \(destination)")
|
||||
await container.shell.quiet("ln -s \(source) \(destination)")
|
||||
return
|
||||
|
||||
@@ -31,8 +31,11 @@ class PhpConfigurationFile: CreatedFromFile {
|
||||
var lines: [String]
|
||||
|
||||
/** Resolves a PHP configuration file (.ini) */
|
||||
static func from(filePath: String) -> Self? {
|
||||
let path = filePath.replacingOccurrences(of: "~", with: Paths.homePath)
|
||||
static func from(
|
||||
filePath: String,
|
||||
container: Container = App.shared.container
|
||||
) -> Self? {
|
||||
let path = filePath.replacingOccurrences(of: "~", with: container.paths.homePath)
|
||||
|
||||
do {
|
||||
let fileContents = try App.shared.container.filesystem.getStringFromFile(path)
|
||||
|
||||
@@ -43,8 +43,8 @@ class PhpInstallation {
|
||||
init(container: Container = App.shared.container, _ version: String) {
|
||||
self.container = container
|
||||
|
||||
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config",
|
||||
phpExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php"
|
||||
let phpConfigExecutablePath = "\(container.paths.optPath)/php@\(version)/bin/php-config",
|
||||
phpExecutablePath = "\(container.paths.optPath)/php@\(version)/bin/php"
|
||||
|
||||
versionNumber = VersionNumber.make(from: version)!
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ extension InternalSwitcher {
|
||||
// MARK: - Corrections
|
||||
|
||||
public func disableDefaultPhpFpmPool(_ version: String) async -> FixApplied {
|
||||
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||
let pool = "\(App.shared.container.paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||
|
||||
if App.shared.container.filesystem.fileExists(pool) {
|
||||
Log.info("A default `www.conf` file was found in the php-fpm.d directory for PHP \(version).")
|
||||
let existing = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||
let new = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf.disabled-by-phpmon"
|
||||
let existing = "\(App.shared.container.paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||
let new = "\(App.shared.container.paths.etcPath)/php/\(version)/php-fpm.d/www.conf.disabled-by-phpmon"
|
||||
do {
|
||||
if App.shared.container.filesystem.fileExists(new) {
|
||||
Log.info("A moved `www.conf.disabled-by-phpmon` file was found for PHP \(version), "
|
||||
@@ -59,7 +59,7 @@ extension InternalSwitcher {
|
||||
|
||||
// For each of the files, attempt to fix anything that is wrong
|
||||
let outcomes = files.map { file in
|
||||
let configFileExists = App.shared.container.filesystem.fileExists("\(Paths.etcPath)/php/\(version)/" + file.destination)
|
||||
let configFileExists = App.shared.container.filesystem.fileExists("\(App.shared.container.paths.etcPath)/php/\(version)/" + file.destination)
|
||||
|
||||
if configFileExists {
|
||||
return false
|
||||
@@ -79,7 +79,7 @@ extension InternalSwitcher {
|
||||
}
|
||||
|
||||
try App.shared.container.filesystem.writeAtomicallyToFile(
|
||||
"\(Paths.etcPath)/php/\(version)" + file.destination,
|
||||
"\(App.shared.container.paths.etcPath)/php/\(version)" + file.destination,
|
||||
content: contents
|
||||
)
|
||||
} catch {
|
||||
@@ -102,7 +102,7 @@ extension InternalSwitcher {
|
||||
destination: "/php-fpm.d/valet-fpm.conf",
|
||||
source: "/cli/stubs/etc-phpfpm-valet.conf",
|
||||
replacements: [
|
||||
"VALET_USER": Paths.whoami,
|
||||
"VALET_USER": App.shared.container.paths.whoami,
|
||||
"VALET_HOME_PATH": "~/.config/valet".replacingTildeWithHomeDirectory,
|
||||
"valet.sock": "valet\(version.replacingOccurrences(of: ".", with: "")).sock"
|
||||
],
|
||||
@@ -112,7 +112,7 @@ extension InternalSwitcher {
|
||||
destination: "/conf.d/error_log.ini",
|
||||
source: "/cli/stubs/etc-phpfpm-error_log.ini",
|
||||
replacements: [
|
||||
"VALET_USER": Paths.whoami,
|
||||
"VALET_USER": App.shared.container.paths.whoami,
|
||||
"VALET_HOME_PATH": "~/.config/valet".replacingTildeWithHomeDirectory
|
||||
],
|
||||
applies: { return true }
|
||||
|
||||
@@ -10,6 +10,6 @@ import Foundation
|
||||
|
||||
protocol CreatedFromFile {
|
||||
|
||||
static func from(filePath: String) -> Self?
|
||||
static func from(filePath: String, container: Container) -> Self?
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class RealShell: ShellProtocol {
|
||||
var completeCommand = ""
|
||||
|
||||
// Basic export (PATH)
|
||||
completeCommand += "export PATH=\(Paths.binPath):$PATH && "
|
||||
completeCommand += "export PATH=\(container.paths.binPath):$PATH && "
|
||||
|
||||
// Put additional exports (as defined by the user) in between
|
||||
if !self.exports.isEmpty {
|
||||
|
||||
@@ -10,6 +10,7 @@ class Container {
|
||||
var shell: ShellProtocol!
|
||||
var filesystem: FileSystemProtocol!
|
||||
var command: CommandProtocol!
|
||||
var paths: Paths!
|
||||
|
||||
var favorites: Favorites!
|
||||
var warningManager: WarningManager!
|
||||
@@ -20,6 +21,7 @@ class Container {
|
||||
self.shell = RealShell(container: self)
|
||||
self.filesystem = RealFileSystem(container: self)
|
||||
self.command = RealCommand()
|
||||
self.paths = Paths(container: self)
|
||||
|
||||
self.favorites = Favorites()
|
||||
self.warningManager = WarningManager(container: self)
|
||||
|
||||
@@ -23,12 +23,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
*/
|
||||
let state: App
|
||||
|
||||
/**
|
||||
The paths singleton that determines where Homebrew is installed,
|
||||
and where to look for binaries.
|
||||
*/
|
||||
let paths: Paths
|
||||
|
||||
/**
|
||||
The Valet singleton that determines all information
|
||||
about Valet and its current configuration.
|
||||
@@ -93,7 +87,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
Log.separator(as: .info)
|
||||
}
|
||||
|
||||
self.paths = Paths.shared
|
||||
self.valet = Valet.shared
|
||||
self.brew = Brew.shared
|
||||
super.init()
|
||||
|
||||
@@ -166,7 +166,7 @@ class AppUpdater {
|
||||
}
|
||||
|
||||
private func cleanupCaskroom() {
|
||||
let path = Paths.caskroomPath
|
||||
let path = App.shared.container.paths.caskroomPath
|
||||
|
||||
if App.shared.container.filesystem.directoryExists(path) {
|
||||
Log.info("Removing the Caskroom directory for PHP Monitor...")
|
||||
|
||||
@@ -48,7 +48,7 @@ class ValetServicesManager: ServicesManager {
|
||||
.map { $0.name }
|
||||
|
||||
let rootJson = await App.shared.container.shell
|
||||
.pipe("sudo \(Paths.brew) services info --all --json")
|
||||
.pipe("sudo \(App.shared.container.paths.brew) services info --all --json")
|
||||
.out.data(using: .utf8)!
|
||||
|
||||
return try! JSONDecoder()
|
||||
@@ -63,7 +63,7 @@ class ValetServicesManager: ServicesManager {
|
||||
.map { $0.name }
|
||||
|
||||
let normalJson = await App.shared.container.shell
|
||||
.pipe("\(Paths.brew) services info --all --json")
|
||||
.pipe("\(App.shared.container.paths.brew) services info --all --json")
|
||||
.out.data(using: .utf8)!
|
||||
|
||||
return try! JSONDecoder()
|
||||
|
||||
@@ -101,15 +101,15 @@ class Startup {
|
||||
// The Homebrew binary must exist.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: { return !App.shared.container.filesystem.fileExists(Paths.brew) },
|
||||
name: "`\(Paths.brew)` exists",
|
||||
command: { return !App.shared.container.filesystem.fileExists(App.shared.container.paths.brew) },
|
||||
name: "`\(App.shared.container.paths.brew)` exists",
|
||||
titleText: "alert.homebrew_missing.title".localized,
|
||||
subtitleText: "alert.homebrew_missing.subtitle".localized,
|
||||
descriptionText: "alert.homebrew_missing.info".localized(
|
||||
App.architecture
|
||||
.replacingOccurrences(of: "x86_64", with: "Intel")
|
||||
.replacingOccurrences(of: "arm64", with: "Apple Silicon"),
|
||||
Paths.brew
|
||||
App.shared.container.paths.brew
|
||||
),
|
||||
buttonText: "alert.homebrew_missing.quit".localized,
|
||||
requiresAppRestart: true
|
||||
@@ -119,12 +119,12 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return await !App.shared.container.shell.pipe("ls \(Paths.optPath) | grep php").out.contains("php")
|
||||
return await !App.shared.container.shell.pipe("ls \(App.shared.container.paths.optPath) | grep php").out.contains("php")
|
||||
},
|
||||
name: "`ls \(Paths.optPath) | grep php` returned php result",
|
||||
name: "`ls \(App.shared.container.paths.optPath) | grep php` returned php result",
|
||||
titleText: "startup.errors.php_opt.title".localized,
|
||||
subtitleText: "startup.errors.php_opt.subtitle".localized(
|
||||
Paths.optPath
|
||||
App.shared.container.paths.optPath
|
||||
),
|
||||
descriptionText: "startup.errors.php_opt.desc".localized
|
||||
)
|
||||
@@ -134,24 +134,24 @@ class Startup {
|
||||
// The PHP binary must exist.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: { return !App.shared.container.filesystem.fileExists(Paths.php) },
|
||||
name: "`\(Paths.php)` exists",
|
||||
command: { return !App.shared.container.filesystem.fileExists(App.shared.container.paths.php) },
|
||||
name: "`\(App.shared.container.paths.php)` exists",
|
||||
titleText: "startup.errors.php_binary.title".localized,
|
||||
subtitleText: "startup.errors.php_binary.subtitle".localized,
|
||||
descriptionText: "startup.errors.php_binary.desc".localized(Paths.php)
|
||||
descriptionText: "startup.errors.php_binary.desc".localized(App.shared.container.paths.php)
|
||||
),
|
||||
// =================================================================================
|
||||
// Ensure that the main PHP installation is not broken.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return await App.shared.container.shell.pipe("\(Paths.binPath)/php -v").err
|
||||
return await App.shared.container.shell.pipe("\(App.shared.container.paths.binPath)/php -v").err
|
||||
.contains("Library not loaded")
|
||||
},
|
||||
name: "no `dyld` issue (`Library not loaded`) detected",
|
||||
titleText: "startup.errors.dyld_library.title".localized,
|
||||
subtitleText: "startup.errors.dyld_library.subtitle".localized(
|
||||
Paths.optPath
|
||||
App.shared.container.paths.optPath
|
||||
),
|
||||
descriptionText: "startup.errors.dyld_library.desc".localized
|
||||
),
|
||||
@@ -160,14 +160,14 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return !(App.shared.container.filesystem.fileExists(Paths.valet)
|
||||
return !(App.shared.container.filesystem.fileExists(App.shared.container.paths.valet)
|
||||
|| App.shared.container.filesystem.fileExists("~/.composer/vendor/bin/valet"))
|
||||
},
|
||||
name: "`valet` binary exists",
|
||||
titleText: "startup.errors.valet_executable.title".localized,
|
||||
subtitleText: "startup.errors.valet_executable.subtitle".localized,
|
||||
descriptionText: "startup.errors.valet_executable.desc".localized(
|
||||
Paths.valet
|
||||
App.shared.container.paths.valet
|
||||
)
|
||||
),
|
||||
// =================================================================================
|
||||
@@ -176,14 +176,14 @@ class Startup {
|
||||
// functioning correctly. Let the user know that they need to run `valet trust`.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: { return await !App.shared.container.shell.pipe("cat /private/etc/sudoers.d/brew").out.contains(Paths.brew) },
|
||||
command: { return await !App.shared.container.shell.pipe("cat /private/etc/sudoers.d/brew").out.contains(App.shared.container.paths.brew) },
|
||||
name: "`/private/etc/sudoers.d/brew` contains brew",
|
||||
titleText: "startup.errors.sudoers_brew.title".localized,
|
||||
subtitleText: "startup.errors.sudoers_brew.subtitle".localized,
|
||||
descriptionText: "startup.errors.sudoers_brew.desc".localized
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return await !App.shared.container.shell.pipe("cat /private/etc/sudoers.d/valet").out.contains(Paths.valet) },
|
||||
command: { return await !App.shared.container.shell.pipe("cat /private/etc/sudoers.d/valet").out.contains(App.shared.container.paths.valet) },
|
||||
name: "`/private/etc/sudoers.d/valet` contains valet",
|
||||
titleText: "startup.errors.sudoers_valet.title".localized,
|
||||
subtitleText: "startup.errors.sudoers_valet.subtitle".localized,
|
||||
@@ -207,7 +207,7 @@ class Startup {
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
// Detect additional binaries (e.g. Composer)
|
||||
Paths.shared.detectBinaryPaths()
|
||||
App.shared.container.paths.detectBinaryPaths()
|
||||
// Load the configuration file (config.json)
|
||||
Valet.shared.loadConfiguration()
|
||||
// This check fails when the config is nil
|
||||
@@ -226,7 +226,7 @@ class Startup {
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
return await BrewDiagnostics.shared.cannotLoadService("dnsmasq")
|
||||
},
|
||||
name: "`sudo \(Paths.brew) services info` JSON loaded",
|
||||
name: "`sudo \(App.shared.container.paths.brew) services info` JSON loaded",
|
||||
titleText: "startup.errors.services_json_error.title".localized,
|
||||
subtitleText: "startup.errors.services_json_error.subtitle".localized,
|
||||
descriptionText: "startup.errors.services_json_error.desc".localized
|
||||
|
||||
@@ -22,9 +22,9 @@ import ContainerMacro
|
||||
self.shouldNotify = notify
|
||||
self.completion = completion
|
||||
|
||||
Paths.shared.detectBinaryPaths()
|
||||
container.paths.detectBinaryPaths()
|
||||
|
||||
if Paths.composer == nil {
|
||||
if container.paths.composer == nil {
|
||||
self.presentMissingAlert()
|
||||
return
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import ContainerMacro
|
||||
}
|
||||
|
||||
private func runComposerUpdateShellCommand() async throws {
|
||||
let command = "\(Paths.composer!) global update"
|
||||
let command = "\(container.paths.composer!) global update"
|
||||
|
||||
self.window?.addToConsole("\(command)\n")
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class BrewPermissionFixer {
|
||||
|
||||
var broken: [DueOwnershipFormula] = []
|
||||
|
||||
/**
|
||||
@@ -61,8 +62,8 @@ class BrewPermissionFixer {
|
||||
? "php"
|
||||
: "php@\(formula)"
|
||||
|
||||
let binFolderOwned = isOwnedByRoot(path: "\(Paths.optPath)/\(realFormula)/bin")
|
||||
let sbinFolderOwned = isOwnedByRoot(path: "\(Paths.optPath)/\(realFormula)/sbin")
|
||||
let binFolderOwned = isOwnedByRoot(path: "\(container.paths.optPath)/\(realFormula)/bin")
|
||||
let sbinFolderOwned = isOwnedByRoot(path: "\(container.paths.optPath)/\(realFormula)/sbin")
|
||||
|
||||
if binFolderOwned || sbinFolderOwned {
|
||||
Log.warn("\(formula) is owned by root")
|
||||
@@ -70,14 +71,14 @@ class BrewPermissionFixer {
|
||||
if binFolderOwned {
|
||||
broken.append(DueOwnershipFormula(
|
||||
formula: realFormula,
|
||||
path: "\(Paths.optPath)/\(realFormula)/bin"
|
||||
path: "\(container.paths.optPath)/\(realFormula)/bin"
|
||||
))
|
||||
}
|
||||
|
||||
if sbinFolderOwned {
|
||||
broken.append(DueOwnershipFormula(
|
||||
formula: realFormula,
|
||||
path: "\(Paths.optPath)/\(realFormula)/sbin"
|
||||
path: "\(container.paths.optPath)/\(realFormula)/sbin"
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -92,8 +93,8 @@ class BrewPermissionFixer {
|
||||
return broken
|
||||
.map { b in
|
||||
return """
|
||||
\(Paths.brew) services stop \(b.formula) \
|
||||
&& chown -R \(Paths.whoami):admin \(b.path)
|
||||
\(container.paths.brew) services stop \(b.formula) \
|
||||
&& chown -R \(container.paths.whoami):admin \(b.path)
|
||||
"""
|
||||
}
|
||||
.joined(
|
||||
|
||||
@@ -21,7 +21,7 @@ class Brew {
|
||||
|
||||
/// Determine which version of Homebrew is installed.
|
||||
public func determineVersion() async {
|
||||
let output = await shell.pipe("\(Paths.brew) --version")
|
||||
let output = await shell.pipe("\(container.paths.brew) --version")
|
||||
self.version = try? VersionNumber.parse(output.out)
|
||||
|
||||
if let version = version {
|
||||
|
||||
@@ -24,7 +24,7 @@ class BrewDiagnostics {
|
||||
*/
|
||||
public func loadInstalledTaps() async {
|
||||
installedTaps = await shell
|
||||
.pipe("\(Paths.brew) tap")
|
||||
.pipe("\(container.paths.brew) tap")
|
||||
.out
|
||||
.split(separator: "\n")
|
||||
.map { string in
|
||||
@@ -52,14 +52,14 @@ class BrewDiagnostics {
|
||||
*/
|
||||
public var customCaskInstalled: Bool {
|
||||
return installedTaps.contains("nicoverbruggen/cask")
|
||||
&& filesystem.directoryExists(Paths.caskroomPath)
|
||||
&& filesystem.directoryExists(container.paths.caskroomPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Determines whether to use the regular `nginx` or `nginx-full` formula.
|
||||
*/
|
||||
public var usesNginxFullFormula: Bool {
|
||||
guard let destination = try? filesystem.getDestinationOfSymlink("\(Paths.binPath)/nginx") else { return false }
|
||||
guard let destination = try? filesystem.getDestinationOfSymlink("\(container.paths.binPath)/nginx") else { return false }
|
||||
|
||||
// Verify that the `nginx` binary is symlinked to a directory that includes `nginx-full`.
|
||||
return destination.contains("/nginx-full/")
|
||||
@@ -76,7 +76,7 @@ class BrewDiagnostics {
|
||||
let regex = try! NSRegularExpression(pattern: "^php@[0-9]+\\.[0-9]+$", options: .caseInsensitive)
|
||||
|
||||
// Check for incorrect versions
|
||||
if let contents = try? filesystem.getShallowContentsOfDirectory("\(Paths.optPath)")
|
||||
if let contents = try? filesystem.getShallowContentsOfDirectory("\(container.paths.optPath)")
|
||||
.filter({
|
||||
let range = NSRange($0.startIndex..., in: $0)
|
||||
return regex.firstMatch(in: $0, options: [], range: range) != nil
|
||||
@@ -84,19 +84,19 @@ class BrewDiagnostics {
|
||||
|
||||
for symlink in contents {
|
||||
let version = symlink.replacingOccurrences(of: "php@", with: "")
|
||||
if let destination = try? filesystem.getDestinationOfSymlink("\(Paths.optPath)/\(symlink)") {
|
||||
if let destination = try? filesystem.getDestinationOfSymlink("\(container.paths.optPath)/\(symlink)") {
|
||||
if !destination.contains("Cellar/php/\(version)")
|
||||
&& !destination.contains("Cellar/php@\(version)") {
|
||||
Log.err("Symlink for \(symlink) is incorrect. Removing...")
|
||||
do {
|
||||
try filesystem.remove("\(Paths.optPath)/\(symlink)")
|
||||
try filesystem.remove("\(container.paths.optPath)/\(symlink)")
|
||||
Log.info("Incorrect symlink for \(symlink) has been successfully removed.")
|
||||
} catch {
|
||||
Log.err("Symlink for \(symlink) was incorrect but could not be removed!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.warn("Could not read symlink at: \(Paths.optPath)/\(symlink)! Symlink check skipped.")
|
||||
Log.warn("Could not read symlink at: \(container.paths.optPath)/\(symlink)! Symlink check skipped.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,7 +219,7 @@ class BrewDiagnostics {
|
||||
*/
|
||||
public func cannotLoadService(_ name: String) async -> Bool {
|
||||
let nginxJson = await shell
|
||||
.pipe("sudo \(Paths.brew) services info \(name) --json")
|
||||
.pipe("sudo \(container.paths.brew) services info \(name) --json")
|
||||
.out
|
||||
|
||||
let serviceInfo = try? JSONDecoder().decode(
|
||||
|
||||
@@ -59,7 +59,7 @@ struct BrewPhpExtension: Hashable, Comparable {
|
||||
}
|
||||
|
||||
static func hasInstallationReceipt(for formulaName: String) -> Bool {
|
||||
return App.shared.container.filesystem.fileExists("\(Paths.optPath)/\(formulaName)/INSTALL_RECEIPT.json")
|
||||
return App.shared.container.filesystem.fileExists("\(App.shared.container.paths.optPath)/\(formulaName)/INSTALL_RECEIPT.json")
|
||||
}
|
||||
|
||||
static func < (lhs: BrewPhpExtension, rhs: BrewPhpExtension) -> Bool {
|
||||
|
||||
@@ -77,7 +77,7 @@ struct BrewPhpFormula: Equatable {
|
||||
.replacingOccurrences(of: "shivammathur/php/", with: "")
|
||||
.replacingOccurrences(of: "php@" + PhpEnvironments.brewPhpAlias, with: "php")
|
||||
|
||||
return "\(Paths.optPath)/\(resolved)/bin"
|
||||
return "\(App.shared.container.paths.optPath)/\(resolved)/bin"
|
||||
}
|
||||
|
||||
/// The short version associated with this formula, if installed.
|
||||
@@ -105,7 +105,7 @@ struct BrewPhpFormula: Equatable {
|
||||
}
|
||||
|
||||
return App.shared.container.filesystem.fileExists(
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-php/Formula/php@\(version).rb"
|
||||
"\(App.shared.container.paths.tapPath)/shivammathur/homebrew-php/Formula/php@\(version).rb"
|
||||
.replacingOccurrences(of: "php@" + PhpEnvironments.brewPhpAlias, with: "php")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ class BrewPhpFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
|
||||
if loadOutdated {
|
||||
let command = """
|
||||
\(Paths.brew) update >/dev/null && \
|
||||
\(Paths.brew) outdated --json --formulae
|
||||
\(container.paths.brew) update >/dev/null && \
|
||||
\(container.paths.brew) outdated --json --formulae
|
||||
"""
|
||||
|
||||
let rawJsonText = await shell.pipe(command).out
|
||||
|
||||
@@ -10,7 +10,7 @@ import Foundation
|
||||
|
||||
class BrewTapFormulae {
|
||||
public static func from(tap: String) -> [String: [BrewPhpExtension]] {
|
||||
let directory = "\(Paths.tapPath)/\(tap)/Formula"
|
||||
let directory = "\(App.shared.container.paths.tapPath)/\(tap)/Formula"
|
||||
|
||||
let files = try? App.shared.container.filesystem.getShallowContentsOfDirectory(directory)
|
||||
|
||||
@@ -35,7 +35,7 @@ class BrewTapFormulae {
|
||||
|
||||
// Create a new BrewPhpExtension object (determines if installed)
|
||||
let phpExtension = BrewPhpExtension(
|
||||
path: "\(Paths.tapPath)/\(tap)/Formula/\(file)",
|
||||
path: "\(App.shared.container.paths.tapPath)/\(tap)/Formula/\(file)",
|
||||
name: phpExtensionName,
|
||||
phpVersion: phpVersion
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ class InstallPhpExtensionCommand: BrewCommand {
|
||||
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
export HOMEBREW_DOWNLOAD_CONCURRENCY=auto; \
|
||||
\(Paths.brew) install \(self.installing.map { $0.formulaName }.joined(separator: " ")) --force
|
||||
\(container.paths.brew) install \(self.installing.map { $0.formulaName }.joined(separator: " ")) --force
|
||||
"""
|
||||
|
||||
try await run(shell: shell, command, onProgress)
|
||||
@@ -68,7 +68,7 @@ class InstallPhpExtensionCommand: BrewCommand {
|
||||
|
||||
// Restart PHP-FPM
|
||||
if let installed = self.installing.first {
|
||||
await Actions.restartPhpFpm(version: installed.phpVersion)
|
||||
await Actions().restartPhpFpm(version: installed.phpVersion)
|
||||
}
|
||||
|
||||
// Check which version of PHP are now installed
|
||||
|
||||
@@ -41,7 +41,7 @@ class RemovePhpExtensionCommand: BrewCommand {
|
||||
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
export HOMEBREW_DOWNLOAD_CONCURRENCY=auto; \
|
||||
\(Paths.brew) remove \(phpExtension.formulaName) --force --ignore-dependencies
|
||||
\(container.paths.brew) remove \(phpExtension.formulaName) --force --ignore-dependencies
|
||||
"""
|
||||
|
||||
var loggedMessages: [String] = []
|
||||
@@ -66,7 +66,7 @@ class RemovePhpExtensionCommand: BrewCommand {
|
||||
|
||||
await PhpEnvironments.detectPhpVersions()
|
||||
|
||||
await Actions.restartPhpFpm(version: phpExtension.phpVersion)
|
||||
await Actions().restartPhpFpm(version: phpExtension.phpVersion)
|
||||
|
||||
await MainMenu.shared.refreshActiveInstallation()
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
let command = """
|
||||
export HOMEBREW_DOWNLOAD_CONCURRENCY=auto; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
\(Paths.brew) upgrade php;
|
||||
\(Paths.brew) install php@\(short);
|
||||
\(container.paths.brew) upgrade php;
|
||||
\(container.paths.brew) install php@\(short);
|
||||
"""
|
||||
|
||||
// Run the upgrade command
|
||||
@@ -116,7 +116,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
export HOMEBREW_DOWNLOAD_CONCURRENCY=auto; \
|
||||
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
\(Paths.brew) upgrade \(self.upgrading.map { $0.name }.joined(separator: " "))
|
||||
\(container.paths.brew) upgrade \(self.upgrading.map { $0.name }.joined(separator: " "))
|
||||
"""
|
||||
|
||||
try await run(shell: shell, command, onProgress)
|
||||
@@ -131,7 +131,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
let command = """
|
||||
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
\(Paths.brew) install \(self.installing.map { $0.name }.joined(separator: " ")) --force
|
||||
\(container.paths.brew) install \(self.installing.map { $0.name }.joined(separator: " ")) --force
|
||||
"""
|
||||
|
||||
try await run(shell: shell, command, onProgress)
|
||||
@@ -163,7 +163,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=true; \
|
||||
\(Paths.brew) reinstall \(requiringRepair.joined(separator: " ")) --force
|
||||
\(container.paths.brew) reinstall \(requiringRepair.joined(separator: " ")) --force
|
||||
"""
|
||||
|
||||
try await run(shell: shell, command, onProgress)
|
||||
|
||||
@@ -39,7 +39,7 @@ class RemovePhpVersionCommand: BrewCommand {
|
||||
export HOMEBREW_DOWNLOAD_CONCURRENCY=auto; \
|
||||
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
|
||||
\(Paths.brew) remove \(formula) --force --ignore-dependencies
|
||||
\(container.paths.brew) remove \(formula) --force --ignore-dependencies
|
||||
"""
|
||||
|
||||
do {
|
||||
|
||||
@@ -20,8 +20,11 @@ class NginxConfigurationFile: CreatedFromFile {
|
||||
var tld: String
|
||||
|
||||
/** Resolves an nginx configuration file (.conf) */
|
||||
static func from(filePath: String) -> Self? {
|
||||
let path = filePath.replacingOccurrences(of: "~", with: Paths.homePath)
|
||||
static func from(
|
||||
filePath: String,
|
||||
container: Container = App.shared.container
|
||||
) -> Self? {
|
||||
let path = filePath.replacingOccurrences(of: "~", with: container.paths.homePath)
|
||||
|
||||
do {
|
||||
let fileContents = try String(contentsOfFile: path)
|
||||
|
||||
@@ -25,7 +25,7 @@ class ValetInteractor {
|
||||
// MARK: - Managing Domains
|
||||
|
||||
public func link(path: String, domain: String) async throws {
|
||||
await shell.quiet("cd '\(path)' && \(Paths.valet) link '\(domain)' && valet links")
|
||||
await shell.quiet("cd '\(path)' && \(container.paths.valet) link '\(domain)' && valet links")
|
||||
}
|
||||
|
||||
public func unlink(site: ValetSite) async throws {
|
||||
@@ -34,11 +34,11 @@ class ValetInteractor {
|
||||
|
||||
public func proxy(domain: String, proxy: String, secure: Bool) async throws {
|
||||
let command = secure
|
||||
? "\(Paths.valet) proxy \(domain) \(proxy) --secure"
|
||||
: "\(Paths.valet) proxy \(domain) \(proxy)"
|
||||
? "\(container.paths.valet) proxy \(domain) \(proxy) --secure"
|
||||
: "\(container.paths.valet) proxy \(domain) \(proxy)"
|
||||
|
||||
await shell.quiet(command)
|
||||
await Actions.restartNginx()
|
||||
await Actions().restartNginx()
|
||||
}
|
||||
|
||||
public func remove(proxy: ValetProxy) async throws {
|
||||
@@ -56,11 +56,11 @@ class ValetInteractor {
|
||||
|
||||
// Use modernized version of command using domain name
|
||||
// This will allow us to secure multiple domains that use the same path
|
||||
var command = "sudo \(Paths.valet) \(action) '\(site.name)' && exit;"
|
||||
var command = "sudo \(container.paths.valet) \(action) '\(site.name)' && exit;"
|
||||
|
||||
// For Valet 2, use the old syntax; this has a known issue so Valet 3+ is preferred
|
||||
if !Valet.enabled(feature: .isolatedSites) {
|
||||
command = "cd '\(site.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
|
||||
command = "cd '\(site.absolutePath)' && sudo \(container.paths.valet) \(action) && exit;"
|
||||
}
|
||||
|
||||
// Run the command
|
||||
@@ -80,11 +80,11 @@ class ValetInteractor {
|
||||
// Build the list of commands we will need to run
|
||||
let commands: [String] = [
|
||||
// Unproxy the given domain
|
||||
"\(Paths.valet) unproxy \(proxy.domain)",
|
||||
"\(container.paths.valet) unproxy \(proxy.domain)",
|
||||
// Re-create the proxy (with the inverse secured status)
|
||||
originalSecureStatus
|
||||
? "\(Paths.valet) proxy \(proxy.domain) \(proxy.target)"
|
||||
: "\(Paths.valet) proxy \(proxy.domain) \(proxy.target) --secure"
|
||||
? "\(container.paths.valet) proxy \(proxy.domain) \(proxy.target)"
|
||||
: "\(container.paths.valet) proxy \(proxy.domain) \(proxy.target) --secure"
|
||||
]
|
||||
|
||||
// Run the commands
|
||||
@@ -101,11 +101,11 @@ class ValetInteractor {
|
||||
}
|
||||
|
||||
// Restart nginx to load the new configuration
|
||||
await Actions.restartNginx()
|
||||
await Actions().restartNginx()
|
||||
}
|
||||
|
||||
public func isolate(site: ValetSite, version: String) async throws {
|
||||
let command = "sudo \(Paths.valet) isolate php@\(version) --site '\(site.name)'"
|
||||
let command = "sudo \(container.paths.valet) isolate php@\(version) --site '\(site.name)'"
|
||||
|
||||
// Run the command
|
||||
await shell.quiet(command)
|
||||
@@ -121,7 +121,7 @@ class ValetInteractor {
|
||||
}
|
||||
|
||||
public func unisolate(site: ValetSite) async throws {
|
||||
let command = "sudo \(Paths.valet) unisolate --site '\(site.name)'"
|
||||
let command = "sudo \(container.paths.valet) unisolate --site '\(site.name)'"
|
||||
|
||||
// Run the command
|
||||
await shell.quiet(command)
|
||||
|
||||
@@ -21,7 +21,7 @@ class ValetSite: ValetListable {
|
||||
/// replacing the user's home folder with ~.
|
||||
lazy var absolutePathRelative: String = {
|
||||
return self.absolutePath
|
||||
.replacingOccurrences(of: Paths.homePath, with: "~")
|
||||
.replacingOccurrences(of: container.paths.homePath, with: "~")
|
||||
}()
|
||||
|
||||
/// The TLD used to locate this site.
|
||||
|
||||
@@ -68,7 +68,7 @@ class Valet {
|
||||
}
|
||||
|
||||
lazy var installed: Bool = {
|
||||
return filesystem.fileExists(Paths.binPath.appending("/valet"))
|
||||
return filesystem.fileExists(container.paths.binPath.appending("/valet"))
|
||||
&& filesystem.anyExists("~/.config/valet")
|
||||
}()
|
||||
|
||||
@@ -250,13 +250,13 @@ class Valet {
|
||||
|
||||
if version.short == "5.6" {
|
||||
// The main PHP config file should contain `valet.sock` and then we're probably fine?
|
||||
let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf"
|
||||
let fileName = "\(container.paths.etcPath)/php/5.6/php-fpm.conf"
|
||||
return await shell.pipe("cat \(fileName)").out
|
||||
.contains("valet.sock")
|
||||
}
|
||||
|
||||
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
||||
return filesystem.fileExists("\(Paths.etcPath)/php/\(version.short)/php-fpm.d/valet-fpm.conf")
|
||||
return filesystem.fileExists("\(container.paths.etcPath)/php/\(version.short)/php-fpm.d/valet-fpm.conf")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@ extension MainMenu {
|
||||
|
||||
@MainActor @objc func linkPhpBinary() {
|
||||
Task {
|
||||
await Actions.linkPhp()
|
||||
await Actions().linkPhp()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
asyncExecution {
|
||||
try Actions.fixHomebrewPermissions()
|
||||
try Actions().fixHomebrewPermissions()
|
||||
} success: {
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
@@ -63,27 +63,27 @@ extension MainMenu {
|
||||
|
||||
@objc func restartPhpFpm() {
|
||||
Task { // Simple restart service
|
||||
await Actions.restartPhpFpm()
|
||||
await Actions().restartPhpFpm()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func restartNginx() {
|
||||
Task { // Simple restart service
|
||||
await Actions.restartNginx()
|
||||
await Actions().restartNginx()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func restartDnsMasq() {
|
||||
Task { // Simple restart service
|
||||
await Actions.restartDnsMasq()
|
||||
await Actions().restartDnsMasq()
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor @objc func restartValetServices() {
|
||||
Task { // Restart services and show notification
|
||||
await Actions.restartDnsMasq()
|
||||
await Actions.restartPhpFpm()
|
||||
await Actions.restartNginx()
|
||||
await Actions().restartDnsMasq()
|
||||
await Actions().restartPhpFpm()
|
||||
await Actions().restartNginx()
|
||||
|
||||
LocalNotification.send(
|
||||
title: "notification.services_restarted".localized,
|
||||
@@ -95,7 +95,7 @@ extension MainMenu {
|
||||
|
||||
@MainActor @objc func stopValetServices() {
|
||||
Task { // Stop services and show notification
|
||||
await Actions.stopValetServices()
|
||||
await Actions().stopValetServices()
|
||||
|
||||
LocalNotification.send(
|
||||
title: "notification.services_stopped".localized,
|
||||
@@ -158,7 +158,7 @@ extension MainMenu {
|
||||
await sender.phpExtension?.toggle()
|
||||
|
||||
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
||||
await Actions.restartPhpFpm()
|
||||
await Actions().restartPhpFpm()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,23 +233,23 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
if install.hasErrorState {
|
||||
Actions.openGenericPhpConfigFolder()
|
||||
Actions().openGenericPhpConfigFolder()
|
||||
return
|
||||
}
|
||||
|
||||
Actions.openPhpConfigFolder(version: install.version.short)
|
||||
Actions().openPhpConfigFolder(version: install.version.short)
|
||||
}
|
||||
|
||||
@objc func openPhpMonitorConfigurationFile() {
|
||||
Actions.openPhpMonitorConfigFile()
|
||||
Actions().openPhpMonitorConfigFile()
|
||||
}
|
||||
|
||||
@objc func openGlobalComposerFolder() {
|
||||
Actions.openGlobalComposerFolder()
|
||||
Actions().openGlobalComposerFolder()
|
||||
}
|
||||
|
||||
@objc func openValetConfigFolder() {
|
||||
Actions.openValetConfigFolder()
|
||||
Actions().openValetConfigFolder()
|
||||
}
|
||||
|
||||
@objc func switchToPhpVersion(sender: PhpMenuItem) {
|
||||
|
||||
@@ -33,7 +33,7 @@ extension MainMenu {
|
||||
}
|
||||
|
||||
Task { @MainActor in
|
||||
await Actions.fixMyValet()
|
||||
await Actions().fixMyValet()
|
||||
|
||||
if previousVersion == PhpEnvironments.brewPhpAlias || previousVersion == nil {
|
||||
self.presentAlertForSameVersion()
|
||||
|
||||
@@ -51,7 +51,7 @@ extension Preferences {
|
||||
await moveOutdatedConfigurationFile()
|
||||
|
||||
// Attempt to load the file if it exists
|
||||
let url = URL(fileURLWithPath: "\(Paths.homePath)/.config/phpmon/config.json")
|
||||
let url = URL(fileURLWithPath: "\(App.shared.container.paths.homePath)/.config/phpmon/config.json")
|
||||
if App.shared.container.filesystem.fileExists(url.path) {
|
||||
|
||||
Log.info("A custom ~/.config/phpmon/config.json file was found. Attempting to parse...")
|
||||
|
||||
@@ -79,7 +79,7 @@ struct Preset: Codable, Equatable {
|
||||
if self.version != nil {
|
||||
if await !switchToPhpVersionIfValid() {
|
||||
PresetHelper.rollbackPreset = nil
|
||||
await Actions.restartPhpFpm()
|
||||
await Actions().restartPhpFpm()
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ struct Preset: Codable, Equatable {
|
||||
PresetHelper.loadRollbackPresetFromFile()
|
||||
|
||||
// Restart PHP FPM process (also reloads menu, which will show the preset rollback)
|
||||
await Actions.restartPhpFpm()
|
||||
await Actions().restartPhpFpm()
|
||||
|
||||
Task { @MainActor in
|
||||
// Show the correct notification
|
||||
@@ -276,7 +276,7 @@ struct Preset: Codable, Equatable {
|
||||
|
||||
try! String(data: data, encoding: .utf8)!
|
||||
.write(
|
||||
toFile: "\(Paths.homePath)/.config/phpmon/preset_revert.json",
|
||||
toFile: "\(App.shared.container.paths.homePath)/.config/phpmon/preset_revert.json",
|
||||
atomically: true,
|
||||
encoding: .utf8
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ class PresetHelper {
|
||||
|
||||
public static func loadRollbackPresetFromFile() {
|
||||
guard let revert = try? String(
|
||||
contentsOfFile: "\(Paths.homePath)/.config/phpmon/preset_revert.json",
|
||||
contentsOfFile: "\(App.shared.container.paths.homePath)/.config/phpmon/preset_revert.json",
|
||||
encoding: .utf8
|
||||
) else {
|
||||
PresetHelper.rollbackPreset = nil
|
||||
|
||||
@@ -12,7 +12,7 @@ extension App {
|
||||
|
||||
public func prepareHomebrewWatchers() {
|
||||
let notifier = FSNotifier(
|
||||
for: URL(fileURLWithPath: Paths.binPath),
|
||||
for: URL(fileURLWithPath: container.paths.binPath),
|
||||
eventMask: .all,
|
||||
onChange: { Task { await self.onHomebrewPhpModification() } }
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ extension App {
|
||||
return
|
||||
}
|
||||
|
||||
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(install.version.short)")
|
||||
let url = URL(fileURLWithPath: "\(container.paths.etcPath)/php/\(install.version.short)")
|
||||
|
||||
// Check whether the manager exists and schedule on the main thread
|
||||
// if we don't consistently do this, the app will create duplicate watchers
|
||||
|
||||
@@ -142,7 +142,7 @@ extension DomainListVC {
|
||||
await sender.phpExtension?.toggle()
|
||||
|
||||
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
||||
await Actions.restartPhpFpm()
|
||||
await Actions().restartPhpFpm()
|
||||
}
|
||||
|
||||
reloadContextMenu()
|
||||
|
||||
@@ -38,7 +38,7 @@ class BytePhpPreference: PhpPreference {
|
||||
|
||||
override init(container: Container = App.shared.container, key: String) {
|
||||
let value = container.command.execute(
|
||||
path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"],
|
||||
path: container.paths.php, arguments: ["-r", "echo ini_get('\(key)');"],
|
||||
trimNewlines: false
|
||||
)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class PhpConfigChecker {
|
||||
}
|
||||
|
||||
// Do the check
|
||||
let fullFilePath = Paths.etcPath.appending("/php/\(version)/\(file.path)")
|
||||
let fullFilePath = App.shared.container.paths.etcPath.appending("/php/\(version)/\(file.path)")
|
||||
if !App.shared.container.filesystem.fileExists(fullFilePath) {
|
||||
missing.append(fullFilePath)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ extension WarningManager {
|
||||
),
|
||||
Warning(
|
||||
command: {
|
||||
return !App.shared.container.shell.PATH.contains("\(Paths.homePath)/.config/phpmon/bin") &&
|
||||
return !App.shared.container.shell.PATH.contains("\(App.shared.container.paths.homePath)/.config/phpmon/bin") &&
|
||||
!App.shared.container.filesystem.isWriteableFile("/usr/local/bin/")
|
||||
},
|
||||
name: "Helpers cannot be symlinked and not in PATH",
|
||||
@@ -34,7 +34,7 @@ extension WarningManager {
|
||||
"warnings.helper_permissions.symlink"
|
||||
] },
|
||||
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries",
|
||||
fix: Paths.shell == "/bin/zsh" ? {
|
||||
fix: App.shared.container.paths.shell == "/bin/zsh" ? {
|
||||
// Add to PATH
|
||||
await ZshRunCommand().addPhpMonitorPath()
|
||||
// Finally, perform environment checks again
|
||||
|
||||
Reference in New Issue
Block a user