2015-12-30 21:03:45 +00:00
|
|
|
//
|
|
|
|
// Upgrade.swift
|
|
|
|
// mas-cli
|
|
|
|
//
|
|
|
|
// Created by Andrew Naylor on 30/12/2015.
|
|
|
|
// Copyright © 2015 Andrew Naylor. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2018-07-04 20:56:10 +00:00
|
|
|
import Commandant
|
2021-04-26 23:44:17 +00:00
|
|
|
import Foundation
|
2021-05-06 22:14:56 +00:00
|
|
|
import PromiseKit
|
2021-05-09 20:25:20 +00:00
|
|
|
|
2021-05-06 22:14:56 +00:00
|
|
|
import enum Swift.Result
|
2018-07-04 20:56:10 +00:00
|
|
|
|
2018-12-28 22:08:31 +00:00
|
|
|
/// Command which upgrades apps with new versions available in the Mac App Store.
|
2018-10-14 21:35:48 +00:00
|
|
|
public struct UpgradeCommand: CommandProtocol {
|
|
|
|
public typealias Options = UpgradeOptions
|
|
|
|
public let verb = "upgrade"
|
|
|
|
public let function = "Upgrade outdated apps from the Mac App Store"
|
|
|
|
|
2018-12-28 22:08:31 +00:00
|
|
|
private let appLibrary: AppLibrary
|
2021-01-17 10:52:00 +00:00
|
|
|
private let storeSearch: StoreSearch
|
2018-12-28 22:08:31 +00:00
|
|
|
|
2020-03-29 03:15:07 +00:00
|
|
|
/// Public initializer.
|
|
|
|
public init() {
|
|
|
|
self.init(appLibrary: MasAppLibrary())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Internal initializer.
|
|
|
|
/// - Parameter appLibrary: AppLibrary manager.
|
2021-01-17 10:52:00 +00:00
|
|
|
/// - Parameter storeSearch: StoreSearch manager.
|
|
|
|
init(appLibrary: AppLibrary = MasAppLibrary(), storeSearch: StoreSearch = MasStoreSearch()) {
|
2018-12-28 22:08:31 +00:00
|
|
|
self.appLibrary = appLibrary
|
2021-01-17 10:52:00 +00:00
|
|
|
self.storeSearch = storeSearch
|
2018-12-28 22:08:31 +00:00
|
|
|
}
|
2018-10-14 21:35:48 +00:00
|
|
|
|
2019-01-12 01:28:03 +00:00
|
|
|
/// Runs the command.
|
2021-03-22 05:46:17 +00:00
|
|
|
public func run(_ options: Options) -> Result<Void, MASError> {
|
2022-09-15 00:44:23 +00:00
|
|
|
let apps: [(installedApp: SoftwareProduct, storeApp: SearchResult)]
|
2021-04-21 23:09:14 +00:00
|
|
|
do {
|
2021-04-21 23:15:26 +00:00
|
|
|
apps = try findOutdatedApps(options)
|
2021-01-17 10:52:00 +00:00
|
|
|
} catch {
|
|
|
|
// Bubble up MASErrors
|
2021-03-22 22:13:48 +00:00
|
|
|
return .failure(error as? MASError ?? .searchFailed)
|
2015-12-30 21:03:45 +00:00
|
|
|
}
|
2021-04-21 23:09:14 +00:00
|
|
|
|
|
|
|
guard apps.count > 0 else {
|
|
|
|
printWarning("Nothing found to upgrade")
|
|
|
|
return .success(())
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Upgrading \(apps.count) outdated application\(apps.count > 1 ? "s" : ""):")
|
2022-09-23 02:08:02 +00:00
|
|
|
print(
|
|
|
|
apps.map { "\($0.installedApp.appName) (\($0.installedApp.bundleVersion)) -> (\($0.storeApp.version))" }
|
|
|
|
.joined(separator: "\n"))
|
2021-04-21 23:09:14 +00:00
|
|
|
|
2022-09-15 00:44:23 +00:00
|
|
|
let appIds = apps.map(\.installedApp.itemIdentifier.uint64Value)
|
2021-05-06 22:14:56 +00:00
|
|
|
do {
|
|
|
|
try downloadAll(appIds).wait()
|
|
|
|
} catch {
|
|
|
|
return .failure(error as? MASError ?? .downloadFailed(error: error as NSError))
|
2021-04-21 23:09:14 +00:00
|
|
|
}
|
|
|
|
|
2021-05-06 22:14:56 +00:00
|
|
|
return .success(())
|
2015-12-30 21:03:45 +00:00
|
|
|
}
|
2021-04-21 23:15:26 +00:00
|
|
|
|
2022-09-15 00:44:23 +00:00
|
|
|
private func findOutdatedApps(_ options: Options) throws -> [(SoftwareProduct, SearchResult)] {
|
2024-02-18 05:58:11 +00:00
|
|
|
let apps: [SoftwareProduct] =
|
|
|
|
options.apps.isEmpty
|
2022-09-15 00:44:23 +00:00
|
|
|
? appLibrary.installedApps
|
2024-02-18 05:58:11 +00:00
|
|
|
: options.apps.compactMap {
|
|
|
|
if let appId = UInt64($0) {
|
|
|
|
// if argument a UInt64, lookup app by id using argument
|
|
|
|
return appLibrary.installedApp(forId: appId)
|
|
|
|
} else {
|
|
|
|
// if argument not a UInt64, lookup app by name using argument
|
|
|
|
return appLibrary.installedApp(named: $0)
|
2021-04-21 23:15:26 +00:00
|
|
|
}
|
2024-02-18 05:58:11 +00:00
|
|
|
}
|
2021-04-21 23:15:26 +00:00
|
|
|
|
2021-05-06 22:14:56 +00:00
|
|
|
let promises = apps.map { installedApp in
|
2021-04-21 23:15:26 +00:00
|
|
|
// only upgrade apps whose local version differs from the store version
|
2021-05-06 22:14:56 +00:00
|
|
|
firstly {
|
|
|
|
storeSearch.lookup(app: installedApp.itemIdentifier.intValue)
|
2022-09-15 00:44:23 +00:00
|
|
|
}.map { result -> (SoftwareProduct, SearchResult)? in
|
2021-05-06 22:14:56 +00:00
|
|
|
guard let storeApp = result, installedApp.isOutdatedWhenComparedTo(storeApp) else {
|
|
|
|
return nil
|
2021-04-21 23:33:51 +00:00
|
|
|
}
|
2021-04-21 23:15:26 +00:00
|
|
|
|
2022-09-15 00:44:23 +00:00
|
|
|
return (installedApp, storeApp)
|
2021-05-06 22:14:56 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-21 23:33:51 +00:00
|
|
|
|
2021-05-06 22:14:56 +00:00
|
|
|
return try when(fulfilled: promises).wait().compactMap { $0 }
|
2021-04-21 23:15:26 +00:00
|
|
|
}
|
2015-12-30 21:20:25 +00:00
|
|
|
}
|
2016-09-14 21:55:04 +00:00
|
|
|
|
2018-10-14 21:35:48 +00:00
|
|
|
public struct UpgradeOptions: OptionsProtocol {
|
2017-08-19 15:50:35 +00:00
|
|
|
let apps: [String]
|
2019-01-12 01:06:02 +00:00
|
|
|
|
2017-08-19 15:50:35 +00:00
|
|
|
static func create(_ apps: [String]) -> UpgradeOptions {
|
2021-03-22 05:46:17 +00:00
|
|
|
UpgradeOptions(apps: apps)
|
2016-09-14 21:55:04 +00:00
|
|
|
}
|
2019-01-12 01:06:02 +00:00
|
|
|
|
|
|
|
public static func evaluate(_ mode: CommandMode) -> Result<UpgradeOptions, CommandantError<MASError>> {
|
2021-03-22 05:46:17 +00:00
|
|
|
create
|
2019-01-12 01:06:02 +00:00
|
|
|
<*> mode <| Argument(defaultValue: [], usage: "app(s) to upgrade")
|
2016-09-14 21:55:04 +00:00
|
|
|
}
|
2016-09-17 12:58:38 +00:00
|
|
|
}
|