Add support for custom cache directories

This commit is contained in:
Nindi Gill 2022-09-26 13:56:25 +10:00
parent 24d0591822
commit d58bf5137a
7 changed files with 94 additions and 22 deletions

View file

@ -28,6 +28,7 @@
390451E1285740E800E0B563 /* Sequence+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E0285740E800E0B563 /* Sequence+Extension.swift */; }; 390451E1285740E800E0B563 /* Sequence+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E0285740E800E0B563 /* Sequence+Extension.swift */; };
390451E528574F0000E0B563 /* Catalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E428574F0000E0B563 /* Catalog.swift */; }; 390451E528574F0000E0B563 /* Catalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E428574F0000E0B563 /* Catalog.swift */; };
390451E72857510C00E0B563 /* TextTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E62857510B00E0B563 /* TextTag.swift */; }; 390451E72857510C00E0B563 /* TextTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E62857510B00E0B563 /* TextTag.swift */; };
39148CFC28DD55B300011FF5 /* PathControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39148CFB28DD55B300011FF5 /* PathControl.swift */; };
39252A77285A849F00956C74 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A76285A849F00956C74 /* AppDelegate.swift */; }; 39252A77285A849F00956C74 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A76285A849F00956C74 /* AppDelegate.swift */; };
39252A79285A85AF00956C74 /* SettingsInstallersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A78285A85AF00956C74 /* SettingsInstallersView.swift */; }; 39252A79285A85AF00956C74 /* SettingsInstallersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A78285A85AF00956C74 /* SettingsInstallersView.swift */; };
39252A7B285AC50400956C74 /* SettingsDiskImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A7A285AC50400956C74 /* SettingsDiskImagesView.swift */; }; 39252A7B285AC50400956C74 /* SettingsDiskImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A7A285AC50400956C74 /* SettingsDiskImagesView.swift */; };
@ -175,6 +176,7 @@
390451E0285740E800E0B563 /* Sequence+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Extension.swift"; sourceTree = "<group>"; }; 390451E0285740E800E0B563 /* Sequence+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Extension.swift"; sourceTree = "<group>"; };
390451E428574F0000E0B563 /* Catalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catalog.swift; sourceTree = "<group>"; }; 390451E428574F0000E0B563 /* Catalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catalog.swift; sourceTree = "<group>"; };
390451E62857510B00E0B563 /* TextTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextTag.swift; sourceTree = "<group>"; }; 390451E62857510B00E0B563 /* TextTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextTag.swift; sourceTree = "<group>"; };
39148CFB28DD55B300011FF5 /* PathControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathControl.swift; sourceTree = "<group>"; };
39252A76285A849F00956C74 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 39252A76285A849F00956C74 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
39252A78285A85AF00956C74 /* SettingsInstallersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInstallersView.swift; sourceTree = "<group>"; }; 39252A78285A85AF00956C74 /* SettingsInstallersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInstallersView.swift; sourceTree = "<group>"; };
39252A7A285AC50400956C74 /* SettingsDiskImagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiskImagesView.swift; sourceTree = "<group>"; }; 39252A7A285AC50400956C74 /* SettingsDiskImagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDiskImagesView.swift; sourceTree = "<group>"; };
@ -484,6 +486,7 @@
39252AA4285C463A00956C74 /* DynamicTextView.swift */, 39252AA4285C463A00956C74 /* DynamicTextView.swift */,
39252A86285ACE9C00956C74 /* FooterText.swift */, 39252A86285ACE9C00956C74 /* FooterText.swift */,
39252AA0285C2A1600956C74 /* PaddedDivider.swift */, 39252AA0285C2A1600956C74 /* PaddedDivider.swift */,
39148CFB28DD55B300011FF5 /* PathControl.swift */,
39252A84285ACDC800956C74 /* ResetToDefaultButton.swift */, 39252A84285ACDC800956C74 /* ResetToDefaultButton.swift */,
390451C92856F1D300E0B563 /* ScaledImage.swift */, 390451C92856F1D300E0B563 /* ScaledImage.swift */,
390451CB2856F23100E0B563 /* ScaledSystemImage.swift */, 390451CB2856F23100E0B563 /* ScaledSystemImage.swift */,
@ -756,6 +759,7 @@
390451CA2856F1D300E0B563 /* ScaledImage.swift in Sources */, 390451CA2856F1D300E0B563 /* ScaledImage.swift in Sources */,
39252A95285BF83D00956C74 /* MistTask.swift in Sources */, 39252A95285BF83D00956C74 /* MistTask.swift in Sources */,
39CF56272861E10F006FB5D2 /* Codesigner.swift in Sources */, 39CF56272861E10F006FB5D2 /* Codesigner.swift in Sources */,
39148CFC28DD55B300011FF5 /* PathControl.swift in Sources */,
3935F4892866C68000760AB0 /* DownloadSectionHeaderView.swift in Sources */, 3935F4892866C68000760AB0 /* DownloadSectionHeaderView.swift in Sources */,
39252AB5285C706000956C74 /* URL+Extension.swift in Sources */, 39252AB5285C706000956C74 /* URL+Extension.swift in Sources */,
39CF56352862D4BF006FB5D2 /* FileCompressor.swift in Sources */, 39CF56352862D4BF006FB5D2 /* FileCompressor.swift in Sources */,

