Improve output.

Improve errors.

Simplify code.

Partial #313

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-23 14:04:02 -04:00
parent b0d2f23465
commit 06ee9608be
No known key found for this signature in database
4 changed files with 14 additions and 16 deletions

View file

@ -35,7 +35,7 @@ extension Mas {
if dryRun { if dryRun {
for installedApp in installedApps { for installedApp in installedApps {
printInfo("\(installedApp.appName) \(installedApp.bundlePath)") printInfo("'\(installedApp.appName)' '\(installedApp.bundlePath)'")
} }
printInfo("(not removed, dry run)") printInfo("(not removed, dry run)")
} else { } else {

View file

@ -38,8 +38,8 @@ class MasAppLibrary: AppLibrary {
/// - Parameter app: App to be removed. /// - Parameter app: App to be removed.
/// - Throws: Error if there is a problem. /// - Throws: Error if there is a problem.
func uninstallApp(app: SoftwareProduct) throws { func uninstallApp(app: SoftwareProduct) throws {
if !userIsRoot() { if NSUserName() != "root" {
printWarning("Apps installed from the Mac App Store require root permission to remove.") throw MASError.macOSUserMustBeRoot
} }
let appUrl = URL(fileURLWithPath: app.bundlePath) let appUrl = URL(fileURLWithPath: app.bundlePath)
@ -55,11 +55,4 @@ class MasAppLibrary: AppLibrary {
throw MASError.uninstallFailed throw MASError.uninstallFailed
} }
} }
/// Detects whether the current user is root.
///
/// - Returns: true if the current user is root; false otherwise
private func userIsRoot() -> Bool {
NSUserName() == "root"
}
} }

View file

@ -29,6 +29,7 @@ enum MASError: Error, Equatable {
case notInstalled(appID: AppID) case notInstalled(appID: AppID)
case uninstallFailed case uninstallFailed
case macOSUserMustBeRoot
case noData case noData
case jsonParsing(data: Data?) case jsonParsing(data: Data?)
@ -84,6 +85,8 @@ extension MASError: CustomStringConvertible {
return "No apps installed with app ID \(appID)" return "No apps installed with app ID \(appID)"
case .uninstallFailed: case .uninstallFailed:
return "Uninstall failed" return "Uninstall failed"
case .macOSUserMustBeRoot:
return "Apps installed from the Mac App Store require root permission to remove."
case .noData: case .noData:
return "Service did not return data" return "Service did not return data"
case .jsonParsing(let data): case .jsonParsing(let data):

View file

@ -43,9 +43,11 @@ public class UninstallSpec: QuickSpec {
it("finds an app") { it("finds an app") {
mockLibrary.installedApps.append(app) mockLibrary.installedApps.append(app)
expect { expect {
try captureStream(stdout) {
try uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
.toNot(throwError()) }
== "==> 'Some App' '/tmp/Some.app'\n==> (not removed, dry run)\n"
} }
} }
context("wet run") { context("wet run") {
@ -67,12 +69,12 @@ public class UninstallSpec: QuickSpec {
try uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }
} }
== "==> Some App /tmp/Some.app\n==> (not removed, dry run)\n" .toNot(throwError())
} }
it("fails if there is a problem with the trash command") { it("fails if there is a problem with the trash command") {
var brokenUninstall = app var brokenApp = app
brokenUninstall.bundlePath = "/dev/null" brokenApp.bundlePath = "/dev/null"
mockLibrary.installedApps.append(brokenUninstall) mockLibrary.installedApps.append(brokenApp)
expect { expect {
try uninstall.run(appLibrary: mockLibrary) try uninstall.run(appLibrary: mockLibrary)
} }