Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
- Update SDk
- Update packages
- Handle cache error for fetched packages
- Add support for PKG in DMG
  • Loading branch information
dz0ny committed Apr 30, 2024
1 parent a95b07e commit d4ebdd6
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 60 deletions.
17 changes: 17 additions & 0 deletions Pareto Updater.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
4F75DDD9287D512900FD4503 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = 4F75DDD8287D512900FD4503 /* Version */; };
4F7D6FFE28327BC4007D6E8A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F7D6FFD28327BC4007D6E8A /* Constants.swift */; };
4F7D700028327FE4007D6E8A /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F7D6FFF28327FE4007D6E8A /* FileManager.swift */; };
4F804F8E2BE0EAF300372B36 /* SettingsAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 4F804F8D2BE0EAF300372B36 /* SettingsAccess */; };
4F868ED5286B224800383328 /* Menubar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F868ED4286B224800383328 /* Menubar.swift */; };
4F91931C294BA7D000FB1DF7 /* Gramarly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F91931B294BA7D000FB1DF7 /* Gramarly.swift */; };
4F9209DC282BBD7D007897A6 /* Docker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F9209DB282BBD7D007897A6 /* Docker.swift */; };
Expand Down Expand Up @@ -232,6 +233,7 @@
4FE610E4282A88400060C002 /* Alamofire in Frameworks */,
4F3BE0A1286EF61B00DDC1AC /* XMLCoder in Frameworks */,
4FE610E7282A88540060C002 /* Regex in Frameworks */,
4F804F8E2BE0EAF300372B36 /* SettingsAccess in Frameworks */,
4F0DEA7A286D923A00344B92 /* JWTDecode in Frameworks */,
4FE610DB282A87F40060C002 /* Defaults in Frameworks */,
);
Expand Down Expand Up @@ -470,6 +472,7 @@
4F3BE0A0286EF61B00DDC1AC /* XMLCoder */,
4FA755FF287709960064D0ED /* AppUpdater */,
4F75DDD8287D512900FD4503 /* Version */,
4F804F8D2BE0EAF300372B36 /* SettingsAccess */,
);
productName = "Pareto Updater";
productReference = 4F33FE31280808C000585E5A /* Pareto Updater.app */;
Expand Down Expand Up @@ -554,6 +557,7 @@
4F3BE09F286EF61B00DDC1AC /* XCRemoteSwiftPackageReference "XMLCoder" */,
4FA755FE287709960064D0ED /* XCRemoteSwiftPackageReference "AppUpdater" */,
4F75DDD7287D512900FD4503 /* XCRemoteSwiftPackageReference "Version" */,
4F804F8C2BE0EAB300372B36 /* XCRemoteSwiftPackageReference "SettingsAccess" */,
);
productRefGroup = 4F33FE32280808C000585E5A /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -1087,6 +1091,14 @@
kind = branch;
};
};
4F804F8C2BE0EAB300372B36 /* XCRemoteSwiftPackageReference "SettingsAccess" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/orchetect/SettingsAccess";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.4.0;
};
};
4FA755FE287709960064D0ED /* XCRemoteSwiftPackageReference "AppUpdater" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/teamniteo/AppUpdater";
Expand Down Expand Up @@ -1158,6 +1170,11 @@
package = 4F75DDD7287D512900FD4503 /* XCRemoteSwiftPackageReference "Version" */;
productName = Version;
};
4F804F8D2BE0EAF300372B36 /* SettingsAccess */ = {
isa = XCSwiftPackageProductDependency;
package = 4F804F8C2BE0EAB300372B36 /* XCRemoteSwiftPackageReference "SettingsAccess" */;
productName = SettingsAccess;
};
4FA755FF287709960064D0ED /* AppUpdater */ = {
isa = XCSwiftPackageProductDependency;
package = 4FA755FE287709960064D0ED /* XCRemoteSwiftPackageReference "AppUpdater" */;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"originHash" : "4d2deb1ffbf29bdd6f26ea089e45f0ba037553dc39b785a7d7a240869d32cd57",
"pins" : [
{
"identity" : "alamofire",
Expand All @@ -24,7 +25,7 @@
"location" : "https://github.com/hyperoslo/Cache",
"state" : {
"branch" : "master",
"revision" : "eeaf771d8d2e8247fbd6da2e27c986d99803fb1f"
"revision" : "f44a8f6b5ec27730198725ccc542fef0d1cc6b3d"
}
},
{
Expand Down Expand Up @@ -81,13 +82,22 @@
"revision" : "4f95793b3acf6ec2a89ee2b635bca268bbf01315"
}
},
{
"identity" : "settingsaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/orchetect/SettingsAccess",
"state" : {
"revision" : "0fd73c8b5892e88acb13adb7f36a4ba9293a0061",
"version" : "1.4.0"
}
},
{
"identity" : "version",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ParetoSecurity/Version",
"state" : {
"branch" : "master",
"revision" : "9efa0d232cd886e78a6c59fd6eff337ebcbdafce"
"revision" : "88064e19f3c4efd3f7930017ce3ff77378fb22c5"
}
},
{
Expand All @@ -100,5 +110,5 @@
}
}
],
"version" : 2
"version" : 3
}
106 changes: 73 additions & 33 deletions Pareto Updater/Extensions/AppUpdater.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
if isInstalled {
let weekAgo = Date().addingTimeInterval(-7 * 24 * 60 * 60)
let attributes = NSMetadataItem(url: applicationPath)
guard let lastUse = attributes?.value(forAttribute: "kMDItemLastUsedDate") as? Date else { return false }
guard let lastUse = attributes?.value(forAttribute: "kMDItemLastUsedDate") as? Date else {
return false
}
return lastUse >= weekAgo
}
return true
Expand All @@ -90,7 +92,8 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
public var fromAppStore: Bool {
if isInstalled {
let attributes = NSMetadataItem(url: applicationPath)
guard let hasReceipt = attributes?.value(forAttribute: "kMDItemAppStoreHasReceipt") as? Bool else { return false }
guard let hasReceipt = attributes?.value(forAttribute: "kMDItemAppStoreHasReceipt") as? Bool
else { return false }
return hasReceipt
}
return false
Expand All @@ -99,13 +102,16 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
public var isSafariWebApp: Bool {
if isInstalled {
let attributes = Bundle.plistDict(path: applicationPath)
return (((attributes?.value(forKey: "CFBundleIdentifier") as? String)?.contains("com.apple.Safari.WebApp")) != nil)
return
((attributes?.value(forKey: "CFBundleIdentifier") as? String)?.contains(
"com.apple.Safari.WebApp")) != nil
}
return false
}

func downloadLatest(completion: @escaping (URL, URL) -> Void) {
let cachedPath = Constants.cacheFolder.appendingPathComponent("\(appBundle)-\(latestVersion).\(latestURLExtension)")
let cachedPath = Constants.cacheFolder.appendingPathComponent(
"\(appBundle)-\(latestVersion).\(latestURLExtension)")
if FileManager.default.fileExists(atPath: cachedPath.path), Constants.useCacheFolder {
os_log("Update from cache at %{public}s", cachedPath.debugDescription)
completion(latestURL, cachedPath)
Expand All @@ -120,7 +126,10 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
try FileManager.default.removeItem(at: cachedPath)
}
try FileManager.default.moveItem(atPath: response.fileURL!.path, toPath: cachedPath.path)
os_log("Update downloadLatest: %{public} from %{public}s", cachedPath.debugDescription, self.latestURL.debugDescription)
os_log(
"Update downloadLatest: %{public} from %{public}s", cachedPath.debugDescription,
self.latestURL.debugDescription
)
completion(latestURL, cachedPath)
return
} catch {
Expand Down Expand Up @@ -159,43 +168,72 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
switch appFile.pathExtension {
case "dmg":
let mountPoint = URL(string: "/Volumes/" + appBundle)!
os_log("Mount %{public}s is %{public}s", appFile.debugDescription, mountPoint.debugDescription)
os_log(
"Mount %{public}s is %{public}s", appFile.debugDescription, mountPoint.debugDescription
)
if DMGMounter.attach(diskImage: appFile, at: mountPoint) {
do {
let app = try FileManager.default.contentsOfDirectory(at: mountPoint, includingPropertiesForKeys: nil).filter { $0.lastPathComponent.contains(".app") }.first
let app = try FileManager.default.contentsOfDirectory(
at: mountPoint, includingPropertiesForKeys: nil
).filter { $0.lastPathComponent.contains(".app") }.first
let pkg = try FileManager.default.contentsOfDirectory(
at: mountPoint, includingPropertiesForKeys: nil
).filter { $0.lastPathComponent.contains(".pkg") }.first

if app == nil && pkg == nil {
os_log("Failed to find app bundle in DMG %{public}s", appFile.debugDescription)
return AppUpdaterStatus.Failed
}

let downloadedAppBundle = Bundle(url: app!)!
if let installedAppBundle = Bundle(url: applicationPath) {
if !validate(downloadedAppBundle, installedAppBundle) {
os_log("Failed to validate app bundle %{public}s", appBundle)
return AppUpdaterStatus.Failed
}
if app != nil {
os_log("Found app bundle in DMG %{public}s", app.debugDescription)

let localName = installedAppBundle.path.basename(dropExtension: true) + ".backup"
os_log("Archive installedAppBundle: \(installedAppBundle.description)")
try installedAppBundle.path.rename(to: localName)
let downloadedAppBundle = Bundle(url: app!)!
if let installedAppBundle = Bundle(url: applicationPath) {
if !validate(downloadedAppBundle, installedAppBundle) {
os_log("Failed to validate app bundle %{public}s", appBundle)
return AppUpdaterStatus.Failed
}

os_log("Update installedAppBundle: \(installedAppBundle.description) with \(downloadedAppBundle.description)")
try downloadedAppBundle.path.copy(to: installedAppBundle.path, overwrite: true)
let localName = installedAppBundle.path.basename(dropExtension: true) + ".backup"
os_log("Archive installedAppBundle: \(installedAppBundle.description)")
try installedAppBundle.path.rename(to: localName)

if needsStart {
installedAppBundle.launch()
os_log(
"Update installedAppBundle: \(installedAppBundle.description) with \(downloadedAppBundle.description)"
)
try downloadedAppBundle.path.copy(to: installedAppBundle.path, overwrite: true)

if needsStart {
installedAppBundle.launch()
}

let oldBackup = installedAppBundle.path.parent.join(localName)
try? oldBackup.delete()

} else {
os_log("Install AppBundle \(downloadedAppBundle.description)")
try downloadedAppBundle.path.copy(to: Path(url: applicationPath)!, overwrite: true)
}
_ = DMGMounter.detach(mountPoint: mountPoint)

let oldBackup = installedAppBundle.path.parent.join(localName)
try? oldBackup.delete()
if let bundle = Bundle(url: applicationPath), needsStart {
bundle.launch()
}

} else {
os_log("Install AppBundle \(downloadedAppBundle.description)")
try downloadedAppBundle.path.copy(to: Path(url: applicationPath)!, overwrite: true)
return AppUpdaterStatus.Installed
}
_ = DMGMounter.detach(mountPoint: mountPoint)

if let bundle = Bundle(url: applicationPath), needsStart {
bundle.launch()
if pkg != nil {
os_log("Found pkg bundle in DMG %{public}s", pkg.debugDescription)
let task = Process()
task.launchPath = "/usr/bin/open"
task.arguments = [pkg!.path]
task.launch()
_ = DMGMounter.detach(mountPoint: mountPoint)
return AppUpdaterStatus.Installed
}

return AppUpdaterStatus.Installed
} catch {
_ = DMGMounter.detach(mountPoint: mountPoint)
os_log("Failed to check for app bundle %{public}s", error.localizedDescription)
Expand All @@ -216,7 +254,9 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
os_log("Archive installedAppBundle: \(installedAppBundle.description)")
try installedAppBundle.path.rename(to: localName)

os_log("Update installedAppBundle: \(installedAppBundle.description) with \(downloadedAppBundle.description)")
os_log(
"Update installedAppBundle: \(installedAppBundle.description) with \(downloadedAppBundle.description)"
)
try downloadedAppBundle.path.copy(to: installedAppBundle.path, overwrite: true)
if needsStart {
installedAppBundle.launch()
Expand Down Expand Up @@ -316,8 +356,8 @@ public class AppUpdater: Hashable, Identifiable, ObservableObject {
}

public var latestVersion: String {
if let found = try? Constants.versionStorage.existsObject(forKey: appBundle), found {
return latestVersionHook(try! Constants.versionStorage.object(forKey: appBundle))
if let found = try? Constants.versionStorage.object(forKey: appBundle), !found.isEmpty {
return latestVersionHook(found)
} else {
let lock = DispatchSemaphore(value: 0)
DispatchQueue.global(qos: .userInteractive).async { [self] in
Expand Down
6 changes: 3 additions & 3 deletions Pareto Updater/Extensions/Bundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ extension Bundle {
}
return nil
}
static func plistDict (path: URL) -> NSDictionary? {

static func plistDict(path: URL) -> NSDictionary? {
let plist = path.appendingPathComponent("/Contents/Info.plist")
return NSDictionary(contentsOf: plist)
}

static func appVersion(path: URL, key: String = "CFBundleShortVersionString") -> String? {
plistDict(path: path)?.value(forKey: key) as? String
}
Expand Down
17 changes: 2 additions & 15 deletions Pareto Updater/ParetoUpdater.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Defaults
import Foundation
import os.log
import Regex
import SettingsAccess
import SwiftUI

#if !DEBUG
Expand Down Expand Up @@ -343,7 +344,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele
popOver.behavior = .transient
popOver.animates = true
popOver.contentViewController = NSViewController()
popOver.contentViewController?.view = NSHostingView(rootView: AppList().environmentObject(appsStore))
popOver.contentViewController?.view = NSHostingView(rootView: AppList().environmentObject(appsStore).openSettingsAccess())

statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

Expand All @@ -366,10 +367,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele

statusMenu = NSMenu(title: "ParetoUpdater")

let preferencesItem = NSMenuItem(title: "Preferences", action: #selector(AppDelegate.preferences), keyEquivalent: ",")
preferencesItem.target = NSApp.delegate
statusMenu?.addItem(preferencesItem)

let contactItem = NSMenuItem(title: "Contact Support", action: #selector(AppDelegate.contact), keyEquivalent: "c")
contactItem.target = NSApp.delegate
statusMenu?.addItem(contactItem)
Expand Down Expand Up @@ -442,16 +439,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele
NSWorkspace.shared.open(Constants.bugReportURL)
}

@objc
func preferences() {
if #available(macOS 13.0, *) {
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
} else {
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
}
NSApp.activate(ignoringOtherApps: true)
}

func showInstallAppsWindow() {
shouldTerminate = true
statusItem?.isVisible = false
Expand Down
8 changes: 3 additions & 5 deletions Pareto Updater/Views/AppList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
// Created by Janez Troha on 14/04/2022.
//

import SettingsAccess
import SwiftUI

struct AppList: View {
@EnvironmentObject var viewModel: AppBundles
@Environment(\.openSettings) private var openSettings

var body: some View {
VStack(alignment: .leading) {
Expand Down Expand Up @@ -45,11 +47,7 @@ struct AppList: View {
.help("Refresh the status of the apps")

Button {
if #available(macOS 13.0, *) {
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
} else {
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
}
try? openSettings()
NSApp.activate(ignoringOtherApps: true)
} label: {
Image(systemName: "gearshape")
Expand Down
2 changes: 1 addition & 1 deletion Pareto Updater/Views/AppRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ struct AppRow: View {
Button {
onUpdate?()
}
label: {
label: {
Image(systemName: "arrow.down.app.fill")
.resizable()
.aspectRatio(contentMode: .fit)
Expand Down

0 comments on commit d4ebdd6

Please sign in to comment.