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

♻️ Reorganise code for optimal code sharing, add phpmon-cli

- Moved over common functionality to package
- Added phpmon-cli target (for fast switching via the terminal)
This commit is contained in:
2021-12-20 18:25:52 +01:00
parent fa2de1f77c
commit 1ca49f6cbc
23 changed files with 379 additions and 53 deletions

View File

@@ -7,6 +7,7 @@
import Cocoa
import UserNotifications
import PMCommon
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {

View File

@@ -6,6 +6,7 @@
//
import Foundation
import PMCommon
class Startup {

View File

@@ -7,6 +7,7 @@
//
import Foundation
import PMCommon
/// An application that is capable of opening a particular directory (usually of a PHP project).
/// In most cases this is going to be a code editor, but it could also be another application

View File

@@ -7,6 +7,7 @@
//
import Cocoa
import PMCommon
class Filesystem {

View File

@@ -7,6 +7,7 @@
//
import Foundation
import PMCommon
class HomebrewDiagnostics {

View File

@@ -7,6 +7,7 @@
//
import Foundation
import PMCommon
class Valet {

View File

@@ -6,6 +6,7 @@
//
import Cocoa
import PMCommon
class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {

View File

@@ -6,6 +6,7 @@
//
import Foundation
import PMCommon
/**
An installed version of PHP, that was detected by scanning the `/opt/php@version/bin` directory.

View File

@@ -7,6 +7,7 @@
//
import Foundation
import PMCommon
class PhpInstallation {

View File

@@ -8,6 +8,7 @@
import Cocoa
import AppKit
import PMCommon
class SiteListCell: NSTableCellView
{

View File

@@ -9,6 +9,7 @@
import Cocoa
import HotKey
import Carbon
import PMCommon
class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

View File

@@ -1,40 +0,0 @@
//
// Command.swift
// PHP Monitor
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
class Command {
/**
Immediately executes a command.
- Parameter path: The path of the command or program to invoke.
- Parameter arguments: A list of arguments that are passed on.
- Parameter trimNewlines: Removes empty new line output.
*/
public static func execute(path: String, arguments: [String], trimNewlines: Bool = false) -> String {
let task = Process()
task.launchPath = path
task.arguments = arguments
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = String.init(data: data, encoding: String.Encoding.utf8)!
if (trimNewlines) {
return output.components(separatedBy: .newlines)
.filter({ !$0.isEmpty })
.joined(separator: "\n")
}
return output
}
}

View File

@@ -1,74 +0,0 @@
//
// Paths.swift
// PHP Monitor
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Foundation
enum HomebrewDir: String {
case opt = "/opt/homebrew"
case usr = "/usr/local"
}
class Paths {
static let shared = Paths()
var baseDir : HomebrewDir
init() {
let optBrewFound = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew")
let usrBrewFound = Shell.fileExists("\(HomebrewDir.usr.rawValue)/bin/brew")
if (optBrewFound) {
// This is usually the case with Homebrew installed on Apple Silicon
baseDir = .opt
} else if (usrBrewFound) {
// This is usually the case with Homebrew installed on Intel (or Rosetta 2)
baseDir = .usr
} else {
// Falling back to default "legacy" Homebrew location (for Intel)
print("Seems like we couldn't determine the Homebrew directory.")
print("This usually means we're in trouble... (no Homebrew?)")
baseDir = .usr
}
}
// - MARK: Binaries
public static var valet: String {
return "/Users/\(whoami)/.composer/vendor/bin/valet"
}
public static var brew: String {
return "\(binPath)/brew"
}
public static var php: String {
return "\(binPath)/php"
}
public static var phpConfig: String {
return "\(binPath)/php-config"
}
// - MARK: Paths
public static var whoami: String {
return String(Shell.pipe("whoami").split(separator: "\n")[0])
}
public static var binPath: String {
return "\(shared.baseDir.rawValue)/bin"
}
public static var optPath: String {
return "\(shared.baseDir.rawValue)/opt"
}
public static var etcPath: String {
return "\(shared.baseDir.rawValue)/etc"
}
}

View File

@@ -1,179 +0,0 @@
//
// Shell.swift
// PHP Monitor
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
class Shell {
// MARK: - Invoke static functions
public static func run(
_ command: String,
requiresPath: Bool = false
) {
Shell.user.run(command, requiresPath: requiresPath)
}
public static func pipe(
_ command: String,
requiresPath: Bool = false
) -> String {
return Shell.user.pipe(command, requiresPath: requiresPath)
}
// MARK: - Singleton
/**
We now require macOS 11, so no need to detect which terminal to use.
*/
var shell: String = "/bin/sh"
/**
Singleton to access a user shell (with --login)
*/
static let user = Shell()
/**
Runs a shell command without using the output.
Uses the default shell.
- Parameter command: The command to run
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
*/
func run(
_ command: String,
requiresPath: Bool = false
) {
// Equivalent of piping to /dev/null; don't do anything with the string
_ = Shell.pipe(command, requiresPath: requiresPath)
}
/**
Runs a shell command and returns the output.
- Parameter command: The command to run
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
*/
func pipe(
_ command: String,
requiresPath: Bool = false
) -> String {
let shellOutput = self.executeSynchronously(command, requiresPath: requiresPath)
let hasError = (
shellOutput.standardOutput == ""
&& shellOutput.errorOutput.lengthOfBytes(using: .utf8) > 0
)
return !hasError ? shellOutput.standardOutput : shellOutput.errorOutput
}
/**
Runs the command and returns a `ShellOutput` object, which contains info about the process.
- Parameter command: The command to run
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
- Parameter waitUntilExit: Waits for the command to complete before returning the `ShellOutput`
*/
func executeSynchronously(
_ command: String,
requiresPath: Bool = false
) -> ShellOutput {
let outputPipe = Pipe()
let errorPipe = Pipe()
let task = self.createTask(for: command, requiresPath: requiresPath)
task.standardOutput = outputPipe
task.standardError = errorPipe
task.launch()
task.waitUntilExit()
return ShellOutput(
standardOutput: String(
data: outputPipe.fileHandleForReading.readDataToEndOfFile(),
encoding: .utf8
)!,
errorOutput: String(
data: errorPipe.fileHandleForReading.readDataToEndOfFile(),
encoding: .utf8
)!,
task: task
)
}
/**
Checks if a file exists at the provided path.
Uses `/bin/echo` instead of the `builtin` (which does not support `-n`).
*/
public static func fileExists(_ path: String) -> Bool {
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
}
/**
Creates a new process with the correct PATH and shell.
*/
func createTask(for command: String, requiresPath: Bool) -> Process {
let tailoredCommand = requiresPath
? "export PATH=\(Paths.binPath):$PATH && \(command)"
: command
let task = Process()
task.launchPath = self.shell
task.arguments = ["--login", "-c", tailoredCommand]
return task
}
static func captureOutput(
_ task: Process,
didReceiveStdOutData: @escaping (String) -> Void,
didReceiveStdErrData: @escaping (String) -> Void
) {
let outputPipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = outputPipe
task.standardError = errorPipe
[(outputPipe, didReceiveStdOutData), (errorPipe, didReceiveStdErrData)].forEach {
(pipe: Pipe, callback: @escaping (String) -> Void) in
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: pipe.fileHandleForReading,
queue: nil
) { notification in
if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) {
callback(outputString)
}
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
}
}
}
static func haltCapturingOutput(_ task: Process) {
if let pipe = task.standardOutput as? Pipe {
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
}
if let pipe = task.standardError as? Pipe {
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
}
}
}
class ShellOutput {
let standardOutput: String
let errorOutput: String
let task: Process
init(standardOutput: String,
errorOutput: String,
task: Process) {
self.standardOutput = standardOutput
self.errorOutput = errorOutput
self.task = task
}
}

View File

@@ -7,6 +7,7 @@
//
import Foundation
import PMCommon
extension App {