From 0986b97051cb6ce2c6f27ed28742fd432c01d34e Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Mon, 29 Nov 2021 01:17:23 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20`valet`=20command=20?= =?UTF-8?q?(#34),=20read=20Valet=20configuration=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 52 ++++++++++++++++++- phpmon-tests/ValetConfigParserTest.swift | 38 ++++++++++++++ phpmon-tests/ValetTest.swift | 18 +++++++ phpmon-tests/valet-config.json | 8 +++ phpmon/Domain/Core/Actions.swift | 21 ++++++++ .../Homebrew}/HomebrewDiagnostics.swift | 0 .../Homebrew}/HomebrewPackage.swift | 3 +- phpmon/Domain/Integrations/Valet/Valet.swift | 26 ++++++++++ .../Valet/ValetConfiguration.swift | 17 ++++++ phpmon/Domain/Menu/MainMenu.swift | 9 +++- phpmon/Domain/Terminal/Paths.swift | 8 +++ phpmon/Domain/Terminal/Shell.swift | 33 +++++++++--- 12 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 phpmon-tests/ValetConfigParserTest.swift create mode 100644 phpmon-tests/ValetTest.swift create mode 100644 phpmon-tests/valet-config.json rename phpmon/Domain/{Helpers => Integrations/Homebrew}/HomebrewDiagnostics.swift (100%) rename phpmon/Domain/{Helpers => Integrations/Homebrew}/HomebrewPackage.swift (86%) create mode 100644 phpmon/Domain/Integrations/Valet/Valet.swift create mode 100644 phpmon/Domain/Integrations/Valet/ValetConfiguration.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 97615de..f24bd72 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -47,6 +47,14 @@ C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C49EAB46259FC305007F6C3B /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; }; C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; + C4AF9F6E275445F600D44ED0 /* ValetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F6D275445F600D44ED0 /* ValetConfiguration.swift */; }; + C4AF9F6F275445F600D44ED0 /* ValetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F6D275445F600D44ED0 /* ValetConfiguration.swift */; }; + C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; + C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; + C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */; }; + C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; + C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; + C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetTest.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; }; @@ -129,6 +137,11 @@ C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = ""; }; C49EAB45259FC305007F6C3B /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; + C4AF9F6D275445F600D44ED0 /* ValetConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfiguration.swift; sourceTree = ""; }; + C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = ""; }; + C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigParserTest.swift; sourceTree = ""; }; + C4AF9F792754499000D44ED0 /* Valet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valet.swift; sourceTree = ""; }; + C4AF9F7C275454A900D44ED0 /* ValetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetTest.swift; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; @@ -244,6 +257,7 @@ C41E181722CB61EB0072CF09 /* Domain */ = { isa = PBXGroup; children = ( + C4AF9F6B275445D300D44ED0 /* Integrations */, C4B13B1D25C4915000548C3A /* Core */, 54B20EDF263AA22C00D3250E /* PHP */, C4F7808A25D7F918000DBC97 /* Terminal */, @@ -274,10 +288,35 @@ C476FF9722B0DD830098105B /* Alert.swift */, C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */, C474B00524C0E98C00066A22 /* LocalNotification.swift */, + ); + path = Helpers; + sourceTree = ""; + }; + C4AF9F6A275445C900D44ED0 /* Valet */ = { + isa = PBXGroup; + children = ( + C4AF9F792754499000D44ED0 /* Valet.swift */, + C4AF9F6D275445F600D44ED0 /* ValetConfiguration.swift */, + ); + path = Valet; + sourceTree = ""; + }; + C4AF9F6B275445D300D44ED0 /* Integrations */ = { + isa = PBXGroup; + children = ( + C4AF9F6C275445D900D44ED0 /* Homebrew */, + C4AF9F6A275445C900D44ED0 /* Valet */, + ); + path = Integrations; + sourceTree = ""; + }; + C4AF9F6C275445D900D44ED0 /* Homebrew */ = { + isa = PBXGroup; + children = ( C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */, C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, ); - path = Helpers; + path = Homebrew; sourceTree = ""; }; C4B13B1D25C4915000548C3A /* Core */ = { @@ -294,6 +333,7 @@ C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = { isa = PBXGroup; children = ( + C4AF9F70275445FF00D44ED0 /* valet-config.json */, C43A8A1F25D9D1D700591B77 /* brew.json */, C4F780A725D80AE8000DBC97 /* php.ini */, C4F7807D25D7F84B000DBC97 /* Info.plist */, @@ -302,6 +342,8 @@ C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */, C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */, C43A8A1925D9CD1000591B77 /* Utility.swift */, + C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */, + C4AF9F7C275454A900D44ED0 /* ValetTest.swift */, ); path = "phpmon-tests"; sourceTree = ""; @@ -416,6 +458,7 @@ files = ( C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */, C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */, + C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */, C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */, C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */, C4232EE52612526500158FC6 /* Credits.html in Resources */, @@ -431,6 +474,7 @@ files = ( C4F780A825D80AE8000DBC97 /* php.ini in Resources */, C43A8A2025D9D1D700591B77 /* brew.json in Resources */, + C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -445,11 +489,13 @@ C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, + C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, 5420395926135DC100FB00FA /* PrefsVC.swift in Sources */, C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */, C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */, + C4AF9F6E275445F600D44ED0 /* ValetConfiguration.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C42295DD2358D02000E263B2 /* Command.swift in Sources */, @@ -485,6 +531,8 @@ C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */, C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */, + C4AF9F6F275445F600D44ED0 /* ValetConfiguration.swift in Sources */, + C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */, C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */, C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */, C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */, @@ -492,6 +540,7 @@ C42759682627662800093CAE /* NSMenuExtension.swift in Sources */, C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */, C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, + C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */, C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */, C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */, C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */, @@ -500,6 +549,7 @@ C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */, C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */, + C4AF9F7D275454A900D44ED0 /* ValetTest.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, C4F780B725D80B5D000DBC97 /* App.swift in Sources */, C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */, diff --git a/phpmon-tests/ValetConfigParserTest.swift b/phpmon-tests/ValetConfigParserTest.swift new file mode 100644 index 0000000..9e7cf24 --- /dev/null +++ b/phpmon-tests/ValetConfigParserTest.swift @@ -0,0 +1,38 @@ +// +// ValetConfigParserTest.swift +// phpmon-tests +// +// Created by Nico Verbruggen on 29/11/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import XCTest + +class ValetConfigParserTest: XCTestCase { + + static var jsonConfigFileUrl: URL { + return Bundle(for: Self.self).url( + forResource: "valet-config", + withExtension: "json" + )! + } + + func testCanLoadConfigFile() throws { + let json = try? String( + contentsOf: Self.jsonConfigFileUrl, + encoding: .utf8 + ) + let config = try! JSONDecoder().decode( + ValetConfiguration.self, + from: json!.data(using: .utf8)! + ) + + XCTAssertEqual(config.tld, "test") + XCTAssertEqual(config.paths, [ + "/Users/username/.config/valet/Sites", + "/Users/username/Sites" + ]) + XCTAssertEqual(config.loopback, "127.0.0.1") + } + +} diff --git a/phpmon-tests/ValetTest.swift b/phpmon-tests/ValetTest.swift new file mode 100644 index 0000000..6d5b31d --- /dev/null +++ b/phpmon-tests/ValetTest.swift @@ -0,0 +1,18 @@ +// +// ValetTest.swift +// phpmon-tests +// +// Created by Nico Verbruggen on 29/11/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import XCTest + +class ValetTest: XCTestCase { + + func testDetermineValetVersion() { + let version = Actions.valet("--version") + XCTAssert(version.contains("Laravel Valet 2.")) + } + +} diff --git a/phpmon-tests/valet-config.json b/phpmon-tests/valet-config.json new file mode 100644 index 0000000..713f006 --- /dev/null +++ b/phpmon-tests/valet-config.json @@ -0,0 +1,8 @@ +{ + "tld": "test", + "paths": [ + "/Users/username/.config/valet/Sites", + "/Users/username/Sites" + ], + "loopback": "127.0.0.1" +} diff --git a/phpmon/Domain/Core/Actions.swift b/phpmon/Domain/Core/Actions.swift index 00cc313..244400f 100644 --- a/phpmon/Domain/Core/Actions.swift +++ b/phpmon/Domain/Core/Actions.swift @@ -102,6 +102,19 @@ class Actions { brew("services stop dnsmasq", sudo: true) } + /** + Kindly asks Valet to switch to a specific PHP version. + */ + public static func switchToPhpVersionUsingValet( + version: String, + availableVersions: [String], + completed: @escaping () -> Void + ) { + print("Switching to \(version) using Valet") + print(valet("use php@\(version)")) + completed() + } + /** Switching to a new PHP version involves: - unlinking the current version @@ -203,6 +216,14 @@ class Actions { // MARK: Common Shell Commands + /** + Runs a `valet` command. + */ + public static func valet(_ command: String) -> String + { + return Shell.pipe("sudo \(Paths.valet) \(command)", requiresPath: true) + } + /** Runs a `brew` command. Can run as superuser. */ diff --git a/phpmon/Domain/Helpers/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift similarity index 100% rename from phpmon/Domain/Helpers/HomebrewDiagnostics.swift rename to phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift diff --git a/phpmon/Domain/Helpers/HomebrewPackage.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewPackage.swift similarity index 86% rename from phpmon/Domain/Helpers/HomebrewPackage.swift rename to phpmon/Domain/Integrations/Homebrew/HomebrewPackage.swift index 3b2ea9b..062e153 100644 --- a/phpmon/Domain/Helpers/HomebrewPackage.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewPackage.swift @@ -16,7 +16,8 @@ struct HomebrewPackage: Decodable { let linked_keg: String? public var version: String { - return aliases.first!.replacingOccurrences(of: "php@", with: "") + return aliases.first! + .replacingOccurrences(of: "php@", with: "") } } diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift new file mode 100644 index 0000000..cec4e78 --- /dev/null +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -0,0 +1,26 @@ +// +// Valet.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 29/11/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class Valet { + + var version: String + var detectedSites: [String] + + init() { + // Let's see if we can't discern what the Valet version is + // but in order to do so, we'll need to be able to run Valet + // which has, historically, been kind of a pain in the butt + self.version = Actions.valet("--version") + .replacingOccurrences(of: "Laravel Valet ", with: "") + + self.detectedSites = [] + } + +} diff --git a/phpmon/Domain/Integrations/Valet/ValetConfiguration.swift b/phpmon/Domain/Integrations/Valet/ValetConfiguration.swift new file mode 100644 index 0000000..b6ea630 --- /dev/null +++ b/phpmon/Domain/Integrations/Valet/ValetConfiguration.swift @@ -0,0 +1,17 @@ +// +// ValetConfiguration.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 29/11/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class ValetConfiguration: Decodable { + let tld: String + let paths: [String] + let loopback: String +} + + diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift index e73f86b..7ef3e2f 100644 --- a/phpmon/Domain/Menu/MainMenu.swift +++ b/phpmon/Domain/Menu/MainMenu.swift @@ -55,9 +55,14 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { updatePhpVersionInStatusBar() + // Attempt to find out if PHP-FPM is broken let installation = App.phpInstall! installation.notifyAboutBrokenPhpFpm() + // Attempt to find out more info about Valet + let valet = Valet() + print("PHP Monitor has extracted the version number of Valet: \(valet.version)") + // Schedule a request to fetch the PHP version every 60 seconds DispatchQueue.main.async { [self] in App.shared.timer = Timer.scheduledTimer( @@ -349,8 +354,8 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate { } } - // Switch the PHP version - Actions.switchToPhpVersion( + // TODO: Allow for switcher to vary? + Actions.switchToPhpVersionUsingValet( version: sender.version, availableVersions: App.shared.availablePhpVersions, completed: completion diff --git a/phpmon/Domain/Terminal/Paths.swift b/phpmon/Domain/Terminal/Paths.swift index d25cfb4..7effea5 100644 --- a/phpmon/Domain/Terminal/Paths.swift +++ b/phpmon/Domain/Terminal/Paths.swift @@ -39,6 +39,10 @@ class Paths { // - MARK: Binaries + public static var valet: String { + return "/Users/\(whoami)/.composer/vendor/bin/valet" + } + public static var brew: String { return "\(binPath)/brew" } @@ -53,6 +57,10 @@ class Paths { // - MARK: Paths + public static var whoami: String { + return String(Shell.pipe("whoami").split(separator: "\n")[0]) + } + public static var binPath: String { return "\(shared.baseDir.rawValue)/bin" } diff --git a/phpmon/Domain/Terminal/Shell.swift b/phpmon/Domain/Terminal/Shell.swift index 6cf482d..379e48d 100644 --- a/phpmon/Domain/Terminal/Shell.swift +++ b/phpmon/Domain/Terminal/Shell.swift @@ -11,12 +11,18 @@ class Shell { // MARK: - Invoke static functions - public static func run(_ command: String) { + public static func run( + _ command: String, + requiresPath: Bool = false + ) { Shell.user.run(command) } - public static func pipe(_ command: String) -> String { - return Shell.user.pipe(command) + public static func pipe( + _ command: String, + requiresPath: Bool = false + ) -> String { + return Shell.user.pipe(command, requiresPath: requiresPath) } // MARK: - Singleton @@ -49,25 +55,36 @@ class Shell { Uses the default shell. - Parameter command: The command to run + - Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this */ - func run(_ command: String) { + func run( + _ command: String, + requiresPath: Bool = false + ) { // Equivalent of piping to /dev/null; don't do anything with the string - _ = pipe(command) + _ = Shell.pipe(command) } /** Runs a shell command and returns the output. - Parameter command: The command to run - - Parameter shell: Path to the shell to invoke + - Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this */ - func pipe(_ command: String) -> String { + func pipe( + _ command: String, + requiresPath: Bool = false + ) -> String { let task = Process() let outputPipe = Pipe() let errorPipe = Pipe() + let tailoredCommand = requiresPath + ? "export PATH=\(Paths.binPath):$PATH && \(command)" + : command + task.launchPath = self.shell - task.arguments = ["--login", "-c", command] + task.arguments = ["--login", "-c", tailoredCommand] task.standardOutput = outputPipe task.standardError = errorPipe task.launch()