1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2026-04-02 09:30:08 +02:00

♻️ Use discardableResult

- Removed ShellProtocol.quiet(), now that pipe() is discardable
- Detecting PHP versions is also discardable
- The system command is also discardable

This is a nice quality of life change overall, and gets rid of a couple
of silly `_ =` assignments.
This commit is contained in:
2026-02-17 15:17:34 +01:00
parent d0ce16fad2
commit 9856840533
27 changed files with 55 additions and 70 deletions

View File

@@ -122,7 +122,7 @@ class Actions {
try! container.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 // 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") await container.shell.pipe("\(paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
return URL(string: "file:///private/tmp/phpmon_phpinfo.html")! return URL(string: "file:///private/tmp/phpmon_phpinfo.html")!
} }

View File

View File

@@ -18,7 +18,7 @@ func brew(
_ command: String, _ command: String,
sudo: Bool = false, sudo: Bool = false,
) async { ) async {
await container.shell.quiet("\(sudo ? "sudo " : "")" + "\(container.paths.brew) \(command)") await container.shell.pipe("\(sudo ? "sudo " : "")" + "\(container.paths.brew) \(command)")
} }
/** /**
@@ -37,9 +37,9 @@ func sed(
// Check if gsed exists; it is able to follow symlinks, // Check if gsed exists; it is able to follow symlinks,
// which we want to do to toggle the extension // which we want to do to toggle the extension
if container.filesystem.fileExists("\(container.paths.binPath)/gsed") { if container.filesystem.fileExists("\(container.paths.binPath)/gsed") {
await container.shell.quiet("\(container.paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)") await container.shell.pipe("\(container.paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
} else { } else {
await container.shell.quiet("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)") await container.shell.pipe("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
} }
} }
@@ -71,20 +71,3 @@ func delay(seconds: Double) async {
func url(_ string: String) -> URL { func url(_ string: String) -> URL {
return URL(string: string)! return URL(string: string)!
} }
/**
Execute a script with administrative privileges.
*/
func sudo(_ script: String) throws {
let source = "do shell script \"\(script)\" with administrator privileges"
Log.info("Running script via AppleScript as administrator: `\(source)`")
let appleScript = NSAppleScript(source: source)
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
if eventResult == nil {
throw AdminPrivilegeError(kind: .applescriptNilError)
}
}

View File

