runtime clean up

This commit is contained in:
Matt Kiazyk 2023-11-23 13:30:59 -06:00
parent 6ffce23616
commit ec4dc2b3d0
9 changed files with 107 additions and 62 deletions

View file

@ -101,6 +101,7 @@
CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; };
E689540325BE8C64000EBCEA /* DockProgress in Frameworks */ = {isa = PBXBuildFile; productRef = E689540225BE8C64000EBCEA /* DockProgress */; };
E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; };
E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */; };
E872EE4E2808D4F100D3DD8B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E872EE502808D4F100D3DD8B /* Localizable.strings */; };
E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87AB3C42939B65E00D72F43 /* Hardware.swift */; };
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
@ -300,6 +301,7 @@
CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = "<group>"; };
E2AFDCCA28F024D000864ADD /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = "<group>"; };
E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeInstallationStepDetailView.swift; sourceTree = "<group>"; };
E856BB73291EDD3D00DC438B /* XcodesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = XcodesKit; path = Xcodes/XcodesKit; sourceTree = "<group>"; };
E872EE4F2808D4F100D3DD8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
E87AB3C42939B65E00D72F43 /* Hardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hardware.swift; sourceTree = "<group>"; };
@ -623,6 +625,7 @@
children = (
CAFBDC67259A308B003DCC5A /* InfoPane.swift */,
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */,
E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */,
);
path = InfoPane;
sourceTree = "<group>";
@ -875,6 +878,7 @@
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */,
CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */,
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */,
E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */,
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */,
36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */,

View file

@ -499,9 +499,9 @@ extension AppState {
Current.notificationManager.scheduleNotification(title: xcode.id.appleDescription, body: step.description, category: .normal)
}
}
func setInstallationStep(of runtime: DownloadableRuntime, to step: RuntimeInstallationStep) {
DispatchQueue.main.async {
guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return }
self.downloadableRuntimes[index].installState = .installing(step)

View file

@ -49,9 +49,18 @@ extension AppState {
Task {
do {
try await downloadRunTimeFull(runtime: runtime)
DispatchQueue.main.async {
guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return }
self.downloadableRuntimes[index].installState = .installed
}
}
catch {
Logger.appState.error("Error downloading runtime: \(error.localizedDescription)")
DispatchQueue.main.async {
self.error = error
self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription)
}
}
}
@ -83,6 +92,7 @@ extension AppState {
let downloader = Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2
Logger.appState.info("Downloading \(runtime.visibleIdentifier) with \(downloader)")
let url = try await self.downloadRuntime(for: runtime, downloader: downloader, progressChanged: { [unowned self] progress in
DispatchQueue.main.async {
self.setInstallationStep(of: runtime, to: .downloading(progress: progress))
@ -90,15 +100,23 @@ extension AppState {
}).async()
Logger.appState.debug("Done downloading: \(url)")
//self.setInstallationStep(of: runtime, to: .downloading(progress: progress))
DispatchQueue.main.async {
self.setInstallationStep(of: runtime, to: .installing)
}
switch runtime.contentType {
case .package:
try await self.installFromPackage(dmgURL: url, runtime: runtime)
// not supported yet (do we need to for old packages?)
throw "Installing via package not support - please install manually from \(url.description)"
case .diskImage:
try await self.installFromImage(dmgURL: url)
DispatchQueue.main.async {
self.setInstallationStep(of: runtime, to: .trashingArchive)
}
try Current.files.removeItem(at: url)
}
}
@MainActor
func downloadRuntime(for runtime: DownloadableRuntime, downloader: Downloader, progressChanged: @escaping (Progress) -> Void) -> AnyPublisher<URL, Error> {
// Check to see if the dmg is in the expected path in case it was downloaded but failed to install
@ -139,9 +157,9 @@ extension AppState {
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
// return downloadXcodeWithURLSession(
// availableXcode,
// to: destination,
// return downloadRuntimeWithURLSession(
// runtime,
// to: expectedRuntimePath,
// progressChanged: progressChanged
// )
}
@ -163,36 +181,8 @@ extension AppState {
.eraseToAnyPublisher()
}
public func installFromImage(dmgURL: URL) async throws {
try? self.runtimeService.installRuntimeImage(dmgURL: dmgURL)
}
public func installFromPackage(dmgURL: URL, runtime: DownloadableRuntime) async throws {
Logger.appState.info("Mounting DMG")
do {
let mountedUrl = try await self.runtimeService.mountDMG(dmgUrl: dmgURL)
// 2-Get the first path under the mounted path, should be a .pkg
let pkgPath = Path(url: mountedUrl)!.ls().first!
try Path.xcodesCaches.mkdir().setCurrentUserAsOwner()
let expandedPkgPath = Path.xcodesCaches/runtime.identifier
//try expandedPkgPath.mkdir()
Logger.appState.info("PKG Path: \(pkgPath)")
Logger.appState.info("Expanded PKG Path: \(expandedPkgPath)")
//try? Current.files.removeItem(at: expandedPkgPath.url)
// 5-Expand (not install) the pkg to temporary path
try await self.runtimeService.expand(pkgPath: pkgPath, expandedPkgPath: expandedPkgPath)
//try await self.runtimeService.unmountDMG(mountedURL: mountedUrl)
} catch {
Logger.appState.error("Error installing runtime: \(error.localizedDescription)")
}
try await self.runtimeService.installRuntimeImage(dmgURL: dmgURL)
}
}

View file

@ -240,6 +240,12 @@ public struct Files {
return nil
}
}
public var write: (Data, URL) throws -> Void = { try $0.write(to: $1) }
public func write(_ data: Data, to url: URL) throws {
try write(data, url)
}
}
private func _installedXcodes(destination: Path) -> [InstalledXcode] {

View file

@ -288,8 +288,7 @@ struct InfoPane: View {
switch runtime.installState {
case .installing(let installationStep):
Text("INSTALLING")
InstallationStepDetailView(installationStep: installationStep)
RuntimeInstallationStepDetailView(installationStep: installationStep)
.fixedSize(horizontal: false, vertical: true)
default:
EmptyView()

View file

@ -0,0 +1,53 @@
//
// RuntimeInstallationStepDetailView.swift
// Xcodes
//
// Created by Matt Kiazyk on 2023-11-23.
// Copyright © 2023 Robots and Pencils. All rights reserved.
//
import SwiftUI
import XcodesKit
struct RuntimeInstallationStepDetailView: View {
let installationStep: RuntimeInstallationStep
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text(String(format: localizeString("InstallationStepDescription"), installationStep.stepNumber, installationStep.stepCount, installationStep.message))
switch installationStep {
case let .downloading(progress):
ObservingProgressIndicator(
progress,
controlSize: .regular,
style: .bar,
showsAdditionalDescription: true
)
case .installing, .trashingArchive:
ProgressView()
.scaleEffect(0.5)
}
}
}
}
#Preview("Downloading") {
RuntimeInstallationStepDetailView(
installationStep: .downloading(
progress: configure(Progress()) {
$0.kind = .file
$0.fileOperationKind = .downloading
$0.estimatedTimeRemaining = 123
$0.totalUnitCount = 11944848484
$0.completedUnitCount = 848444920
$0.throughput = 9211681
}
))
}
#Preview("Installing") {
RuntimeInstallationStepDetailView(
installationStep: .installing
)
}

