From ed493622919927d3c194454c255520506ceb08ff Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 18 Dec 2021 19:45:50 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20option=20to=20automatically?= =?UTF-8?q?=20run=20`composer=20global=20update`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 28 +++- .../Domain/Helpers/PMWindowController.swift | 20 +-- phpmon/Domain/Menu/MainMenu.swift | 71 ++++++--- phpmon/Domain/Preferences/Preferences.swift | 5 +- phpmon/Domain/Preferences/PrefsVC.swift | 7 + phpmon/Domain/Preferences/PrefsWC.swift | 6 - .../Domain/Progress/ProgressWindow.storyboard | 135 ++++++++++++++++++ phpmon/Domain/Progress/ProgressWindow.swift | 76 ++++++++++ phpmon/Domain/Terminal/Shell.swift | 9 ++ phpmon/Localizable.strings | 11 +- 10 files changed, 328 insertions(+), 40 deletions(-) create mode 100644 phpmon/Domain/Progress/ProgressWindow.storyboard create mode 100644 phpmon/Domain/Progress/ProgressWindow.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 71dde44..67bc156 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -46,6 +46,10 @@ C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; }; C43A8A2025D9D1D700591B77 /* brew.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew.json */; }; C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; }; + C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; + C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; + C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; + C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; }; C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; }; C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; }; @@ -164,6 +168,8 @@ C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; }; C43A8A1F25D9D1D700591B77 /* brew.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = brew.json; sourceTree = ""; }; C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = ""; }; + C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = ""; }; + C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = ""; }; C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = ""; }; C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = ""; }; C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = ""; }; @@ -325,12 +331,22 @@ C47331A0247093AC009A0597 /* Menu */, C464ADAA275A7A25003FCD53 /* SiteList */, 5420395726135DB800FB00FA /* Preferences */, + C44C198F276E3A380072762D /* Progress */, C4811D2822D70D9C00B5F6B3 /* Helpers */, C4F8C0A222D4F100002EFE61 /* Extensions */, ); path = Domain; sourceTree = ""; }; + C44C198F276E3A380072762D /* Progress */ = { + isa = PBXGroup; + children = ( + C44C198C276E3A1C0072762D /* ProgressWindow.swift */, + C44C1990276E44CB0072762D /* ProgressWindow.storyboard */, + ); + path = Progress; + sourceTree = ""; + }; C464ADAA275A7A25003FCD53 /* SiteList */ = { isa = PBXGroup; children = ( @@ -544,6 +560,7 @@ C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */, C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */, C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */, + C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */, C4232EE52612526500158FC6 /* Credits.html in Resources */, 54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */, C473319F2470923A009A0597 /* Localizable.strings in Resources */, @@ -562,6 +579,7 @@ C4F780A825D80AE8000DBC97 /* php.ini in Resources */, C43A8A2025D9D1D700591B77 /* brew.json in Resources */, C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */, + C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -589,6 +607,7 @@ C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C42295DD2358D02000E263B2 /* Command.swift in Sources */, + C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, 54B48B5F275F66AE006D90C5 /* Application.swift in Sources */, C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, @@ -661,6 +680,7 @@ C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */, + C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, @@ -825,7 +845,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 135; + CURRENT_PROJECT_VERSION = 200; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -834,7 +854,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "4.1-rc4"; + MARKETING_VERSION = "5.0-wip"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -850,7 +870,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 135; + CURRENT_PROJECT_VERSION = 200; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -859,7 +879,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = "4.1-rc4"; + MARKETING_VERSION = "5.0-wip"; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/phpmon/Domain/Helpers/PMWindowController.swift b/phpmon/Domain/Helpers/PMWindowController.swift index 3165a36..d6c8f89 100644 --- a/phpmon/Domain/Helpers/PMWindowController.swift +++ b/phpmon/Domain/Helpers/PMWindowController.swift @@ -25,6 +25,18 @@ class PMWindowController: NSWindowController, NSWindowDelegate { App.shared.register(window: windowName) } + func windowWillClose(_ notification: Notification) { + App.shared.remove(window: windowName) + } + + deinit { + print("Window controller '\(windowName)' was deinitialized") + } + +} + +extension NSWindowController { + public func positionWindowInTopLeftCorner() { guard let frame = NSScreen.main?.frame else { return } guard let window = self.window else { return } @@ -37,12 +49,4 @@ class PMWindowController: NSWindowController, NSWindowDelegate { ), display: true) } - func windowWillClose(_ notification: Notification) { - App.shared.remove(window: windowName) - } - - deinit { - print("Window controller '\(windowName)' was deinitialized") - } - } diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index 5c6eb4b..3a21662 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -328,6 +328,16 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } @objc func updateComposerDependencies() { + self.updateGlobalDependencies(notify: true, completion: { _ in }) + } + + func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { + var window: ProgressWindowController? = ProgressWindowController.display( + title: "alert.composer_progress.title".localized, + description: "alert.composer_progress.info".localized + ) + window?.setType(info: true) + DispatchQueue.global(qos: .userInitiated).async { let output = Shell.user.executeSynchronously( "composer global update", requiresPath: true @@ -335,33 +345,49 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { let task = Shell.user.createTask(for: "composer global update", requiresPath: true) + DispatchQueue.main.async { + window?.addToConsole("composer global update\n") + } + Shell.captureOutput( task, didReceiveStdOutData: { string in + DispatchQueue.main.async { + window?.addToConsole(string) + } print("\(string.trimmingCharacters(in: .newlines))") }, didReceiveStdErrData: { string in + DispatchQueue.main.async { + window?.addToConsole(string) + } print("\(string.trimmingCharacters(in: .newlines))") } ) task.launch() task.waitUntilExit() + Shell.haltCapturingOutput(task) DispatchQueue.main.async { - if output.task.terminationStatus > 0 { - // Error code means > 0 - Alert.notify( - message: "alert.composer_failure.title".localized, - info: "alert.composer_failure.info".localized, - style: .critical - ) + if output.task.terminationStatus <= 0 { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + window?.close() + if (notify) { + LocalNotification.send( + title: "alert.composer_success.title".localized, + subtitle: "alert.composer_success.info".localized + ) + } + window = nil + completion(true) + } } else { - // Error code -1 is OK - LocalNotification.send( - title: "alert.composer_success.title".localized, - subtitle: "alert.composer_success.info".localized - ) + window?.setType(info: false) + window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized + window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized + window = nil + completion(false) } } } @@ -406,13 +432,22 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { updatePhpVersionInStatusBar() update() - // Send a notification that the switch has been completed - LocalNotification.send( - title: String(format: "notification.version_changed_title".localized, sender.version), - subtitle: String(format: "notification.version_changed_desc".localized, sender.version) - ) + let sendLocalNotification = { + LocalNotification.send( + title: String(format: "notification.version_changed_title".localized, sender.version), + subtitle: String(format: "notification.version_changed_desc".localized, sender.version) + ) + App.phpInstall?.notifyAboutBrokenPhpFpm() + } - App.phpInstall?.notifyAboutBrokenPhpFpm() + // Run composer updates + if Preferences.preferences[.autoComposerGlobalUpdateAfterSwitch] as! Bool == true { + self.updateGlobalDependencies(notify: false, completion: { success in + sendLocalNotification() + }) + } else { + sendLocalNotification() + } } } diff --git a/phpmon/Domain/Preferences/Preferences.swift b/phpmon/Domain/Preferences/Preferences.swift index 4cb9efc..7897c06 100644 --- a/phpmon/Domain/Preferences/Preferences.swift +++ b/phpmon/Domain/Preferences/Preferences.swift @@ -13,6 +13,7 @@ enum PreferenceName: String { case shouldDisplayDynamicIcon = "use_dynamic_icon" case fullPhpVersionDynamicIcon = "full_php_in_menu_bar" case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle" + case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch" case useInternalSwitcher = "use_phpmon_switcher" case globalHotkey = "global_hotkey" } @@ -47,7 +48,8 @@ class Preferences { PreferenceName.shouldDisplayDynamicIcon.rawValue: true, PreferenceName.fullPhpVersionDynamicIcon.rawValue: false, PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true, - PreferenceName.useInternalSwitcher.rawValue: false + PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false, + PreferenceName.useInternalSwitcher.rawValue: false, ]) if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) { @@ -72,6 +74,7 @@ class Preferences { .shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any, .fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any, .autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any, + .autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any, .useInternalSwitcher: UserDefaults.standard.bool(forKey: PreferenceName.useInternalSwitcher.rawValue) as Any, // Part 2: Always Strings diff --git a/phpmon/Domain/Preferences/PrefsVC.swift b/phpmon/Domain/Preferences/PrefsVC.swift index ee3208d..d8603b1 100644 --- a/phpmon/Domain/Preferences/PrefsVC.swift +++ b/phpmon/Domain/Preferences/PrefsVC.swift @@ -74,6 +74,13 @@ class PrefsVC: NSViewController { preference: .autoServiceRestartAfterExtensionToggle, action: {} ), + CheckboxPreferenceView.make( + sectionText: "prefs.switcher".localized, + descriptionText: "prefs.auto_composer_update_desc".localized, + checkboxText: "prefs.auto_composer_update_title".localized, + preference: .autoComposerGlobalUpdateAfterSwitch, + action: {} + ), /* DISABLED UNTIL VALET SWITCHING IS OK (see #34) CheckboxPreferenceView.make( sectionText: "", diff --git a/phpmon/Domain/Preferences/PrefsWC.swift b/phpmon/Domain/Preferences/PrefsWC.swift index a2c69c3..f2ef5b9 100644 --- a/phpmon/Domain/Preferences/PrefsWC.swift +++ b/phpmon/Domain/Preferences/PrefsWC.swift @@ -21,12 +21,6 @@ class PrefsWC: PMWindowController { return "Preferences" } - // MARK: - Window Lifecycle - - override func windowDidLoad() { - super.windowDidLoad() - } - // MARK: - Key Interaction override func keyDown(with event: NSEvent) { diff --git a/phpmon/Domain/Progress/ProgressWindow.storyboard b/phpmon/Domain/Progress/ProgressWindow.storyboard new file mode 100644 index 0000000..d6c2e1d --- /dev/null +++ b/phpmon/Domain/Progress/ProgressWindow.storyboard @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpmon/Domain/Progress/ProgressWindow.swift b/phpmon/Domain/Progress/ProgressWindow.swift new file mode 100644 index 0000000..5b9bbec --- /dev/null +++ b/phpmon/Domain/Progress/ProgressWindow.swift @@ -0,0 +1,76 @@ +// +// ProgressView.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 18/12/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation +import AppKit + +class ProgressWindowController: NSWindowController, NSWindowDelegate { + + static func display(title: String, description: String) -> ProgressWindowController { + let storyboard = NSStoryboard(name: "ProgressWindow" , bundle : nil) + + let windowController = storyboard.instantiateController( + withIdentifier: "progressWindow" + ) as! ProgressWindowController + + windowController.showWindow(windowController) + windowController.window?.makeKeyAndOrderFront(nil) + windowController.positionWindowInTopLeftCorner() + + windowController.progressView?.labelTitle.stringValue = title + windowController.progressView?.labelDescription.stringValue = description + + NSApp.activate(ignoringOtherApps: true) + + return windowController + } + + var progressView: ProgressViewController? { + return self.contentViewController as? ProgressViewController + } + + public func addToConsole(_ string: String) { + guard let textView = self.progressView?.textView else { + return + } + + textView.string = textView.string + string + textView.scrollToEndOfDocument(nil) + } + + public func setType(info: Bool = true) { + guard let imageView = self.progressView?.imageViewType else { + return + } + + imageView.image = NSImage(named: info ? "NSInfo" : "NSCaution") + } + + func windowWillClose(_ notification: Notification) { + self.contentViewController = nil + } + + deinit { + print("Deinitializing Progress Window Controller") + } + +} + +class ProgressViewController: NSViewController { + + @IBOutlet weak var labelTitle: NSTextField! + @IBOutlet weak var labelDescription: NSTextField! + + @IBOutlet var textView: NSTextView! + @IBOutlet weak var imageViewType: NSImageView! + + deinit { + print("Deinitializing Progress View Controller") + } + +} diff --git a/phpmon/Domain/Terminal/Shell.swift b/phpmon/Domain/Terminal/Shell.swift index 4f26e7c..fd4d650 100644 --- a/phpmon/Domain/Terminal/Shell.swift +++ b/phpmon/Domain/Terminal/Shell.swift @@ -153,6 +153,15 @@ class Shell { } } } + + 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 { diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 916b79c..589d46c 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -90,6 +90,7 @@ "prefs.global_shortcut" = "Global shortcut:"; "prefs.dynamic_icon" = "Dynamic icon:"; "prefs.services" = "Services:"; +"prefs.switcher" = "Switcher:"; "prefs.auto_restart_services_title" = "Auto-restart PHP-FPM"; "prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when\nyou check or uncheck an extension. Slightly slower when enabled, \nbut this applies the extension change immediately for all sites \nyou're serving, no need to restart PHP-FPM manually."; @@ -108,6 +109,9 @@ switcher or are having issues with `valet use php`, you may use PHP Monitor's own switcher which is slightly faster, but might cause issues with permissions in your Homebrew directories, since PHP Monitor controls the services."; + +"prefs.auto_composer_update_title" = "Automatically run `composer global update`"; +"prefs.auto_composer_update_desc" = "When checked, will automatically ask Composer to run\n`global update` whenever you switch versions. This will update\nall global dependencies every time you switch."; "prefs.shortcut_set" = "Set global shortcut"; "prefs.shortcut_listening" = ""; @@ -131,13 +135,14 @@ directories, since PHP Monitor controls the services."; // ALERTS // Composer Update +"alert.composer_progress.title" = "Updating global dependencies..."; +"alert.composer_progress.info" = "Your global Composer dependencies are being updated. Please wait a bit!"; + "alert.composer_success.title" = "Global dependencies updated"; "alert.composer_success.info" = "Your global Composer dependencies have been updated."; "alert.composer_failure.title" = "Updating global dependencies failed"; -"alert.composer_failure.info" = "Something went wrong updating your global Composer dependencies. - -To find out what went wrong, try running `composer global update` in a terminal window."; +"alert.composer_failure.info" = "Something went wrong updating your global Composer dependencies. You can find more information in the terminal output below. You’ll have to manually fix this problem in your own terminal."; // Force Reload Started "alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP";