mirror of
https://github.com/Homebrew/BrewUI.git
synced 2026-10-02 08:34:36 +08:00
Show a deprecated badge on installed formulae
brew info already returns deprecated. Without a badge, a formulae-only filter hid those rows and there was no visual cue on the list. Fixes #180
This commit is contained in:
@@ -46,6 +46,7 @@ private extension BrewInfoFormula {
|
||||
linkedKeg: BrewInfoJSON.trimmedOrNil(linkedKeg),
|
||||
pinned: pinned,
|
||||
kegOnly: kegOnly,
|
||||
deprecated: deprecated,
|
||||
caveats: BrewInfoJSON.trimmedOrNil(caveats),
|
||||
)
|
||||
}
|
||||
@@ -69,6 +70,7 @@ private extension BrewInfoCask {
|
||||
outdated: outdated,
|
||||
tap: BrewInfoJSON.trimmedOrNil(tap),
|
||||
installedOnRequest: installedOnRequest,
|
||||
deprecated: deprecated,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ struct BrewInfoFormula: Decodable {
|
||||
var kegOnly: Bool
|
||||
var caveats: String?
|
||||
var outdated: Bool
|
||||
var deprecated: Bool
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
@@ -61,6 +62,7 @@ struct BrewInfoFormula: Decodable {
|
||||
kegOnly = (try? container.decode(Bool.self, forKey: .kegOnly)) ?? false
|
||||
caveats = try? container.decode(String.self, forKey: .caveats)
|
||||
outdated = (try? container.decode(Bool.self, forKey: .outdated)) ?? false
|
||||
deprecated = (try? container.decode(Bool.self, forKey: .deprecated)) ?? false
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
@@ -80,6 +82,7 @@ struct BrewInfoFormula: Decodable {
|
||||
case kegOnly = "keg_only"
|
||||
case caveats
|
||||
case outdated
|
||||
case deprecated
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +126,7 @@ struct BrewInfoCask: Decodable {
|
||||
var installedOnRequest: Bool
|
||||
var dependencies: [HomebrewPackageID]
|
||||
var outdated: Bool
|
||||
var deprecated: Bool
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
@@ -140,6 +144,7 @@ struct BrewInfoCask: Decodable {
|
||||
forKeys: [.dependencies, .dependsOn],
|
||||
)
|
||||
outdated = (try? container.decode(Bool.self, forKey: .outdated)) ?? false
|
||||
deprecated = (try? container.decode(Bool.self, forKey: .deprecated)) ?? false
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
@@ -155,6 +160,7 @@ struct BrewInfoCask: Decodable {
|
||||
case dependencies
|
||||
case dependsOn = "depends_on"
|
||||
case outdated
|
||||
case deprecated
|
||||
}
|
||||
|
||||
var firstDisplayName: String? {
|
||||
|
||||
@@ -27,6 +27,8 @@ public struct InstalledBrewPackage: Identifiable, Hashable, Sendable {
|
||||
public var pinned: Bool
|
||||
/// True when the formula is keg-only (not linked into the prefix by default).
|
||||
public var kegOnly: Bool
|
||||
/// True when Homebrew marks the formula/cask deprecated.
|
||||
public var deprecated: Bool
|
||||
/// Post-install caveats text, if any.
|
||||
public var caveats: String?
|
||||
|
||||
@@ -97,6 +99,7 @@ public struct InstalledBrewPackage: Identifiable, Hashable, Sendable {
|
||||
linkedKeg: String? = nil,
|
||||
pinned: Bool = false,
|
||||
kegOnly: Bool = false,
|
||||
deprecated: Bool = false,
|
||||
caveats: String? = nil,
|
||||
) {
|
||||
self.package = package
|
||||
@@ -111,6 +114,7 @@ public struct InstalledBrewPackage: Identifiable, Hashable, Sendable {
|
||||
self.linkedKeg = linkedKeg
|
||||
self.pinned = pinned
|
||||
self.kegOnly = kegOnly
|
||||
self.deprecated = deprecated
|
||||
self.caveats = caveats
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ final class InstalledListRowViewModel {
|
||||
package.outdated && availableVersionLabel != nil
|
||||
}
|
||||
|
||||
var isDeprecated: Bool {
|
||||
package.deprecated
|
||||
}
|
||||
|
||||
var versionPresentation: InstalledListRowVersionPresentation {
|
||||
if showsUpgradeAvailable, let latest = availableVersionLabel {
|
||||
return .upgrade(current: installedVersionLabel, latest: latest)
|
||||
@@ -71,6 +75,8 @@ final class InstalledListRowViewModel {
|
||||
parts.append(installedVersionLabel)
|
||||
if showsUpgradeAvailable, let latest = availableVersionLabel {
|
||||
parts.append("Upgrade available to \(latest)")
|
||||
} else if isDeprecated {
|
||||
parts.append("Deprecated")
|
||||
} else {
|
||||
parts.append("Installed and up to date")
|
||||
}
|
||||
|
||||
@@ -118,10 +118,16 @@ struct InstalledListRowView: View {
|
||||
private func statusBadge(viewModel: InstalledListRowViewModel) -> some View {
|
||||
if viewModel.showsUpgradeAvailable {
|
||||
InstalledOutdatedBadge()
|
||||
} else if viewModel.isDeprecated {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.body)
|
||||
.foregroundStyle(Color.brewStatusWarning)
|
||||
.help("Deprecated")
|
||||
} else {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.font(.body)
|
||||
.foregroundStyle(Color.brewStatusSuccess)
|
||||
.help("Installed and up to date")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,4 +104,27 @@ struct BrewInfoJSONMappingTests {
|
||||
|
||||
#expect(payload.installedPackages().map(\.name) == ["aria2", "Firefox", "Zsh"])
|
||||
}
|
||||
|
||||
@Test func `deprecated formulae and casks keep the flag from brew info json`() throws {
|
||||
let json = """
|
||||
{
|
||||
"formulae": [
|
||||
{ "name": "youtube-dl", "deprecated": true },
|
||||
{ "name": "git", "deprecated": false }
|
||||
],
|
||||
"casks": [
|
||||
{ "token": "docker", "deprecated": true },
|
||||
{ "token": "alfred" }
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
let payload = try JSONDecoder().decode(BrewInfoJSON.self, from: Data(json.utf8))
|
||||
let packages = payload.installedPackages()
|
||||
|
||||
#expect(packages.first { $0.name == "youtube-dl" }?.deprecated == true)
|
||||
#expect(packages.first { $0.name == "git" }?.deprecated == false)
|
||||
#expect(packages.first { $0.name == "docker" }?.deprecated == true)
|
||||
#expect(packages.first { $0.name == "alfred" }?.deprecated == false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,18 @@ struct InstalledListRowViewModelTests {
|
||||
#expect(viewModel.name == "Visual Studio Code")
|
||||
}
|
||||
|
||||
@Test func `deprecated package is not described as up to date`() {
|
||||
var package = InstalledBrewPackage.fixture(name: "youtube-dl")
|
||||
package.deprecated = true
|
||||
let viewModel = InstalledListRowViewModel(
|
||||
package: package,
|
||||
brewCommandCenter: NoopBrewCommandCenter.forTesting(),
|
||||
)
|
||||
#expect(viewModel.isDeprecated)
|
||||
#expect(viewModel.accessibilitySummary.contains("Deprecated"))
|
||||
#expect(!viewModel.accessibilitySummary.contains("up to date"))
|
||||
}
|
||||
|
||||
@Test func `observeRowUpdates applies first phase from noop center`() async {
|
||||
let package = InstalledBrewPackage.fixture(name: "git", kind: .formula)
|
||||
let center = NoopBrewCommandCenter.forTesting()
|
||||
|
||||
Reference in New Issue
Block a user