View file

@ -11,7 +11,7 @@ import Path
public enum RuntimeInstallState: Equatable {
case notInstalled
case installing(RuntimeInstallationStep)
case installed(Path)
case installed
var notInstalled: Bool {
switch self {

View file

@ -9,11 +9,8 @@ import Foundation
public enum RuntimeInstallationStep: Equatable, CustomStringConvertible {
case downloading(progress: Progress)
case unarchiving
case moving(destination: String)
case installing
case trashingArchive
case checkingSecurity
case finishing
public var description: String {
"(\(stepNumber)/\(stepCount)) \(message)"
@ -23,29 +20,20 @@ public enum RuntimeInstallationStep: Equatable, CustomStringConvertible {
switch self {
case .downloading:
return localizeString("Downloading")
case .unarchiving:
return localizeString("Unarchiving")
case .moving(let destination):
return String(format: localizeString("Moving"), destination)
case .installing:
return localizeString("Installing")
case .trashingArchive:
return localizeString("TrashingArchive")
case .checkingSecurity:
return localizeString("CheckingSecurity")
case .finishing:
return localizeString("Finishing")
}
}
public var stepNumber: Int {
switch self {
case .downloading: return 1
case .unarchiving: return 2
case .moving: return 3
case .trashingArchive: return 4
case .checkingSecurity: return 5
case .finishing: return 6
case .installing: return 2
case .trashingArchive: return 3
}
}
public var stepCount: Int { 6 }
public var stepCount: Int { 3 }
}

View file

@ -54,10 +54,8 @@ public struct RuntimeService {
}
}
public func installRuntimeImage(dmgURL: URL) throws {
Task {
_ = try await Current.shell.installRuntimeImage(dmgURL)
}
public func installRuntimeImage(dmgURL: URL) async throws {
_ = try await Current.shell.installRuntimeImage(dmgURL)
}
public func mountDMG(dmgUrl: URL) async throws -> URL {
@ -72,13 +70,20 @@ public struct RuntimeService {
}
public func unmountDMG(mountedURL: URL) async throws {
let url = try await Current.shell.unmountDmg(mountedURL)
_ = try await Current.shell.unmountDmg(mountedURL)
}
public func expand(pkgPath: Path, expandedPkgPath: Path) async throws {
_ = try await Current.shell.expandPkg(pkgPath.url, expandedPkgPath.url)
}
public func createPkg(pkgPath: Path, expandedPkgPath: Path) async throws {
_ = try await Current.shell.createPkg(pkgPath.url, expandedPkgPath.url)
}
public func installPkg(pkgPath: Path, expandedPkgPath: Path) async throws {
_ = try await Current.shell.installPkg(pkgPath.url, expandedPkgPath.url.absoluteString)
}
}
extension String: Error {}