1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 03:50:08 +02:00

♻️ Reworked helper scripts

- Add 'Welcome Tour' to First Aid menu
- Updated 'Welcome Tour'
- Helpers are now always written to ~/.config/phpmon/bin
- Updated helpers (now symlinked)
- Updated checks for when to symlink helpers
This commit is contained in:
2022-08-14 23:57:46 +02:00
parent 237185995d
commit bbbdce6b44
10 changed files with 124 additions and 43 deletions

View File

@ -1677,7 +1677,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 920;
CURRENT_PROJECT_VERSION = 950;
DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
@ -1704,7 +1704,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 920;
CURRENT_PROJECT_VERSION = 950;
DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;

View File

@ -19,9 +19,12 @@ public class Paths {
private var userName: String
private var PATH: String
init() {
baseDir = App.architecture != "x86_64" ? .opt : .usr
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
PATH = String(Shell.pipe("echo $PATH")).trimmingCharacters(in: .whitespacesAndNewlines)
}
public func detectBinaryPaths() {
@ -57,6 +60,10 @@ public class Paths {
return shared.userName
}
public static var PATH: String {
return shared.PATH
}
public static var cellarPath: String {
return "\(shared.baseDir.rawValue)/Cellar"
}

View File

@ -33,6 +33,15 @@ class Filesystem {
return exists && !isDirectory.boolValue
}
public static func fileIsSymlink(_ path: String) -> Bool {
do {
let attribs = try FileManager.default.attributesOfItem(atPath: path)
return attribs[.type] as! FileAttributeType == FileAttributeType.typeSymbolicLink
} catch {
return false
}
}
/**
Checks if a directory exists at the provided path.
*/

View File

@ -16,8 +16,18 @@ class PhpHelper {
// 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 = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)"
// Check if the ~/.config/phpmon/bin directory is in the PATH
let inPath = Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin")
// Check if we can create symlinks (`/usr/local/bin` must be writable)
let canWriteSymlinks = FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
do {
let destination = "/usr/local/bin/pm\(dotless)"
Shell.run("mkdir -p ~/.config/phpmon/bin")
if FileManager.default.fileExists(atPath: destination) {
let contents = try String(contentsOfFile: destination)
if !contents.contains(keyPhrase) {
@ -52,10 +62,40 @@ class PhpHelper {
// Make sure the file is executable
Shell.run("chmod +x \(destination)")
// Create a symlink if the folder is not in the PATH
if !inPath {
// First, check if we can create symlinks at all
if !canWriteSymlinks {
Log.err("PHP Monitor does not have permission to symlink `/usr/local/bin/\(dotless)`.")
return
}
// Write the symlink
self.createSymlink(dotless)
}
} catch {
print(error)
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))")
}
}
private static func createSymlink(_ dotless: String) {
let source = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)"
let destination = "/usr/local/bin/pm\(dotless)"
if !Filesystem.fileExists(destination) {
Log.info("Creating new symlink: \(destination)")
Shell.run("ln -s \(source) \(destination)")
return
}
if !Filesystem.fileIsSymlink(destination) {
Log.info("Overwriting existing file with new symlink: \(destination)")
Shell.run("ln -fs \(source) \(destination)")
return
}
Log.info("Symlink in \(destination) already exists, OK.")
}
}

View File

@ -57,6 +57,9 @@ extension MainMenu {
let installation = PhpEnv.phpInstall
installation.notifyAboutBrokenPhpFpm()
// Check for other problems
WarningManager.shared.evaluateWarnings()
// Set up the config watchers on launch (updated automatically when switching)
Log.info("Setting up watchers...")
App.shared.handlePhpConfigWatcher()
@ -85,15 +88,11 @@ extension MainMenu {
// Start the background refresh timer
startSharedTimer()
// Check warnings
WarningManager.shared.evaluateWarnings()
// Update the stats
Stats.incrementSuccessfulLaunchCount()
Stats.evaluateSponsorMessageShouldBeDisplayed()
// Present first launch screen if needed
#warning("You should definitely tweak this view again")
if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview {
Log.info("Should present the first launch screen!")
DispatchQueue.main.async {

View File

@ -126,6 +126,16 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
ServicesManager.shared.loadData()
}
/**
Shows the Welcome Tour screen, again.
Did this need a comment? No, probably not.
*/
@objc func showWelcomeTour() {
DispatchQueue.main.async {
OnboardingWindowController.show()
}
}
/** Reloads the menu in the background, using `asyncExecution`. */
@objc func reloadPhpMonitorMenuInBackground() {
asyncExecution({

View File

@ -199,6 +199,10 @@ extension StatusMenu {
let servicesMenu = NSMenu()
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
servicesMenu.addItem(NSMenuItem(title: "mi_view_onboarding".localized,
action: #selector(MainMenu.showWelcomeTour), keyEquivalent: ""))
let fixMyValetMenuItem = NSMenuItem(
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""
@ -216,22 +220,15 @@ extension StatusMenu {
servicesMenu.addItem(NSMenuItem.separator())
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_dnsmasq".localized,
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_php_fpm".localized,
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_nginx".localized,
action: #selector(MainMenu.restartNginx), keyEquivalent: "n")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_valet_services".localized,
action: #selector(MainMenu.restartValetServices), keyEquivalent: "s")
)
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized,
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized,
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized,
action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
servicesMenu.addItem(NSMenuItem(title: "mi_restart_valet_services".localized,
action: #selector(MainMenu.restartValetServices), keyEquivalent: "s"))
servicesMenu.addItem(
NSMenuItem(title: "mi_stop_valet_services".localized,
action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"),

View File

@ -25,18 +25,17 @@ struct OnboardingTextItem: View {
Text(title.localizedForSwiftUI)
.font(.system(size: 14))
.lineLimit(3)
HStack {
Text(description.localizedForSwiftUI)
.foregroundColor(Color.secondary)
.font(.system(size: 13))
.lineLimit(3)
.fixedSize(horizontal: false, vertical: true)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
Text(description.localizedForSwiftUI)
.foregroundColor(Color.secondary)
.font(.system(size: 13))
.lineLimit(6)
.fixedSize(horizontal: false, vertical: true)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray.opacity(0.3), lineWidth: 1))
.overlay(RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray.opacity(0.3), lineWidth: 1))
}
}
@ -57,9 +56,13 @@ struct OnboardingView: View {
.padding(.bottom, 5)
Text("onboarding.explore".localized)
.padding(.bottom)
.padding(.trailing)
}
.padding(.top, 10)
}
.padding(.leading)
.padding(.trailing)
VStack {
VStack(alignment: .leading, spacing: 10) {
OnboardingTextItem(
@ -67,6 +70,11 @@ struct OnboardingView: View {
title: "onboarding.tour.menu_bar.title",
description: "onboarding.tour.menu_bar"
)
OnboardingTextItem(
icon: "checkmark.circle.fill",
title: "onboarding.tour.services.title",
description: "onboarding.tour.services"
)
OnboardingTextItem(
icon: "list.bullet.circle.fill",
title: "onboarding.tour.domains.title",
@ -79,6 +87,7 @@ struct OnboardingView: View {
)
}
}.padding()
VStack(spacing: 20) {
HStack {
Image(systemName: "questionmark.circle.fill")

View File

@ -32,11 +32,16 @@ class WarningManager {
),
Warning(
command: {
!Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") &&
!FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
},
name: "`/usr/local/bin` not writable",
name: "Helpers cannot be symlinked and not in PATH",
title: "warnings.helper_permissions.title",
paragraphs: ["warnings.helper_permissions.description", "warnings.helper_permissions.unavailable"],
paragraphs: [
"warnings.helper_permissions.description",
"warnings.helper_permissions.unavailable",
"warnings.helper_permissions.symlink"
],
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
)
]
@ -59,7 +64,7 @@ class WarningManager {
for check in self.evaluations {
if await check.applies() {
Log.info("[WARNING] \(check.name)")
Log.info("[DOCTOR] \(check.name) (!)")
self.warnings.append(check)
continue
}

View File

@ -74,6 +74,8 @@
"mi_no_presets" = "No presets available.";
"mi_set_up_presets" = "Learn more about presets...";
"mi_view_onboarding" = "Show Welcome Tour...";
"mi_xdebug_available_modes" = "Available Modes";
"mi_xdebug_actions" = "Actions";
"mi_xdebug_disable_all" = "Disable All Modes";
@ -518,7 +520,8 @@ If you are seeing this message but are confused why this folder has gone missing
"warnings.helper_permissions.title" = "PHP Monitors helpers are currently unavailable.";
"warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version.";
"warnings.helper_permissions.unavailable" = "However, these helpers are currently *unavailable* because PHP Monitor could not create the required symlinks (alternatively, you could add PHP Monitor's helper directory to your `PATH` variable to make this warning go away as well).";
"warnings.helper_permissions.unavailable" = "However, these helpers are potentially *unavailable* because PHP Monitor cannot currently create or update the required symlinks.";
"warnings.helper_permissions.symlink" = "If you do not wish to make `/usr/local/bin` writable, you can add PHP Monitor's helper directory to your `PATH` variable to make this warning go away. (Click on ”Learn More” to find out how to fix this issue.)";
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
@ -527,13 +530,15 @@ If you are seeing this message but are confused why this folder has gone missing
"onboarding.title" = "Welcome Tour";
"onboarding.welcome" = "Welcome to PHP Monitor!";
"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer.";
"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer. You can find a more comprehensive list of features on GitHub.";
"onboarding.tour.menu_bar.title" = "Get Started";
"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate config files, and more.";
"onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers.";
"onboarding.tour.domains.title" = "Domains";
"onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked.";
"onboarding.tour.isolation.title" = "Isolation";
"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain!";
"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon.";
"onboarding.tour.services.title" = "Manage Services";
"onboarding.tour.services" = "Once you click on the menu bar item, you can see at a glance based on the checkmarks or crosses if all of the Homebrew services are up and running. You can also click on a service to quickly toggle it. You can also add your own!";
"onboarding.tour.domains.title" = "Manage Domains";
"onboarding.tour.domains" = "By opening the Domains window via the menu bar item, you can view which domains are linked and parked, as well as active nginx proxies.";
"onboarding.tour.isolation.title" = "Isolate Domains";
"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain, and that domain only!";
"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon (under First Aid & Services).";
"onboarding.tour.close" = "Close Tour";