mirror of https://github.com/buresdv/Cork
~ Refactoring of install process
This commit is contained in:
parent
35eeb39096
commit
6e569376a1
|
|
@ -7,7 +7,17 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum PackageInstallationProcessSteps
|
||||
enum PackageInstallationProcessSteps: Equatable
|
||||
{
|
||||
case ready, searching, presentingSearchResults, installing, finished, fatalError, requiresSudoPassword, wrongArchitecture, binaryAlreadyExists, anotherProcessAlreadyRunning, installationTerminatedUnexpectedly
|
||||
case ready
|
||||
case searching
|
||||
case presentingSearchResults
|
||||
case installing(packageToInstall: BrewPackage)
|
||||
case finished
|
||||
case fatalError(packageThatWasGettingInstalled: BrewPackage)
|
||||
case requiresSudoPassword(packageThatWasGettingInstalled: BrewPackage)
|
||||
case wrongArchitecture(packageThatWasGettingInstalled: BrewPackage)
|
||||
case binaryAlreadyExists(packageThatWasGettingInstalled: BrewPackage)
|
||||
case anotherProcessAlreadyRunning
|
||||
case installationTerminatedUnexpectedly
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// Package Install Initialization Error.swift
|
||||
// Cork
|
||||
//
|
||||
// Created by David Bureš - P on 22.04.2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum PackageInstallationInitializationError: Error
|
||||
{
|
||||
case couldNotStartInstallProcessWithPackage(package: BrewPackage?)
|
||||
}
|
||||
|
|
@ -10,7 +10,10 @@ import CorkShared
|
|||
|
||||
class InstallationProgressTracker: ObservableObject
|
||||
{
|
||||
@Published var packageBeingInstalled: PackageInProgressOfBeingInstalled = .init(package: .init(name: "", type: .formula, installedOn: nil, versions: [], sizeInBytes: 0, downloadCount: nil), installationStage: .downloadingCask, packageInstallationProgress: 0)
|
||||
@Published var installationStage: PackageInstallationStage = .downloadingCask
|
||||
@Published var installationProgress: Double = 0
|
||||
|
||||
@Published var realTimeTerminalOutput: [RealTimeTerminalLine] = .init()
|
||||
|
||||
@Published var numberOfPackageDependencies: Int = 0
|
||||
@Published var numberInLineOfPackageCurrentlyBeingFetched: Int = 0
|
||||
|
|
@ -38,30 +41,28 @@ class InstallationProgressTracker: ObservableObject
|
|||
}
|
||||
|
||||
@MainActor
|
||||
func installPackage(using brewData: BrewDataStorage, cachedPackagesTracker: CachedPackagesTracker) async throws -> TerminalOutput
|
||||
func installPackage(packageToInstall: BrewPackage, using brewData: BrewDataStorage, cachedPackagesTracker: CachedPackagesTracker) async throws -> TerminalOutput
|
||||
{
|
||||
let package: BrewPackage = packageBeingInstalled.package
|
||||
|
||||
AppConstants.shared.logger.debug("Installing package \(package.name, privacy: .auto)")
|
||||
AppConstants.shared.logger.debug("Installing package \(packageToInstall.name, privacy: .auto)")
|
||||
|
||||
var installationResult: TerminalOutput = .init(standardOutput: "", standardError: "")
|
||||
|
||||
if package.type == .formula
|
||||
if packageToInstall.type == .formula
|
||||
{
|
||||
AppConstants.shared.logger.info("Package \(package.name, privacy: .public) is Formula")
|
||||
AppConstants.shared.logger.info("Package \(packageToInstall.name, privacy: .public) is Formula")
|
||||
|
||||
let output: String = try await installFormula(using: brewData).joined(separator: "")
|
||||
let output: String = try await installFormula(packageToInstall).joined(separator: "")
|
||||
|
||||
installationResult.standardOutput.append(output)
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = 10
|
||||
installationProgress = 10
|
||||
|
||||
packageBeingInstalled.installationStage = .finished
|
||||
installationStage = .finished
|
||||
}
|
||||
else
|
||||
{
|
||||
AppConstants.shared.logger.info("Package is Cask")
|
||||
try await installCask(using: brewData)
|
||||
try await installCask(packageToInstall)
|
||||
}
|
||||
|
||||
do
|
||||
|
|
@ -77,17 +78,16 @@ class InstallationProgressTracker: ObservableObject
|
|||
}
|
||||
|
||||
@MainActor
|
||||
private func installFormula(using _: BrewDataStorage) async throws -> [String]
|
||||
private func installFormula(_ packageToInstall: BrewPackage) async throws -> [String]
|
||||
{
|
||||
let package: BrewPackage = packageBeingInstalled.package
|
||||
var packageDependencies: [String] = .init()
|
||||
/// For some reason, the line `fetching [package name]` appears twice during the matching process, and the first one is a dud. Ignore that first one.
|
||||
var hasAlreadyMatchedLineAboutInstallingPackageItself: Bool = false
|
||||
var installOutput: [String] = .init()
|
||||
|
||||
AppConstants.shared.logger.info("Package \(package.name, privacy: .public) is Formula")
|
||||
AppConstants.shared.logger.info("Package \(packageToInstall.name, privacy: .public) is Formula")
|
||||
|
||||
let (stream, process): (AsyncStream<StreamedTerminalOutput>, Process) = shell(AppConstants.shared.brewExecutablePath, ["install", package.name])
|
||||
let (stream, process): (AsyncStream<StreamedTerminalOutput>, Process) = shell(AppConstants.shared.brewExecutablePath, ["install", packageToInstall.name])
|
||||
installationProcess = process
|
||||
for await output in stream
|
||||
{
|
||||
|
|
@ -99,7 +99,7 @@ class InstallationProgressTracker: ObservableObject
|
|||
|
||||
if showRealTimeTerminalOutputs
|
||||
{
|
||||
packageBeingInstalled.realTimeTerminalOutput.append(RealTimeTerminalLine(line: outputLine))
|
||||
realTimeTerminalOutput.append(RealTimeTerminalLine(line: outputLine))
|
||||
}
|
||||
|
||||
AppConstants.shared.logger.info("Does the line contain an element from the array? \(outputLine.containsElementFromArray(packageDependencies), privacy: .public)")
|
||||
|
|
@ -107,7 +107,7 @@ class InstallationProgressTracker: ObservableObject
|
|||
if outputLine.contains("Fetching dependencies")
|
||||
{
|
||||
// First, we have to get a list of all the dependencies
|
||||
var matchedDependencies: String = try outputLine.regexMatch("(?<=\(package.name): ).*?(.*)")
|
||||
var matchedDependencies: String = try outputLine.regexMatch("(?<=\(packageToInstall.name): ).*?(.*)")
|
||||
matchedDependencies = matchedDependencies.replacingOccurrences(of: " and", with: ",") // The last dependency is different, because it's preceded by "and" instead of "," so let's replace that "and" with "," so we can split it nicely
|
||||
|
||||
AppConstants.shared.logger.debug("Matched Dependencies: \(matchedDependencies, privacy: .auto)")
|
||||
|
|
@ -120,43 +120,43 @@ class InstallationProgressTracker: ObservableObject
|
|||
|
||||
numberOfPackageDependencies = packageDependencies.count // Assign the number of dependencies to the tracker for the user to see
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = 1
|
||||
installationProgress = 1
|
||||
}
|
||||
|
||||
else if outputLine.contains("Installing dependencies") || outputLine.contains("Installing \(package.name) dependency")
|
||||
else if outputLine.contains("Installing dependencies") || outputLine.contains("Installing \(packageToInstall.name) dependency")
|
||||
{
|
||||
AppConstants.shared.logger.info("Will install dependencies!")
|
||||
packageBeingInstalled.installationStage = .installingDependencies
|
||||
installationStage = .installingDependencies
|
||||
|
||||
// Increment by 1 for each package that finished installing
|
||||
numberInLineOfPackageCurrentlyBeingInstalled = numberInLineOfPackageCurrentlyBeingInstalled + 1
|
||||
AppConstants.shared.logger.info("Installing dependency \(self.numberInLineOfPackageCurrentlyBeingInstalled) of \(packageDependencies.count)")
|
||||
|
||||
// TODO: Add a math formula for advancing the stepper
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + Double(Double(10) / (Double(3) * Double(numberOfPackageDependencies)))
|
||||
installationProgress = installationProgress + Double(Double(10) / (Double(3) * Double(numberOfPackageDependencies)))
|
||||
}
|
||||
|
||||
else if outputLine.contains("Already downloaded") || (outputLine.contains("Fetching") && outputLine.containsElementFromArray(packageDependencies))
|
||||
{
|
||||
AppConstants.shared.logger.info("Will fetch dependencies!")
|
||||
packageBeingInstalled.installationStage = .fetchingDependencies
|
||||
installationStage = .fetchingDependencies
|
||||
|
||||
numberInLineOfPackageCurrentlyBeingFetched = numberInLineOfPackageCurrentlyBeingFetched + 1
|
||||
|
||||
AppConstants.shared.logger.info("Fetching dependency \(self.numberInLineOfPackageCurrentlyBeingFetched) of \(packageDependencies.count)")
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + Double(Double(10) / (Double(3) * (Double(numberOfPackageDependencies) * Double(5))))
|
||||
installationProgress = installationProgress + Double(Double(10) / (Double(3) * (Double(numberOfPackageDependencies) * Double(5))))
|
||||
}
|
||||
|
||||
else if outputLine.contains("Fetching \(package.name)") || outputLine.contains("Installing \(package.name)")
|
||||
else if outputLine.contains("Fetching \(packageToInstall.name)") || outputLine.contains("Installing \(packageToInstall.name)")
|
||||
{
|
||||
if hasAlreadyMatchedLineAboutInstallingPackageItself
|
||||
{ /// Only the second line about the package being installed is valid
|
||||
AppConstants.shared.logger.info("Will install the package itself!")
|
||||
packageBeingInstalled.installationStage = .installingPackage
|
||||
installationStage = .installingPackage
|
||||
|
||||
// TODO: Add a math formula for advancing the stepper
|
||||
packageBeingInstalled.packageInstallationProgress = Double(packageBeingInstalled.packageInstallationProgress) + Double((Double(10) - Double(packageBeingInstalled.packageInstallationProgress)) / Double(2))
|
||||
installationProgress = Double(installationProgress) + Double((Double(10) - Double(installationProgress)) / Double(2))
|
||||
|
||||
AppConstants.shared.logger.info("Stepper value: \(Double(Double(10) / (Double(3) * Double(self.numberOfPackageDependencies))))")
|
||||
}
|
||||
|
|
@ -164,47 +164,45 @@ class InstallationProgressTracker: ObservableObject
|
|||
{ /// When it appears for the first time, ignore it
|
||||
AppConstants.shared.logger.info("Matched the dud line about the package itself being installed!")
|
||||
hasAlreadyMatchedLineAboutInstallingPackageItself = true
|
||||
packageBeingInstalled.packageInstallationProgress = Double(packageBeingInstalled.packageInstallationProgress) + Double((Double(10) - Double(packageBeingInstalled.packageInstallationProgress)) / Double(2))
|
||||
installationProgress = Double(installationProgress) + Double((Double(10) - Double(installationProgress)) / Double(2))
|
||||
}
|
||||
}
|
||||
|
||||
installOutput.append(outputLine)
|
||||
|
||||
AppConstants.shared.logger.debug("Current installation stage: \(self.packageBeingInstalled.installationStage.description, privacy: .public)")
|
||||
AppConstants.shared.logger.debug("Current installation stage: \(self.installationStage.description, privacy: .public)")
|
||||
|
||||
case .standardError(let errorLine):
|
||||
AppConstants.shared.logger.error("Errored out: \(errorLine, privacy: .public)")
|
||||
|
||||
if showRealTimeTerminalOutputs
|
||||
{
|
||||
packageBeingInstalled.realTimeTerminalOutput.append(RealTimeTerminalLine(line: errorLine))
|
||||
realTimeTerminalOutput.append(RealTimeTerminalLine(line: errorLine))
|
||||
}
|
||||
|
||||
if errorLine.contains("a password is required")
|
||||
{
|
||||
AppConstants.shared.logger.warning("Install requires sudo")
|
||||
|
||||
packageBeingInstalled.installationStage = .requiresSudoPassword
|
||||
installationStage = .requiresSudoPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = 10
|
||||
installationProgress = 10
|
||||
|
||||
packageBeingInstalled.installationStage = .finished
|
||||
installationStage = .finished
|
||||
|
||||
return installOutput
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func installCask(using _: BrewDataStorage) async throws
|
||||
func installCask(_ packageToInstall: BrewPackage) async throws
|
||||
{
|
||||
let package: BrewPackage = packageBeingInstalled.package
|
||||
|
||||
AppConstants.shared.logger.info("Package is Cask")
|
||||
AppConstants.shared.logger.debug("Installing package \(package.name, privacy: .public)")
|
||||
AppConstants.shared.logger.debug("Installing package \(packageToInstall.name, privacy: .public)")
|
||||
|
||||
let (stream, process): (AsyncStream<StreamedTerminalOutput>, Process) = shell(AppConstants.shared.brewExecutablePath, ["install", "--no-quarantine", package.name])
|
||||
let (stream, process): (AsyncStream<StreamedTerminalOutput>, Process) = shell(AppConstants.shared.brewExecutablePath, ["install", "--no-quarantine", packageToInstall.name])
|
||||
installationProcess = process
|
||||
for await output in stream
|
||||
{
|
||||
|
|
@ -215,56 +213,56 @@ class InstallationProgressTracker: ObservableObject
|
|||
|
||||
if showRealTimeTerminalOutputs
|
||||
{
|
||||
packageBeingInstalled.realTimeTerminalOutput.append(RealTimeTerminalLine(line: outputLine))
|
||||
realTimeTerminalOutput.append(RealTimeTerminalLine(line: outputLine))
|
||||
}
|
||||
|
||||
if outputLine.contains("Downloading")
|
||||
{
|
||||
AppConstants.shared.logger.info("Will download Cask")
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + 2
|
||||
installationProgress = installationProgress + 2
|
||||
|
||||
packageBeingInstalled.installationStage = .downloadingCask
|
||||
installationStage = .downloadingCask
|
||||
}
|
||||
else if outputLine.contains("Installing Cask")
|
||||
{
|
||||
AppConstants.shared.logger.info("Will install Cask")
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + 2
|
||||
installationProgress = installationProgress + 2
|
||||
|
||||
packageBeingInstalled.installationStage = .installingCask
|
||||
installationStage = .installingCask
|
||||
}
|
||||
else if outputLine.contains("Moving App")
|
||||
{
|
||||
AppConstants.shared.logger.info("Moving App")
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + 2
|
||||
installationProgress = installationProgress + 2
|
||||
|
||||
packageBeingInstalled.installationStage = .movingCask
|
||||
installationStage = .movingCask
|
||||
}
|
||||
else if outputLine.contains("Linking binary")
|
||||
{
|
||||
AppConstants.shared.logger.info("Linking Binary")
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + 2
|
||||
installationProgress = installationProgress + 2
|
||||
|
||||
packageBeingInstalled.installationStage = .linkingCaskBinary
|
||||
installationStage = .linkingCaskBinary
|
||||
}
|
||||
else if outputLine.contains("Purging files")
|
||||
{
|
||||
AppConstants.shared.logger.info("Purging old version of cask \(package.name)")
|
||||
AppConstants.shared.logger.info("Purging old version of cask \(packageToInstall.name)")
|
||||
|
||||
packageBeingInstalled.installationStage = .installingCask
|
||||
installationStage = .installingCask
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = packageBeingInstalled.packageInstallationProgress + 1
|
||||
installationProgress = installationProgress + 1
|
||||
}
|
||||
else if outputLine.contains("was successfully installed")
|
||||
{
|
||||
AppConstants.shared.logger.info("Finished installing app")
|
||||
|
||||
packageBeingInstalled.installationStage = .finished
|
||||
installationStage = .finished
|
||||
|
||||
packageBeingInstalled.packageInstallationProgress = 10
|
||||
installationProgress = 10
|
||||
}
|
||||
|
||||
case .standardError(let errorLine):
|
||||
|
|
@ -272,26 +270,26 @@ class InstallationProgressTracker: ObservableObject
|
|||
|
||||
if showRealTimeTerminalOutputs
|
||||
{
|
||||
packageBeingInstalled.realTimeTerminalOutput.append(RealTimeTerminalLine(line: errorLine))
|
||||
realTimeTerminalOutput.append(RealTimeTerminalLine(line: errorLine))
|
||||
}
|
||||
|
||||
if errorLine.contains("a password is required")
|
||||
{
|
||||
AppConstants.shared.logger.warning("Install requires sudo")
|
||||
|
||||
packageBeingInstalled.installationStage = .requiresSudoPassword
|
||||
installationStage = .requiresSudoPassword
|
||||
}
|
||||
else if errorLine.contains("there is already an App at")
|
||||
{
|
||||
AppConstants.shared.logger.warning("The app already exists")
|
||||
|
||||
packageBeingInstalled.installationStage = .binaryAlreadyExists
|
||||
installationStage = .binaryAlreadyExists
|
||||
}
|
||||
else if errorLine.contains(/depends on hardware architecture being.+but you are running/)
|
||||
{
|
||||
AppConstants.shared.logger.warning("Package is wrong architecture")
|
||||
|
||||
packageBeingInstalled.installationStage = .wrongArchitecture
|
||||
installationStage = .wrongArchitecture
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
// Created by David Bureš on 03.07.2022.
|
||||
//
|
||||
|
||||
import ButtonKit
|
||||
import CorkNotifications
|
||||
import CorkShared
|
||||
import SwiftUI
|
||||
import ButtonKit
|
||||
|
||||
struct AddFormulaView: View
|
||||
{
|
||||
|
|
@ -42,7 +42,32 @@ struct AddFormulaView: View
|
|||
|
||||
var isDismissable: Bool
|
||||
{
|
||||
[.ready, .presentingSearchResults, .fatalError, .anotherProcessAlreadyRunning, .binaryAlreadyExists, .requiresSudoPassword, .wrongArchitecture, .anotherProcessAlreadyRunning, .installationTerminatedUnexpectedly, .installing].contains(packageInstallationProcessStep)
|
||||
if case .installing = packageInstallationProcessStep
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
if case .binaryAlreadyExists = packageInstallationProcessStep
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
if case .fatalError = packageInstallationProcessStep
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
if case .wrongArchitecture = packageInstallationProcessStep
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
if case .requiresSudoPassword = packageInstallationProcessStep
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
return [.ready, .presentingSearchResults, .anotherProcessAlreadyRunning, .anotherProcessAlreadyRunning, .installationTerminatedUnexpectedly].contains(packageInstallationProcessStep)
|
||||
}
|
||||
|
||||
var sheetTitle: LocalizedStringKey
|
||||
|
|
@ -109,32 +134,40 @@ struct AddFormulaView: View
|
|||
installationProgressTracker: installationProgressTracker
|
||||
)
|
||||
|
||||
case .installing:
|
||||
case .installing(let packageToInstall):
|
||||
InstallingPackageView(
|
||||
installationProgressTracker: installationProgressTracker,
|
||||
packageToInstall: packageToInstall,
|
||||
packageInstallationProcessStep: $packageInstallationProcessStep
|
||||
)
|
||||
|
||||
case .finished:
|
||||
InstallationFinishedSuccessfullyView()
|
||||
|
||||
case .fatalError: /// This shows up when the function for executing the install action throws an error
|
||||
InstallationFatalErrorView(installationProgressTracker: installationProgressTracker)
|
||||
case .fatalError(let packageThatWasGettingInstalled): /// This shows up when the function for executing the install action throws an error
|
||||
InstallationFatalErrorView(
|
||||
installationProgressTracker: installationProgressTracker,
|
||||
packageThatWasGettingInstalled: packageThatWasGettingInstalled
|
||||
)
|
||||
|
||||
case .requiresSudoPassword:
|
||||
SudoRequiredView(installationProgressTracker: installationProgressTracker)
|
||||
case .requiresSudoPassword(let packageThatWasGettingInstalled):
|
||||
SudoRequiredView(
|
||||
packageThatWasGettingInstalled: packageThatWasGettingInstalled
|
||||
)
|
||||
|
||||
case .wrongArchitecture:
|
||||
WrongArchitectureView(installationProgressTracker: installationProgressTracker)
|
||||
case .wrongArchitecture(let packageThatWasGettingInstalled):
|
||||
WrongArchitectureView(
|
||||
packageThatWasGettingInstalled: packageThatWasGettingInstalled
|
||||
)
|
||||
|
||||
case .binaryAlreadyExists:
|
||||
BinaryAlreadyExistsView(installationProgressTracker: installationProgressTracker)
|
||||
case .binaryAlreadyExists(let packageThatWasGettingInstalled):
|
||||
BinaryAlreadyExistsView(installationProgressTracker: installationProgressTracker, packageThatWasGettingInstalled: packageThatWasGettingInstalled)
|
||||
|
||||
case .anotherProcessAlreadyRunning:
|
||||
AnotherProcessAlreadyRunningView()
|
||||
|
||||
case .installationTerminatedUnexpectedly:
|
||||
InstallationTerminatedUnexpectedlyView(terminalOutputOfTheInstallation: installationProgressTracker.packageBeingInstalled.realTimeTerminalOutput)
|
||||
InstallationTerminatedUnexpectedlyView(terminalOutputOfTheInstallation: installationProgressTracker.realTimeTerminalOutput)
|
||||
}
|
||||
}
|
||||
.navigationTitle(sheetTitle)
|
||||
|
|
|
|||
|
|
@ -125,11 +125,10 @@ struct InstallationInitialView: View
|
|||
return
|
||||
}
|
||||
|
||||
installationProgressTracker.packageBeingInstalled = PackageInProgressOfBeingInstalled(package: packageToInstall, installationStage: .ready, packageInstallationProgress: 0)
|
||||
|
||||
AppConstants.shared.logger.debug("Packages to install: \(installationProgressTracker.packageBeingInstalled.package.name, privacy: .public)")
|
||||
AppConstants.shared.logger.debug("Packages to install: \(packageToInstall.name, privacy: .public)")
|
||||
|
||||
packageInstallationProcessStep = .installing
|
||||
packageInstallationProcessStep = .installing(packageToInstall: packageToInstall)
|
||||
|
||||
} label: {
|
||||
Text("add-package.install.action")
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
// Created by David Bureš on 29.09.2023.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CorkShared
|
||||
import SwiftUI
|
||||
|
||||
struct InstallingPackageView: View
|
||||
{
|
||||
|
|
@ -19,6 +19,8 @@ struct InstallingPackageView: View
|
|||
|
||||
@ObservedObject var installationProgressTracker: InstallationProgressTracker
|
||||
|
||||
let packageToInstall: BrewPackage
|
||||
|
||||
@Binding var packageInstallationProcessStep: PackageInstallationProcessSteps
|
||||
|
||||
@State var isShowingRealTimeOutput: Bool = false
|
||||
|
|
@ -27,13 +29,13 @@ struct InstallingPackageView: View
|
|||
{
|
||||
VStack(alignment: .leading)
|
||||
{
|
||||
if installationProgressTracker.packageBeingInstalled.installationStage != .finished
|
||||
if installationProgressTracker.installationStage != .finished
|
||||
{
|
||||
ProgressView(value: installationProgressTracker.packageBeingInstalled.packageInstallationProgress, total: 10)
|
||||
ProgressView(value: installationProgressTracker.installationProgress, total: 10)
|
||||
{
|
||||
VStack(alignment: .leading)
|
||||
{
|
||||
switch installationProgressTracker.packageBeingInstalled.installationStage
|
||||
switch installationProgressTracker.installationStage
|
||||
{
|
||||
case .ready:
|
||||
Text("add-package.install.ready")
|
||||
|
|
@ -56,36 +58,36 @@ struct InstallingPackageView: View
|
|||
|
||||
// CASKS
|
||||
case .downloadingCask:
|
||||
Text("add-package.install.downloading-cask-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add-package.install.downloading-cask-\(packageToInstall.name)")
|
||||
|
||||
case .installingCask:
|
||||
Text("add-package.install.installing-cask-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add-package.install.installing-cask-\(packageToInstall.name)")
|
||||
|
||||
case .linkingCaskBinary:
|
||||
Text("add-package.install.linking-cask-binary")
|
||||
|
||||
case .movingCask:
|
||||
Text("add-package.install.moving-cask-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add-package.install.moving-cask-\(packageToInstall.name)")
|
||||
|
||||
case .requiresSudoPassword:
|
||||
Text("add-package.install.requires-sudo-password-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add-package.install.requires-sudo-password-\(packageToInstall.name)")
|
||||
.onAppear
|
||||
{
|
||||
packageInstallationProcessStep = .requiresSudoPassword
|
||||
packageInstallationProcessStep = .requiresSudoPassword(packageThatWasGettingInstalled: packageToInstall)
|
||||
}
|
||||
|
||||
case .wrongArchitecture:
|
||||
Text("add-package.install.wrong-architecture.title")
|
||||
.onAppear
|
||||
{
|
||||
packageInstallationProcessStep = .wrongArchitecture
|
||||
packageInstallationProcessStep = .wrongArchitecture(packageThatWasGettingInstalled: packageToInstall)
|
||||
}
|
||||
|
||||
case .binaryAlreadyExists:
|
||||
Text("add-package.install.binary-already-exists-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add-package.install.binary-already-exists-\(packageToInstall.name)")
|
||||
.onAppear
|
||||
{
|
||||
packageInstallationProcessStep = .binaryAlreadyExists
|
||||
packageInstallationProcessStep = .binaryAlreadyExists(packageThatWasGettingInstalled: packageToInstall)
|
||||
}
|
||||
|
||||
case .terminatedUnexpectedly:
|
||||
|
|
@ -96,7 +98,7 @@ struct InstallingPackageView: View
|
|||
}
|
||||
}
|
||||
LiveTerminalOutputView(
|
||||
lineArray: $installationProgressTracker.packageBeingInstalled.realTimeTerminalOutput,
|
||||
lineArray: $installationProgressTracker.realTimeTerminalOutput,
|
||||
isRealTimeTerminalOutputExpanded: $isShowingRealTimeOutput
|
||||
)
|
||||
}
|
||||
|
|
@ -117,19 +119,19 @@ struct InstallingPackageView: View
|
|||
do
|
||||
{
|
||||
let installationResult: TerminalOutput = try await installationProgressTracker.installPackage(
|
||||
using: brewData,
|
||||
packageToInstall: packageToInstall, using: brewData,
|
||||
cachedPackagesTracker: cachedPackagesTracker
|
||||
)
|
||||
|
||||
AppConstants.shared.logger.debug("Installation result:\nStandard output: \(installationResult.standardOutput, privacy: .public)\nStandard error: \(installationResult.standardError, privacy: .public)")
|
||||
|
||||
/// Check if the package installation stag at the end of the install process was something unexpected. Normal package installations go through multiple steps, and the three listed below are not supposed to be the end state. This means that something went wrong during the installation
|
||||
let installationStage: PackageInstallationStage = installationProgressTracker.packageBeingInstalled.installationStage
|
||||
let installationStage: PackageInstallationStage = installationProgressTracker.installationStage
|
||||
if [.installingCask, .installingPackage, .ready].contains(installationStage)
|
||||
{
|
||||
AppConstants.shared.logger.warning("The installation process quit before it was supposed to")
|
||||
|
||||
installationProgressTracker.packageBeingInstalled.installationStage = .terminatedUnexpectedly
|
||||
installationProgressTracker.installationStage = .terminatedUnexpectedly
|
||||
}
|
||||
}
|
||||
catch let fatalInstallationError
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import CorkShared
|
||||
import SwiftUI
|
||||
import ButtonKit
|
||||
|
||||
struct PresentingSearchResultsView: View
|
||||
{
|
||||
|
|
@ -149,12 +150,19 @@ struct PresentingSearchResultsView: View
|
|||
@ViewBuilder
|
||||
var startInstallProcessButton: some View
|
||||
{
|
||||
Button
|
||||
// This has to be an AsyncButton so it shakes
|
||||
AsyncButton
|
||||
{
|
||||
packageInstallationProcessStep = .installing
|
||||
guard let packageToInstall = foundPackageSelection else
|
||||
{
|
||||
throw PackageInstallationInitializationError.couldNotStartInstallProcessWithPackage(package: nil)
|
||||
}
|
||||
|
||||
packageInstallationProcessStep = .installing(packageToInstall: packageToInstall)
|
||||
} label: {
|
||||
Text("add-package.install.action")
|
||||
}
|
||||
.throwableButtonStyle(.shake)
|
||||
.keyboardShortcut(.defaultAction)
|
||||
.disabled(foundPackageSelection == nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ struct BinaryAlreadyExistsView: View, Sendable
|
|||
|
||||
@ObservedObject var installationProgressTracker: InstallationProgressTracker
|
||||
|
||||
let packageThatWasGettingInstalled: BrewPackage
|
||||
|
||||
var body: some View
|
||||
{
|
||||
ComplexWithImage(image: Image(localURL: URL(filePath: "/System/Library/CoreServices/KeyboardSetupAssistant.app/Contents/Resources/AppIcon.icns"))!)
|
||||
|
|
@ -24,7 +26,7 @@ struct BinaryAlreadyExistsView: View, Sendable
|
|||
VStack(alignment: .leading, spacing: 10)
|
||||
{
|
||||
HeadlineWithSubheadline(
|
||||
headline: "add-package.install.binary-already-exists-\(installationProgressTracker.packageBeingInstalled.package.name)",
|
||||
headline: "add-package.install.binary-already-exists-\(packageThatWasGettingInstalled.name)",
|
||||
subheadline: "add-package.install.binary-already-exists.subheadline",
|
||||
alignment: .leading
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ struct InstallationFatalErrorView: View
|
|||
{
|
||||
@ObservedObject var installationProgressTracker: InstallationProgressTracker
|
||||
|
||||
let packageThatWasGettingInstalled: BrewPackage
|
||||
|
||||
var body: some View
|
||||
{
|
||||
ComplexWithIcon(systemName: "exclamationmark.triangle")
|
||||
{
|
||||
HeadlineWithSubheadline(
|
||||
headline: "add-package.fatal-error-\(installationProgressTracker.packageBeingInstalled.package.name)",
|
||||
headline: "add-package.fatal-error-\(packageThatWasGettingInstalled.name)",
|
||||
subheadline: "add-package.fatal-error.description",
|
||||
alignment: .leading
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct SudoRequiredView: View, Sendable
|
|||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var brewData: BrewDataStorage
|
||||
|
||||
@ObservedObject var installationProgressTracker: InstallationProgressTracker
|
||||
let packageThatWasGettingInstalled: BrewPackage
|
||||
|
||||
var body: some View
|
||||
{
|
||||
|
|
@ -24,14 +24,14 @@ struct SudoRequiredView: View, Sendable
|
|||
{
|
||||
VStack(alignment: .leading, spacing: 10)
|
||||
{
|
||||
Text("add-package.install.requires-sudo-password-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add-package.install.requires-sudo-password-\(packageThatWasGettingInstalled.name)")
|
||||
.font(.headline)
|
||||
|
||||
ManualInstallInstructions(installationProgressTracker: installationProgressTracker)
|
||||
manualInstallInstructions
|
||||
}
|
||||
}
|
||||
|
||||
Text("add.package.install.requires-sudo-password.terminal-instructions-\(installationProgressTracker.packageBeingInstalled.package.name)")
|
||||
Text("add.package.install.requires-sudo-password.terminal-instructions-\(packageThatWasGettingInstalled.name)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
|
@ -51,19 +51,15 @@ struct SudoRequiredView: View, Sendable
|
|||
}
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
|
||||
private struct ManualInstallInstructions: View
|
||||
@ViewBuilder
|
||||
var manualInstallInstructions: some View
|
||||
{
|
||||
let installationProgressTracker: InstallationProgressTracker
|
||||
|
||||
var manualInstallCommand: String
|
||||
{
|
||||
return "brew install \(installationProgressTracker.packageBeingInstalled.package.type == .cask ? "--cask" : "") \(installationProgressTracker.packageBeingInstalled.package.name)"
|
||||
return "brew install \(packageThatWasGettingInstalled.type == .cask ? "--cask" : "") \(packageThatWasGettingInstalled.name)"
|
||||
}
|
||||
|
||||
var body: some View
|
||||
{
|
||||
VStack
|
||||
{
|
||||
Text("add-package.install.requires-sudo-password.description")
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct WrongArchitectureView: View, Sendable
|
|||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var brewData: BrewDataStorage
|
||||
|
||||
@ObservedObject var installationProgressTracker: InstallationProgressTracker
|
||||
let packageThatWasGettingInstalled: BrewPackage
|
||||
|
||||
var body: some View
|
||||
{
|
||||
|
|
@ -24,7 +24,7 @@ struct WrongArchitectureView: View, Sendable
|
|||
{
|
||||
HeadlineWithSubheadline(
|
||||
headline: "add-package.install.wrong-architecture.title",
|
||||
subheadline: "add-package.install.wrong-architecture-\(installationProgressTracker.packageBeingInstalled.package.name).user-architecture-is-\(ProcessInfo().CPUArchitecture == .arm ? "Apple Silicon" : "Intel")",
|
||||
subheadline: "add-package.install.wrong-architecture-\(packageThatWasGettingInstalled.name).user-architecture-is-\(ProcessInfo().CPUArchitecture == .arm ? "Apple Silicon" : "Intel")",
|
||||
alignment: .leading
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue