mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-22 03:24:40 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba4f00fa78 | ||
|
|
1380ece108 | ||
|
|
9cdb7087f2 | ||
|
|
c3e37854c3 |
+1
-1
@@ -74,7 +74,7 @@ task:
|
|||||||
- security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k password101 build.keychain
|
- security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k password101 build.keychain
|
||||||
- xcrun notarytool store-credentials "notarytool" --apple-id "hello@cirruslabs.org" --team-id "9M2P8L4D89" --password $AC_PASSWORD
|
- xcrun notarytool store-credentials "notarytool" --apple-id "hello@cirruslabs.org" --team-id "9M2P8L4D89" --password $AC_PASSWORD
|
||||||
install_script:
|
install_script:
|
||||||
- brew install go goreleaser/tap/goreleaser-pro
|
- brew install go goreleaser/tap/goreleaser-pro getsentry/tools/sentry-cli
|
||||||
- brew install mitchellh/gon/gon
|
- brew install mitchellh/gon/gon
|
||||||
info_script:
|
info_script:
|
||||||
- security find-identity -v
|
- security find-identity -v
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ struct IP: AsyncParsableCommand {
|
|||||||
let vmMACAddress = MACAddress(fromString: vmConfig.macAddress.string)!
|
let vmMACAddress = MACAddress(fromString: vmConfig.macAddress.string)!
|
||||||
|
|
||||||
guard let ipViaDHCP = try await IP.resolveIP(vmMACAddress, secondsToWait: wait) else {
|
guard let ipViaDHCP = try await IP.resolveIP(vmMACAddress, secondsToWait: wait) else {
|
||||||
throw RuntimeError("no IP address found, is your VM running?")
|
throw RuntimeError.NoIPAddressFound("no IP address found, is your VM running?")
|
||||||
}
|
}
|
||||||
|
|
||||||
let arpCache = try ARPCache()
|
let arpCache = try ARPCache()
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ struct Login: AsyncParsableCommand {
|
|||||||
credentialsProviders: [credentialsProvider])
|
credentialsProviders: [credentialsProvider])
|
||||||
try await registry.ping()
|
try await registry.ping()
|
||||||
} catch {
|
} catch {
|
||||||
throw RuntimeError("invalid credentials: \(error)")
|
throw RuntimeError.InvalidCredentials("invalid credentials: \(error)")
|
||||||
}
|
}
|
||||||
|
|
||||||
try KeychainCredentialsProvider().store(host: host, user: user, password: password)
|
try KeychainCredentialsProvider().store(host: host, user: user, password: password)
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ struct Run: AsyncParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if try !FileLock(lockURL: additionalDiskAttachment.url).trylock() {
|
if try !FileLock(lockURL: additionalDiskAttachment.url).trylock() {
|
||||||
throw RuntimeError("disk \(additionalDiskAttachment.url.path) seems to be already in use, "
|
throw RuntimeError.DiskAlreadyInUse("disk \(additionalDiskAttachment.url.path) seems to be already in use, "
|
||||||
+ "unmount it first in Finder")
|
+ "unmount it first in Finder")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ struct Run: AsyncParsableCommand {
|
|||||||
// [1]: https://man.openbsd.org/fcntl
|
// [1]: https://man.openbsd.org/fcntl
|
||||||
let lock = try PIDLock(lockURL: vmDir.configURL)
|
let lock = try PIDLock(lockURL: vmDir.configURL)
|
||||||
if try !lock.trylock() {
|
if try !lock.trylock() {
|
||||||
throw RuntimeError("Virtual machine \"\(name)\" is already running!", exitCode: 2)
|
throw RuntimeError.VMAlreadyRunning("VM \"\(name)\" is already running!")
|
||||||
}
|
}
|
||||||
|
|
||||||
let task = Task {
|
let task = Task {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ struct Stop: AsyncParsableCommand {
|
|||||||
// Find the VM's PID
|
// Find the VM's PID
|
||||||
var pid = try lock.pid()
|
var pid = try lock.pid()
|
||||||
if pid == 0 {
|
if pid == 0 {
|
||||||
throw RuntimeError("VM \(name) is not running", exitCode: 2)
|
throw RuntimeError.VMNotRunning("VM \"\(name)\" is not running")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to gracefully terminate the VM
|
// Try to gracefully terminate the VM
|
||||||
@@ -54,7 +54,7 @@ struct Stop: AsyncParsableCommand {
|
|||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
let details = Errno(rawValue: CInt(errno))
|
let details = Errno(rawValue: CInt(errno))
|
||||||
|
|
||||||
throw RuntimeError("failed to forcefully terminate the VM \(name): \(details)")
|
throw RuntimeError.VMTerminationFailed("failed to forcefully terminate the VM \"\(name)\": \(details)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ struct RemoteName: Comparable, Hashable, CustomStringConvertible {
|
|||||||
try ParseTreeWalker().walk(referenceCollector, try parser.root())
|
try ParseTreeWalker().walk(referenceCollector, try parser.root())
|
||||||
|
|
||||||
if let error = errorCollector.error {
|
if let error = errorCollector.error {
|
||||||
throw RuntimeError("failed to parse remote name: \(error)")
|
throw RuntimeError.FailedToParseRemoteName("\(error)")
|
||||||
}
|
}
|
||||||
|
|
||||||
host = referenceCollector.host!
|
host = referenceCollector.host!
|
||||||
@@ -120,7 +120,7 @@ struct RemoteName: Comparable, Hashable, CustomStringConvertible {
|
|||||||
} else if reference.starts(with: ":") {
|
} else if reference.starts(with: ":") {
|
||||||
self.reference = Reference(tag: String(reference.dropFirst(1)))
|
self.reference = Reference(tag: String(reference.dropFirst(1)))
|
||||||
} else {
|
} else {
|
||||||
throw RuntimeError("failed to parse remote name: unknown reference format")
|
throw RuntimeError.FailedToParseRemoteName("unknown reference format")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.reference = Reference(tag: "latest")
|
self.reference = Reference(tag: "latest")
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class PIDLock {
|
|||||||
|
|
||||||
let details = Errno(rawValue: CInt(errno))
|
let details = Errno(rawValue: CInt(errno))
|
||||||
|
|
||||||
throw RuntimeError("\(message): \(details)")
|
throw RuntimeError.PIDLockFailed("\(message): \(details)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return (true, result)
|
return (true, result)
|
||||||
|
|||||||
@@ -100,13 +100,15 @@ struct Root: AsyncParsableCommand {
|
|||||||
SentrySDK.capture(error: error)
|
SentrySDK.capture(error: error)
|
||||||
SentrySDK.flush(timeout: 2.seconds.timeInterval)
|
SentrySDK.flush(timeout: 2.seconds.timeInterval)
|
||||||
|
|
||||||
print(error)
|
// Handle a non-ArgumentParser's exception that requires a specific exit code to be set
|
||||||
|
if let errorWithExitCode = error as? HasExitCode {
|
||||||
|
print(error)
|
||||||
|
|
||||||
if let runtimeError = error as? RuntimeError {
|
Foundation.exit(errorWithExitCode.exitCode)
|
||||||
Foundation.exit(runtimeError.exitCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Foundation.exit(1)
|
// Handle any other exception, including ArgumentParser's ones
|
||||||
|
exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ extension URL {
|
|||||||
let times = [accessDate.asTimeval(), modificationDate.asTimeval()]
|
let times = [accessDate.asTimeval(), modificationDate.asTimeval()]
|
||||||
let ret = utimes(path, times)
|
let ret = utimes(path, times)
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
throw RuntimeError("utimes(2) failed: \(ret.explanation())")
|
throw RuntimeError.FailedToUpdateAccessDate("utimes(2) failed: \(ret.explanation())")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct VMDirectory: Prunable {
|
|||||||
|
|
||||||
func initialize(overwrite: Bool = false) throws {
|
func initialize(overwrite: Bool = false) throws {
|
||||||
if !overwrite && initialized {
|
if !overwrite && initialized {
|
||||||
throw RuntimeError("VM directory is already initialized, preventing overwrite")
|
throw RuntimeError.VMDirectoryAlreadyInitialized("VM directory is already initialized, preventing overwrite")
|
||||||
}
|
}
|
||||||
|
|
||||||
try FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true, attributes: nil)
|
try FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true, attributes: nil)
|
||||||
@@ -53,11 +53,11 @@ struct VMDirectory: Prunable {
|
|||||||
|
|
||||||
func validate() throws {
|
func validate() throws {
|
||||||
if !FileManager.default.fileExists(atPath: baseURL.path) {
|
if !FileManager.default.fileExists(atPath: baseURL.path) {
|
||||||
throw RuntimeError("the specified VM does not exist")
|
throw RuntimeError.VMDoesNotExist(name: baseURL.lastPathComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !initialized {
|
if !initialized {
|
||||||
throw RuntimeError("VM is missing some of its files (\(configURL.lastPathComponent),"
|
throw RuntimeError.VMMissingFiles("VM is missing some of its files (\(configURL.lastPathComponent),"
|
||||||
+ " \(diskURL.lastPathComponent) or \(nvramURL.lastPathComponent))")
|
+ " \(diskURL.lastPathComponent) or \(nvramURL.lastPathComponent))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class VMStorageHelper {
|
|||||||
return try closure()
|
return try closure()
|
||||||
} catch {
|
} catch {
|
||||||
if error.isFileNotFound() {
|
if error.isFileNotFound() {
|
||||||
throw RuntimeError("source VM \"\(name)\" not found, is it listed in \"tart list\"?")
|
throw RuntimeError.VMDoesNotExist(name: name)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
@@ -40,17 +40,66 @@ extension Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RuntimeError: Error, CustomStringConvertible {
|
enum RuntimeError : Error {
|
||||||
let message: String
|
case VMDoesNotExist(name: String)
|
||||||
let exitCode: Int32
|
case VMMissingFiles(_ message: String)
|
||||||
|
case VMNotRunning(_ message: String)
|
||||||
|
case VMAlreadyRunning(_ message: String)
|
||||||
|
case NoIPAddressFound(_ message: String)
|
||||||
|
case DiskAlreadyInUse(_ message: String)
|
||||||
|
case FailedToUpdateAccessDate(_ message: String)
|
||||||
|
case PIDLockFailed(_ message: String)
|
||||||
|
case FailedToParseRemoteName(_ message: String)
|
||||||
|
case VMTerminationFailed(_ message: String)
|
||||||
|
case InvalidCredentials(_ message: String)
|
||||||
|
case VMDirectoryAlreadyInitialized(_ message: String)
|
||||||
|
}
|
||||||
|
|
||||||
init(_ message: String, exitCode: Int32 = 1) {
|
protocol HasExitCode {
|
||||||
self.message = message
|
var exitCode: Int32 { get }
|
||||||
self.exitCode = exitCode
|
}
|
||||||
|
|
||||||
|
extension RuntimeError : CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .VMDoesNotExist(let name):
|
||||||
|
return "the specified VM \"\(name)\" does not exist"
|
||||||
|
case .VMMissingFiles(let message):
|
||||||
|
return message
|
||||||
|
case .VMNotRunning(let message):
|
||||||
|
return message
|
||||||
|
case .VMAlreadyRunning(let message):
|
||||||
|
return message
|
||||||
|
case .NoIPAddressFound(let message):
|
||||||
|
return message
|
||||||
|
case .DiskAlreadyInUse(let message):
|
||||||
|
return message
|
||||||
|
case .FailedToUpdateAccessDate(let message):
|
||||||
|
return message
|
||||||
|
case .PIDLockFailed(let message):
|
||||||
|
return message
|
||||||
|
case .FailedToParseRemoteName(let cause):
|
||||||
|
return "failed to parse remote name: \(cause)"
|
||||||
|
case .VMTerminationFailed(let message):
|
||||||
|
return message
|
||||||
|
case .InvalidCredentials(let message):
|
||||||
|
return message
|
||||||
|
case .VMDirectoryAlreadyInitialized(let message):
|
||||||
|
return message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var description: String {
|
extension RuntimeError : HasExitCode {
|
||||||
message
|
var exitCode: Int32 {
|
||||||
|
switch self {
|
||||||
|
case .VMNotRunning:
|
||||||
|
return 2
|
||||||
|
case .VMAlreadyRunning:
|
||||||
|
return 2
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +109,7 @@ class RuntimeError: Error, CustomStringConvertible {
|
|||||||
extension RuntimeError : CustomNSError {
|
extension RuntimeError : CustomNSError {
|
||||||
var errorUserInfo: [String : Any] {
|
var errorUserInfo: [String : Any] {
|
||||||
[
|
[
|
||||||
NSDebugDescriptionErrorKey: message,
|
NSDebugDescriptionErrorKey: description,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user