mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-10 21:10:08 +02:00
👌 Async switcher (Swift concurrency)
This commit is contained in:
@@ -117,14 +117,10 @@ class Actions {
|
|||||||
If this does not solve the issue, the user may need to install additional
|
If this does not solve the issue, the user may need to install additional
|
||||||
extensions and/or run `composer global update`.
|
extensions and/or run `composer global update`.
|
||||||
*/
|
*/
|
||||||
public static func fixMyValet(completed: @escaping () -> Void) {
|
public static func fixMyValet() async {
|
||||||
InternalSwitcher().performSwitch(to: PhpEnv.brewPhpAlias, completion: {
|
await InternalSwitcher().performSwitch(to: PhpEnv.brewPhpAlias)
|
||||||
Task { // Restart all services asynchronously and fire callback upon completion
|
|
||||||
await brew("services restart \(Homebrew.Formulae.dnsmasq)", sudo: Homebrew.Formulae.dnsmasq.elevated)
|
await brew("services restart \(Homebrew.Formulae.dnsmasq)", sudo: Homebrew.Formulae.dnsmasq.elevated)
|
||||||
await brew("services restart \(Homebrew.Formulae.php)", sudo: Homebrew.Formulae.php.elevated)
|
await brew("services restart \(Homebrew.Formulae.php)", sudo: Homebrew.Formulae.php.elevated)
|
||||||
await brew("services restart \(Homebrew.Formulae.nginx)", sudo: Homebrew.Formulae.nginx.elevated)
|
await brew("services restart \(Homebrew.Formulae.nginx)", sudo: Homebrew.Formulae.nginx.elevated)
|
||||||
completed()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,8 +42,8 @@ class ActivePhpInstallation {
|
|||||||
do {
|
do {
|
||||||
try determineVersion()
|
try determineVersion()
|
||||||
} catch {
|
} catch {
|
||||||
// TODO: Throw up an alert if the PHP version cannot be parsed
|
#warning("In future versions of PHP Monitor, this should not crash")
|
||||||
fatalError("Could not determine or parse PHP version")
|
fatalError("Could not determine or parse PHP version; aborting")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the list of ini files that are loaded
|
// Initialize the list of ini files that are loaded
|
||||||
|
@@ -88,7 +88,7 @@ class PhpExtension {
|
|||||||
|
|
||||||
if !isRunningTests {
|
if !isRunningTests {
|
||||||
// When running unit tests, the MainMenu will not be available
|
// When running unit tests, the MainMenu will not be available
|
||||||
// TODO: Fix this dependency issue, set up a notification mechanism
|
// TODO: Investigate an alternate approach w/ notification or publishable
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
MainMenu.shared.rebuild()
|
MainMenu.shared.rebuild()
|
||||||
}
|
}
|
||||||
|
@@ -19,31 +19,31 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
Please note that depending on which version is installed,
|
Please note that depending on which version is installed,
|
||||||
the version that is switched to may or may not be identical to `php`
|
the version that is switched to may or may not be identical to `php`
|
||||||
(without @version).
|
(without @version).
|
||||||
|
|
||||||
TODO: Use `async` and use structured concurrency: https://www.hackingwithswift.com/swift/5.5/structured-concurrency
|
|
||||||
*/
|
*/
|
||||||
func performSwitch(to version: String, completion: @escaping () -> Void) {
|
func performSwitch(to version: String) async {
|
||||||
Log.info("Switching to \(version), unlinking all versions...")
|
Log.info("Switching to \(version), unlinking all versions...")
|
||||||
|
|
||||||
let versions = getVersionsToBeHandled(version)
|
let versions = getVersionsToBeHandled(version)
|
||||||
let group = DispatchGroup()
|
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
await withTaskGroup(of: String.self, body: { group in
|
||||||
group.enter()
|
for available in PhpEnv.shared.availablePhpVersions {
|
||||||
|
group.addTask {
|
||||||
Task {
|
|
||||||
await self.disableDefaultPhpFpmPool(available)
|
await self.disableDefaultPhpFpmPool(available)
|
||||||
await self.stopPhpVersion(available)
|
await self.stopPhpVersion(available)
|
||||||
group.leave()
|
return available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.notify(queue: .global(qos: .userInitiated)) {
|
var unlinked: [String] = []
|
||||||
Task {
|
for await version in group {
|
||||||
Log.info("All versions have been unlinked!")
|
unlinked.append(version)
|
||||||
Log.info("Linking the new version!")
|
}
|
||||||
|
|
||||||
|
Log.info("These versions have been unlinked: \(unlinked)")
|
||||||
|
Log.info("Linking the new version \(version)!")
|
||||||
|
|
||||||
for formula in versions {
|
for formula in versions {
|
||||||
|
Log.info("Will start PHP \(version)... (primary: \(version == formula))")
|
||||||
await self.startPhpVersion(formula, primary: (version == formula))
|
await self.startPhpVersion(formula, primary: (version == formula))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,9 +51,7 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
await brew("services restart nginx", sudo: true)
|
await brew("services restart nginx", sudo: true)
|
||||||
|
|
||||||
Log.info("The new version(s) have been linked!")
|
Log.info("The new version(s) have been linked!")
|
||||||
completion()
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVersionsToBeHandled(_ primary: String) -> Set<String> {
|
func getVersionsToBeHandled(_ primary: String) -> Set<String> {
|
||||||
|
@@ -18,6 +18,6 @@ protocol PhpSwitcherDelegate: AnyObject {
|
|||||||
|
|
||||||
protocol PhpSwitcher {
|
protocol PhpSwitcher {
|
||||||
|
|
||||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
func performSwitch(to version: String) async
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -29,8 +29,10 @@ class FakeServicesManager: ServicesManager {
|
|||||||
self.services = []
|
self.services = []
|
||||||
self.reapplyServices()
|
self.reapplyServices()
|
||||||
|
|
||||||
|
Task { @MainActor in
|
||||||
self.firstRunComplete = true
|
self.firstRunComplete = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func reapplyServices() {
|
private func reapplyServices() {
|
||||||
let services = self.formulae.map {
|
let services = self.formulae.map {
|
||||||
|
@@ -15,7 +15,7 @@ class ServicesManager: ObservableObject {
|
|||||||
|
|
||||||
@Published var services = [Service]()
|
@Published var services = [Service]()
|
||||||
|
|
||||||
@Published var firstRunComplete: Bool = false
|
@Published @MainActor var firstRunComplete: Bool = false
|
||||||
|
|
||||||
public static func useFake() {
|
public static func useFake() {
|
||||||
ServicesManager.shared = FakeServicesManager.init(
|
ServicesManager.shared = FakeServicesManager.init(
|
||||||
|
@@ -15,9 +15,12 @@ class ValetServicesManager: ServicesManager {
|
|||||||
// Load the initial services state
|
// Load the initial services state
|
||||||
Task {
|
Task {
|
||||||
await self.reloadServicesStatus()
|
await self.reloadServicesStatus()
|
||||||
|
|
||||||
|
Task { @MainActor in
|
||||||
firstRunComplete = true
|
firstRunComplete = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The last known state of all Homebrew services.
|
The last known state of all Homebrew services.
|
||||||
|
@@ -142,17 +142,14 @@ class Valet {
|
|||||||
in use. This allows PHP Monitor to do different things when Valet 3.0 is enabled.
|
in use. This allows PHP Monitor to do different things when Valet 3.0 is enabled.
|
||||||
*/
|
*/
|
||||||
public func evaluateFeatureSupport() {
|
public func evaluateFeatureSupport() {
|
||||||
let isVersion2 = version.isSameMajorVersionAs(try! VersionNumber.parse("2.0"))
|
switch version.major {
|
||||||
let isVersion3 = version.isSameMajorVersionAs(try! VersionNumber.parse("3.0"))
|
case 2:
|
||||||
let isVersion4 = version.isSameMajorVersionAs(try! VersionNumber.parse("4.0"))
|
|
||||||
|
|
||||||
if isVersion2 {
|
|
||||||
Log.info("You are running Valet v2. Support for site isolation is disabled.")
|
Log.info("You are running Valet v2. Support for site isolation is disabled.")
|
||||||
} else if isVersion3 || isVersion4 {
|
case 3, 4:
|
||||||
Log.info("You are running Valet v3 or v4. Support for site isolation is available.")
|
Log.info("You are running Valet v\(version.major). Support for site isolation is available.")
|
||||||
self.features.append(.isolatedSites)
|
self.features.append(.isolatedSites)
|
||||||
} else {
|
default:
|
||||||
// TODO: Show an alert and notify that some features might not work
|
#warning("An alert should be presented here")
|
||||||
Log.err("This version of Valet is not supported.")
|
Log.err("This version of Valet is not supported.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -240,15 +240,12 @@ extension MainMenu {
|
|||||||
Task(priority: .userInitiated) { [unowned self] in
|
Task(priority: .userInitiated) { [unowned self] in
|
||||||
updatePhpVersionInStatusBar()
|
updatePhpVersionInStatusBar()
|
||||||
rebuild()
|
rebuild()
|
||||||
PhpEnv.switcher.performSwitch(
|
await PhpEnv.switcher.performSwitch(to: version)
|
||||||
to: version,
|
|
||||||
completion: {
|
|
||||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
App.shared.handlePhpConfigWatcher()
|
App.shared.handlePhpConfigWatcher()
|
||||||
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Async
|
// MARK: - Async
|
||||||
@@ -259,8 +256,6 @@ extension MainMenu {
|
|||||||
await MainMenu.shared.switchToPhp("8.1")
|
await MainMenu.shared.switchToPhp("8.1")
|
||||||
// thing to do after the switch
|
// thing to do after the switch
|
||||||
```
|
```
|
||||||
Since this async function uses `withCheckedContinuation`
|
|
||||||
any code after will run only after the switcher is done.
|
|
||||||
*/
|
*/
|
||||||
func switchToPhp(_ version: String) async {
|
func switchToPhp(_ version: String) async {
|
||||||
Task { @MainActor [self] in
|
Task { @MainActor [self] in
|
||||||
@@ -270,19 +265,13 @@ extension MainMenu {
|
|||||||
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await withCheckedContinuation({ continuation in
|
|
||||||
updatePhpVersionInStatusBar()
|
updatePhpVersionInStatusBar()
|
||||||
rebuild()
|
rebuild()
|
||||||
PhpEnv.switcher.performSwitch(
|
await PhpEnv.switcher.performSwitch(to: version)
|
||||||
to: version,
|
|
||||||
completion: {
|
|
||||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
App.shared.handlePhpConfigWatcher()
|
App.shared.handlePhpConfigWatcher()
|
||||||
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
continuation.resume()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -31,8 +31,9 @@ extension MainMenu {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Actions.fixMyValet {
|
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
|
await Actions.fixMyValet()
|
||||||
|
|
||||||
if previousVersion == PhpEnv.brewPhpAlias {
|
if previousVersion == PhpEnv.brewPhpAlias {
|
||||||
self.presentAlertForSameVersion()
|
self.presentAlertForSameVersion()
|
||||||
} else {
|
} else {
|
||||||
@@ -40,7 +41,6 @@ extension MainMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor private func presentAlertForMissingFormula() {
|
@MainActor private func presentAlertForMissingFormula() {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
|
@@ -137,7 +137,9 @@ struct ServiceView: View {
|
|||||||
? Color("IconColorGreen")
|
? Color("IconColorGreen")
|
||||||
: Color("IconColorRed")
|
: Color("IconColorRed")
|
||||||
)
|
)
|
||||||
}.frame(width: 25, height: 25)
|
}
|
||||||
|
.focusable(false)
|
||||||
|
.frame(width: 25, height: 25)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.frame(minWidth: 70)
|
}.frame(minWidth: 70)
|
||||||
|
Reference in New Issue
Block a user