mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-04-05 18:50:08 +02:00
🚧 WIP: Refactoring
This commit is contained in:
@@ -87,8 +87,6 @@
|
||||
03CC1FF52E3D23130050FC18 /* ZshRunCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CC1FF32E3D230B0050FC18 /* ZshRunCommand.swift */; };
|
||||
03CC1FF62E3D23130050FC18 /* ZshRunCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CC1FF32E3D230B0050FC18 /* ZshRunCommand.swift */; };
|
||||
03CC1FF72E3D23130050FC18 /* ZshRunCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CC1FF32E3D230B0050FC18 /* ZshRunCommand.swift */; };
|
||||
03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; };
|
||||
03E36FE828D9219000636F7F /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; };
|
||||
03FE39E72E81682800B7B5AC /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 03FE39E52E81682800B7B5AC /* AppIcon.icon */; };
|
||||
03FE39E82E81682800B7B5AC /* AppIconEAP.icon in Resources */ = {isa = PBXBuildFile; fileRef = 03FE39E62E81682800B7B5AC /* AppIconEAP.icon */; };
|
||||
03FE39EA2E81694500B7B5AC /* AppIconUD.icon in Resources */ = {isa = PBXBuildFile; fileRef = 03FE39E92E81694500B7B5AC /* AppIconUD.icon */; };
|
||||
@@ -383,7 +381,6 @@
|
||||
C470150B2C46D81E0069AAE7 /* NVAlert in Frameworks */ = {isa = PBXBuildFile; productRef = C470150A2C46D81E0069AAE7 /* NVAlert */; };
|
||||
C470150D2C46D83E0069AAE7 /* NVAlert in Frameworks */ = {isa = PBXBuildFile; productRef = C470150C2C46D83E0069AAE7 /* NVAlert */; };
|
||||
C4709CA228524B3400088BB8 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4709CA128524B3400088BB8 /* StatsView.swift */; };
|
||||
C471E79328F9B21F0021E251 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; };
|
||||
C471E79428F9B23B0021E251 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
||||
C471E79528F9B2420021E251 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
||||
C471E7BF28F9B90F0021E251 /* StartupTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471E7BE28F9B90F0021E251 /* StartupTest.swift */; };
|
||||
@@ -393,12 +390,8 @@
|
||||
C471E7CC28F9BA5B0021E251 /* TestableShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4928DB966A007ACC74 /* TestableShell.swift */; };
|
||||
C471E7CD28F9BA600021E251 /* ShellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */; };
|
||||
C471E7CE28F9BA600021E251 /* RealShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46EBC4628DB9644007ACC74 /* RealShell.swift */; };
|
||||
C471E7CF28F9BA600021E251 /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; };
|
||||
C471E7D028F9BA630021E251 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
||||
C471E7D128F9BA630021E251 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
||||
C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; };
|
||||
C471E7D328F9BA8F0021E251 /* ActiveShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E36FE628D9219000636F7F /* ActiveShell.swift */; };
|
||||
C471E7D428F9BA8F0021E251 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; };
|
||||
C471E7D528F9BA8F0021E251 /* TestableConfigurations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40F505428ECA64E004AD45B /* TestableConfigurations.swift */; };
|
||||
C471E7D628F9BA8F0021E251 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
||||
C471E7D728F9BA8F0021E251 /* TestableFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AD38B128ECD9D300FA8D83 /* TestableFileSystem.swift */; };
|
||||
@@ -410,9 +403,7 @@
|
||||
C471E7DD28F9BAA30021E251 /* CommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE928F7643D0026AC4E /* CommandProtocol.swift */; };
|
||||
C471E7DE28F9BAA30021E251 /* CommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE928F7643D0026AC4E /* CommandProtocol.swift */; };
|
||||
C471E7DF28F9BAAB0021E251 /* RealCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* RealCommand.swift */; };
|
||||
C471E7E028F9BAAB0021E251 /* ActiveCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE628F764050026AC4E /* ActiveCommand.swift */; };
|
||||
C471E7E128F9BAAB0021E251 /* RealCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* RealCommand.swift */; };
|
||||
C471E7E228F9BAAB0021E251 /* ActiveCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE628F764050026AC4E /* ActiveCommand.swift */; };
|
||||
C471E7E328F9BAC20021E251 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
||||
C471E7E428F9BAC20021E251 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||
C471E7E528F9BAC20021E251 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||
@@ -794,7 +785,6 @@
|
||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||
C4C8900328F0E28800CE5E97 /* FileSystemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */; };
|
||||
C4C8900528F0E3D100CE5E97 /* RealFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */; };
|
||||
C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; };
|
||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
||||
C4C8E81B276F54E5003AC782 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; };
|
||||
@@ -880,8 +870,6 @@
|
||||
C4E2E86A28FC3002003B070C /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
||||
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
||||
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
||||
C4E49DE728F764050026AC4E /* ActiveCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE628F764050026AC4E /* ActiveCommand.swift */; };
|
||||
C4E49DE828F764050026AC4E /* ActiveCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE628F764050026AC4E /* ActiveCommand.swift */; };
|
||||
C4E49DEA28F7643D0026AC4E /* CommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE928F7643D0026AC4E /* CommandProtocol.swift */; };
|
||||
C4E49DEB28F7643D0026AC4E /* CommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DE928F7643D0026AC4E /* CommandProtocol.swift */; };
|
||||
C4E49DED28F764A00026AC4E /* TestableCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E49DEC28F764A00026AC4E /* TestableCommand.swift */; };
|
||||
@@ -1007,7 +995,6 @@
|
||||
03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Driver.swift"; sourceTree = "<group>"; };
|
||||
03CC1FE42E3D220F0050FC18 /* InstallHomebrew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallHomebrew.swift; sourceTree = "<group>"; };
|
||||
03CC1FF32E3D230B0050FC18 /* ZshRunCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZshRunCommand.swift; sourceTree = "<group>"; };
|
||||
03E36FE628D9219000636F7F /* ActiveShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveShell.swift; sourceTree = "<group>"; };
|
||||
03FE39E52E81682800B7B5AC /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = "<group>"; };
|
||||
03FE39E62E81682800B7B5AC /* AppIconEAP.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIconEAP.icon; sourceTree = "<group>"; };
|
||||
03FE39E92E81694500B7B5AC /* AppIconUD.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIconUD.icon; sourceTree = "<group>"; };
|
||||
@@ -1214,7 +1201,6 @@
|
||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
|
||||
C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemProtocol.swift; sourceTree = "<group>"; };
|
||||
C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealFileSystem.swift; sourceTree = "<group>"; };
|
||||
C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveFileSystem.swift; sourceTree = "<group>"; };
|
||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
||||
C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigWatchManager.swift; sourceTree = "<group>"; };
|
||||
C4CB250429B28BB800CA4492 /* MainMenuTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTest.swift; sourceTree = "<group>"; };
|
||||
@@ -1248,7 +1234,6 @@
|
||||
C4E2E85B28FC282B003B070C /* TestableConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableConfiguration.swift; sourceTree = "<group>"; };
|
||||
C4E2E86328FC2F1B003B070C /* XCPMApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCPMApplication.swift; sourceTree = "<group>"; };
|
||||
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
|
||||
C4E49DE628F764050026AC4E /* ActiveCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveCommand.swift; sourceTree = "<group>"; };
|
||||
C4E49DE928F7643D0026AC4E /* CommandProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandProtocol.swift; sourceTree = "<group>"; };
|
||||
C4E49DEC28F764A00026AC4E /* TestableCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableCommand.swift; sourceTree = "<group>"; };
|
||||
C4E684082AF26B830023ED25 /* BrewTapFormulae.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewTapFormulae.swift; sourceTree = "<group>"; };
|
||||
@@ -2229,7 +2214,6 @@
|
||||
C4C8900128F0E27900CE5E97 /* Filesystem */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */,
|
||||
C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */,
|
||||
C4C8900228F0E28800CE5E97 /* FileSystemProtocol.swift */,
|
||||
);
|
||||
@@ -2297,7 +2281,6 @@
|
||||
C4E49DE528F763E20026AC4E /* Command */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4E49DE628F764050026AC4E /* ActiveCommand.swift */,
|
||||
C4B5853D2770FE3900DA4FBE /* RealCommand.swift */,
|
||||
C4E49DE928F7643D0026AC4E /* CommandProtocol.swift */,
|
||||
);
|
||||
@@ -2356,7 +2339,6 @@
|
||||
C4F787A628EF811000790735 /* Shell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03E36FE628D9219000636F7F /* ActiveShell.swift */,
|
||||
C46EBC4628DB9644007ACC74 /* RealShell.swift */,
|
||||
C46EBC4328DB95F0007ACC74 /* ShellProtocol.swift */,
|
||||
);
|
||||
@@ -2702,7 +2684,6 @@
|
||||
C47DF1AF299D5A3B0007055D /* LoginItemManager.swift in Sources */,
|
||||
C4292D542B023F61004F0D2A /* PhpExtensionManagerWindowController.swift in Sources */,
|
||||
C4D3661A291173EA006BD146 /* DictionaryExtension.swift in Sources */,
|
||||
C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */,
|
||||
C409349D298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
|
||||
C43931CA29C4C03F0069165B /* Brew.swift in Sources */,
|
||||
@@ -2728,7 +2709,6 @@
|
||||
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||
C4E49DE728F764050026AC4E /* ActiveCommand.swift in Sources */,
|
||||
C43B8FD52BA9BAD3000C02BE /* UnavailableContentView.swift in Sources */,
|
||||
032DAC2A2E8BEB5B0018E01C /* RealApi.swift in Sources */,
|
||||
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||
@@ -2815,7 +2795,6 @@
|
||||
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
||||
C412E5FC25700D5300A1FB67 /* HomebrewDecodable.swift in Sources */,
|
||||
03BFF52E2E313244007F96FA /* StatusMenu+Driver.swift in Sources */,
|
||||
03E36FE728D9219000636F7F /* ActiveShell.swift in Sources */,
|
||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */,
|
||||
C4D5857C2A7038DB00DDBB63 /* ByteLimitView.swift in Sources */,
|
||||
@@ -3059,7 +3038,6 @@
|
||||
C471E7D728F9BA8F0021E251 /* TestableFileSystem.swift in Sources */,
|
||||
C471E81A28F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */,
|
||||
C471E7E128F9BAAB0021E251 /* RealCommand.swift in Sources */,
|
||||
C471E7E228F9BAAB0021E251 /* ActiveCommand.swift in Sources */,
|
||||
03263A392E86D5EC00BD0415 /* UpdateScheduler.swift in Sources */,
|
||||
C471E80A28F9BADC0021E251 /* CreatedFromFile.swift in Sources */,
|
||||
C471E80528F9BAD40021E251 /* ActivePhpInstallation.swift in Sources */,
|
||||
@@ -3092,7 +3070,6 @@
|
||||
C471E7D928F9BA8F0021E251 /* TestableShell.swift in Sources */,
|
||||
C471E81428F9BAE80021E251 /* NSWindowExtension.swift in Sources */,
|
||||
C43BCD4629FBEF40001547BC /* ModifyPhpVersionCommand.swift in Sources */,
|
||||
C471E7D328F9BA8F0021E251 /* ActiveShell.swift in Sources */,
|
||||
C42106682AFA9FF400DF3732 /* PhpVersionManagerView+Actions.swift in Sources */,
|
||||
C4B79EC829CA474200A483EE /* FakeCommand.swift in Sources */,
|
||||
C471E7DE28F9BAA30021E251 /* CommandProtocol.swift in Sources */,
|
||||
@@ -3110,7 +3087,6 @@
|
||||
C489E0BD2A220A4200323F5E /* FakeBrewFormulaeHandler.swift in Sources */,
|
||||
C45E2A77291992DA005C7CFD /* FeatureTestCase.swift in Sources */,
|
||||
C471E82028F9BB290021E251 /* NginxConfigurationFile.swift in Sources */,
|
||||
C471E7D428F9BA8F0021E251 /* ActiveFileSystem.swift in Sources */,
|
||||
032DAC2D2E8BEB6B0018E01C /* ApiProtocol.swift in Sources */,
|
||||
C4513F902B13E2E6001AD760 /* PhpExtensionManagerWindowController.swift in Sources */,
|
||||
C471E81528F9BAE80021E251 /* ArrayExtension.swift in Sources */,
|
||||
@@ -3273,7 +3249,6 @@
|
||||
03CC1FE72E3D22120050FC18 /* InstallHomebrew.swift in Sources */,
|
||||
C4CE7F9929683B43000102CF /* PhpVersionNumberCollection.swift in Sources */,
|
||||
C471E7FC28F9BACE0021E251 /* HomebrewDecodable.swift in Sources */,
|
||||
C471E7CF28F9BA600021E251 /* ActiveShell.swift in Sources */,
|
||||
C4BB393C2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
|
||||
C471E7F628F9BAC80021E251 /* PhpHelper.swift in Sources */,
|
||||
039E1D7B2E5F0F300072D13D /* ValetUpgrader.swift in Sources */,
|
||||
@@ -3288,7 +3263,6 @@
|
||||
C471E81228F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */,
|
||||
C471E7DF28F9BAAB0021E251 /* RealCommand.swift in Sources */,
|
||||
C469E701294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */,
|
||||
C471E7E028F9BAAB0021E251 /* ActiveCommand.swift in Sources */,
|
||||
C40175BB2903108900763A68 /* ValetInteractor.swift in Sources */,
|
||||
C471E80928F9BADC0021E251 /* CreatedFromFile.swift in Sources */,
|
||||
C471E80128F9BAD40021E251 /* ActivePhpInstallation.swift in Sources */,
|
||||
@@ -3312,7 +3286,6 @@
|
||||
C43BCD4729FBEF40001547BC /* ModifyPhpVersionCommand.swift in Sources */,
|
||||
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */,
|
||||
C4513F8E2B13E2E5001AD760 /* PhpExtensionManagerWindowController.swift in Sources */,
|
||||
C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */,
|
||||
C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */,
|
||||
C471E7F528F9BAC80021E251 /* PhpEnvironments.swift in Sources */,
|
||||
C471E7ED28F9BAC30021E251 /* Process.swift in Sources */,
|
||||
@@ -3486,13 +3459,10 @@
|
||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||
5489625928313231004F647A /* CreatedFromFile.swift in Sources */,
|
||||
C4513F932B13E2FB001AD760 /* PhpExtensionManagerView.swift in Sources */,
|
||||
C471E79328F9B21F0021E251 /* ActiveFileSystem.swift in Sources */,
|
||||
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
||||
03E36FE828D9219000636F7F /* ActiveShell.swift in Sources */,
|
||||
0396160E2E74A61E002DD7F6 /* LoggableEvent.swift in Sources */,
|
||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||
C4E2E86528FC2F1B003B070C /* XCPMApplication.swift in Sources */,
|
||||
C4E49DE828F764050026AC4E /* ActiveCommand.swift in Sources */,
|
||||
C489E0BC2A220A4200323F5E /* FakeBrewFormulaeHandler.swift in Sources */,
|
||||
036C390B2E5C8CC5008DAEDF /* PackagistP2Response.swift in Sources */,
|
||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||
|
||||
@@ -13,6 +13,8 @@ public struct ContainerAccessMacro: MemberMacro {
|
||||
// This should be kept in sync with the Container class
|
||||
let allContainerServices: [(name: String, type: String)] = [
|
||||
("shell", "ShellProtocol"),
|
||||
("filesystem", "FileSystemProtocol"),
|
||||
("command", "CommandProtocol"),
|
||||
("favorites", "Favorites"),
|
||||
("warningManager", "WarningManager")
|
||||
]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// ActiveCommand.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 12/10/2022.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
var Command: CommandProtocol {
|
||||
return ActiveCommand.shared
|
||||
}
|
||||
|
||||
class ActiveCommand {
|
||||
static var shared: CommandProtocol = RealCommand()
|
||||
|
||||
public static func useTestable(_ output: [String: String]) {
|
||||
Self.shared = TestableCommand(commands: output)
|
||||
}
|
||||
|
||||
public static func useSystem() {
|
||||
Self.shared = RealCommand()
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class Actions {
|
||||
// MARK: - Other Actions
|
||||
|
||||
public func createTempPhpInfoFile() async -> URL {
|
||||
try! FileSystem.writeAtomicallyToFile("/tmp/phpmon_phpinfo.php", content: "<?php phpinfo();")
|
||||
try! container.filesystem.writeAtomicallyToFile("/tmp/phpmon_phpinfo.php", content: "<?php phpinfo();")
|
||||
|
||||
// Tell php-cgi to run the PHP and output as an .html file
|
||||
await container.shell.quiet("\(Paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
|
||||
|
||||
@@ -13,32 +13,38 @@ import Foundation
|
||||
/**
|
||||
Runs a `brew` command. Can run as superuser.
|
||||
*/
|
||||
func brew(_ command: String, sudo: Bool = false) async {
|
||||
await Shell.quiet("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)")
|
||||
func brew(_ command: String, sudo: Bool = false, shell: ShellProtocol = App.shared.container.shell) async {
|
||||
await shell.quiet("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)")
|
||||
}
|
||||
|
||||
/**
|
||||
Runs `sed` in order to replace all occurrences of a string in a specific file with another.
|
||||
*/
|
||||
func sed(file: String, original: String, replacement: String) async {
|
||||
func sed(
|
||||
file: String,
|
||||
original: String,
|
||||
replacement: String,
|
||||
filesystem: FileSystemProtocol = App.shared.container.filesystem,
|
||||
shell: ShellProtocol = App.shared.container.shell
|
||||
) async {
|
||||
// Escape slashes (or `sed` won't work)
|
||||
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
||||
let e_replacement = replacement.replacingOccurrences(of: "/", with: "\\/")
|
||||
|
||||
// Check if gsed exists; it is able to follow symlinks,
|
||||
// which we want to do to toggle the extension
|
||||
if FileSystem.fileExists("\(Paths.binPath)/gsed") {
|
||||
await Shell.quiet("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
if filesystem.fileExists("\(Paths.binPath)/gsed") {
|
||||
await shell.quiet("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
} else {
|
||||
await Shell.quiet("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
await shell.quiet("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Uses `grep` to determine whether a particular query string can be found in a particular file.
|
||||
*/
|
||||
func grepContains(file: String, query: String) async -> Bool {
|
||||
return await Shell.pipe("""
|
||||
func grepContains(file: String, query: String, shell: ShellProtocol = App.shared.container.shell) async -> Bool {
|
||||
return await shell.pipe("""
|
||||
grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO"
|
||||
""").out
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
@@ -22,7 +22,7 @@ struct HomebrewFormulae {
|
||||
}
|
||||
|
||||
static var nginx: HomebrewFormula {
|
||||
return BrewDiagnostics.usesNginxFullFormula
|
||||
return BrewDiagnostics.shared.usesNginxFullFormula
|
||||
? HomebrewFormula("nginx-full", elevated: true)
|
||||
: HomebrewFormula("nginx", elevated: true)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class Log {
|
||||
system_quiet("mkdir -p ~/.config/phpmon 2> /dev/null")
|
||||
system_quiet("rm ~/.config/phpmon/last_session.log 2> /dev/null")
|
||||
system_quiet("touch ~/.config/phpmon/last_session.log 2> /dev/null")
|
||||
self.logExists = FileSystem.fileExists(self.logFilePath)
|
||||
self.logExists = App.shared.container.filesystem.fileExists(self.logFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ public class Paths {
|
||||
|
||||
// Ensure that if a different location is used, it takes precendence
|
||||
if baseDir == .usr
|
||||
&& FileSystem.directoryExists("/usr/local/homebrew")
|
||||
&& !FileSystem.directoryExists("/usr/local/Cellar") {
|
||||
&& App.shared.container.filesystem.directoryExists("/usr/local/homebrew")
|
||||
&& !App.shared.container.filesystem.directoryExists("/usr/local/Cellar") {
|
||||
Log.warn("Using /usr/local/homebrew as base directory!")
|
||||
baseDir = .usr_hb
|
||||
}
|
||||
@@ -74,12 +74,12 @@ public class Paths {
|
||||
}
|
||||
|
||||
public static var homePath: String {
|
||||
if FileSystem is RealFileSystem {
|
||||
if App.shared.container.filesystem is RealFileSystem {
|
||||
return NSHomeDirectory()
|
||||
}
|
||||
|
||||
if FileSystem is TestableFileSystem {
|
||||
let fs = FileSystem as! TestableFileSystem
|
||||
if App.shared.container.filesystem is TestableFileSystem {
|
||||
let fs = App.shared.container.filesystem as! TestableFileSystem
|
||||
return fs.homeDirectory
|
||||
}
|
||||
|
||||
@@ -123,11 +123,11 @@ public class Paths {
|
||||
// (PHP Monitor will not use the user's own PATH)
|
||||
|
||||
private func detectComposerBinary() {
|
||||
if FileSystem.fileExists("/usr/local/bin/composer") {
|
||||
if App.shared.container.filesystem.fileExists("/usr/local/bin/composer") {
|
||||
Paths.composer = "/usr/local/bin/composer"
|
||||
} else if FileSystem.fileExists("/opt/homebrew/bin/composer") {
|
||||
} else if App.shared.container.filesystem.fileExists("/opt/homebrew/bin/composer") {
|
||||
Paths.composer = "/opt/homebrew/bin/composer"
|
||||
} else if FileSystem.fileExists("/usr/local/homebrew/bin/composer") {
|
||||
} else if App.shared.container.filesystem.fileExists("/usr/local/homebrew/bin/composer") {
|
||||
Paths.composer = "/usr/local/homebrew/bin/composer"
|
||||
} else {
|
||||
Paths.composer = nil
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
//
|
||||
// FS.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 08/10/2022.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@available(*, deprecated, message: "Use an injected `Container` instance to access this instead.")
|
||||
var FileSystem: FileSystemProtocol {
|
||||
return App.shared.container.filesystem
|
||||
}
|
||||
@@ -7,10 +7,12 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
/// An application that is capable of opening a particular directory (usually of a PHP project).
|
||||
/// In most cases this is going to be a code editor, but it could also be another application
|
||||
/// that supports opening those directories, like a visual Git client or a terminal app.
|
||||
@ContainerAccess
|
||||
class Application {
|
||||
|
||||
enum AppType {
|
||||
@@ -34,19 +36,19 @@ class Application {
|
||||
(This will open the app if it isn't open yet.)
|
||||
*/
|
||||
@objc public func openDirectory(file: String) {
|
||||
Task { await Shell.quiet("/usr/bin/open -a \"\(name)\" \"\(file)\"") }
|
||||
Task { await shell.quiet("/usr/bin/open -a \"\(name)\" \"\(file)\"") }
|
||||
}
|
||||
|
||||
/** Checks if the app is installed. */
|
||||
func isInstalled() async -> Bool {
|
||||
|
||||
let (process, output) = try! await Shell.attach(
|
||||
let (process, output) = try! await shell.attach(
|
||||
"/usr/bin/open -Ra \"\(name)\"",
|
||||
didReceiveOutput: { _, _ in },
|
||||
withTimeout: 2.0
|
||||
)
|
||||
|
||||
if Shell is TestableShell {
|
||||
if shell is TestableShell {
|
||||
// When testing, check the error output (must not be empty)
|
||||
return !output.hasError
|
||||
} else {
|
||||
|
||||
@@ -41,7 +41,7 @@ class ActivePhpInstallation {
|
||||
// MARK: - Initializer
|
||||
|
||||
public static func load() -> ActivePhpInstallation? {
|
||||
if !FileSystem.fileExists(Paths.phpConfig) {
|
||||
if !App.shared.container.filesystem.fileExists(Paths.phpConfig) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class ActivePhpInstallation {
|
||||
_or_ if the output contains the word "Warning" or "Error". In normal situations this should not be the case.
|
||||
*/
|
||||
private func determineVersion() throws {
|
||||
let output = Command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
||||
let output = command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
||||
|
||||
self.hasErrorState = (output == "" || output.contains("Warning") || output.contains("Error"))
|
||||
|
||||
@@ -115,7 +115,7 @@ class ActivePhpInstallation {
|
||||
- Parameter key: The key of the `ini` value that needs to be retrieved. For example, you can use `memory_limit`.
|
||||
*/
|
||||
private func getByteCount(key: String) -> String {
|
||||
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"], trimNewlines: false)
|
||||
let value = command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"], trimNewlines: false)
|
||||
|
||||
// Check if the value is unlimited
|
||||
if value == "-1" {
|
||||
|
||||
@@ -44,8 +44,8 @@ class PhpEnvironments {
|
||||
|
||||
// Check if that version actually corresponds to an older version
|
||||
let phpConfigExecutablePath = "\(Paths.optPath)/php/bin/php-config"
|
||||
if FileSystem.fileExists(phpConfigExecutablePath) {
|
||||
let longVersionString = Command.execute(
|
||||
if container.filesystem.fileExists(phpConfigExecutablePath) {
|
||||
let longVersionString = container.command.execute(
|
||||
path: phpConfigExecutablePath,
|
||||
arguments: ["--version"],
|
||||
trimNewlines: false
|
||||
@@ -115,7 +115,7 @@ class PhpEnvironments {
|
||||
static var homebrewBrewPhpAlias: String {
|
||||
if PhpEnvironments.shared.homebrewPackage == nil {
|
||||
// For UI testing and as a fallback, determine this version by using (fake) php-config
|
||||
let version = Command.execute(path: "/opt/homebrew/bin/php-config",
|
||||
let version = App.shared.container.command.execute(path: "/opt/homebrew/bin/php-config",
|
||||
arguments: ["--version"],
|
||||
trimNewlines: true)
|
||||
return try! VersionNumber.parse(version).short
|
||||
@@ -184,7 +184,7 @@ class PhpEnvironments {
|
||||
let phpAlias = homebrewPackage.version
|
||||
|
||||
// Avoid inserting a duplicate
|
||||
if !supportedVersions.contains(phpAlias) && FileSystem.fileExists("\(Paths.optPath)/php/bin/php") {
|
||||
if !supportedVersions.contains(phpAlias) && container.filesystem.fileExists("\(Paths.optPath)/php/bin/php") {
|
||||
let phpAliasInstall = PhpInstallation(phpAlias)
|
||||
// Before inserting, ensure that the actual output matches the alias
|
||||
// if that isn't the case, our formula remains out-of-date
|
||||
@@ -237,7 +237,7 @@ class PhpEnvironments {
|
||||
// is supported and where the binary exists (avoids broken installs)
|
||||
if !output.contains(version)
|
||||
&& supported.contains(version)
|
||||
&& (checkBinaries ? FileSystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true) {
|
||||
&& (checkBinaries ? container.filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true) {
|
||||
output.insert(version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,20 +26,20 @@ class PhpHelper {
|
||||
let inPath = container.shell.PATH.contains("\(Paths.homePath)/.config/phpmon/bin")
|
||||
|
||||
// Check if we can create symlinks (`/usr/local/bin` must be writable)
|
||||
let canWriteSymlinks = FileSystem.isWriteableFile("/usr/local/bin/")
|
||||
let canWriteSymlinks = App.shared.container.filesystem.isWriteableFile("/usr/local/bin/")
|
||||
|
||||
Task { // Create the appropriate folders and check if the files exist
|
||||
do {
|
||||
if !FileSystem.directoryExists("~/.config/phpmon/bin") {
|
||||
if !App.shared.container.filesystem.directoryExists("~/.config/phpmon/bin") {
|
||||
Task { @MainActor in
|
||||
try FileSystem.createDirectory(
|
||||
try App.shared.container.filesystem.createDirectory(
|
||||
"~/.config/phpmon/bin",
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if FileSystem.fileExists(destination) {
|
||||
if App.shared.container.filesystem.fileExists(destination) {
|
||||
let contents = try String(contentsOfFile: destination)
|
||||
if !contents.contains(keyPhrase) {
|
||||
Log.info("The file at '\(destination)' already exists and was not generated by PHP Monitor "
|
||||
@@ -58,10 +58,10 @@ class PhpHelper {
|
||||
: zshScript(path, keyPhrase, version, dotless)
|
||||
|
||||
Task { @MainActor in
|
||||
try FileSystem.writeAtomicallyToFile(destination, content: script)
|
||||
try App.shared.container.filesystem.writeAtomicallyToFile(destination, content: script)
|
||||
|
||||
if !FileSystem.isExecutableFile(destination) {
|
||||
try FileSystem.makeExecutable(destination)
|
||||
if !App.shared.container.filesystem.isExecutableFile(destination) {
|
||||
try App.shared.container.filesystem.makeExecutable(destination)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,13 +121,13 @@ class PhpHelper {
|
||||
let source = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||
let destination = "/usr/local/bin/pm\(dotless)"
|
||||
|
||||
if !FileSystem.fileExists(destination) {
|
||||
if !App.shared.container.filesystem.fileExists(destination) {
|
||||
Log.info("Creating new symlink: \(destination)")
|
||||
await container.shell.quiet("ln -s \(source) \(destination)")
|
||||
return
|
||||
}
|
||||
|
||||
if !FileSystem.isSymlink(destination) {
|
||||
if !App.shared.container.filesystem.isSymlink(destination) {
|
||||
Log.info("Overwriting existing file with new symlink: \(destination)")
|
||||
await container.shell.quiet("ln -fs \(source) \(destination)")
|
||||
return
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class PhpInstallation {
|
||||
|
||||
var versionNumber: VersionNumber
|
||||
@@ -54,8 +56,8 @@ class PhpInstallation {
|
||||
}
|
||||
|
||||
private func determineVersion(_ phpConfigExecutablePath: String, _ phpExecutablePath: String) {
|
||||
if FileSystem.fileExists(phpConfigExecutablePath) {
|
||||
let longVersionString = Command.execute(
|
||||
if filesystem.fileExists(phpConfigExecutablePath) {
|
||||
let longVersionString = command.execute(
|
||||
path: phpConfigExecutablePath,
|
||||
arguments: ["--version"],
|
||||
trimNewlines: false
|
||||
@@ -76,8 +78,8 @@ class PhpInstallation {
|
||||
}
|
||||
|
||||
private func determineHealth(_ phpExecutablePath: String) {
|
||||
if FileSystem.fileExists(phpExecutablePath) {
|
||||
let testCommand = Command.execute(
|
||||
if filesystem.fileExists(phpExecutablePath) {
|
||||
let testCommand = command.execute(
|
||||
path: phpExecutablePath,
|
||||
arguments: ["-v"],
|
||||
trimNewlines: false,
|
||||
@@ -94,7 +96,7 @@ class PhpInstallation {
|
||||
}
|
||||
|
||||
private func determineIniFiles(_ phpExecutablePath: String) {
|
||||
let paths = Shell
|
||||
let paths = shell
|
||||
.sync("\(phpExecutablePath) --ini | grep -E -o '(/[^ ]+\\.ini)'").out
|
||||
.split(separator: "\n")
|
||||
.map { String($0) }
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
//
|
||||
// Shell.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 20/09/2022.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@available(*, deprecated, message: "Use an injected `Container` instance to access this instead.")
|
||||
var Shell: ShellProtocol {
|
||||
return App.shared.container.shell
|
||||
}
|
||||
@@ -124,8 +124,6 @@ public struct TestableConfiguration: Codable {
|
||||
Log.separator()
|
||||
Log.info("Applying to container...")
|
||||
App.shared.container.overrideWith(config: self)
|
||||
Log.info("Applying fake commands...")
|
||||
ActiveCommand.useTestable(commandOutput)
|
||||
Log.info("Applying temporary preference overrides...")
|
||||
preferenceOverrides.forEach { (key: PreferenceName, value: Any?) in
|
||||
Preferences.shared.cachedPreferences[key] = value
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
class Container {
|
||||
var shell: ShellProtocol!
|
||||
var filesystem: FileSystemProtocol!
|
||||
var command: CommandProtocol!
|
||||
|
||||
var favorites: Favorites!
|
||||
var warningManager: WarningManager!
|
||||
@@ -18,6 +19,7 @@ class Container {
|
||||
public func prepare() {
|
||||
self.shell = RealShell(container: self)
|
||||
self.filesystem = RealFileSystem(container: self)
|
||||
self.command = RealCommand()
|
||||
|
||||
self.favorites = Favorites()
|
||||
self.warningManager = WarningManager(container: self)
|
||||
@@ -26,5 +28,6 @@ class Container {
|
||||
public func overrideWith(config: TestableConfiguration) {
|
||||
self.shell = TestableShell(expectations: config.shellOutput)
|
||||
self.filesystem = TestableFileSystem(files: config.filesystem)
|
||||
self.command = TestableCommand(commands: config.commandOutput)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class AppUpdater {
|
||||
.localized(latestVersionOnline.humanReadable),
|
||||
subtitle: "updater.alerts.newer_version_available.subtitle"
|
||||
.localized,
|
||||
description: BrewDiagnostics.customCaskInstalled
|
||||
description: BrewDiagnostics.shared.customCaskInstalled
|
||||
? "updater.installation_source.brew".localized(command)
|
||||
: "updater.installation_source.direct".localized
|
||||
)
|
||||
@@ -152,7 +152,7 @@ class AppUpdater {
|
||||
|
||||
system_quiet("cp -R \"\(updater)\" \"\(updaterDirectory)/PHP Monitor Self-Updater.app\"")
|
||||
|
||||
try! FileSystem.writeAtomicallyToFile(
|
||||
try! App.shared.container.filesystem.writeAtomicallyToFile(
|
||||
"\(updaterDirectory)/update.json",
|
||||
content: "{ \"url\": \"\(caskFile.url)\", \"sha256\": \"\(caskFile.sha256)\" }"
|
||||
)
|
||||
@@ -168,10 +168,10 @@ class AppUpdater {
|
||||
private func cleanupCaskroom() {
|
||||
let path = Paths.caskroomPath
|
||||
|
||||
if FileSystem.directoryExists(path) {
|
||||
if App.shared.container.filesystem.directoryExists(path) {
|
||||
Log.info("Removing the Caskroom directory for PHP Monitor...")
|
||||
do {
|
||||
try FileSystem.remove(path)
|
||||
try App.shared.container.filesystem.remove(path)
|
||||
Log.info("Removed the Caskroom directory at `\(path)`.")
|
||||
} catch {
|
||||
Log.err("Automatically removing the Caskroom directory at `\(path)` failed.")
|
||||
@@ -183,7 +183,7 @@ class AppUpdater {
|
||||
|
||||
public static func checkIfUpdateWasPerformed() {
|
||||
// Cleanup the upgrade.success file
|
||||
if FileSystem.fileExists("~/.config/phpmon/updater/upgrade.success") {
|
||||
if App.shared.container.filesystem.fileExists("~/.config/phpmon/updater/upgrade.success") {
|
||||
Task { @MainActor in
|
||||
if App.identifier.contains(".phpmon.eap") {
|
||||
LocalNotification.send(
|
||||
@@ -201,13 +201,13 @@ class AppUpdater {
|
||||
}
|
||||
|
||||
Log.info("The `upgrade.success` file was found! An update was installed. Cleaning up...")
|
||||
try? FileSystem.remove("~/.config/phpmon/updater/upgrade.success")
|
||||
try? App.shared.container.filesystem.remove("~/.config/phpmon/updater/upgrade.success")
|
||||
}
|
||||
|
||||
// Cleanup the previous updater
|
||||
if FileSystem.anyExists("~/.config/phpmon/updater/PHP Monitor Self-Updater.app") {
|
||||
if App.shared.container.filesystem.anyExists("~/.config/phpmon/updater/PHP Monitor Self-Updater.app") {
|
||||
Log.info("A remnant of the self-updater must still be removed...")
|
||||
try? FileSystem.remove("~/.config/phpmon/updater/PHP Monitor Self-Updater.app")
|
||||
try? App.shared.container.filesystem.remove("~/.config/phpmon/updater/PHP Monitor Self-Updater.app")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
import Foundation
|
||||
import Cocoa
|
||||
import NVAlert
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class ValetServicesManager: ServicesManager {
|
||||
override init() {
|
||||
super.init()
|
||||
@@ -46,7 +48,7 @@ class ValetServicesManager: ServicesManager {
|
||||
.filter { $0.elevated }
|
||||
.map { $0.name }
|
||||
|
||||
let rootJson = await Shell
|
||||
let rootJson = await App.shared.container.shell
|
||||
.pipe("sudo \(Paths.brew) services info --all --json")
|
||||
.out.data(using: .utf8)!
|
||||
|
||||
@@ -61,7 +63,7 @@ class ValetServicesManager: ServicesManager {
|
||||
.filter { !$0.elevated }
|
||||
.map { $0.name }
|
||||
|
||||
let normalJson = await Shell
|
||||
let normalJson = await App.shared.container.shell
|
||||
.pipe("\(Paths.brew) services info --all --json")
|
||||
.out.data(using: .utf8)!
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class Startup {
|
||||
// The Homebrew binary must exist.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: { return !FileSystem.fileExists(Paths.brew) },
|
||||
command: { return !App.shared.container.filesystem.fileExists(Paths.brew) },
|
||||
name: "`\(Paths.brew)` exists",
|
||||
titleText: "alert.homebrew_missing.title".localized,
|
||||
subtitleText: "alert.homebrew_missing.subtitle".localized,
|
||||
@@ -119,7 +119,7 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return await !Shell.pipe("ls \(Paths.optPath) | grep php").out.contains("php")
|
||||
return await !App.shared.container.shell.pipe("ls \(Paths.optPath) | grep php").out.contains("php")
|
||||
},
|
||||
name: "`ls \(Paths.optPath) | grep php` returned php result",
|
||||
titleText: "startup.errors.php_opt.title".localized,
|
||||
@@ -134,7 +134,7 @@ class Startup {
|
||||
// The PHP binary must exist.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: { return !FileSystem.fileExists(Paths.php) },
|
||||
command: { return !App.shared.container.filesystem.fileExists(Paths.php) },
|
||||
name: "`\(Paths.php)` exists",
|
||||
titleText: "startup.errors.php_binary.title".localized,
|
||||
subtitleText: "startup.errors.php_binary.subtitle".localized,
|
||||
@@ -145,7 +145,7 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return await Shell.pipe("\(Paths.binPath)/php -v").err
|
||||
return await App.shared.container.shell.pipe("\(Paths.binPath)/php -v").err
|
||||
.contains("Library not loaded")
|
||||
},
|
||||
name: "no `dyld` issue (`Library not loaded`) detected",
|
||||
@@ -160,8 +160,8 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return !(FileSystem.fileExists(Paths.valet)
|
||||
|| FileSystem.fileExists("~/.composer/vendor/bin/valet"))
|
||||
return !(App.shared.container.filesystem.fileExists(Paths.valet)
|
||||
|| App.shared.container.filesystem.fileExists("~/.composer/vendor/bin/valet"))
|
||||
},
|
||||
name: "`valet` binary exists",
|
||||
titleText: "startup.errors.valet_executable.title".localized,
|
||||
@@ -176,14 +176,14 @@ class Startup {
|
||||
// functioning correctly. Let the user know that they need to run `valet trust`.
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: { return await !Shell.pipe("cat /private/etc/sudoers.d/brew").out.contains(Paths.brew) },
|
||||
command: { return await !App.shared.container.shell.pipe("cat /private/etc/sudoers.d/brew").out.contains(Paths.brew) },
|
||||
name: "`/private/etc/sudoers.d/brew` contains brew",
|
||||
titleText: "startup.errors.sudoers_brew.title".localized,
|
||||
subtitleText: "startup.errors.sudoers_brew.subtitle".localized,
|
||||
descriptionText: "startup.errors.sudoers_brew.desc".localized
|
||||
),
|
||||
EnvironmentCheck(
|
||||
command: { return await !Shell.pipe("cat /private/etc/sudoers.d/valet").out.contains(Paths.valet) },
|
||||
command: { return await !App.shared.container.shell.pipe("cat /private/etc/sudoers.d/valet").out.contains(Paths.valet) },
|
||||
name: "`/private/etc/sudoers.d/valet` contains valet",
|
||||
titleText: "startup.errors.sudoers_valet.title".localized,
|
||||
subtitleText: "startup.errors.sudoers_valet.subtitle".localized,
|
||||
@@ -194,7 +194,7 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return !FileSystem.directoryExists("~/.config/valet")
|
||||
return !App.shared.container.filesystem.directoryExists("~/.config/valet")
|
||||
},
|
||||
name: "`.config/valet` not empty (Valet installed)",
|
||||
titleText: "startup.errors.valet_not_installed.title".localized,
|
||||
@@ -223,8 +223,8 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
await BrewDiagnostics.loadInstalledTaps()
|
||||
return await BrewDiagnostics.cannotLoadService("dnsmasq")
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
return await BrewDiagnostics.shared.cannotLoadService("dnsmasq")
|
||||
},
|
||||
name: "`sudo \(Paths.brew) services info` JSON loaded",
|
||||
titleText: "startup.errors.services_json_error.title".localized,
|
||||
@@ -236,9 +236,9 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
let nodePath = await Shell.pipe("which node").out
|
||||
let nodePath = await App.shared.container.shell.pipe("which node").out
|
||||
return App.architecture == "x86_64"
|
||||
&& FileSystem.fileExists("/usr/local/bin/which")
|
||||
&& App.shared.container.filesystem.fileExists("/usr/local/bin/which")
|
||||
&& nodePath.contains("env: node: No such file or directory")
|
||||
},
|
||||
name: "`env: node` issue does not apply",
|
||||
@@ -265,7 +265,7 @@ class Startup {
|
||||
// =================================================================================
|
||||
EnvironmentCheck(
|
||||
command: {
|
||||
return await Shell.pipe("valet --version").out
|
||||
return await App.shared.container.shell.pipe("valet --version").out
|
||||
.contains("Composer detected issues in your platform")
|
||||
},
|
||||
name: "no global composer issues",
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
import ContainerMacro
|
||||
|
||||
@MainActor class ComposerWindow {
|
||||
@MainActor @ContainerAccess class ComposerWindow {
|
||||
private var shouldNotify: Bool! = nil
|
||||
private var completion: ((Bool) -> Void)! = nil
|
||||
private var window: TerminalProgressWindowController?
|
||||
@@ -55,7 +56,7 @@ import NVAlert
|
||||
|
||||
self.window?.addToConsole("\(command)\n")
|
||||
|
||||
let (process, _) = try await Shell.attach(
|
||||
let (process, _) = try await shell.attach(
|
||||
command,
|
||||
didReceiveOutput: { [weak self] (incoming, _) in
|
||||
guard let window = self?.window else { return }
|
||||
|
||||
@@ -50,7 +50,7 @@ struct ProjectTypeDetection {
|
||||
public static func detectFallbackDependency(_ basePath: String) -> String? {
|
||||
for entry in Self.FileMapping {
|
||||
let found = entry.value
|
||||
.map { path in return FileSystem.anyExists(basePath + path) }
|
||||
.map { path in return App.shared.container.filesystem.anyExists(basePath + path) }
|
||||
.contains(true)
|
||||
|
||||
if found {
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class Brew {
|
||||
static let shared = Brew()
|
||||
|
||||
@@ -19,7 +21,7 @@ class Brew {
|
||||
|
||||
/// Determine which version of Homebrew is installed.
|
||||
public func determineVersion() async {
|
||||
let output = await Shell.pipe("\(Paths.brew) --version")
|
||||
let output = await shell.pipe("\(Paths.brew) --version")
|
||||
self.version = try? VersionNumber.parse(output.out)
|
||||
|
||||
if let version = version {
|
||||
|
||||
@@ -8,18 +8,22 @@
|
||||
|
||||
import Foundation
|
||||
import NVAlert
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class BrewDiagnostics {
|
||||
public static let shared = BrewDiagnostics()
|
||||
|
||||
/**
|
||||
Determines the Homebrew taps the user has installed.
|
||||
*/
|
||||
public static var installedTaps: [String] = []
|
||||
public var installedTaps: [String] = []
|
||||
|
||||
/**
|
||||
Load which taps are installed.
|
||||
*/
|
||||
public static func loadInstalledTaps() async {
|
||||
installedTaps = await Shell
|
||||
public func loadInstalledTaps() async {
|
||||
installedTaps = await shell
|
||||
.pipe("\(Paths.brew) tap")
|
||||
.out
|
||||
.split(separator: "\n")
|
||||
@@ -31,13 +35,13 @@ class BrewDiagnostics {
|
||||
/**
|
||||
Logs a bunch of useful information during startup.
|
||||
*/
|
||||
public static func logBootInformation() {
|
||||
Log.info(BrewDiagnostics.customCaskInstalled
|
||||
public func logBootInformation() {
|
||||
Log.info(customCaskInstalled
|
||||
? "[BREW] The app has been installed via Homebrew Cask."
|
||||
: "[BREW] The app has been installed directly (optimal)."
|
||||
)
|
||||
|
||||
Log.info(BrewDiagnostics.usesNginxFullFormula
|
||||
Log.info(usesNginxFullFormula
|
||||
? "[BREW] The app will be using the `nginx-full` formula."
|
||||
: "[BREW] The app will be using the `nginx` formula."
|
||||
)
|
||||
@@ -46,21 +50,20 @@ class BrewDiagnostics {
|
||||
/**
|
||||
Determines whether the PHP Monitor Cask is installed.
|
||||
*/
|
||||
public static var customCaskInstalled: Bool = {
|
||||
public var customCaskInstalled: Bool {
|
||||
return installedTaps.contains("nicoverbruggen/cask")
|
||||
&& FileSystem.directoryExists(Paths.caskroomPath)
|
||||
}()
|
||||
&& filesystem.directoryExists(Paths.caskroomPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Determines whether to use the regular `nginx` or `nginx-full` formula.
|
||||
*/
|
||||
public static var usesNginxFullFormula: Bool = {
|
||||
guard let destination = try? FileManager.default
|
||||
.destinationOfSymbolicLink(atPath: "\(Paths.binPath)/nginx") else { return false }
|
||||
public var usesNginxFullFormula: Bool {
|
||||
guard let destination = try? filesystem.getDestinationOfSymlink("\(Paths.binPath)/nginx") else { return false }
|
||||
|
||||
// Verify that the `nginx` binary is symlinked to a directory that includes `nginx-full`.
|
||||
return destination.contains("/nginx-full/")
|
||||
}()
|
||||
}
|
||||
|
||||
/**
|
||||
It is possible to have outdated symlinks for PHP installations. This can mean that certain PHP installations
|
||||
@@ -68,12 +71,12 @@ class BrewDiagnostics {
|
||||
|
||||
To ensure this does not cause issues, PHP Monitor will automatically remove all incorrect PHP symlinks.
|
||||
*/
|
||||
public static func checkForOutdatedPhpInstallationSymlinks() async {
|
||||
public func checkForOutdatedPhpInstallationSymlinks() async {
|
||||
// Set up a regular expression
|
||||
let regex = try! NSRegularExpression(pattern: "^php@[0-9]+\\.[0-9]+$", options: .caseInsensitive)
|
||||
|
||||
// Check for incorrect versions
|
||||
if let contents = try? FileSystem.getShallowContentsOfDirectory("\(Paths.optPath)")
|
||||
if let contents = try? filesystem.getShallowContentsOfDirectory("\(Paths.optPath)")
|
||||
.filter({
|
||||
let range = NSRange($0.startIndex..., in: $0)
|
||||
return regex.firstMatch(in: $0, options: [], range: range) != nil
|
||||
@@ -81,12 +84,12 @@ class BrewDiagnostics {
|
||||
|
||||
for symlink in contents {
|
||||
let version = symlink.replacingOccurrences(of: "php@", with: "")
|
||||
if let destination = try? FileSystem.getDestinationOfSymlink("\(Paths.optPath)/\(symlink)") {
|
||||
if let destination = try? filesystem.getDestinationOfSymlink("\(Paths.optPath)/\(symlink)") {
|
||||
if !destination.contains("Cellar/php/\(version)")
|
||||
&& !destination.contains("Cellar/php@\(version)") {
|
||||
Log.err("Symlink for \(symlink) is incorrect. Removing...")
|
||||
do {
|
||||
try FileSystem.remove("\(Paths.optPath)/\(symlink)")
|
||||
try filesystem.remove("\(Paths.optPath)/\(symlink)")
|
||||
Log.info("Incorrect symlink for \(symlink) has been successfully removed.")
|
||||
} catch {
|
||||
Log.err("Symlink for \(symlink) was incorrect but could not be removed!")
|
||||
@@ -106,7 +109,7 @@ class BrewDiagnostics {
|
||||
|
||||
This check only needs to be performed if the `shivammathur/php` tap is active.
|
||||
*/
|
||||
public static func checkForCaskConflict() async {
|
||||
public func checkForCaskConflict() async {
|
||||
if await hasAliasConflict() {
|
||||
presentAlertAboutConflict()
|
||||
}
|
||||
@@ -116,7 +119,7 @@ class BrewDiagnostics {
|
||||
It is possible to upgrade PHP, but forget running `valet install`.
|
||||
This results in a scenario where a rogue www.conf file exists.
|
||||
*/
|
||||
public static func checkForValetMisconfiguration() async {
|
||||
public func checkForValetMisconfiguration() async {
|
||||
Log.info("Checking for PHP-FPM issues with Valet...")
|
||||
|
||||
guard let install = PhpEnvironments.phpInstall else {
|
||||
@@ -138,7 +141,7 @@ class BrewDiagnostics {
|
||||
}
|
||||
}
|
||||
|
||||
public static func verifyThirdPartyTaps() async {
|
||||
public func verifyThirdPartyTaps() async {
|
||||
let requiredTaps = [
|
||||
"shivammathur/php",
|
||||
"shivammathur/extensions"
|
||||
@@ -157,8 +160,8 @@ class BrewDiagnostics {
|
||||
/**
|
||||
Check if the alias conflict as documented in `checkForCaskConflict` actually occurred.
|
||||
*/
|
||||
private static func hasAliasConflict() async -> Bool {
|
||||
let tapAlias = await Shell.pipe("brew info shivammathur/php/php --json").out
|
||||
private func hasAliasConflict() async -> Bool {
|
||||
let tapAlias = await shell.pipe("brew info shivammathur/php/php --json").out
|
||||
|
||||
if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") || tapAlias.isEmpty {
|
||||
Log.info("The user does not appear to have tapped: shivammathur/php")
|
||||
@@ -198,7 +201,7 @@ class BrewDiagnostics {
|
||||
/**
|
||||
Show this alert in case the tapped Cask does cause issues because of the conflict.
|
||||
*/
|
||||
private static func presentAlertAboutConflict() {
|
||||
private func presentAlertAboutConflict() {
|
||||
Task { @MainActor in
|
||||
NVAlert()
|
||||
.withInformation(
|
||||
@@ -214,8 +217,8 @@ class BrewDiagnostics {
|
||||
In order to see if we support the --json syntax, we'll query nginx.
|
||||
If the JSON response cannot be parsed, Homebrew is probably out of date.
|
||||
*/
|
||||
public static func cannotLoadService(_ name: String) async -> Bool {
|
||||
let nginxJson = await Shell
|
||||
public func cannotLoadService(_ name: String) async -> Bool {
|
||||
let nginxJson = await shell
|
||||
.pipe("sudo \(Paths.brew) services info \(name) --json")
|
||||
.out
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ struct BrewPhpExtension: Hashable, Comparable {
|
||||
}
|
||||
|
||||
static func hasInstallationReceipt(for formulaName: String) -> Bool {
|
||||
return FileSystem.fileExists("\(Paths.optPath)/\(formulaName)/INSTALL_RECEIPT.json")
|
||||
return App.shared.container.filesystem.fileExists("\(Paths.optPath)/\(formulaName)/INSTALL_RECEIPT.json")
|
||||
}
|
||||
|
||||
static func < (lhs: BrewPhpExtension, rhs: BrewPhpExtension) -> Bool {
|
||||
@@ -74,7 +74,7 @@ struct BrewPhpExtension: Hashable, Comparable {
|
||||
let regexPattern = #"depends_on "(.*)""#
|
||||
var dependencies: [String] = []
|
||||
|
||||
guard let content = try? FileSystem.getStringFromFile(path) else {
|
||||
guard let content = try? App.shared.container.filesystem.getStringFromFile(path) else {
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ struct BrewPhpFormula: Equatable {
|
||||
return false
|
||||
}
|
||||
|
||||
return FileSystem.fileExists(
|
||||
return App.shared.container.filesystem.fileExists(
|
||||
"\(Paths.tapPath)/shivammathur/homebrew-php/Formula/php@\(version).rb"
|
||||
.replacingOccurrences(of: "php@" + PhpEnvironments.brewPhpAlias, with: "php")
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
protocol HandlesBrewPhpFormulae {
|
||||
func loadPhpVersions(loadOutdated: Bool) async -> [BrewPhpFormula]
|
||||
@@ -23,6 +24,7 @@ extension HandlesBrewPhpFormulae {
|
||||
}
|
||||
}
|
||||
|
||||
@ContainerAccess
|
||||
class BrewPhpFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
public func loadPhpVersions(loadOutdated: Bool) async -> [BrewPhpFormula] {
|
||||
var outdated: [OutdatedFormula]?
|
||||
@@ -33,7 +35,7 @@ class BrewPhpFormulaeHandler: HandlesBrewPhpFormulae {
|
||||
\(Paths.brew) outdated --json --formulae
|
||||
"""
|
||||
|
||||
let rawJsonText = await Shell.pipe(command).out
|
||||
let rawJsonText = await shell.pipe(command).out
|
||||
.data(using: .utf8)!
|
||||
outdated = try? JSONDecoder().decode(
|
||||
OutdatedFormulae.self,
|
||||
|
||||
@@ -12,7 +12,7 @@ class BrewTapFormulae {
|
||||
public static func from(tap: String) -> [String: [BrewPhpExtension]] {
|
||||
let directory = "\(Paths.tapPath)/\(tap)/Formula"
|
||||
|
||||
let files = try? FileSystem.getShallowContentsOfDirectory(directory)
|
||||
let files = try? App.shared.container.filesystem.getShallowContentsOfDirectory(directory)
|
||||
|
||||
var availableExtensions = [String: [BrewPhpExtension]]()
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ struct CaskFile {
|
||||
|
||||
private static func loadFromApi(_ url: URL) async -> String {
|
||||
if App.hasLoadedTestableConfiguration || url.absoluteString.contains("https://raw.githubusercontent.com") {
|
||||
return await Shell.pipe("curl -s --max-time 10 '\(url.absoluteString)'").out
|
||||
return await App.shared.container.shell.pipe("curl -s --max-time 10 '\(url.absoluteString)'").out
|
||||
} else {
|
||||
return await Shell.pipe("""
|
||||
return await App.shared.container.shell.pipe("""
|
||||
curl -s --max-time 10 \
|
||||
-H "User-Agent: phpmon-curl/1.0" \
|
||||
-H "X-phpmon-version: \(App.shortVersion) (\(App.bundleVersion))" \
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
protocol BrewCommand {
|
||||
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws
|
||||
func execute(shell: ShellProtocol, onProgress: @escaping (BrewCommandProgress) -> Void) async throws
|
||||
|
||||
func getCommandTitle() -> String
|
||||
}
|
||||
@@ -78,10 +78,14 @@ extension BrewCommand {
|
||||
return nil
|
||||
}
|
||||
|
||||
internal func run(_ command: String, _ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
internal func run(
|
||||
shell: ShellProtocol,
|
||||
_ command: String,
|
||||
_ onProgress: @escaping (BrewCommandProgress) -> Void
|
||||
) async throws {
|
||||
var loggedMessages: [String] = []
|
||||
|
||||
let (process, _) = try! await Shell.attach(
|
||||
let (process, _) = try! await shell.attach(
|
||||
command,
|
||||
didReceiveOutput: { text, _ in
|
||||
if !text.isEmpty {
|
||||
@@ -104,15 +108,18 @@ extension BrewCommand {
|
||||
}
|
||||
}
|
||||
|
||||
internal func checkPhpTap(_ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
if !BrewDiagnostics.installedTaps.contains("shivammathur/php") {
|
||||
internal func checkPhpTap(
|
||||
shell: ShellProtocol,
|
||||
_ onProgress: @escaping (BrewCommandProgress) -> Void
|
||||
) async throws {
|
||||
if !BrewDiagnostics.shared.installedTaps.contains("shivammathur/php") {
|
||||
let command = "brew tap shivammathur/php"
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
|
||||
if !BrewDiagnostics.installedTaps.contains("shivammathur/extensions") {
|
||||
if !BrewDiagnostics.shared.installedTaps.contains("shivammathur/extensions") {
|
||||
let command = "brew tap shivammathur/extensions"
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class InstallPhpExtensionCommand: BrewCommand {
|
||||
let installing: [BrewPhpExtension]
|
||||
|
||||
@@ -23,7 +25,7 @@ class InstallPhpExtensionCommand: BrewCommand {
|
||||
self.installing = extensions
|
||||
}
|
||||
|
||||
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
func execute(shell: ShellProtocol, onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
let progressTitle = "phpman.steps.wait".localized
|
||||
|
||||
onProgress(.create(
|
||||
@@ -33,16 +35,16 @@ class InstallPhpExtensionCommand: BrewCommand {
|
||||
))
|
||||
|
||||
// Make sure the tap is installed
|
||||
try await self.checkPhpTap(onProgress)
|
||||
try await self.checkPhpTap(shell: shell, onProgress)
|
||||
|
||||
// Make sure that the extension(s) are installed
|
||||
try await self.installPackages(onProgress)
|
||||
try await self.installPackages(shell, onProgress)
|
||||
|
||||
// Finally, complete all operations
|
||||
await self.completedOperations(onProgress)
|
||||
}
|
||||
|
||||
private func installPackages(_ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
private func installPackages(_ shell: ShellProtocol, _ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
// If no installations are needed, early exit
|
||||
if self.installing.isEmpty {
|
||||
return
|
||||
@@ -55,7 +57,7 @@ class InstallPhpExtensionCommand: BrewCommand {
|
||||
\(Paths.brew) install \(self.installing.map { $0.formulaName }.joined(separator: " ")) --force
|
||||
"""
|
||||
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
|
||||
private func completedOperations(_ onProgress: @escaping (BrewCommandProgress) -> Void) async {
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class RemovePhpExtensionCommand: BrewCommand {
|
||||
public let phpExtension: BrewPhpExtension
|
||||
|
||||
@@ -19,7 +21,7 @@ class RemovePhpExtensionCommand: BrewCommand {
|
||||
return "phpman.steps.removing".localized(phpExtension.name)
|
||||
}
|
||||
|
||||
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
func execute(shell: ShellProtocol, onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
onProgress(.create(
|
||||
value: 0.2,
|
||||
title: getCommandTitle(),
|
||||
@@ -42,7 +44,7 @@ class RemovePhpExtensionCommand: BrewCommand {
|
||||
|
||||
var loggedMessages: [String] = []
|
||||
|
||||
let (process, _) = try! await Shell.attach(
|
||||
let (process, _) = try! await shell.attach(
|
||||
command,
|
||||
didReceiveOutput: { text, _ in
|
||||
if !text.isEmpty {
|
||||
@@ -77,7 +79,7 @@ class RemovePhpExtensionCommand: BrewCommand {
|
||||
// The extension's default configuration file can be removed
|
||||
Log.info("The extension was found in a default extension .ini location. Purging that .ini file.")
|
||||
do {
|
||||
try FileSystem.remove(ext.file)
|
||||
try filesystem.remove(ext.file)
|
||||
} catch {
|
||||
Log.err("The file `\(ext.file)` could not be removed.")
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class ModifyPhpVersionCommand: BrewCommand {
|
||||
let title: String
|
||||
let installing: [BrewPhpFormula]
|
||||
@@ -32,17 +34,19 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
re-installed and linked again.
|
||||
*/
|
||||
public init(
|
||||
container: Container = App.shared.container,
|
||||
title: String,
|
||||
upgrading: [BrewPhpFormula],
|
||||
installing: [BrewPhpFormula]
|
||||
) {
|
||||
self.container = container
|
||||
self.title = title
|
||||
self.installing = installing
|
||||
self.upgrading = upgrading
|
||||
self.phpGuard = PhpGuard()
|
||||
}
|
||||
|
||||
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
func execute(shell: ShellProtocol, onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
let progressTitle = "phpman.steps.wait".localized
|
||||
|
||||
onProgress(.create(
|
||||
@@ -58,7 +62,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
})
|
||||
|
||||
// Make sure the tap is installed
|
||||
try await self.checkPhpTap(onProgress)
|
||||
try await self.checkPhpTap(shell: shell, onProgress)
|
||||
|
||||
if unavailable == nil {
|
||||
// Try to run all upgrade and installation operations
|
||||
@@ -99,7 +103,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
"""
|
||||
|
||||
// Run the upgrade command
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
|
||||
private func upgradePackages(_ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
@@ -115,7 +119,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
\(Paths.brew) upgrade \(self.upgrading.map { $0.name }.joined(separator: " "))
|
||||
"""
|
||||
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
|
||||
private func installPackages(_ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
@@ -130,7 +134,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
\(Paths.brew) install \(self.installing.map { $0.name }.joined(separator: " ")) --force
|
||||
"""
|
||||
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
|
||||
private func repairBrokenPackages(_ onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
@@ -162,7 +166,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
\(Paths.brew) reinstall \(requiringRepair.joined(separator: " ")) --force
|
||||
"""
|
||||
|
||||
try await run(command, onProgress)
|
||||
try await run(shell: shell, command, onProgress)
|
||||
}
|
||||
|
||||
private func completedOperations(_ onProgress: @escaping (BrewCommandProgress) -> Void) async {
|
||||
@@ -170,7 +174,7 @@ class ModifyPhpVersionCommand: BrewCommand {
|
||||
onProgress(.create(value: 0.95, title: self.title, description: "phpman.steps.reloading".localized))
|
||||
|
||||
// Ensure all symlinks are correctly linked
|
||||
await BrewDiagnostics.checkForOutdatedPhpInstallationSymlinks()
|
||||
await BrewDiagnostics.shared.checkForOutdatedPhpInstallationSymlinks()
|
||||
|
||||
// Check which version of PHP are now installed
|
||||
await PhpEnvironments.detectPhpVersions()
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class RemovePhpVersionCommand: BrewCommand {
|
||||
let formula: String
|
||||
let version: String
|
||||
@@ -25,7 +27,7 @@ class RemovePhpVersionCommand: BrewCommand {
|
||||
return "phpman.steps.removing".localized("PHP \(version)...")
|
||||
}
|
||||
|
||||
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
func execute(shell: ShellProtocol, onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
onProgress(.create(
|
||||
value: 0.2,
|
||||
title: getCommandTitle(),
|
||||
@@ -47,7 +49,7 @@ class RemovePhpVersionCommand: BrewCommand {
|
||||
|
||||
var loggedMessages: [String] = []
|
||||
|
||||
let (process, _) = try! await Shell.attach(
|
||||
let (process, _) = try! await shell.attach(
|
||||
command,
|
||||
didReceiveOutput: { text, _ in
|
||||
if !text.isEmpty {
|
||||
|
||||
@@ -19,7 +19,7 @@ class FakeCommand: BrewCommand {
|
||||
self.version = version
|
||||
}
|
||||
|
||||
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
func execute(shell: ShellProtocol, onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
|
||||
onProgress(.create(value: 0.2, title: "Hello", description: "Doing the work"))
|
||||
await delay(seconds: 2)
|
||||
onProgress(.create(value: 0.5, title: "Hello", description: "Doing some more work"))
|
||||
|
||||
@@ -14,7 +14,7 @@ class ValetUpgrader {
|
||||
let path = "~/.composer/composer.json".replacingTildeWithHomeDirectory
|
||||
|
||||
do {
|
||||
if FileSystem.fileExists(path) {
|
||||
if App.shared.container.filesystem.fileExists(path) {
|
||||
return try JSONDecoder().decode(
|
||||
ComposerJson.self,
|
||||
from: String(
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
struct ValetInteractionError: Error {
|
||||
/// The command the user should try (and failed).
|
||||
var command: String
|
||||
}
|
||||
|
||||
@ContainerAccess
|
||||
class ValetInteractor {
|
||||
static var shared = ValetInteractor()
|
||||
|
||||
@@ -23,11 +25,11 @@ class ValetInteractor {
|
||||
// MARK: - Managing Domains
|
||||
|
||||
public func link(path: String, domain: String) async throws {
|
||||
await Shell.quiet("cd '\(path)' && \(Paths.valet) link '\(domain)' && valet links")
|
||||
await shell.quiet("cd '\(path)' && \(Paths.valet) link '\(domain)' && valet links")
|
||||
}
|
||||
|
||||
public func unlink(site: ValetSite) async throws {
|
||||
await Shell.quiet("valet unlink '\(site.name)'")
|
||||
await shell.quiet("valet unlink '\(site.name)'")
|
||||
}
|
||||
|
||||
public func proxy(domain: String, proxy: String, secure: Bool) async throws {
|
||||
@@ -35,12 +37,12 @@ class ValetInteractor {
|
||||
? "\(Paths.valet) proxy \(domain) \(proxy) --secure"
|
||||
: "\(Paths.valet) proxy \(domain) \(proxy)"
|
||||
|
||||
await Shell.quiet(command)
|
||||
await shell.quiet(command)
|
||||
await Actions.restartNginx()
|
||||
}
|
||||
|
||||
public func remove(proxy: ValetProxy) async throws {
|
||||
await Shell.quiet("valet unproxy '\(proxy.domain)'")
|
||||
await shell.quiet("valet unproxy '\(proxy.domain)'")
|
||||
}
|
||||
|
||||
// MARK: - Modifying Domains
|
||||
@@ -62,7 +64,7 @@ class ValetInteractor {
|
||||
}
|
||||
|
||||
// Run the command
|
||||
await Shell.quiet(command)
|
||||
await shell.quiet(command)
|
||||
|
||||
// Check if the secured status has actually changed
|
||||
site.determineSecured()
|
||||
@@ -87,7 +89,7 @@ class ValetInteractor {
|
||||
|
||||
// Run the commands
|
||||
for command in commands {
|
||||
await Shell.quiet(command)
|
||||
await shell.quiet(command)
|
||||
}
|
||||
|
||||
// Check if the secured status has actually changed
|
||||
@@ -106,7 +108,7 @@ class ValetInteractor {
|
||||
let command = "sudo \(Paths.valet) isolate php@\(version) --site '\(site.name)'"
|
||||
|
||||
// Run the command
|
||||
await Shell.quiet(command)
|
||||
await shell.quiet(command)
|
||||
|
||||
// Check if the secured status has actually changed
|
||||
site.determineIsolated()
|
||||
@@ -122,7 +124,7 @@ class ValetInteractor {
|
||||
let command = "sudo \(Paths.valet) unisolate --site '\(site.name)'"
|
||||
|
||||
// Run the command
|
||||
await Shell.quiet(command)
|
||||
await shell.quiet(command)
|
||||
|
||||
// Check if the secured status has actually changed
|
||||
site.determineIsolated()
|
||||
|
||||
@@ -78,7 +78,7 @@ class ValetProxy: ValetListable {
|
||||
// MARK: - Interactions
|
||||
|
||||
func determineSecured() {
|
||||
self.secured = FileSystem.fileExists("~/.config/valet/Certificates/\(self.domain).\(self.tld).key")
|
||||
self.secured = container.filesystem.fileExists("~/.config/valet/Certificates/\(self.domain).\(self.tld).key")
|
||||
}
|
||||
|
||||
func toggleFavorite() {
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class ValetDomainScanner: DomainScanner {
|
||||
|
||||
// MARK: - Sites
|
||||
@@ -15,7 +17,7 @@ class ValetDomainScanner: DomainScanner {
|
||||
func resolveSiteCount(paths: [String]) -> Int {
|
||||
return paths.map { path in
|
||||
do {
|
||||
let entries = try FileSystem
|
||||
let entries = try filesystem
|
||||
.getShallowContentsOfDirectory(path)
|
||||
|
||||
return entries
|
||||
@@ -35,7 +37,7 @@ class ValetDomainScanner: DomainScanner {
|
||||
|
||||
paths.forEach { path in
|
||||
do {
|
||||
let entries = try FileSystem
|
||||
let entries = try filesystem
|
||||
.getShallowContentsOfDirectory(path)
|
||||
|
||||
return entries.forEach {
|
||||
@@ -59,7 +61,7 @@ class ValetDomainScanner: DomainScanner {
|
||||
// Get the TLD from the global Valet object
|
||||
let tld = Valet.shared.config.tld
|
||||
|
||||
if !FileSystem.anyExists(path) {
|
||||
if !filesystem.anyExists(path) {
|
||||
Log.warn("Could not parse the site: \(path), skipping!")
|
||||
}
|
||||
|
||||
@@ -69,9 +71,9 @@ class ValetDomainScanner: DomainScanner {
|
||||
return nil
|
||||
}
|
||||
|
||||
if FileSystem.isSymlink(path) {
|
||||
return ValetSite(aliasPath: path, tld: tld)
|
||||
} else if FileSystem.isDirectory(path) {
|
||||
if filesystem.isSymlink(path) {
|
||||
return ValetSite(filesystem: filesystem, aliasPath: path, tld: tld)
|
||||
} else if filesystem.isDirectory(path) {
|
||||
return ValetSite(absolutePath: path, tld: tld)
|
||||
}
|
||||
|
||||
@@ -85,7 +87,7 @@ class ValetDomainScanner: DomainScanner {
|
||||
private func isSite(_ entry: String, forPath path: String) -> Bool {
|
||||
let siteDir = path + "/" + entry
|
||||
|
||||
return (FileSystem.isDirectory(siteDir) || FileSystem.isSymlink(siteDir))
|
||||
return (App.shared.container.filesystem.isDirectory(siteDir) || App.shared.container.filesystem.isSymlink(siteDir))
|
||||
}
|
||||
|
||||
// MARK: - Proxies
|
||||
|
||||
@@ -96,9 +96,9 @@ class ValetSite: ValetListable {
|
||||
self.init(name: name, tld: tld, absolutePath: absolutePath)
|
||||
}
|
||||
|
||||
convenience init(aliasPath: String, tld: String) {
|
||||
convenience init(filesystem: FileSystemProtocol, aliasPath: String, tld: String) {
|
||||
let name = URL(fileURLWithPath: aliasPath).lastPathComponent
|
||||
let absolutePath = try! FileSystem.getDestinationOfSymlink(aliasPath)
|
||||
let absolutePath = try! filesystem.getDestinationOfSymlink(aliasPath)
|
||||
self.init(name: name, tld: tld, absolutePath: absolutePath, aliasPath: aliasPath)
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class ValetSite: ValetListable {
|
||||
Determine whether a site is isolated.
|
||||
*/
|
||||
public func determineIsolated() {
|
||||
if let version = ValetSite.isolatedVersion("~/.config/valet/Nginx/\(self.name).\(self.tld)") {
|
||||
if let version = ValetSite.isolatedVersion(container, "~/.config/valet/Nginx/\(self.name).\(self.tld)") {
|
||||
if !PhpEnvironments.shared.cachedPhpInstallations.keys.contains(version) {
|
||||
Log.err("The PHP version \(version) is isolated for the site \(self.name) "
|
||||
+ "but that PHP version is unavailable.")
|
||||
@@ -123,7 +123,7 @@ class ValetSite: ValetListable {
|
||||
- Note: The file is not validated, only its presence is checked.
|
||||
*/
|
||||
public func determineSecured() {
|
||||
secured = FileSystem.fileExists("~/.config/valet/Certificates/\(self.name).\(self.tld).key")
|
||||
secured = filesystem.fileExists("~/.config/valet/Certificates/\(self.name).\(self.tld).key")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,7 +185,7 @@ class ValetSite: ValetListable {
|
||||
let path = "\(absolutePath)/composer.json"
|
||||
|
||||
do {
|
||||
if FileSystem.fileExists(path) {
|
||||
if filesystem.fileExists(path) {
|
||||
let decoded = try JSONDecoder().decode(
|
||||
ComposerJson.self,
|
||||
from: String(
|
||||
@@ -216,7 +216,7 @@ class ValetSite: ValetListable {
|
||||
for (suffix, source) in files {
|
||||
do {
|
||||
let path = "\(absolutePath)/\(suffix)"
|
||||
if FileSystem.fileExists(path) {
|
||||
if filesystem.fileExists(path) {
|
||||
return try self.handleValetFile(path, source)
|
||||
}
|
||||
} catch {
|
||||
@@ -274,8 +274,11 @@ class ValetSite: ValetListable {
|
||||
|
||||
// MARK: - File Parsing
|
||||
|
||||
public static func isolatedVersion(_ filePath: String) -> String? {
|
||||
if FileSystem.fileExists(filePath) {
|
||||
public static func isolatedVersion(
|
||||
_ container: Container = App.shared.container,
|
||||
_ filePath: String
|
||||
) -> String? {
|
||||
if container.filesystem.fileExists(filePath) {
|
||||
return NginxConfigurationFile
|
||||
.from(filePath: filePath)?
|
||||
.isolatedVersion ?? nil
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
/**
|
||||
This class is responsible for handling the state of Valet throughout PHP Monitor. A singleton instance is created
|
||||
and accessible throughout the lifecycle of the app, unless the user has decided to not use Valet. In that case,
|
||||
only a restricted subset of functionality is available in the app.
|
||||
*/
|
||||
@ContainerAccess
|
||||
class Valet {
|
||||
|
||||
enum FeatureFlag {
|
||||
@@ -65,8 +67,8 @@ class Valet {
|
||||
}
|
||||
|
||||
lazy var installed: Bool = {
|
||||
return FileSystem.fileExists(Paths.binPath.appending("/valet"))
|
||||
&& FileSystem.anyExists("~/.config/valet")
|
||||
return filesystem.fileExists(Paths.binPath.appending("/valet"))
|
||||
&& filesystem.anyExists("~/.config/valet")
|
||||
}()
|
||||
|
||||
/**
|
||||
@@ -89,7 +91,7 @@ class Valet {
|
||||
and the app cannot start.
|
||||
*/
|
||||
public func updateVersionNumber() async {
|
||||
let output = await Shell.pipe("valet --version").out
|
||||
let output = await shell.pipe("valet --version").out
|
||||
|
||||
// Failure condition #1: does not contain Laravel Valet
|
||||
if !output.contains("Laravel Valet") {
|
||||
@@ -119,7 +121,7 @@ class Valet {
|
||||
do {
|
||||
config = try JSONDecoder().decode(
|
||||
Valet.Configuration.self,
|
||||
from: FileSystem.getStringFromFile("~/.config/valet/config.json").data(using: .utf8)!
|
||||
from: filesystem.getStringFromFile("~/.config/valet/config.json").data(using: .utf8)!
|
||||
)
|
||||
} catch {
|
||||
Log.err(error)
|
||||
@@ -206,7 +208,7 @@ class Valet {
|
||||
Determine if any platform issues are detected when running `valet --version`.
|
||||
*/
|
||||
public func hasPlatformIssues() async -> Bool {
|
||||
return await Shell.pipe("valet --version")
|
||||
return await shell.pipe("valet --version")
|
||||
.out.contains("Composer detected issues in your platform")
|
||||
}
|
||||
|
||||
@@ -248,12 +250,12 @@ class Valet {
|
||||
if version.short == "5.6" {
|
||||
// The main PHP config file should contain `valet.sock` and then we're probably fine?
|
||||
let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf"
|
||||
return await Shell.pipe("cat \(fileName)").out
|
||||
return await shell.pipe("cat \(fileName)").out
|
||||
.contains("valet.sock")
|
||||
}
|
||||
|
||||
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
||||
return FileSystem.fileExists("\(Paths.etcPath)/php/\(version.short)/php-fpm.d/valet-fpm.conf")
|
||||
return filesystem.fileExists("\(Paths.etcPath)/php/\(version.short)/php-fpm.d/valet-fpm.conf")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,13 +34,13 @@ extension MainMenu {
|
||||
await PhpEnvironments.shared.determinePhpAlias()
|
||||
|
||||
// Make sure that broken symlinks are removed ASAP
|
||||
await BrewDiagnostics.checkForOutdatedPhpInstallationSymlinks()
|
||||
await BrewDiagnostics.shared.checkForOutdatedPhpInstallationSymlinks()
|
||||
|
||||
// Initialize preferences
|
||||
_ = Preferences.shared
|
||||
|
||||
// Put some useful diagnostics information in log
|
||||
BrewDiagnostics.logBootInformation()
|
||||
BrewDiagnostics.shared.logBootInformation()
|
||||
|
||||
// Attempt to find out more info about Valet
|
||||
if Valet.shared.version != nil {
|
||||
@@ -58,10 +58,10 @@ extension MainMenu {
|
||||
|
||||
// Verify third party taps
|
||||
// The missing tap(s) will be actionable later
|
||||
await BrewDiagnostics.verifyThirdPartyTaps()
|
||||
await BrewDiagnostics.shared.verifyThirdPartyTaps()
|
||||
|
||||
// Check for an alias conflict
|
||||
await BrewDiagnostics.checkForCaskConflict()
|
||||
await BrewDiagnostics.shared.checkForCaskConflict()
|
||||
|
||||
// Attempt to find out if PHP-FPM is broken
|
||||
PhpEnvironments.prepare()
|
||||
@@ -92,7 +92,7 @@ extension MainMenu {
|
||||
await Valet.shared.startPreloadingSites()
|
||||
|
||||
// After preloading sites, check for PHP-FPM pool conflicts
|
||||
await BrewDiagnostics.checkForValetMisconfiguration()
|
||||
await BrewDiagnostics.shared.checkForValetMisconfiguration()
|
||||
|
||||
// Check if PHP-FPM is broken (should be fixed automatically if phpmon >= 6.0)
|
||||
await Valet.shared.notifyAboutBrokenPhpFpm()
|
||||
|
||||
@@ -45,7 +45,7 @@ struct CustomPrefs: Decodable {
|
||||
extension Preferences {
|
||||
func loadCustomPreferences() async {
|
||||
// Ensure the configuration directory is created if missing
|
||||
await Shell.quiet("mkdir -p ~/.config/phpmon")
|
||||
await App.shared.container.shell.quiet("mkdir -p ~/.config/phpmon")
|
||||
|
||||
// Move the legacy file
|
||||
await moveOutdatedConfigurationFile()
|
||||
@@ -64,7 +64,7 @@ extension Preferences {
|
||||
func moveOutdatedConfigurationFile() async {
|
||||
if FileSystem.fileExists("~/.phpmon.conf.json") && !FileSystem.fileExists("~/.config/phpmon/config.json") {
|
||||
Log.info("An outdated configuration file was found. Moving it...")
|
||||
await Shell.quiet("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json")
|
||||
await App.shared.container.shell.quiet("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json")
|
||||
Log.info("The configuration file was copied successfully!")
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ extension Preferences {
|
||||
|
||||
if customPreferences.hasEnvironmentVariables() {
|
||||
Log.info("Configuring the additional exports...")
|
||||
if let shell = Shell as? RealShell {
|
||||
if let shell = App.shared.container.shell as? RealShell {
|
||||
shell.exports = customPreferences.exportAsString
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class Stats {
|
||||
*/
|
||||
public static func evaluateSponsorMessageShouldBeDisplayed() {
|
||||
|
||||
if Shell is TestableShell {
|
||||
if App.shared.container.shell is TestableShell {
|
||||
return Log.info("A fake shell is in use, skipping sponsor alert.")
|
||||
}
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ struct Preset: Codable, Equatable {
|
||||
private func persistRevert() async {
|
||||
let data = try! JSONEncoder().encode(self.revertSnapshot)
|
||||
|
||||
await Shell.quiet("mkdir -p ~/.config/phpmon")
|
||||
await App.shared.container.shell.quiet("mkdir -p ~/.config/phpmon")
|
||||
|
||||
try! String(data: data, encoding: .utf8)!
|
||||
.write(
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class InstallHomebrew {
|
||||
public func run() async throws {
|
||||
let script = """
|
||||
@@ -13,7 +16,7 @@ class InstallHomebrew {
|
||||
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
"""
|
||||
|
||||
_ = try await Shell.attach(script, didReceiveOutput: { (string: String, _: ShellStream) in
|
||||
_ = try await shell.attach(script, didReceiveOutput: { (string: String, _: ShellStream) in
|
||||
print(string)
|
||||
}, withTimeout: 60 * 10)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
// Copyright © 2025 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class ZshRunCommand {
|
||||
/**
|
||||
Adds a given line to .zshrc, which may be needed to adjust the PATH.
|
||||
*/
|
||||
private func add(_ text: String) async -> Bool {
|
||||
let outcome = await Shell.pipe("""
|
||||
let outcome = await shell.pipe("""
|
||||
touch ~/.zshrc && \
|
||||
grep -qxF '\(text)' ~/.zshrc \
|
||||
|| echo '\n\n\(text)\n' >> ~/.zshrc
|
||||
|
||||
@@ -16,11 +16,14 @@ class Paths {
|
||||
|
||||
static let shared = Paths()
|
||||
var baseDir: HomebrewDir
|
||||
var userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||
var userName: String
|
||||
|
||||
init() {
|
||||
let optBrewFound = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew")
|
||||
let usrBrewFound = Shell.fileExists("\(HomebrewDir.usr.rawValue)/bin/brew")
|
||||
let shell = App.shared.container.shell
|
||||
self.userName = String(shell.sync("whoami").out.split(separator: "\n")[0])
|
||||
|
||||
let optBrewFound = App.shared.container.filesystem.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew")
|
||||
let usrBrewFound = App.shared.container.filesystem.fileExists("\(HomebrewDir.usr.rawValue)/bin/brew")
|
||||
|
||||
if optBrewFound {
|
||||
// This is usually the case with Homebrew installed on Apple Silicon
|
||||
|
||||
@@ -59,7 +59,7 @@ class ConfigWatchManager {
|
||||
eventMask: DispatchSource.FileSystemEvent,
|
||||
behaviour: ConfigFSNotifier.Behaviour = .reloadsMenu
|
||||
) {
|
||||
if !FileSystem.anyExists(url.path) {
|
||||
if !App.shared.container.filesystem.anyExists(url.path) {
|
||||
Log.warn("No watcher was created for \(url.path) because the requested file does not exist.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
|
||||
let path = pathControl.url!.path
|
||||
let name = inputDomainName.stringValue
|
||||
|
||||
if !FileSystem.anyExists(path) {
|
||||
if !App.shared.container.filesystem.anyExists(path) {
|
||||
Alert.confirm(
|
||||
onWindow: view.window!,
|
||||
messageText: "domain_list.alert.folder_missing.title".localized,
|
||||
|
||||
@@ -32,11 +32,11 @@ extension DomainListVC {
|
||||
}
|
||||
|
||||
@objc func openInFinder() {
|
||||
Task { return await Shell.quiet("open '\(selectedSite!.absolutePath)'") }
|
||||
Task { return await App.shared.container.shell.quiet("open '\(selectedSite!.absolutePath)'") }
|
||||
}
|
||||
|
||||
@objc func openInTerminal() {
|
||||
Task { await Shell.quiet("open -b com.apple.terminal '\(selectedSite!.absolutePath)'") }
|
||||
Task { await App.shared.container.shell.quiet("open -b com.apple.terminal '\(selectedSite!.absolutePath)'") }
|
||||
}
|
||||
|
||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||
@@ -50,7 +50,7 @@ extension DomainListVC {
|
||||
let rowToReload = tableView.selectedRow
|
||||
|
||||
waitAndExecute {
|
||||
await Shell.quiet(command)
|
||||
await App.shared.container.shell.quiet(command)
|
||||
} completion: { [self] in
|
||||
beforeCellReload()
|
||||
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ContainerMacro
|
||||
|
||||
@ContainerAccess
|
||||
class BytePhpPreference: PhpPreference {
|
||||
enum UnitOption: String, CaseIterable {
|
||||
case kilobyte = "K"
|
||||
@@ -35,8 +37,8 @@ class BytePhpPreference: PhpPreference {
|
||||
didSet { updatedFieldValue() }
|
||||
}
|
||||
|
||||
override init(key: String) {
|
||||
let value = Command.execute(
|
||||
init(container: Container = App.shared.container, key: String) {
|
||||
let value = container.command.execute(
|
||||
path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"],
|
||||
trimNewlines: false
|
||||
)
|
||||
@@ -47,6 +49,7 @@ class BytePhpPreference: PhpPreference {
|
||||
self.value = value
|
||||
}
|
||||
super.init(key: key)
|
||||
self.container = container
|
||||
}
|
||||
|
||||
// MARK: Save Value
|
||||
|
||||
@@ -36,7 +36,7 @@ class PhpConfigChecker {
|
||||
|
||||
// Do the check
|
||||
let fullFilePath = Paths.etcPath.appending("/php/\(version)/\(file.path)")
|
||||
if !FileSystem.fileExists(fullFilePath) {
|
||||
if !App.shared.container.filesystem.fileExists(fullFilePath) {
|
||||
missing.append(fullFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ extension WarningManager {
|
||||
return [
|
||||
Warning(
|
||||
command: {
|
||||
return await Shell.pipe("sysctl -n sysctl.proc_translated").out
|
||||
return await App.shared.container.shell.pipe("sysctl -n sysctl.proc_translated").out
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
|
||||
},
|
||||
name: "Running PHP Monitor with Rosetta on Apple Silicon",
|
||||
@@ -23,7 +23,7 @@ extension WarningManager {
|
||||
),
|
||||
Warning(
|
||||
command: {
|
||||
return !Shell.PATH.contains("\(Paths.homePath)/.config/phpmon/bin") &&
|
||||
return !App.shared.container.shell.PATH.contains("\(Paths.homePath)/.config/phpmon/bin") &&
|
||||
!FileSystem.isWriteableFile("/usr/local/bin/")
|
||||
},
|
||||
name: "Helpers cannot be symlinked and not in PATH",
|
||||
@@ -73,7 +73,7 @@ extension WarningManager {
|
||||
),
|
||||
Warning(
|
||||
command: {
|
||||
!BrewDiagnostics.installedTaps.contains("shivammathur/php")
|
||||
!BrewDiagnostics.shared.installedTaps.contains("shivammathur/php")
|
||||
},
|
||||
name: "`shivammathur/php` tap is missing",
|
||||
title: "warnings.php_tap_missing.title",
|
||||
@@ -82,14 +82,14 @@ extension WarningManager {
|
||||
] },
|
||||
url: "https://github.com/shivammathur/homebrew-php",
|
||||
fix: {
|
||||
await Shell.quiet("brew tap shivammathur/php")
|
||||
await BrewDiagnostics.loadInstalledTaps()
|
||||
await App.shared.container.shell.quiet("brew tap shivammathur/php")
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
await self.checkEnvironment()
|
||||
}
|
||||
),
|
||||
Warning(
|
||||
command: {
|
||||
!BrewDiagnostics.installedTaps.contains("shivammathur/extensions")
|
||||
!BrewDiagnostics.shared.installedTaps.contains("shivammathur/extensions")
|
||||
},
|
||||
name: "`shivammathur/extensions` tap is missing",
|
||||
title: "warnings.extensions_tap_missing.title",
|
||||
@@ -98,8 +98,8 @@ extension WarningManager {
|
||||
] },
|
||||
url: "https://github.com/shivammathur/homebrew-extensions",
|
||||
fix: {
|
||||
await Shell.quiet("brew tap shivammathur/extensions")
|
||||
await BrewDiagnostics.loadInstalledTaps()
|
||||
await App.shared.container.shell.quiet("brew tap shivammathur/extensions")
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
await self.checkEnvironment()
|
||||
}
|
||||
),
|
||||
|
||||
@@ -61,7 +61,7 @@ class WarningManager: ObservableObject {
|
||||
func checkEnvironment() async {
|
||||
container.shell.reload()
|
||||
|
||||
await BrewDiagnostics.loadInstalledTaps()
|
||||
await BrewDiagnostics.shared.loadInstalledTaps()
|
||||
|
||||
if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil {
|
||||
self.temporaryWarnings = self.evaluations
|
||||
|
||||
@@ -66,7 +66,7 @@ extension PhpExtensionManagerView {
|
||||
|
||||
do {
|
||||
self.status.busy = true
|
||||
try await command.execute { progress in
|
||||
try await command.execute(shell: App.shared.container.shell) { progress in
|
||||
Task { @MainActor in
|
||||
self.status.title = progress.title
|
||||
self.status.description = progress.description
|
||||
|
||||
@@ -22,7 +22,7 @@ extension PhpVersionManagerView {
|
||||
|
||||
do {
|
||||
self.setBusyStatus(true)
|
||||
try await command.execute { progress in
|
||||
try await command.execute(shell: App.shared.container.shell) { progress in
|
||||
Task { @MainActor in
|
||||
self.status.title = progress.title
|
||||
self.status.description = progress.description
|
||||
@@ -107,7 +107,7 @@ extension PhpVersionManagerView {
|
||||
|
||||
do {
|
||||
self.setBusyStatus(true)
|
||||
try await command.execute { progress in
|
||||
try await command.execute(shell: App.shared.container.shell) { progress in
|
||||
Task { @MainActor in
|
||||
self.status.title = progress.title
|
||||
self.status.description = progress.description
|
||||
|
||||
Reference in New Issue
Block a user