-
-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
~ Rewriting of package loading function
- Loading branch information
Showing
5 changed files
with
281 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
Cork/Logic/Package Loading/Helper Logic/Check if Package Was Installed Intentionally.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// | ||
// Check if Package Was Installed Intentionally.swift | ||
// Cork | ||
// | ||
// Created by David Bureš on 13.11.2024. | ||
// | ||
|
||
import Foundation | ||
import CorkShared | ||
|
||
enum IntentionalInstallationDiscoveryError: Error | ||
{ | ||
/// The function could not determine the most relevant version of the package to read the recepit from | ||
case failedToDetermineMostRelevantVersion(packageURL: URL) | ||
|
||
/// The installation receipt is there, but cannot be read due to permission issues | ||
case failedToReadInstallationRecepit(packageURL: URL) | ||
|
||
/// The installation receipt could be read, but not parsed | ||
case failedToParseInstallationReceipt(packageURL: URL) | ||
|
||
/// The installation receipt is missing completely | ||
case installationReceiptMissingCompletely(packageURL: URL) | ||
|
||
/// The provided `URL` has an unexpected form | ||
case unexpectedFolderName(packageURL: URL) | ||
} | ||
|
||
extension URL | ||
{ | ||
/// This function checks whether the package was installed intentionally. | ||
/// - For Formulae, this info gets read from the install receipt | ||
/// - Casks are always instaled intentionally | ||
/// - Parameter versionURLs: All available versions for this package. Some packages have multiple versions installed at a time (for example, the package `xz` might have versions 1.2 and 1.3 installed at once) | ||
/// - Returns: Indication whether this package was installed intentionally or not | ||
func checkIfPackageWasInstalledIntentionally(versionURLs: [URL]) async throws(IntentionalInstallationDiscoveryError) -> Bool | ||
{ | ||
|
||
// TODO: Convert this so it uses the most recent version instead of a random one | ||
guard let localPackagePath = versionURLs.first | ||
else | ||
{ | ||
throw .failedToDetermineMostRelevantVersion(packageURL: self) | ||
|
||
//throw .failedWhileLoadingCertainPackage(lastPathComponent, self, failureReason: String(localized: "error.package-loading.could-not-load-version-to-check-from-available-versions")) | ||
} | ||
|
||
if path.contains("Cellar") | ||
{ | ||
let localPackageInfoJSONPath: URL = localPackagePath.appendingPathComponent("INSTALL_RECEIPT.json", conformingTo: .json) | ||
if FileManager.default.fileExists(atPath: localPackageInfoJSONPath.path) | ||
{ | ||
struct InstallRecepitParser: Codable | ||
{ | ||
let installedOnRequest: Bool | ||
} | ||
|
||
let decoder: JSONDecoder = { | ||
let decoder: JSONDecoder = .init() | ||
decoder.keyDecodingStrategy = .convertFromSnakeCase | ||
|
||
return decoder | ||
}() | ||
|
||
do | ||
{ | ||
let installReceiptContents: Data = try .init(contentsOf: localPackageInfoJSONPath) | ||
|
||
do | ||
{ | ||
return try decoder.decode(InstallRecepitParser.self, from: installReceiptContents).installedOnRequest | ||
} | ||
catch let installReceiptParsingError | ||
{ | ||
AppConstants.shared.logger.error("Failed to decode install receipt for package \(self.lastPathComponent, privacy: .public) with error \(installReceiptParsingError.localizedDescription, privacy: .public)") | ||
|
||
throw IntentionalInstallationDiscoveryError.failedToParseInstallationReceipt(packageURL: self) | ||
|
||
//throw PackageLoadingError.failedWhileLoadingCertainPackage(self.lastPathComponent, self, failureReason: String(localized: "error.package-loading.could-not-decode-installa-receipt-\(installReceiptParsingError.localizedDescription)")) | ||
} | ||
} | ||
catch let installReceiptLoadingError | ||
{ | ||
AppConstants.shared.logger.error("Failed to load contents of install receipt for package \(self.lastPathComponent, privacy: .public) with error \(installReceiptLoadingError.localizedDescription, privacy: .public)") | ||
|
||
throw .failedToReadInstallationRecepit(packageURL: self) | ||
|
||
//throw .failedWhileLoadingCertainPackage(self.lastPathComponent, self, failureReason: String(localized: "error.package-loading.could-not-convert-contents-of-install-receipt-to-data-\(installReceiptLoadingError.localizedDescription)")) | ||
} | ||
} | ||
else | ||
{ /// There's no install receipt for this package - silently fail and return that the packagw was not installed intentionally | ||
// TODO: Add a setting like "Strictly check for errors" that would instead throw an error here | ||
|
||
AppConstants.shared.logger.error("There appears to be no install receipt for package \(localPackageInfoJSONPath.lastPathComponent, privacy: .public)") | ||
|
||
let shouldStrictlyCheckForHomebrewErrors: Bool = UserDefaults.standard.bool(forKey: "strictlyCheckForHomebrewErrors") | ||
|
||
if shouldStrictlyCheckForHomebrewErrors | ||
{ | ||
throw .installationReceiptMissingCompletely(packageURL: self) | ||
|
||
//throw .failedWhileLoadingCertainPackage(lastPathComponent, self, failureReason: String(localized: "error.package-loading.missing-install-receipt")) | ||
} | ||
else | ||
{ | ||
return false | ||
} | ||
} | ||
} | ||
else if path.contains("Caskroom") | ||
{ | ||
return true | ||
} | ||
else | ||
{ | ||
throw .unexpectedFolderName(packageURL: self) | ||
//throw .failedWhileLoadingCertainPackage(lastPathComponent, self, failureReason: String(localized: "error.package-loading.unexpected-folder-name")) | ||
} | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
Cork/Logic/Package Loading/Helper Logic/Filter Symlinks.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// | ||
// Filter Symlinks.swift | ||
// Cork | ||
// | ||
// Created by David Bureš on 13.11.2024. | ||
// | ||
|
||
import Foundation | ||
|
||
extension [URL] | ||
{ | ||
/// Filter out all symlinks from an array of URLs | ||
var withoutSymlinks: [URL] | ||
{ | ||
return self.filter | ||
{ url in | ||
/// If the existence of a symlink cannot be verified, be safe and return `false` | ||
guard let isSymlink = url.isSymlink() | ||
else | ||
{ | ||
return false | ||
} | ||
|
||
/// `isSymlink` is `true` for a symlink. Therefore, if we want to filter out symlinks, we have to return the opposite of `true`, which is `false` | ||
return !isSymlink | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
Cork/Logic/Package Loading/Helper Logic/Get Package Type from URL.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// | ||
// Get Package Type from URL.swift | ||
// Cork | ||
// | ||
// Created by David Bureš on 13.11.2024. | ||
// | ||
|
||
import Foundation | ||
|
||
extension URL | ||
{ | ||
/// Determine a package's type type from its URL | ||
var packageType: PackageType | ||
{ | ||
if self.pathComponents.contains("Cellar") | ||
{ | ||
return .formula | ||
} | ||
else | ||
{ | ||
return .cask | ||
} | ||
} | ||
} |
Oops, something went wrong.