diff --git a/Sources/BrewRepositories/BrewInstalledPackagesRepository.swift b/Sources/BrewRepositories/BrewInstalledPackagesRepository.swift index 2fb55582..cc7c7f48 100644 --- a/Sources/BrewRepositories/BrewInstalledPackagesRepository.swift +++ b/Sources/BrewRepositories/BrewInstalledPackagesRepository.swift @@ -224,17 +224,24 @@ public final class BrewInstalledPackagesRepository: InstalledPackagesRepository } private func decodeInfoJSON(from standardOutput: String) throws -> BrewInfoJSON { - let data = Data(standardOutput.utf8) - do { - return try JSONDecoder().decode(BrewInfoJSON.self, from: data) - } catch { - throw BrewCommandError.launchFailed( - underlying: String( - localized: "Failed to decode Homebrew JSON output.", - comment: "Installed tab JSON decode failure", - ), - ) + let decoder = JSONDecoder() + if let payload = try? decoder.decode(BrewInfoJSON.self, from: Data(standardOutput.utf8)) { + return payload + } + + for jsonStart in standardOutput.indices where standardOutput[jsonStart] == "{" { + let jsonOutput = standardOutput[jsonStart...] + if let payload = try? decoder.decode(BrewInfoJSON.self, from: Data(jsonOutput.utf8)) { + return payload + } } + + throw BrewCommandError.launchFailed( + underlying: String( + localized: "Failed to decode Homebrew JSON output.", + comment: "Installed tab JSON decode failure", + ), + ) } } diff --git a/Tests/BrewRepositoriesTests/BrewInstalledPackagesRepositoryTests.swift b/Tests/BrewRepositoriesTests/BrewInstalledPackagesRepositoryTests.swift index 0be2f346..410eb139 100644 --- a/Tests/BrewRepositoriesTests/BrewInstalledPackagesRepositoryTests.swift +++ b/Tests/BrewRepositoriesTests/BrewInstalledPackagesRepositoryTests.swift @@ -13,6 +13,27 @@ import Foundation import Testing struct BrewInstalledPackagesRepositoryTests { + @Test @MainActor func `load ignores interactive shell output before installed info json`() async { + let runner = MockBrewCommandRunner( + responses: InstalledPackagesTestSupport.installedInfoJSONResponse( + standardOutput: """ + \u{1B}]1337;SetUserVar=badge={"theme":"dark"}\u{7} + { + "formulae": [ + { "name": "wget", "versions": { "stable": "1.0.0" }, "installed": [{ "version": "1.0.0" }] } + ], + "casks": [] + } + """, + ), + ) + let repo = InstalledPackagesTestSupport.repository(commandRunner: runner) + + let packages = await InstalledPackagesTestSupport.loadedPackages(from: repo) + + #expect(packages.map(\.name) == ["wget"]) + } + @Test @MainActor func `load returns sorted packages for mixed formula and cask json payload`() async throws { let json = """ {