mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-07 20:10:08 +02:00
♻️ Refactor startup procedure
This commit is contained in:
@ -511,8 +511,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */,
|
||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
||||
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */,
|
||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
||||
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */,
|
||||
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
||||
@ -587,8 +587,8 @@
|
||||
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
||||
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */,
|
||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||
);
|
||||
path = App;
|
||||
sourceTree = "<group>";
|
||||
|
@ -11,7 +11,7 @@ import XCTest
|
||||
class ValetTest: XCTestCase {
|
||||
|
||||
func testDetermineValetVersion() {
|
||||
let version = valet("--version")
|
||||
let version = valet("--version", sudo: false)
|
||||
XCTAssert(version.contains("Laravel Valet 2."))
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,11 @@
|
||||
// MARK: Common Shell Commands
|
||||
|
||||
/**
|
||||
Runs a `valet` command.
|
||||
Runs a `valet` command. Defaults to running as superuser.
|
||||
*/
|
||||
func valet(_ command: String) -> String
|
||||
func valet(_ command: String, sudo: Bool = true) -> String
|
||||
{
|
||||
return Shell.pipe("sudo \(Paths.valet) \(command)", requiresPath: true)
|
||||
return Shell.pipe("\(sudo ? "sudo " : "")" + "\(Paths.valet) \(command)", requiresPath: true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,11 +54,11 @@ class Alert {
|
||||
/**
|
||||
Notify the user about something by showing an alert.
|
||||
*/
|
||||
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
|
||||
public static func notify(message: String, info: String, button: String = "OK", style: NSAlert.Style = .informational) {
|
||||
_ = present(
|
||||
messageText: message,
|
||||
informativeText: info,
|
||||
buttonTitle: "OK",
|
||||
buttonTitle: button,
|
||||
secondButtonTitle: "",
|
||||
style: style
|
||||
)
|
||||
|
@ -10,8 +10,71 @@ import AppKit
|
||||
|
||||
class Startup {
|
||||
|
||||
public var checks: [EnvironmentCheck] = [
|
||||
EnvironmentCheck(
|
||||
command: { return !FileManager.default.fileExists(atPath: Paths.brew) },
|
||||
titleText: "alert.homebrew_missing.title".localized,
|
||||
descriptionText: "alert.homebrew_missing.info".localized(
|
||||
App.architecture
|
||||
.replacingOccurrences(of: "x86_64", with: "Intel")
|
||||
.replacingOccurrences(of: "arm64", with: "Apple Silicon"),
|
||||
Paths.brew
|
||||
),
|
||||
buttonText: "alert.homebrew_missing.quit".localized,
|
||||
requiresAppRestart: true
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return !Shell.fileExists(Paths.php) },
|
||||
titleText: "startup.errors.php_binary.title".localized,
|
||||
descriptionText: "startup.errors.php_binary.desc".localized(
|
||||
Paths.php
|
||||
)
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return !Shell.pipe("ls \(Paths.optPath) | grep php").contains("php") },
|
||||
titleText: "startup.errors.php_opt.title".localized,
|
||||
descriptionText: "startup.errors.php_opt.desc".localized(
|
||||
Paths.optPath
|
||||
)
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return !(Shell.fileExists(Paths.valet)
|
||||
|| Shell.fileExists("~/.composer/vendor/bin/valet"))
|
||||
},
|
||||
titleText: "startup.errors.valet_executable.title".localized,
|
||||
descriptionText: "startup.errors.valet_executable.desc".localized(
|
||||
Paths.valet
|
||||
)
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return HomebrewDiagnostics.cannotLoadService() },
|
||||
titleText: "startup.errors.services_json_error.title".localized,
|
||||
descriptionText: "startup.errors.services_json_error.desc".localized
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return !Shell.pipe("cat /private/etc/sudoers.d/brew").contains(Paths.brew) },
|
||||
titleText: "startup.errors.sudoers_brew.title".localized,
|
||||
descriptionText: "startup.errors.sudoers_brew.desc".localized
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return !Shell.pipe("cat /private/etc/sudoers.d/valet").contains(Paths.valet) },
|
||||
titleText: "startup.errors.sudoers_valet.title".localized,
|
||||
descriptionText: "startup.errors.sudoers_valet.desc".localized
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
// Determine the Valet version only AFTER confirming the correct permission is in place
|
||||
// or otherwise this command will never return a valid version number
|
||||
Valet.shared.version = VersionExtractor.from(valet("--version", sudo: false))
|
||||
return Valet.shared.version == nil
|
||||
},
|
||||
titleText: "startup.errors.valet_version_unknown.title".localized,
|
||||
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
||||
)
|
||||
]
|
||||
|
||||
public var failed: Bool = false
|
||||
public var failureCallback = {}
|
||||
|
||||
/**
|
||||
Checks the user's environment and checks if PHP Monitor can be used properly.
|
||||
@ -20,65 +83,45 @@ class Startup {
|
||||
- Parameter success: Callback that is fired if the application can proceed with launch
|
||||
- Parameter failure: Callback that is fired if the application must retry launch
|
||||
*/
|
||||
func checkEnvironment(success: () -> Void, failure: @escaping () -> Void)
|
||||
func checkEnvironment(success: @escaping () -> Void, failure: @escaping () -> Void)
|
||||
{
|
||||
failureCallback = failure
|
||||
// Do the important system setup checks
|
||||
Log.info("The user is running PHP Monitor with the architecture: \(App.architecture)")
|
||||
|
||||
performEnvironmentCheck(
|
||||
!Shell.fileExists("\(Paths.binPath)/php"),
|
||||
messageText: "startup.errors.php_binary.title".localized,
|
||||
informativeText: "startup.errors.php_binary.desc".localized
|
||||
)
|
||||
|
||||
performEnvironmentCheck(
|
||||
!Shell.pipe("ls \(Paths.optPath) | grep php").contains("php"),
|
||||
messageText: "startup.errors.php_opt.title".localized,
|
||||
informativeText: "startup.errors.php_opt.desc".localized
|
||||
)
|
||||
|
||||
performEnvironmentCheck(
|
||||
// Check for Valet; it can be symlinked or in .composer/vendor/bin
|
||||
!(Shell.fileExists("\(Paths.binPath))/valet")
|
||||
|| Shell.fileExists("~/.composer/vendor/bin/valet")
|
||||
),
|
||||
messageText: "startup.errors.valet_executable.title".localized,
|
||||
informativeText: "startup.errors.valet_executable.desc".localized
|
||||
)
|
||||
|
||||
performEnvironmentCheck(
|
||||
HomebrewDiagnostics.cannotLoadService(),
|
||||
messageText: "startup.errors.services_json_error.title".localized,
|
||||
informativeText: "startup.errors.services_json_error.desc".localized
|
||||
)
|
||||
|
||||
performEnvironmentCheck(
|
||||
!Shell.pipe("cat /private/etc/sudoers.d/brew").contains("\(Paths.binPath)/brew"),
|
||||
messageText: "startup.errors.sudoers_brew.title".localized,
|
||||
informativeText: "startup.errors.sudoers_brew.desc".localized
|
||||
)
|
||||
|
||||
performEnvironmentCheck(
|
||||
// Check for Valet; it MUST be symlinked thanks to sudoers
|
||||
!(Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet")
|
||||
|| Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/opt/homebrew/bin/valet")
|
||||
),
|
||||
messageText: "startup.errors.sudoers_valet.title".localized,
|
||||
informativeText: "startup.errors.sudoers_valet.desc".localized
|
||||
)
|
||||
|
||||
// Determine the Valet version only AFTER confirming the correct permission is in place
|
||||
Valet.shared.version = VersionExtractor.from(valet("--version"))
|
||||
performEnvironmentCheck(
|
||||
Valet.shared.version == nil,
|
||||
messageText: "startup.errors.valet_version_unknown.title".localized,
|
||||
informativeText: "startup.errors.valet_version_unknown.desc".localized
|
||||
)
|
||||
|
||||
if (!failed) {
|
||||
initializeSwitcher()
|
||||
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
||||
success()
|
||||
for check in self.checks {
|
||||
let failureCondition = check.command()
|
||||
|
||||
if !failureCondition {
|
||||
continue
|
||||
}
|
||||
|
||||
failed = true
|
||||
|
||||
if check.requiresAppRestart {
|
||||
Alert.notify(
|
||||
message: check.titleText,
|
||||
info: check.descriptionText,
|
||||
button: check.buttonText,
|
||||
style: .critical
|
||||
)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
Alert.notify(
|
||||
message: check.titleText,
|
||||
info: check.descriptionText,
|
||||
style: .critical
|
||||
)
|
||||
}
|
||||
|
||||
if failed {
|
||||
failure()
|
||||
return
|
||||
}
|
||||
|
||||
initializeSwitcher()
|
||||
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
||||
success()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,31 +134,32 @@ class Startup {
|
||||
appDelegate.initializeSwitcher()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - EnvironmentCheck struct
|
||||
|
||||
/**
|
||||
Perform an environment check.
|
||||
|
||||
- Parameter condition: Fail condition to check for; if this returns `true`, the alert will be shown
|
||||
- Parameter messageText: Short description of what is wrong
|
||||
- Parameter informativeText: Expanded description of the environment check that failed
|
||||
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
|
||||
Checks that require an app restart will always lead to an alert and app termination shortly after.
|
||||
*/
|
||||
private func performEnvironmentCheck(
|
||||
_ condition: Bool,
|
||||
messageText: String,
|
||||
informativeText: String
|
||||
) {
|
||||
if (!condition) { return }
|
||||
|
||||
DispatchQueue.main.async { [self] in
|
||||
// Present the information to the user
|
||||
Alert.notify(
|
||||
message: messageText,
|
||||
info: informativeText,
|
||||
style: .critical
|
||||
)
|
||||
// Only breaking issues will throw the extra retry modal
|
||||
failureCallback()
|
||||
struct EnvironmentCheck {
|
||||
let command: () -> Bool
|
||||
let titleText: String
|
||||
let descriptionText: String
|
||||
let buttonText: String
|
||||
let requiresAppRestart: Bool
|
||||
|
||||
init(
|
||||
command: @escaping () -> Bool,
|
||||
titleText: String,
|
||||
descriptionText: String,
|
||||
buttonText: String = "OK",
|
||||
requiresAppRestart: Bool = false
|
||||
) {
|
||||
self.command = command
|
||||
self.titleText = titleText
|
||||
self.descriptionText = descriptionText
|
||||
self.buttonText = buttonText
|
||||
self.requiresAppRestart = requiresAppRestart
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,29 +16,11 @@ extension MainMenu {
|
||||
// Start with the icon
|
||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||
|
||||
// Do the important system setup checks
|
||||
Log.info("The user is running PHP Monitor with the architecture: \(App.architecture)")
|
||||
|
||||
// Make sure Homebrew is installed
|
||||
if !FileManager.default.fileExists(atPath: Paths.brew) {
|
||||
_ = Alert.present(
|
||||
messageText: "alert.homebrew_missing.title".localized,
|
||||
informativeText: "alert.homebrew_missing.info".localized(
|
||||
App.architecture
|
||||
.replacingOccurrences(of: "x86_64", with: "Intel")
|
||||
.replacingOccurrences(of: "arm64", with: "Apple Silicon"),
|
||||
Paths.brew
|
||||
),
|
||||
buttonTitle: "alert.homebrew_missing.quit".localized,
|
||||
style: .critical
|
||||
)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Perform environment boot checks
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
Startup().checkEnvironment(success: { onEnvironmentPass() },
|
||||
failure: { onEnvironmentFail() }
|
||||
DispatchQueue.main.async {
|
||||
Startup().checkEnvironment(
|
||||
success: { self.onEnvironmentPass() },
|
||||
failure: { self.onEnvironmentFail() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -181,12 +181,6 @@
|
||||
"notification.services_restarted" = "Valet services restarted";
|
||||
"notification.services_restarted_desc" = "All services have been successfully restarted.";
|
||||
|
||||
// ALERTS
|
||||
|
||||
"alert.homebrew_missing.title" = "PHP Monitor cannot start";
|
||||
"alert.homebrew_missing.info" = "You are running PHP Monitor with the following architecture: %@.\n\nA working Homebrew binary is expected in `%@`, but was not found, so PHP Monitor cannot work.\n\nIf you have not installed Homebrew yet, please do so. (If you are on Apple Silicon, make sure your Homebrew and PHP Monitor use the same architecture, by enabling or disabling Rosetta where needed.)\n\nPHP Monitor will now quit, please restart the app after fixing this issue.";
|
||||
"alert.homebrew_missing.quit" = "Quit";
|
||||
|
||||
// Composer Update
|
||||
"alert.composer_missing.title" = "Composer not found!";
|
||||
"alert.composer_missing.info" = "Make sure you have Composer available in `/usr/local/bin/composer`. If Composer is located somewhere else, please create a symlink, like so (make sure to use the correct path):\n\n`ln -s /path/to/composer /usr/local/bin`.";
|
||||
@ -258,17 +252,23 @@ You can do this by running `composer global update` in your terminal. After that
|
||||
|
||||
// STARTUP
|
||||
|
||||
/// 0. Architecture mismatch
|
||||
|
||||
"alert.homebrew_missing.title" = "PHP Monitor cannot start";
|
||||
"alert.homebrew_missing.info" = "You are running PHP Monitor with the following architecture: %@.\n\nA working Homebrew binary is expected in `%@`, but was not found, so PHP Monitor cannot work.\n\nIf you have not installed Homebrew yet, please do so. (If you are on Apple Silicon, make sure your Homebrew and PHP Monitor use the same architecture, by enabling or disabling Rosetta where needed.)\n\nPHP Monitor will now quit, please restart the app after fixing this issue.";
|
||||
"alert.homebrew_missing.quit" = "Quit";
|
||||
|
||||
/// 1. PHP binary not found
|
||||
"startup.errors.php_binary.title" = "PHP is not correctly installed";
|
||||
"startup.errors.php_binary.desc" = "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php` (or `/opt/homebrew/bin/php`). The app will not work correctly until you resolve this issue. (Usually `brew link php` resolves this issue.)";
|
||||
"startup.errors.php_binary.desc" = "You must install PHP via brew. Try running `which php` in Terminal, it should return `%@`. The app will not work correctly until you resolve this issue. (Usually `brew link php` resolves this issue.)";
|
||||
|
||||
/// 2. PHP not found in /usr/local/opt or /opt/homebrew/opt
|
||||
"startup.errors.php_opt.title" = "PHP is not correctly installed";
|
||||
"startup.errors.php_opt.desc" = "PHP alias was not found in `/usr/local/opt` or `/opt/homebrew/opt`. The app will not work correctly until you resolve this issue. If you already have the `php` formula installed, you may need to run `brew install php` in order for PHP Monitor to detect this installation.";
|
||||
"startup.errors.php_opt.desc" = "PHP alias was not found in `%@`. The app will not work correctly until you resolve this issue. If you already have the `php` formula installed, you may need to run `brew install php` in order for PHP Monitor to detect this installation.";
|
||||
|
||||
/// 3a. Valet not installed
|
||||
"startup.errors.valet_executable.title" = "Laravel Valet is not correctly installed";
|
||||
"startup.errors.valet_executable.desc" = "You must install Valet with composer. Try running `which valet` in Terminal, it should return `/usr/local/bin/valet` or `/opt/homebrew/bin/valet`. The app will not work correctly until you resolve this issue. (PHP Monitor checks for the existence of `valet` in either of these paths.)";
|
||||
"startup.errors.valet_executable.desc" = "You must install Valet with composer. Try running `which valet` in Terminal, it should return `%@`. The app will not work correctly until you resolve this issue. (PHP Monitor checks for the existence of `valet` in either of these paths.)";
|
||||
|
||||
/// 3b. Valet configuration file missing [currently not enabled]
|
||||
"startup.errors.valet_config.title" = "Laravel Valet configuration file missing";
|
||||
|
Reference in New Issue
Block a user