@@ -87,7 +87,7 @@ class RealFileSystem: FileSystemProtocol {
// MARK: FS Attributes // MARK: FS Attributes
func makeExecutable(_ path: String) throws { func makeExecutable(_ path: String) throws {
_ = container.shell.sync("chmod +x \(path.replacingTildeWithHomeDirectory)") container.shell.sync("chmod +x \(path.replacingTildeWithHomeDirectory)")
} }
// MARK: - Checks // MARK: - Checks

View File

@@ -48,7 +48,7 @@ class Application {
(This will open the app if it isn't open yet.) (This will open the app if it isn't open yet.)
*/ */
@objc public func open(arg: String) { @objc public func open(arg: String) {
Task { await container.shell.quiet("/usr/bin/open -a \"\(name)\" \"\(arg)\"") } Task { await container.shell.pipe("/usr/bin/open -a \"\(name)\" \"\(arg)\"") }
} }
/** /**

View File

@@ -11,6 +11,7 @@ import Foundation
/** /**
Run a simple blocking Shell command on the user's own system. Run a simple blocking Shell command on the user's own system.
*/ */
@discardableResult
public func system(_ command: String) -> String { public func system(_ command: String) -> String {
let task = Process() let task = Process()
task.launchPath = "/bin/sh" task.launchPath = "/bin/sh"

View File

@@ -218,7 +218,7 @@ class PhpEnvironments {
} }
public func reloadPhpVersions() async { public func reloadPhpVersions() async {
_ = await self.detectPhpVersions() await self.detectPhpVersions()
} }
/** /**
@@ -228,6 +228,7 @@ class PhpEnvironments {
Returns a `Set<String>` of installations that are considered valid. Returns a `Set<String>` of installations that are considered valid.
*/ */
@discardableResult
public func detectPhpVersions() async -> Set<String> { public func detectPhpVersions() async -> Set<String> {
let files = await container.shell.pipe("ls \(container.paths.optPath) | grep php@").out let files = await container.shell.pipe("ls \(container.paths.optPath) | grep php@").out

View File

@@ -127,13 +127,13 @@ class PhpHelper {
if !container.filesystem.fileExists(destination) { if !container.filesystem.fileExists(destination) {
Log.info("Creating new symlink: \(destination)") Log.info("Creating new symlink: \(destination)")
await container.shell.quiet("ln -s \(source) \(destination)") await container.shell.pipe("ln -s \(source) \(destination)")
return return
} }
if !App.shared.container.filesystem.isSymlink(destination) { if !App.shared.container.filesystem.isSymlink(destination) {
Log.info("Overwriting existing file with new symlink: \(destination)") Log.info("Overwriting existing file with new symlink: \(destination)")
await container.shell.quiet("ln -fs \(source) \(destination)") await container.shell.pipe("ln -fs \(source) \(destination)")
return return
} }

View File

@@ -11,6 +11,7 @@ import Foundation
extension InternalSwitcher { extension InternalSwitcher {
typealias FixApplied = Bool typealias FixApplied = Bool
@discardableResult
public func ensureValetConfigurationIsValidForPhpVersion(_ version: String) async -> FixApplied { public func ensureValetConfigurationIsValidForPhpVersion(_ version: String) async -> FixApplied {
// Early exit if Valet is not installed // Early exit if Valet is not installed
if !Valet.installed { if !Valet.installed {

View File

@@ -53,7 +53,7 @@ class InternalSwitcher: PhpSwitcher {
for formula in versions { for formula in versions {
if Valet.installed { if Valet.installed {
Log.info("Ensuring that the Valet configuration is valid...") Log.info("Ensuring that the Valet configuration is valid...")
_ = await self.ensureValetConfigurationIsValidForPhpVersion(formula) await self.ensureValetConfigurationIsValidForPhpVersion(formula)
} }
Log.info("Will start PHP \(version)... (primary: \(version == formula))") Log.info("Will start PHP \(version)... (primary: \(version == formula))")
@@ -112,7 +112,7 @@ class InternalSwitcher: PhpSwitcher {
if Valet.enabled(feature: .isolatedSites) && primary { if Valet.enabled(feature: .isolatedSites) && primary {
let socketVersion = version.replacing(".", with: "") let socketVersion = version.replacing(".", with: "")
await container.shell.quiet("ln -sF ~/.config/valet/valet\(socketVersion).sock ~/.config/valet/valet.sock") await container.shell.pipe("ln -sF ~/.config/valet/valet\(socketVersion).sock ~/.config/valet/valet.sock")
Log.info("Symlinked new socket version (valet\(socketVersion).sock → valet.sock).") Log.info("Symlinked new socket version (valet\(socketVersion).sock → valet.sock).")
} }
} }

View File

@@ -185,6 +185,7 @@ class RealShell: ShellProtocol, @unchecked Sendable {
return .out(stdOut, stdErr) return .out(stdOut, stdErr)
} }
@discardableResult
func pipe(_ command: String) async -> ShellOutput { func pipe(_ command: String) async -> ShellOutput {
let process = getShellProcess(for: command) let process = getShellProcess(for: command)
@@ -220,6 +221,7 @@ class RealShell: ShellProtocol, @unchecked Sendable {
} }
} }
@discardableResult
func pipe(_ command: String, timeout: TimeInterval) async -> ShellOutput { func pipe(_ command: String, timeout: TimeInterval) async -> ShellOutput {
let process = getShellProcess(for: command) let process = getShellProcess(for: command)
@@ -285,10 +287,7 @@ class RealShell: ShellProtocol, @unchecked Sendable {
} }
} }
func quiet(_ command: String) async { @discardableResult
_ = await self.pipe(command)
}
func attach( func attach(
_ command: String, _ command: String,
didReceiveOutput: @escaping (String, ShellStream) -> Void, didReceiveOutput: @escaping (String, ShellStream) -> Void,

View File

@@ -22,6 +22,7 @@ protocol ShellProtocol {
let output = Shell.sync("php -v") let output = Shell.sync("php -v")
``` ```
*/ */
@discardableResult
func sync(_ command: String) -> ShellOutput func sync(_ command: String) -> ShellOutput
/** /**
@@ -33,6 +34,7 @@ protocol ShellProtocol {
let output = await Shell.pipe("php -v") let output = await Shell.pipe("php -v")
``` ```
*/ */
@discardableResult
func pipe(_ command: String) async -> ShellOutput func pipe(_ command: String) async -> ShellOutput
/** /**
@@ -43,14 +45,9 @@ protocol ShellProtocol {
- Parameter timeout: Timeout in seconds. If the command exceeds this, it is terminated. - Parameter timeout: Timeout in seconds. If the command exceeds this, it is terminated.
- Returns: The shell output. If the command times out, returns empty output. - Returns: The shell output. If the command times out, returns empty output.
*/ */
@discardableResult
func pipe(_ command: String, timeout: TimeInterval) async -> ShellOutput func pipe(_ command: String, timeout: TimeInterval) async -> ShellOutput
/**
Run a command asynchronously, without returning the output of the command.
Returns the most relevant output (prefers error output if it exists).
*/
func quiet(_ command: String) async
/** /**
Runs a command asynchronously, and fires closure with `stdout` or `stderr` data as it comes in. Runs a command asynchronously, and fires closure with `stdout` or `stderr` data as it comes in.
@@ -61,6 +58,7 @@ protocol ShellProtocol {
Unlike `sync`, `pipe` and `quiet`, you can capture both `stdout` and `stderr` with this mechanism. Unlike `sync`, `pipe` and `quiet`, you can capture both `stdout` and `stderr` with this mechanism.
The end result is still the most relevant output (where error output is preferred if it exists). The end result is still the most relevant output (where error output is preferred if it exists).
*/ */
@discardableResult
func attach( func attach(
_ command: String, _ command: String,
didReceiveOutput: @escaping (String, ShellStream) -> Void, didReceiveOutput: @escaping (String, ShellStream) -> Void,

View File

@@ -19,6 +19,7 @@ public class TestableShell: ShellProtocol {
var expectations: [String: BatchFakeShellOutput] = [:] var expectations: [String: BatchFakeShellOutput] = [:]
@discardableResult
func sync(_ command: String) -> ShellOutput { func sync(_ command: String) -> ShellOutput {
// This assertion will only fire during test builds // This assertion will only fire during test builds
assert(expectations.keys.contains(command), "No response declared for command: \(command)") assert(expectations.keys.contains(command), "No response declared for command: \(command)")
@@ -30,19 +31,18 @@ public class TestableShell: ShellProtocol {
return expectation.syncOutput() return expectation.syncOutput()
} }
func quiet(_ command: String) async { @discardableResult
_ = try! await self.attach(command, didReceiveOutput: { _, _ in }, withTimeout: 60)
}
func pipe(_ command: String) async -> ShellOutput { func pipe(_ command: String) async -> ShellOutput {
return await pipe(command, timeout: 60) return await pipe(command, timeout: 60)
} }
@discardableResult
func pipe(_ command: String, timeout: TimeInterval) async -> ShellOutput { func pipe(_ command: String, timeout: TimeInterval) async -> ShellOutput {
let (_, output) = try! await self.attach(command, didReceiveOutput: { _, _ in }, withTimeout: timeout) let (_, output) = try! await self.attach(command, didReceiveOutput: { _, _ in }, withTimeout: timeout)
return output return output
} }
@discardableResult
func attach( func attach(
_ command: String, _ command: String,
didReceiveOutput: @escaping (String, ShellStream) -> Void, didReceiveOutput: @escaping (String, ShellStream) -> Void,

View File

@@ -71,7 +71,7 @@ class RemovePhpExtensionCommand: BrewCommand {
await performExtensionCleanup(for: ext) await performExtensionCleanup(for: ext)
} }
_ = await container.phpEnvs.detectPhpVersions() await container.phpEnvs.detectPhpVersions()
await Actions(container).restartPhpFpm(version: phpExtension.phpVersion) await Actions(container).restartPhpFpm(version: phpExtension.phpVersion)

View File

@@ -82,7 +82,7 @@ class ModifyPhpVersionCommand: BrewCommand {
} }
// Re-check the installed versions // Re-check the installed versions
_ = await container.phpEnvs.detectPhpVersions() await container.phpEnvs.detectPhpVersions()
// After performing operations, attempt to run repairs if needed // After performing operations, attempt to run repairs if needed
try await self.repairBrokenPackages(onProgress) try await self.repairBrokenPackages(onProgress)
@@ -186,7 +186,7 @@ class ModifyPhpVersionCommand: BrewCommand {
await BrewDiagnostics.shared.checkForOutdatedPhpInstallationSymlinks() await BrewDiagnostics.shared.checkForOutdatedPhpInstallationSymlinks()
// Check which version of PHP are now installed // Check which version of PHP are now installed
_ = await container.phpEnvs.detectPhpVersions() await container.phpEnvs.detectPhpVersions()
// Keep track of the currently installed version // Keep track of the currently installed version
await MainMenu.shared.refreshActiveInstallation() await MainMenu.shared.refreshActiveInstallation()

View File

@@ -74,7 +74,7 @@ class RemovePhpVersionCommand: BrewCommand {
if process.terminationStatus == 0 { if process.terminationStatus == 0 {
onProgress(.create(value: 0.95, title: getCommandTitle(), description: "phpman.steps.reloading".localized)) onProgress(.create(value: 0.95, title: getCommandTitle(), description: "phpman.steps.reloading".localized))
_ = await container.phpEnvs.detectPhpVersions() await container.phpEnvs.detectPhpVersions()
await MainMenu.shared.refreshActiveInstallation() await MainMenu.shared.refreshActiveInstallation()

View File

@@ -34,11 +34,11 @@ class ValetInteractor {
// MARK: - Managing Domains // MARK: - Managing Domains
public func link(path: String, domain: String) async throws { public func link(path: String, domain: String) async throws {
await container.shell.quiet("cd '\(path)' && \(container.paths.valet) link '\(domain)' && valet links") await container.shell.pipe("cd '\(path)' && \(container.paths.valet) link '\(domain)' && valet links")
} }
public func unlink(site: ValetSite) async throws { public func unlink(site: ValetSite) async throws {
await container.shell.quiet("valet unlink '\(site.name)'") await container.shell.pipe("valet unlink '\(site.name)'")
} }
public func proxy(domain: String, proxy: String, secure: Bool) async throws { public func proxy(domain: String, proxy: String, secure: Bool) async throws {
@@ -46,12 +46,12 @@ class ValetInteractor {
? "\(container.paths.valet) proxy \(domain) \(proxy) --secure" ? "\(container.paths.valet) proxy \(domain) \(proxy) --secure"
: "\(container.paths.valet) proxy \(domain) \(proxy)" : "\(container.paths.valet) proxy \(domain) \(proxy)"
await container.shell.quiet(command) await container.shell.pipe(command)
await Actions(container).restartNginx() await Actions(container).restartNginx()
} }
public func remove(proxy: ValetProxy) async throws { public func remove(proxy: ValetProxy) async throws {
await container.shell.quiet("valet unproxy '\(proxy.domain)'") await container.shell.pipe("valet unproxy '\(proxy.domain)'")
} }
// MARK: - Modifying Domains // MARK: - Modifying Domains
@@ -73,7 +73,7 @@ class ValetInteractor {
} }
// Run the command // Run the command
await container.shell.quiet(command) await container.shell.pipe(command)
// Check if the secured status has actually changed // Check if the secured status has actually changed
site.determineSecured() site.determineSecured()
@@ -98,7 +98,7 @@ class ValetInteractor {
// Run the commands // Run the commands
for command in commands { for command in commands {
await container.shell.quiet(command) await container.shell.pipe(command)
} }
// Check if the secured status has actually changed // Check if the secured status has actually changed
@@ -117,7 +117,7 @@ class ValetInteractor {
let command = "sudo \(container.paths.valet) isolate php@\(version) --site '\(site.name)'" let command = "sudo \(container.paths.valet) isolate php@\(version) --site '\(site.name)'"
// Run the command // Run the command
await container.shell.quiet(command) await container.shell.pipe(command)
// Check if the secured status has actually changed // Check if the secured status has actually changed
site.determineIsolated() site.determineIsolated()
@@ -133,7 +133,7 @@ class ValetInteractor {
let command = "sudo \(container.paths.valet) unisolate --site '\(site.name)'" let command = "sudo \(container.paths.valet) unisolate --site '\(site.name)'"
// Run the command // Run the command
await container.shell.quiet(command) await container.shell.pipe(command)
// Check if the secured status has actually changed // Check if the secured status has actually changed
site.determineIsolated() site.determineIsolated()

View File

@@ -37,7 +37,7 @@ struct CustomPrefs: Decodable {
extension Preferences { extension Preferences {
func loadCustomPreferences() async { func loadCustomPreferences() async {
// Ensure the configuration directory is created if missing // Ensure the configuration directory is created if missing
await container.shell.quiet("mkdir -p ~/.config/phpmon") await container.shell.pipe("mkdir -p ~/.config/phpmon")
// Move the legacy file // Move the legacy file
await moveOutdatedConfigurationFile() await moveOutdatedConfigurationFile()
@@ -57,7 +57,7 @@ extension Preferences {
if container.filesystem.fileExists("~/.phpmon.conf.json") if container.filesystem.fileExists("~/.phpmon.conf.json")
&& !container.filesystem.fileExists("~/.config/phpmon/config.json") { && !container.filesystem.fileExists("~/.config/phpmon/config.json") {
Log.info("An outdated configuration file was found. Moving it...") Log.info("An outdated configuration file was found. Moving it...")
await container.shell.quiet("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json") await container.shell.pipe("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json")
Log.info("The configuration file was copied successfully!") Log.info("The configuration file was copied successfully!")
} }
} }

View File

@@ -35,7 +35,7 @@ class AppearancePreferencesVC: GenericPreferenceVC {
let vc = NSStoryboard(name: "Main", bundle: nil) let vc = NSStoryboard(name: "Main", bundle: nil)
.instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC
_ = vc.addView(when: true, vc.getDynamicIconPV()) vc.addView(when: true, vc.getDynamicIconPV())
.addView(when: true, vc.getIconOptionsPV()) .addView(when: true, vc.getIconOptionsPV())
.addView(when: true, vc.getIconDensityPV()) .addView(when: true, vc.getIconDensityPV())
.addView(when: true, vc.getMenuIconsPV()) .addView(when: true, vc.getMenuIconsPV())

View File

@@ -28,6 +28,7 @@ class GenericPreferenceVC: NSViewController {
Log.perf("deinit: \(String(describing: self)).\(#function)") Log.perf("deinit: \(String(describing: self)).\(#function)")
} }
@discardableResult
func addView(when condition: Bool, _ view: NSView) -> GenericPreferenceVC { func addView(when condition: Bool, _ view: NSView) -> GenericPreferenceVC {
if condition { if condition {
self.views.append(view) self.views.append(view)

View File

@@ -276,7 +276,7 @@ struct Preset: Codable, Equatable {
private func persistRevert() async { private func persistRevert() async {
let data = try! JSONEncoder().encode(self.revertSnapshot) let data = try! JSONEncoder().encode(self.revertSnapshot)
await container.shell.quiet("mkdir -p ~/.config/phpmon") await container.shell.pipe("mkdir -p ~/.config/phpmon")
try! String(data: data, encoding: .utf8)! try! String(data: data, encoding: .utf8)!
.write( .write(

View File

@@ -27,7 +27,7 @@ class InstallHomebrew {
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
""" """
_ = try await container.shell.attach(script, didReceiveOutput: { (string: String, _: ShellStream) in try await container.shell.attach(script, didReceiveOutput: { (string: String, _: ShellStream) in
print(string) print(string)
}, withTimeout: 60 * 10) }, withTimeout: 60 * 10)
} }

View File

@@ -21,6 +21,7 @@ class ZshRunCommand {
/** /**
Adds a given line to .zshrc, which may be needed to adjust the PATH. Adds a given line to .zshrc, which may be needed to adjust the PATH.
*/ */
@discardableResult
private func add(_ text: String) async -> Bool { private func add(_ text: String) async -> Bool {
// Escape single quotes to prevent shell injection // Escape single quotes to prevent shell injection
let escaped = text.replacingOccurrences(of: "'", with: "'\\''") let escaped = text.replacingOccurrences(of: "'", with: "'\\''")
@@ -44,13 +45,13 @@ class ZshRunCommand {
Adds Homebrew binaries to the PATH. Adds Homebrew binaries to the PATH.
*/ */
public func addHomebrewPath() async { public func addHomebrewPath() async {
_ = await add("export PATH=$HOME/bin:/opt/homebrew/bin:$PATH") await add("export PATH=$HOME/bin:/opt/homebrew/bin:$PATH")
} }
/** /**
Adds PHP Monitor binaries to the PATH. Adds PHP Monitor binaries to the PATH.
*/ */
public func addPhpMonitorPath() async { public func addPhpMonitorPath() async {
_ = await add("export PATH=$HOME/bin:~/.config/phpmon/bin:$PATH") await add("export PATH=$HOME/bin:~/.config/phpmon/bin:$PATH")
} }
} }

View File

@@ -31,11 +31,11 @@ extension DomainListVC {
} }
@objc func openInFinder() { @objc func openInFinder() {
Task { return await App.shared.container.shell.quiet("open '\(selectedSite!.absolutePath)'") } Task { return await App.shared.container.shell.pipe("open '\(selectedSite!.absolutePath)'") }
} }
@objc func openInTerminal() { @objc func openInTerminal() {
Task { await App.shared.container.shell.quiet("open -b com.apple.terminal '\(selectedSite!.absolutePath)'") } Task { await App.shared.container.shell.pipe("open -b com.apple.terminal '\(selectedSite!.absolutePath)'") }
} }
@objc func openWithApp(sender: ApplicationMenuItem) { @objc func openWithApp(sender: ApplicationMenuItem) {
@@ -58,7 +58,7 @@ extension DomainListVC {
let rowToReload = tableView.selectedRow let rowToReload = tableView.selectedRow
waitAndExecute { waitAndExecute {
await App.shared.container.shell.quiet(command) await App.shared.container.shell.pipe(command)
} completion: { [self] in } completion: { [self] in
beforeCellReload() beforeCellReload()
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4]) tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])

View File

@@ -87,7 +87,7 @@ extension WarningManager {
] }, ] },
url: "https://github.com/shivammathur/homebrew-php", url: "https://github.com/shivammathur/homebrew-php",
fix: { fix: {
await self.container.shell.quiet("brew tap shivammathur/php") await self.container.shell.pipe("brew tap shivammathur/php")
await BrewDiagnostics.shared.loadInstalledTaps() await BrewDiagnostics.shared.loadInstalledTaps()
await self.checkEnvironment() await self.checkEnvironment()
} }
@@ -103,7 +103,7 @@ extension WarningManager {
] }, ] },
url: "https://github.com/shivammathur/homebrew-extensions", url: "https://github.com/shivammathur/homebrew-extensions",
fix: { fix: {
await self.container.shell.quiet("brew tap shivammathur/extensions") await self.container.shell.pipe("brew tap shivammathur/extensions")
await BrewDiagnostics.shared.loadInstalledTaps() await BrewDiagnostics.shared.loadInstalledTaps()
await self.checkEnvironment() await self.checkEnvironment()
} }

View File

@@ -63,7 +63,7 @@ struct RealFileSystemTest {
#expect(filesystem.directoryExists("\(temporaryDirectory)/brew/etc/lib")) #expect(filesystem.directoryExists("\(temporaryDirectory)/brew/etc/lib"))
#expect(filesystem.directoryExists("\(temporaryDirectory)/brew/etc/lib/c")) #expect(filesystem.directoryExists("\(temporaryDirectory)/brew/etc/lib/c"))
_ = system("ln -s \(temporaryDirectory)/brew/etc/lib/c \(temporaryDirectory)/c") system("ln -s \(temporaryDirectory)/brew/etc/lib/c \(temporaryDirectory)/c")
#expect(filesystem.directoryExists("\(temporaryDirectory)/c")) #expect(filesystem.directoryExists("\(temporaryDirectory)/c"))
#expect(filesystem.isSymlink("\(temporaryDirectory)/c")) #expect(filesystem.isSymlink("\(temporaryDirectory)/c"))
#expect( #expect(

View File

@@ -89,9 +89,9 @@ struct RealShellTest {
let start = ContinuousClock.now let start = ContinuousClock.now
await withTaskGroup(of: Void.self) { group in await withTaskGroup(of: Void.self) { group in
group.addTask { await container.shell.quiet("php -r \"usleep(700000);\"") } group.addTask { await container.shell.pipe("php -r \"usleep(700000);\"") }
group.addTask { await container.shell.quiet("php -r \"usleep(700000);\"") } group.addTask { await container.shell.pipe("php -r \"usleep(700000);\"") }
group.addTask { await container.shell.quiet("php -r \"usleep(700000);\"") } group.addTask { await container.shell.pipe("php -r \"usleep(700000);\"") }
} }
let duration = start.duration(to: .now) let duration = start.duration(to: .now)