View file

@ -14,11 +14,12 @@ struct InstallerCreator {
/// Creates a recently downloaded macOS Installer. /// Creates a recently downloaded macOS Installer.
/// ///
/// - Parameters: /// - Parameters:
/// - installer: The selected macOS Installer that was downloaded. /// - installer: The selected macOS Installer that was downloaded.
/// - mountPoint: The URL of the directory mount point. /// - mountPoint: The URL of the directory mount point.
/// - cacheDirectory: The cache directory storing all macOS Installer components.
/// ///
/// - Throws: A `MistError` if the downloaded macOS Installer fails to generate. /// - Throws: A `MistError` if the downloaded macOS Installer fails to generate.
static func create(_ installer: Installer, mountPoint: URL) async throws { static func create(_ installer: Installer, mountPoint: URL, cacheDirectory: String) async throws {
guard let url: URL = URL(string: installer.distributionURL) else { guard let url: URL = URL(string: installer.distributionURL) else {
throw MistError.invalidURL(installer.distributionURL) throw MistError.invalidURL(installer.distributionURL)
@ -26,7 +27,7 @@ struct InstallerCreator {
try await DirectoryRemover.remove(installer.temporaryInstallerURL) try await DirectoryRemover.remove(installer.temporaryInstallerURL)
let cacheDirectoryURL: URL = URL(fileURLWithPath: .cacheDirectory) let cacheDirectoryURL: URL = URL(fileURLWithPath: cacheDirectory)
let distributionURL: URL = cacheDirectoryURL.appendingPathComponent(installer.id).appendingPathComponent(url.lastPathComponent) let distributionURL: URL = cacheDirectoryURL.appendingPathComponent(installer.id).appendingPathComponent(url.lastPathComponent)
var argumentsArrays: [[String]] = [ var argumentsArrays: [[String]] = [
["installer", "-pkg", distributionURL.path, "-target", mountPoint.path] ["installer", "-pkg", distributionURL.path, "-target", mountPoint.path]

View file

@ -137,6 +137,7 @@ class TaskManager: ObservableObject {
destination destinationURL: URL?, destination destinationURL: URL?,
exports: [InstallerExportType], exports: [InstallerExportType],
cacheDownloads: Bool, cacheDownloads: Bool,
cacheDirectory: String,
retries: Int, retries: Int,
delay retryDelay: Int, delay retryDelay: Int,
applicationFilename: String, applicationFilename: String,
@ -150,7 +151,7 @@ class TaskManager: ObservableObject {
packageSigningIdentity: String packageSigningIdentity: String
) throws -> [(section: MistTaskSection, tasks: [MistTask])] { ) throws -> [(section: MistTaskSection, tasks: [MistTask])] {
var taskGroups: [(section: MistTaskSection, tasks: [MistTask])] = [] var taskGroups: [(section: MistTaskSection, tasks: [MistTask])] = []
let cacheDirectoryURL: URL = URL(fileURLWithPath: .cacheDirectory).appendingPathComponent(installer.id) let cacheDirectoryURL: URL = URL(fileURLWithPath: cacheDirectory).appendingPathComponent(installer.id)
let temporaryDirectoryURL: URL = URL(fileURLWithPath: .temporaryDirectory) let temporaryDirectoryURL: URL = URL(fileURLWithPath: .temporaryDirectory)
let mountPointURL: URL = URL(fileURLWithPath: "/Volumes/\(installer.id) Temp") let mountPointURL: URL = URL(fileURLWithPath: "/Volumes/\(installer.id) Temp")
@ -165,7 +166,7 @@ class TaskManager: ObservableObject {
), ),
( (
section: .setup, section: .setup,
tasks: installTasks(for: installer, temporaryDirectory: temporaryDirectoryURL, mountPoint: mountPointURL) tasks: installTasks(for: installer, temporaryDirectory: temporaryDirectoryURL, mountPoint: mountPointURL, cacheDirectory: cacheDirectory)
) )
] ]
@ -252,7 +253,7 @@ class TaskManager: ObservableObject {
return tasks return tasks
} }
private static func installTasks(for installer: Installer, temporaryDirectory temporaryDirectoryURL: URL, mountPoint mountPointURL: URL) -> [MistTask] { private static func installTasks(for installer: Installer, temporaryDirectory temporaryDirectoryURL: URL, mountPoint mountPointURL: URL, cacheDirectory: String) -> [MistTask] {
let imageURL: URL = temporaryDirectoryURL.appendingPathComponent("\(installer.id) Temp.dmg") let imageURL: URL = temporaryDirectoryURL.appendingPathComponent("\(installer.id) Temp.dmg")
let mountPointURL: URL = URL(fileURLWithPath: "/Volumes/\(installer.id) Temp") let mountPointURL: URL = URL(fileURLWithPath: "/Volumes/\(installer.id) Temp")
@ -268,7 +269,7 @@ class TaskManager: ObservableObject {
try await DiskImageMounter.mount(imageURL, mountPoint: mountPointURL) try await DiskImageMounter.mount(imageURL, mountPoint: mountPointURL)
}, },
MistTask(type: .create, description: "macOS Installer in Disk Image") { MistTask(type: .create, description: "macOS Installer in Disk Image") {
try await InstallerCreator.create(installer, mountPoint: mountPointURL) try await InstallerCreator.create(installer, mountPoint: mountPointURL, cacheDirectory: cacheDirectory)
} }
] ]
} }

View file

@ -0,0 +1,27 @@
//
// PathControl.swift
// Mist
//
// Created by Nindi Gill on 23/9/2022.
//
import SwiftUI
struct PathControl: NSViewRepresentable {
@Binding var path: String
func makeNSView(context: Context) -> NSPathControl {
NSPathControl()
}
func updateNSView(_ nsView: NSPathControl, context: Context) {
nsView.url = URL(fileURLWithPath: path)
}
}
struct PathControl_Previews: PreviewProvider {
static var previews: some View {
PathControl(path: .constant(.cacheDirectory))
}
}

View file

@ -9,6 +9,7 @@ import SwiftUI
struct InstallerListRow: View { struct InstallerListRow: View {
@AppStorage("cacheDownloads") private var cacheDownloads: Bool = false @AppStorage("cacheDownloads") private var cacheDownloads: Bool = false
@AppStorage("cacheDirectory") private var cacheDirectory: String = .cacheDirectory
@AppStorage("applicationFilename") private var applicationFilename: String = .applicationFilenameTemplate @AppStorage("applicationFilename") private var applicationFilename: String = .applicationFilenameTemplate
@AppStorage("diskImageFilename") private var diskImageFilename: String = .diskImageFilenameTemplate @AppStorage("diskImageFilename") private var diskImageFilename: String = .diskImageFilenameTemplate
@AppStorage("diskImageSign") private var diskImageSign: Bool = false @AppStorage("diskImageSign") private var diskImageSign: Bool = false
@ -83,6 +84,7 @@ struct InstallerListRow: View {
destination: openPanel.url, destination: openPanel.url,
exports: exports, exports: exports,
cacheDownloads: cacheDownloads, cacheDownloads: cacheDownloads,
cacheDirectory: cacheDirectory,
retries: retries, retries: retries,
delay: retryDelay, delay: retryDelay,
applicationFilename: applicationFilename, applicationFilename: applicationFilename,

View file

@ -8,29 +8,44 @@
import SwiftUI import SwiftUI
struct SettingsInstallersCacheView: View { struct SettingsInstallersCacheView: View {
@Binding var enabled: Bool @Binding var cacheDownloads: Bool
@Binding var cacheDirectory: String
@State private var cacheSize: String = "" @State private var cacheSize: String = ""
@State private var buttonClicked: Bool = false @State private var buttonClicked: Bool = false
@State private var openPanel: NSOpenPanel = NSOpenPanel()
var body: some View { var body: some View {
HStack { VStack(alignment: .leading) {
VStack(alignment: .leading) { HStack(alignment: .firstTextBaseline) {
Toggle(isOn: $enabled) { VStack(alignment: .leading) {
Text("Cache downloads") Toggle(isOn: $cacheDownloads) {
Text("Cache downloads")
}
FooterText("Speed up future operations by caching a local copy of macOS Installer files.")
} }
FooterText("Speed up future operations by caching a local copy of macOS Installer files.") Spacer()
Button("Select...") {
selectCacheDirectory()
}
.disabled(!cacheDownloads)
} }
Spacer() PathControl(path: $cacheDirectory)
VStack { .disabled(true)
HStack(alignment: .firstTextBaseline) {
FooterText("Cache directory currently contains \(cacheSize) of data.")
Spacer()
Button("Empty Cache...") { Button("Empty Cache...") {
buttonClicked.toggle() buttonClicked.toggle()
} }
FooterText(cacheSize)
} }
.disabled(!cacheDownloads)
} }
.onAppear { .onAppear {
getCacheSize() getCacheSize()
} }
.onChange(of: cacheDirectory) { _ in
getCacheSize()
}
.alert(isPresented: $buttonClicked) { .alert(isPresented: $buttonClicked) {
Alert( Alert(
title: Text("Empty Cache Directory?"), title: Text("Empty Cache Directory?"),
@ -41,9 +56,28 @@ struct SettingsInstallersCacheView: View {
} }
} }
private func selectCacheDirectory() {
openPanel.prompt = "Select"
openPanel.canCreateDirectories = true
openPanel.canChooseFiles = false
openPanel.canChooseDirectories = true
openPanel.resolvesAliases = true
openPanel.allowsMultipleSelection = false
openPanel.isAccessoryViewDisclosed = true
let response: NSApplication.ModalResponse = openPanel.runModal()
guard response == .OK,
let url: URL = openPanel.url else {
return
}
cacheDirectory = url.path
}
private func getCacheSize() { private func getCacheSize() {
let url: URL = URL(fileURLWithPath: .cacheDirectory) let url: URL = URL(fileURLWithPath: cacheDirectory)
var isDirectory: ObjCBool = false var isDirectory: ObjCBool = false
do { do {
@ -61,10 +95,10 @@ struct SettingsInstallersCacheView: View {
private func emptyCache() { private func emptyCache() {
do { do {
let paths: [String] = try FileManager.default.contentsOfDirectory(atPath: .cacheDirectory) let paths: [String] = try FileManager.default.contentsOfDirectory(atPath: cacheDirectory)
for path in paths { for path in paths {
let url: URL = URL(fileURLWithPath: .cacheDirectory + "/" + path) let url: URL = URL(fileURLWithPath: cacheDirectory + "/" + path)
try FileManager.default.removeItem(at: url) try FileManager.default.removeItem(at: url)
} }
} catch { } catch {
@ -75,6 +109,6 @@ struct SettingsInstallersCacheView: View {
struct SettingsInstallersCacheView_Previews: PreviewProvider { struct SettingsInstallersCacheView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
SettingsInstallersCacheView(enabled: .constant(true)) SettingsInstallersCacheView(cacheDownloads: .constant(true), cacheDirectory: .constant(.cacheDirectory))
} }
} }

View file

@ -9,9 +9,11 @@ import SwiftUI
struct SettingsInstallersView: View { struct SettingsInstallersView: View {
@AppStorage("cacheDownloads") private var cacheDownloads: Bool = false @AppStorage("cacheDownloads") private var cacheDownloads: Bool = false
@AppStorage("cacheDirectory") private var cacheDirectory: String = .cacheDirectory
@State private var catalogRows: [CatalogRow] = [] @State private var catalogRows: [CatalogRow] = []
@State private var selectedCatalogRow: CatalogRow? @State private var selectedCatalogRow: CatalogRow?
private let cacheDownloadsDefault: Bool = false private let cacheDownloadsDefault: Bool = false
private let cacheDirectoryDefault: String = .cacheDirectory
private var defaultCatalogRows: [CatalogRow] = Catalog.urls.map { CatalogRow(url: $0) } private var defaultCatalogRows: [CatalogRow] = Catalog.urls.map { CatalogRow(url: $0) }
private let imageName: String = "Installer" private let imageName: String = "Installer"
private let title: String = "Installers" private let title: String = "Installers"
@ -21,7 +23,7 @@ struct SettingsInstallersView: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
SettingsHeaderView(imageName: imageName, title: title, description: description, fade: .constant(false)) SettingsHeaderView(imageName: imageName, title: title, description: description, fade: .constant(false))
PaddedDivider() PaddedDivider()
SettingsInstallersCacheView(enabled: $cacheDownloads) SettingsInstallersCacheView(cacheDownloads: $cacheDownloads, cacheDirectory: $cacheDirectory)
PaddedDivider() PaddedDivider()
SettingsInstallersCatalogsView(catalogRows: $catalogRows, selectedCatalogRow: $selectedCatalogRow) SettingsInstallersCatalogsView(catalogRows: $catalogRows, selectedCatalogRow: $selectedCatalogRow)
PaddedDivider() PaddedDivider()
@ -50,6 +52,7 @@ struct SettingsInstallersView: View {
private func reset() { private func reset() {
cacheDownloads = cacheDownloadsDefault cacheDownloads = cacheDownloadsDefault
cacheDirectory = cacheDirectoryDefault
catalogRows = defaultCatalogRows catalogRows = defaultCatalogRows
} }
} }