From 8efaf72c0f11df413c196a9e3103789834c75762 Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Wed, 22 Apr 2026 11:13:03 -0400 Subject: [PATCH 1/2] Fix Homebrew "Cannot install under Rosetta 2" error on Apple Silicon --- .../Helpers/HomebrewPkgDetailsHelper.cs | 10 +-- .../Helpers/HomebrewSourceHelper.cs | 10 +-- .../Homebrew.cs | 90 ++++++------------- 3 files changed, 31 insertions(+), 79 deletions(-) diff --git a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs index 6979a39d9c..80e660b475 100644 --- a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs @@ -17,15 +17,7 @@ protected override void GetDetails_UnSafe(IPackageDetails details) { using var p = new Process { - StartInfo = new ProcessStartInfo - { - FileName = Manager.Status.ExecutablePath, - Arguments = $"info --json=v2 {details.Package.Id}", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, + StartInfo = ((Homebrew)Manager).MakeBrewStartInfo($"info --json=v2 {details.Package.Id}"), }; IProcessTaskLogger logger = Manager.TaskLogger.CreateNew( diff --git a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs index b83ac638d7..da98f05b33 100644 --- a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs @@ -21,15 +21,7 @@ protected override IReadOnlyList GetSources_UnSafe() using var p = new Process { - StartInfo = new ProcessStartInfo - { - FileName = Manager.Status.ExecutablePath, - Arguments = "tap", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, + StartInfo = ((Homebrew)Manager).MakeBrewStartInfo("tap"), }; IProcessTaskLogger logger = Manager.TaskLogger.CreateNew(LoggableTaskType.ListSources, p); p.Start(); diff --git a/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs b/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs index 593e8f722c..3ac5bd00df 100644 --- a/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs +++ b/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs @@ -98,23 +98,35 @@ protected override void _loadManagerExecutableFile( out string callArguments) { (found, path) = GetExecutableFile(); - callArguments = ""; + // Force ARM64 when brew is at the Apple Silicon prefix. Without this, a + // Rosetta-translated UniGetUI process spawns the x86_64 slice of the fat + // brew binary, which then refuses to operate against /opt/homebrew. + if (path == "/opt/homebrew/bin/brew") + { + callArguments = $"-arm64 {path}"; + path = "/usr/bin/arch"; + } + else + { + callArguments = ""; + } } + internal ProcessStartInfo MakeBrewStartInfo(string arguments) => new() + { + FileName = Status.ExecutablePath, + Arguments = Status.ExecutableCallArgs.Length > 0 + ? $"{Status.ExecutableCallArgs} {arguments}" + : arguments, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + }; + protected override void _loadManagerVersion(out string version) { - using var p = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Status.ExecutablePath, - Arguments = "--version", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, - }; + using var p = new Process { StartInfo = MakeBrewStartInfo("--version") }; p.Start(); // First line: "Homebrew 4.x.x" version = p.StandardOutput.ReadLine()?.Replace("Homebrew ", "").Trim() ?? ""; @@ -125,18 +137,7 @@ protected override void _loadManagerVersion(out string version) public override void RefreshPackageIndexes() { - using var p = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Status.ExecutablePath, - Arguments = "update", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, - }; + using var p = new Process { StartInfo = MakeBrewStartInfo("update") }; IProcessTaskLogger logger = TaskLogger.CreateNew(LoggableTaskType.RefreshIndexes, p); p.Start(); logger.AddToStdOut(p.StandardOutput.ReadToEnd()); @@ -155,18 +156,7 @@ protected override IReadOnlyList FindPackages_UnSafe(string query) IManagerSource caskSource = SourcesHelper.Factory.GetSourceOrDefault("Homebrew Cask"); IManagerSource currentSection = formulaeSource; - using var p = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Status.ExecutablePath, - Arguments = $"search {query}", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, - }; + using var p = new Process { StartInfo = MakeBrewStartInfo($"search {query}") }; IProcessTaskLogger logger = TaskLogger.CreateNew(LoggableTaskType.FindPackages, p); p.Start(); @@ -209,18 +199,7 @@ private IReadOnlyList ListInstalledByType(string typeFlag, string sourc var packages = new List(); IManagerSource source = SourcesHelper.Factory.GetSourceOrDefault(sourceName); - using var p = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Status.ExecutablePath, - Arguments = $"list {typeFlag} --versions", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, - }; + using var p = new Process { StartInfo = MakeBrewStartInfo($"list {typeFlag} --versions") }; IProcessTaskLogger logger = TaskLogger.CreateNew(LoggableTaskType.ListInstalledPackages, p); p.Start(); @@ -250,18 +229,7 @@ protected override IReadOnlyList GetAvailableUpdates_UnSafe() foreach (var pkg in GetInstalledPackages()) installed.TryAdd(pkg.Id, pkg); - using var p = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Status.ExecutablePath, - Arguments = "outdated --verbose", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, - }; + using var p = new Process { StartInfo = MakeBrewStartInfo("outdated --verbose") }; IProcessTaskLogger logger = TaskLogger.CreateNew(LoggableTaskType.ListUpdates, p); p.Start(); From 9d5da985399853a72e1518a43ba0fdb885b1082c Mon Sep 17 00:00:00 2001 From: Gabriel Dufresne Date: Thu, 23 Apr 2026 09:51:10 -0400 Subject: [PATCH 2/2] made some fix mentionned by copilot --- .../Helpers/HomebrewPkgDetailsHelper.cs | 6 ++++-- .../Helpers/HomebrewSourceHelper.cs | 6 ++++-- .../Homebrew.cs | 12 ++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs index 80e660b475..43acf290a5 100644 --- a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewPkgDetailsHelper.cs @@ -10,14 +10,16 @@ namespace UniGetUI.PackageEngine.Managers.HomebrewManager; internal sealed class HomebrewPkgDetailsHelper : BasePkgDetailsHelper { + private readonly Homebrew _brew; + public HomebrewPkgDetailsHelper(Homebrew manager) - : base(manager) { } + : base(manager) { _brew = manager; } protected override void GetDetails_UnSafe(IPackageDetails details) { using var p = new Process { - StartInfo = ((Homebrew)Manager).MakeBrewStartInfo($"info --json=v2 {details.Package.Id}"), + StartInfo = _brew.MakeBrewStartInfo($"info --json=v2 {details.Package.Id}"), }; IProcessTaskLogger logger = Manager.TaskLogger.CreateNew( diff --git a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs index da98f05b33..7919bff5d7 100644 --- a/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Homebrew/Helpers/HomebrewSourceHelper.cs @@ -10,8 +10,10 @@ namespace UniGetUI.PackageEngine.Managers.HomebrewManager; internal sealed class HomebrewSourceHelper : BaseSourceHelper { + private readonly Homebrew _brew; + public HomebrewSourceHelper(Homebrew manager) - : base(manager) { } + : base(manager) { _brew = manager; } // ── Source listing ───────────────────────────────────────────────────── @@ -21,7 +23,7 @@ protected override IReadOnlyList GetSources_UnSafe() using var p = new Process { - StartInfo = ((Homebrew)Manager).MakeBrewStartInfo("tap"), + StartInfo = _brew.MakeBrewStartInfo("tap"), }; IProcessTaskLogger logger = Manager.TaskLogger.CreateNew(LoggableTaskType.ListSources, p); p.Start(); diff --git a/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs b/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs index 3ac5bd00df..e500766314 100644 --- a/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs +++ b/src/UniGetUI.PackageEngine.Managers.Homebrew/Homebrew.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using UniGetUI.Core.Logging; using UniGetUI.Core.Tools; @@ -98,10 +99,13 @@ protected override void _loadManagerExecutableFile( out string callArguments) { (found, path) = GetExecutableFile(); - // Force ARM64 when brew is at the Apple Silicon prefix. Without this, a - // Rosetta-translated UniGetUI process spawns the x86_64 slice of the fat - // brew binary, which then refuses to operate against /opt/homebrew. - if (path == "/opt/homebrew/bin/brew") + // Force ARM64 when brew is at the Apple Silicon prefix. Without this, + // .NET's posix_spawn may select the x86_64 slice of universal binaries + // (bash, ruby) in the brew script chain, causing Homebrew to detect a + // Rosetta 2 context even when UniGetUI itself is ARM64-native. + if (path == BREW_PATHS[0] + && OperatingSystem.IsMacOS() + && RuntimeInformation.OSArchitecture == System.Runtime.InteropServices.Architecture.Arm64) { callArguments = $"-arm64 {path}"; path = "/usr/bin/arch";