1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 03:50:08 +02:00

🏗 WIP: Rework folder ownership operations

This commit is contained in:
2023-04-23 12:01:30 +02:00
parent 2d0deed4fd
commit b7de54dfa7
8 changed files with 151 additions and 117 deletions

View File

@ -200,6 +200,10 @@
C45B91542956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45B91552956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45B91562956123A00F4EC78 /* FakeServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45B91522956123A00F4EC78 /* FakeServicesManager.swift */; };
C45D654C29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45D654B29F52F74004C28F9 /* BrewPermissionFixer.swift */; };
C45D654D29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45D654B29F52F74004C28F9 /* BrewPermissionFixer.swift */; };
C45D654E29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45D654B29F52F74004C28F9 /* BrewPermissionFixer.swift */; };
C45D654F29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45D654B29F52F74004C28F9 /* BrewPermissionFixer.swift */; };
C45E2A7529199248005C7CFD /* InternalSwitcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471E7AF28F9B4940021E251 /* InternalSwitcherTest.swift */; };
C45E2A77291992DA005C7CFD /* FeatureTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E2A76291992DA005C7CFD /* FeatureTestCase.swift */; };
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
@ -515,10 +519,6 @@
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C4777E0C29D71AFB007F0C67 /* UpgradePhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4777E0A29D71AF0007F0C67 /* UpgradePhpVersionCommand.swift */; };
C4777E0D29D71AFD007F0C67 /* UpgradePhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4777E0A29D71AF0007F0C67 /* UpgradePhpVersionCommand.swift */; };
C4777E0E29D71AFD007F0C67 /* UpgradePhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4777E0A29D71AF0007F0C67 /* UpgradePhpVersionCommand.swift */; };
C4777E0F29D71AFD007F0C67 /* UpgradePhpVersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4777E0A29D71AF0007F0C67 /* UpgradePhpVersionCommand.swift */; };
C47DF1AF299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B0299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
C47DF1B1299D5A3B0007055D /* LoginItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */; };
@ -939,6 +939,7 @@
C45B9148295607F400F4EC78 /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = "<group>"; };
C45B914D295608E300F4EC78 /* ValetServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetServicesManager.swift; sourceTree = "<group>"; };
C45B91522956123A00F4EC78 /* FakeServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeServicesManager.swift; sourceTree = "<group>"; };
C45D654B29F52F74004C28F9 /* BrewPermissionFixer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewPermissionFixer.swift; sourceTree = "<group>"; };
C45E2A76291992DA005C7CFD /* FeatureTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureTestCase.swift; sourceTree = "<group>"; };
C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = "<group>"; };
C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = "<group>"; };
@ -965,7 +966,6 @@
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = "<group>"; };
C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = "<group>"; };
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
C4777E0A29D71AF0007F0C67 /* UpgradePhpVersionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradePhpVersionCommand.swift; sourceTree = "<group>"; };
C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginItemManager.swift; sourceTree = "<group>"; };
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
@ -1507,6 +1507,7 @@
C45B42C329C7C67400366A14 /* Fake */ = {
isa = PBXGroup;
children = (
C4B79EC529CA474200A483EE /* FakeCommand.swift */,
);
path = Fake;
sourceTree = "<group>";
@ -1522,6 +1523,14 @@
path = Services;
sourceTree = "<group>";
};
C45D654A29F52F5F004C28F9 /* Behaviors */ = {
isa = PBXGroup;
children = (
C45D654B29F52F74004C28F9 /* BrewPermissionFixer.swift */,
);
path = Behaviors;
sourceTree = "<group>";
};
C464ADAA275A7A25003FCD53 /* DomainList */ = {
isa = PBXGroup;
children = (
@ -1670,6 +1679,7 @@
C4AF9F6C275445D900D44ED0 /* Homebrew */ = {
isa = PBXGroup;
children = (
C45D654A29F52F5F004C28F9 /* Behaviors */,
C4B79EBA29CA38D100A483EE /* Commands */,
C45B42C329C7C67400366A14 /* Fake */,
C43931C929C4C03F0069165B /* Brew.swift */,
@ -1753,9 +1763,7 @@
isa = PBXGroup;
children = (
C4B79EBB29CA38DB00A483EE /* BrewCommand.swift */,
C4B79EC529CA474200A483EE /* FakeCommand.swift */,
C4B79EC029CA473000A483EE /* InstallPhpVersionCommand.swift */,
C4777E0A29D71AF0007F0C67 /* UpgradePhpVersionCommand.swift */,
C4B79ECA29CA475900A483EE /* RemovePhpVersionCommand.swift */,
);
path = Commands;
@ -2275,7 +2283,6 @@
C43931CA29C4C03F0069165B /* Brew.swift in Sources */,
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */,
C48D6C70279CD2AC00F26D7E /* VersionNumber.swift in Sources */,
C4777E0C29D71AFB007F0C67 /* UpgradePhpVersionCommand.swift in Sources */,
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */,
C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */,
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
@ -2398,6 +2405,7 @@
C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */,
C40508B128ADAB44008FAC1F /* NSMenuItemExtension.swift in Sources */,
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
C45D654C29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */,
C4D3660B29113F20006BD146 /* System.swift in Sources */,
C4D36601291132B7006BD146 /* ValetScanners.swift in Sources */,
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
@ -2524,13 +2532,13 @@
C471E86828F9BB650021E251 /* PreferencesWindowController.swift in Sources */,
C471E86928F9BB650021E251 /* PreferencesWindowController+Hotkey.swift in Sources */,
C48DDD0F29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */,
C4777E0E29D71AFD007F0C67 /* UpgradePhpVersionCommand.swift in Sources */,
C4AFC4B029C4F32F00BF4E0D /* BrewFormula.swift in Sources */,
C471E86A28F9BB650021E251 /* PreferencesVC.swift in Sources */,
C471E86B28F9BB650021E251 /* PreferenceName.swift in Sources */,
C471E86C28F9BB650021E251 /* Preferences.swift in Sources */,
C4D3660D29113F20006BD146 /* System.swift in Sources */,
C471E86D28F9BB650021E251 /* CustomPrefs.swift in Sources */,
C45D654E29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */,
C4E2E84C28FC1E70003B070C /* DataExtension.swift in Sources */,
C471E86E28F9BB650021E251 /* MenuBarIcons.swift in Sources */,
C471E86F28F9BB650021E251 /* Stats.swift in Sources */,
@ -2734,6 +2742,7 @@
C471E8E428F9BB8F0021E251 /* NoWarningsView.swift in Sources */,
C471E8E528F9BB8F0021E251 /* OnboardingView.swift in Sources */,
C4B79EBF29CA38DB00A483EE /* BrewCommand.swift in Sources */,
C45D654F29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */,
C471E8E628F9BB8F0021E251 /* VersionPopoverView.swift in Sources */,
C471E8E728F9BB8F0021E251 /* NoDomainResultsView.swift in Sources */,
C471E8E828F9BB8F0021E251 /* ServicesView.swift in Sources */,
@ -2761,7 +2770,6 @@
C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */,
C490E3BA29BCA368006D2DE6 /* App+BrewWatch.swift in Sources */,
C471E7D028F9BA630021E251 /* FileSystemProtocol.swift in Sources */,
C4777E0F29D71AFD007F0C67 /* UpgradePhpVersionCommand.swift in Sources */,
C471E81228F9BAE80021E251 /* TimeIntervalExtension.swift in Sources */,
C471E7DF28F9BAAB0021E251 /* RealCommand.swift in Sources */,
C469E701294CF7B200A82AB2 /* FakeValetProxy.swift in Sources */,
@ -2845,6 +2853,7 @@
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */,
C4D3661B291173EA006BD146 /* DictionaryExtension.swift in Sources */,
C45D654D29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */,
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */,
@ -2992,7 +3001,6 @@
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */,
C46EBC4828DB9644007ACC74 /* RealShell.swift in Sources */,
C4777E0D29D71AFD007F0C67 /* UpgradePhpVersionCommand.swift in Sources */,
C48DDD0E29C75C9E00D032D9 /* BlockingOverlayView.swift in Sources */,
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */,
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,

View File

@ -0,0 +1,117 @@
//
// BrewPermissionFixer.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/04/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
class BrewPermissionFixer {
var broken: [DueOwnershipFormula] = []
/**
Takes ownership of the /BREW_PATH/Cellar/php/x.y.z/bin folder, for all PHP versions.
This might not be required if the user has only used that version of PHP
with site isolation, so this method checks if it's required first.
This is a required operation for *all* PHP versions when PHP Version Manager is running
operations, since any installation or upgrade may prompt the installation or upgrade
of other PHP versions, in which case the permissions need to set correctly.
*/
public func fixPermissions() async throws {
await determineBrokenFormulae()
if broken.isEmpty {
return
}
let appleScript = NSAppleScript(
source: "do shell script \"\(buildBrokenFormulaeScript())\" with administrator privileges"
)
let eventResult: NSAppleEventDescriptor? = appleScript?
.executeAndReturnError(nil)
if eventResult == nil {
throw HomebrewPermissionError(
kind: .applescriptNilError
)
}
Log.info("Ownership was taken of the folder(s) at: " + broken
.map({ $0.path })
.joined(separator: ", "))
}
/**
Determines which formulae's permissions are broken.
To do so, PHP Monitor resolves which directory needs to be checked and verifies
whether the Homebrew binary directory for the given PHP version is owned by root.
*/
private func determineBrokenFormulae() async {
let formulae = PhpEnv.shared.cachedPhpInstallations.keys
for formula in formulae {
let realFormula = formula == PhpEnv.brewPhpAlias
? "php"
: "php@\(formula)"
let binaryPath = "\(Paths.optPath)/\(realFormula)/bin"
if isOwnedByRoot(path: binaryPath) {
let borked = DueOwnershipFormula(
formula: realFormula,
path: binaryPath
)
Log.warn("\(formula) is owned by root")
broken.append(borked)
}
}
}
/**
Generates the appropriate AppleScript script required to restore permissions.
This script also stops the services prior to taking ownership, which is requirement.
*/
private func buildBrokenFormulaeScript() -> String {
return broken
.map { b in
return """
\(Paths.brew) services stop \(b.formula) \
&& chown -R \(Paths.whoami):admin \(b.path)
"""
}
.joined(
separator: " && "
)
}
/**
Checks if the directory at the path is owned by the `root` user,
by checking the FS owner account name attribute.
*/
private func isOwnedByRoot(path: String) -> Bool {
do {
let attributes = try FileManager.default.attributesOfItem(atPath: path)
if let owner = attributes[.ownerAccountName] as? String {
return owner == "root"
}
} catch {
return true
}
return true
}
struct DueOwnershipFormula {
let formula: String
let path: String
}
}

View File

@ -38,6 +38,14 @@ class InstallPhpVersionCommand: BrewCommand {
\(Paths.brew) install \(formula) --force
"""
#error("Must keep track of the active PHP version (if applicable)")
do {
try await BrewPermissionFixer().fixPermissions()
} catch {
return
}
let (process, _) = try! await Shell.attach(
command,
didReceiveOutput: { text, _ in
@ -56,6 +64,7 @@ class InstallPhpVersionCommand: BrewCommand {
onProgress(.create(value: 0.95, title: progressTitle, description: "Reloading PHP versions..."))
await PhpEnv.detectPhpVersions()
await MainMenu.shared.refreshActiveInstallation()
#error("Must restore active PHP installation (if applicable)")
onProgress(.create(value: 1, title: progressTitle, description: "The installation has succeeded."))
} else {
throw BrewCommandError(error: "The command failed to run correctly.")

View File

@ -35,7 +35,7 @@ class RemovePhpVersionCommand: BrewCommand {
"""
do {
try await self.fixPermissions(for: formula)
try await BrewPermissionFixer().fixPermissions()
} catch {
return
}
@ -59,58 +59,4 @@ class RemovePhpVersionCommand: BrewCommand {
throw BrewCommandError(error: "The command failed to run correctly.")
}
}
/**
Takes ownership of the /BREW_PATH/Cellar/php/x.y.z/bin folder (if required).
This might not be required if the user has only used that version of PHP
with site isolation, so this method checks if it's required first.
*/
private func fixPermissions(for formula: String) async throws {
// Omit the prefix
let path = formula.replacingOccurrences(of: "shivammathur/php/", with: "")
// Binary path needs to be checked for ownership
let binaryPath = "\(Paths.optPath)/\(path)/bin"
// Check if it's even necessary to perform the fix
if !isOwnedByRoot(path: binaryPath) {
return
}
Log.info("Need to take ownership of `\(binaryPath)`...")
let script = """
\(Paths.brew) services stop \(formula) \
&& chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(path)
"""
let appleScript = NSAppleScript(
source: "do shell script \"\(script)\" with administrator privileges"
)
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
if eventResult == nil {
throw HomebrewPermissionError(kind: .applescriptNilError)
}
Log.info("Ownership was taken of the folder at `\(binaryPath)`.")
}
/**
Checks if a given path is owned by root. If so, ownership might need to be taken.
*/
private func isOwnedByRoot(path: String) -> Bool {
do {
let attributes = try FileManager.default.attributesOfItem(atPath: path)
if let owner = attributes[.ownerAccountName] as? String {
return owner == "root"
}
} catch {
return true
}
return true
}
}

View File

@ -1,49 +0,0 @@
//
// InstallPhpVersionCommand.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 21/03/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
public typealias BrewDependent = String
class UpgradePhpVersionCommand: BrewCommand {
let formula: String
let version: String
init(formula: String) {
self.version = formula
.replacingOccurrences(of: "php@", with: "")
.replacingOccurrences(of: "shivammathur/php/", with: "")
self.formula = formula
}
func execute() async throws -> [BrewDependent] {
let command = """
export HOMEBREW_NO_INSTALL_UPGRADE=true; \
export HOMEBREW_NO_INSTALL_CLEANUP=true; \
\(Paths.brew) upgrade \(formula) -n
"""
// Use this command to do a dry-run of the upgrade
// This will let us figure out the impact or failure modes
let (process, _) = try! await Shell.attach(
command,
didReceiveOutput: { text, _ in
if !text.isEmpty {
Log.perf(text)
}
},
withTimeout: .minutes(5)
)
return []
}
func execute(onProgress: @escaping (BrewCommandProgress) -> Void) async throws {
//
}
}

View File

@ -148,11 +148,14 @@ struct PhpFormulaeView: View {
Task { await self.install(formula) }
}
}
/*
// TODO: Remove this and add a "upgrade all" button instead?
if formula.hasUpgrade {
Button("phpman.buttons.update".localizedForSwiftUI) {
Task { await self.install(formula) }
}
}
*/
}
.listRowBackground(index % 2 == 0
? Color.gray.opacity(0)

View File

@ -94,10 +94,10 @@
"phpman.busy.title" = "Checking for updates!";
"phpman.busy.description.outdated" = "Checking if any PHP version is outdated...";
"phpman.version.available_for_installation" = "This version can be installed";
"phpman.version.available_for_installation" = "This version can be installed.";
"phpman.buttons.uninstall" = "Uninstall";
"phpman.buttons.install" = "Install";
"phpman.buttons.update" = "Update";
"phpman.buttons.update_all" = "Update All";
"phpman.title" = "PHP Version Manager";
"phpman.description" = "**PHP Version Manager** lets you install different PHP versions via Homebrew.";