mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
✨ Check if running the app with Rosetta
This commit is contained in:
@ -166,6 +166,8 @@
|
|||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
|
C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
|
||||||
|
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
|
||||||
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
|
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
|
||||||
C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
|
C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
|
||||||
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
||||||
@ -398,6 +400,7 @@
|
|||||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
||||||
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
||||||
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
||||||
|
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentCheck.swift; sourceTree = "<group>"; };
|
||||||
C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
||||||
@ -890,6 +893,7 @@
|
|||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
|
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
|
||||||
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
|
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
|
||||||
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
|
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
|
||||||
C45E76132854A65300B4FE0C /* ServicesManager.swift */,
|
C45E76132854A65300B4FE0C /* ServicesManager.swift */,
|
||||||
@ -1322,6 +1326,7 @@
|
|||||||
C422DDAD28A2DAC600CEAC97 /* WarningsWindowController.swift in Sources */,
|
C422DDAD28A2DAC600CEAC97 /* WarningsWindowController.swift in Sources */,
|
||||||
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
||||||
|
C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */,
|
||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||||
@ -1453,6 +1458,7 @@
|
|||||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
|
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
|
||||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
||||||
|
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */,
|
||||||
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
|
@ -68,6 +68,9 @@ class App {
|
|||||||
/** The services manager, responsible for figuring out what services are active/inactive. */
|
/** The services manager, responsible for figuring out what services are active/inactive. */
|
||||||
var services = ServicesManager.shared
|
var services = ServicesManager.shared
|
||||||
|
|
||||||
|
/** The warning manager, responsible for keeping track of warnings. */
|
||||||
|
var warnings = WarningManager.shared
|
||||||
|
|
||||||
/** Timer that will periodically reload info about the user's PHP installation. */
|
/** Timer that will periodically reload info about the user's PHP installation. */
|
||||||
var timer: Timer?
|
var timer: Timer?
|
||||||
|
|
||||||
|
45
phpmon/Domain/App/EnvironmentCheck.swift
Normal file
45
phpmon/Domain/App/EnvironmentCheck.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// EnvironmentCheck.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/08/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
struct EnvironmentCheck {
|
||||||
|
let command: () async -> Bool
|
||||||
|
let name: String
|
||||||
|
let titleText: String
|
||||||
|
let subtitleText: String
|
||||||
|
let descriptionText: String
|
||||||
|
let buttonText: String
|
||||||
|
let requiresAppRestart: Bool
|
||||||
|
|
||||||
|
init(
|
||||||
|
command: @escaping () async -> Bool,
|
||||||
|
name: String,
|
||||||
|
titleText: String,
|
||||||
|
subtitleText: String,
|
||||||
|
descriptionText: String = "",
|
||||||
|
buttonText: String = "OK",
|
||||||
|
requiresAppRestart: Bool = false
|
||||||
|
) {
|
||||||
|
self.command = command
|
||||||
|
self.name = name
|
||||||
|
self.titleText = titleText
|
||||||
|
self.subtitleText = subtitleText
|
||||||
|
self.descriptionText = descriptionText
|
||||||
|
self.buttonText = buttonText
|
||||||
|
self.requiresAppRestart = requiresAppRestart
|
||||||
|
}
|
||||||
|
|
||||||
|
public func succeeds() async -> Bool {
|
||||||
|
return await !self.command()
|
||||||
|
}
|
||||||
|
}
|
@ -238,42 +238,4 @@ class Startup {
|
|||||||
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
// MARK: - EnvironmentCheck struct
|
|
||||||
|
|
||||||
/**
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
struct EnvironmentCheck {
|
|
||||||
let command: () async -> Bool
|
|
||||||
let name: String
|
|
||||||
let titleText: String
|
|
||||||
let subtitleText: String
|
|
||||||
let descriptionText: String
|
|
||||||
let buttonText: String
|
|
||||||
let requiresAppRestart: Bool
|
|
||||||
|
|
||||||
init(
|
|
||||||
command: @escaping () async -> Bool,
|
|
||||||
name: String,
|
|
||||||
titleText: String,
|
|
||||||
subtitleText: String,
|
|
||||||
descriptionText: String = "",
|
|
||||||
buttonText: String = "OK",
|
|
||||||
requiresAppRestart: Bool = false
|
|
||||||
) {
|
|
||||||
self.command = command
|
|
||||||
self.name = name
|
|
||||||
self.titleText = titleText
|
|
||||||
self.subtitleText = subtitleText
|
|
||||||
self.descriptionText = descriptionText
|
|
||||||
self.buttonText = buttonText
|
|
||||||
self.requiresAppRestart = requiresAppRestart
|
|
||||||
}
|
|
||||||
|
|
||||||
public func succeeds() async -> Bool {
|
|
||||||
return await !self.command()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -79,21 +79,25 @@ extension MainMenu {
|
|||||||
// A non-default TLD is not officially supported since Valet 3.2.x
|
// A non-default TLD is not officially supported since Valet 3.2.x
|
||||||
Valet.notifyAboutUnsupportedTLD()
|
Valet.notifyAboutUnsupportedTLD()
|
||||||
|
|
||||||
|
// Find out which services are active
|
||||||
ServicesManager.shared.loadData()
|
ServicesManager.shared.loadData()
|
||||||
|
|
||||||
// Start the background refresh timer
|
// Start the background refresh timer
|
||||||
startSharedTimer()
|
startSharedTimer()
|
||||||
|
|
||||||
|
// Check warnings
|
||||||
|
WarningManager.shared.evaluateWarnings()
|
||||||
|
|
||||||
// Update the stats
|
// Update the stats
|
||||||
Stats.incrementSuccessfulLaunchCount()
|
Stats.incrementSuccessfulLaunchCount()
|
||||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||||
|
|
||||||
// Present first launch screen if needed
|
// Present first launch screen if needed
|
||||||
#warning("The launch screen will be presented every time you launch the app.")
|
#warning("You should definitely tweak this view again")
|
||||||
if Stats.successfulLaunchCount >= 1 && !isRunningSwiftUIPreview {
|
if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview {
|
||||||
Log.info("Should present the first launch screen!")
|
Log.info("Should present the first launch screen!")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// OnboardingWindowController.show()
|
OnboardingWindowController.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
menu.addRemainingMenuItems()
|
menu.addRemainingMenuItems()
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addWarningsMenuItem()
|
||||||
menu.addCoreMenuItems()
|
menu.addCoreMenuItems()
|
||||||
|
|
||||||
menu.items.forEach({ (item) in
|
menu.items.forEach({ (item) in
|
||||||
|
@ -74,12 +74,21 @@ class StatusMenu: NSMenu {
|
|||||||
self.addFirstAidAndServicesMenuItems()
|
self.addFirstAidAndServicesMenuItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addWarningsMenuItem() {
|
||||||
|
if !WarningManager.shared.hasWarnings() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
let count = WarningManager.shared.warnings.count
|
||||||
|
self.addItem(NSMenuItem(title: (count == 1 ? "mi_warning" : "mi_warnings").localized(count),
|
||||||
|
action: #selector(MainMenu.openWarnings), keyEquivalent: ""))
|
||||||
|
}
|
||||||
|
|
||||||
func addCoreMenuItems() {
|
func addCoreMenuItems() {
|
||||||
self.addItem(NSMenuItem.separator())
|
self.addItem(NSMenuItem.separator())
|
||||||
if (WarningManager.hasWarnings()) {
|
|
||||||
self.addItem(NSMenuItem(title: "mi_warnings".localized(2),
|
|
||||||
action: #selector(MainMenu.openWarnings), keyEquivalent: ""))
|
|
||||||
}
|
|
||||||
self.addItem(NSMenuItem(title: "mi_preferences".localized,
|
self.addItem(NSMenuItem(title: "mi_preferences".localized,
|
||||||
action: #selector(MainMenu.openPrefs), keyEquivalent: ","))
|
action: #selector(MainMenu.openPrefs), keyEquivalent: ","))
|
||||||
self.addItem(NSMenuItem(title: "mi_check_for_updates".localized,
|
self.addItem(NSMenuItem(title: "mi_check_for_updates".localized,
|
||||||
|
@ -12,17 +12,14 @@ struct WarningListView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
WarningView(
|
ForEach(WarningManager.shared.warnings) { warning in
|
||||||
title: "warnings.arm_compatibility_title",
|
WarningView(
|
||||||
description: "warnings.arm_compatibility.description",
|
title: warning.titleText,
|
||||||
documentationUrl: "https://phpmon.app/documentation/apple-silicon-transition"
|
description: warning.descriptionText,
|
||||||
)
|
documentationUrl: warning.url
|
||||||
Divider()
|
)
|
||||||
WarningView(
|
Divider()
|
||||||
title: "warnings.helper_permissions_title",
|
}
|
||||||
description: "warnings.helper_permissions.description"
|
|
||||||
)
|
|
||||||
Divider()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,29 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct Warning {
|
struct Warning: Identifiable {
|
||||||
|
var id = UUID()
|
||||||
|
let command: () async -> Bool
|
||||||
|
let name: String
|
||||||
|
let titleText: String
|
||||||
|
let descriptionText: String
|
||||||
|
let url: String?
|
||||||
|
|
||||||
|
init(
|
||||||
|
command: @escaping () async -> Bool,
|
||||||
|
name: String,
|
||||||
|
titleText: String,
|
||||||
|
descriptionText: String,
|
||||||
|
url: String?
|
||||||
|
) {
|
||||||
|
self.command = command
|
||||||
|
self.name = name
|
||||||
|
self.titleText = titleText
|
||||||
|
self.descriptionText = descriptionText
|
||||||
|
self.url = url
|
||||||
|
}
|
||||||
|
|
||||||
|
public func applies() async -> Bool {
|
||||||
|
return await self.command()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,43 @@ import Foundation
|
|||||||
|
|
||||||
class WarningManager {
|
class WarningManager {
|
||||||
|
|
||||||
// TODO: Singleton initialization at launch
|
static var shared = WarningManager()
|
||||||
|
|
||||||
|
public let evaluations: [Warning] = [
|
||||||
|
Warning(
|
||||||
|
command: {
|
||||||
|
return Shell.pipe("sysctl -n sysctl.proc_translated")
|
||||||
|
.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
|
||||||
|
},
|
||||||
|
name: "Running PHP Monitor with Rosetta on M1",
|
||||||
|
titleText: "warnings.arm_compatibility.title",
|
||||||
|
descriptionText: "warnings.arm_compatibility.description",
|
||||||
|
url: nil
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@Published public var warnings: [Warning] = []
|
||||||
|
|
||||||
|
public func hasWarnings() -> Bool {
|
||||||
|
return !warnings.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateWarnings() {
|
||||||
|
Task { await WarningManager.shared.checkEnvironment() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks the user's environment and checks if any special warnings apply.
|
||||||
|
*/
|
||||||
|
func checkEnvironment() async {
|
||||||
|
self.warnings = []
|
||||||
|
for check in self.evaluations {
|
||||||
|
if await check.applies() {
|
||||||
|
Log.info("[WARNING] \(check.name)")
|
||||||
|
self.warnings.append(check)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
"mi_detected_extensions" = "Detected Extensions";
|
"mi_detected_extensions" = "Detected Extensions";
|
||||||
"mi_no_extensions_detected" = "No additional extensions detected.";
|
"mi_no_extensions_detected" = "No additional extensions detected.";
|
||||||
|
|
||||||
|
"mi_warning" = "⚠️ %i Warning";
|
||||||
"mi_warnings" = "⚠️ %i Warnings...";
|
"mi_warnings" = "⚠️ %i Warnings...";
|
||||||
|
|
||||||
"mi_valet" = "Laravel Valet";
|
"mi_valet" = "Laravel Valet";
|
||||||
@ -510,10 +511,10 @@ If you are seeing this message but are confused why this folder has gone missing
|
|||||||
|
|
||||||
"warnings.title" = "Warnings";
|
"warnings.title" = "Warnings";
|
||||||
|
|
||||||
"warnings.helper_permissions_title" = "Helpers could not be written!";
|
"warnings.helper_permissions.title" = "Helpers could not be written!";
|
||||||
"warnings.helper_permissions.description" = "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there.";
|
"warnings.helper_permissions.description" = "The helper files in `/usr/local/bin` could not be written because PHP Monitor does not have permission to write there.";
|
||||||
|
|
||||||
"warnings.arm_compatibility_title" = "You are running PHP Monitor using Rosetta";
|
"warnings.arm_compatibility.title" = "You are running PHP Monitor using 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.";
|
"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.";
|
||||||
|
|
||||||
// ONBOARDING
|
// ONBOARDING
|
||||||
|
Reference in New Issue
Block a user