Compare commits

...
8 Commits
Author SHA1 Message Date
Fedor Korotkov 8ed2ce159f Install Go 2022-06-17 10:33:32 -04:00
Fedor Korotkov 89217c23a2 Fixes for Ventura (#129)
* Fixes for Ventura

Still a noob in SwiftUI and Swift concurrency, but it seems on Ventura a task group is not actually running on main or something. Either way I think this change simplifies things but launching a VM in a task and then just continuing with either VNC or built-in graphics.

* Check VM's state
2022-06-17 10:04:57 -04:00
Fedor Korotkov 0d633de04a Build Tart inside a Tart VM (#127) 2022-06-17 11:18:26 +03:00
Nikolay Edigaryev f59ef2722d OCI: store uncompressed disk size in manifest (#128) 2022-06-17 11:14:28 +03:00
Fedor Korotkov 7759c72a76 Don't use dispatchMain (#123) 2022-06-14 11:31:31 -04:00
Raymond 4cc8a9925a accept larger disk size (#122) 2022-06-10 09:05:57 -04:00
Nikolay Edigaryev b16bbf587a Registry: log upload location when encountering blob pushing error (#121) 2022-06-08 17:23:16 +03:00
Fedor Korotkov 022317bc17 Build Debug Binary (#117)
* Build Debug Binary

Xcode 14 Beta breaks something when building with a production configuration and Tart can't run a VM.

* Fixed path
2022-06-07 18:14:52 +03:00
10 changed files with 88 additions and 50 deletions
+13 -5
View File
@@ -1,14 +1,16 @@
persistent_worker:
labels:
name: Mac-Mini-M1
task:
name: Test
name: Test on Ventura
persistent_worker:
labels:
name: Mac-Mini-M1
build_script: swift test
test_script: swift test
task:
name: Build
only_if: $CIRRUS_TAG == ''
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
build_script: swift build --product tart
sign_script: codesign --sign - --entitlements Resources/tart.entitlements --force .build/debug/tart
binary_artifacts:
@@ -17,7 +19,13 @@ task:
task:
name: Release
only_if: $CIRRUS_TAG != ''
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
env:
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
install_script: brew install go goreleaser/tap/goreleaser-pro
info_script:
- xcodebuild -version
- swift -version
release_script: goreleaser
+3 -3
View File
@@ -7,13 +7,13 @@ builds:
goarch:
- arm64
prebuilt:
path: .build/{{ .Arch }}-apple-macosx/release/tart
path: .build/{{ .Arch }}-apple-macosx/debug/tart
before:
hooks:
- .ci/set-version.sh
- swift build -c release --product tart
- codesign --sign - --entitlements Resources/tart.entitlements --force .build/arm64-apple-macosx/release/tart
- swift build -c debug --product tart
- codesign --sign - --entitlements Resources/tart.entitlements --force .build/arm64-apple-macosx/debug/tart
archives:
- id: binary
+1 -1
View File
@@ -13,7 +13,7 @@ struct Create: AsyncParsableCommand {
var fromIPSW: String?
@Option(help: ArgumentHelp("Disk size in Gb"))
var diskSize: UInt8 = 50
var diskSize: UInt16 = 50
func validate() throws {
if fromIPSW == nil {
+30 -35
View File
@@ -31,51 +31,46 @@ struct Run: AsyncParsableCommand {
func run() async throws {
if recovery && vnc {
print("You can't run in recovery and use VNC!")
Foundation.exit(1)
Foundation.exit(1)
}
let vmDir = try VMStorageLocal().open(name)
vm = try VM(vmDir: vmDir)
await withThrowingTaskGroup(of: Void.self) { group in
if vnc {
group.addTask(operation: {
do {
print("Waiting for the VM to boot...")
let resolvedIP = try await IP.resolveIP(vm!.config, secondsToWait: 60)
guard let ip = resolvedIP else {
throw IPNotFound()
}
let url = URL(string: "vnc://\(ip)")!
print("Opening \(url)")
NSWorkspace.shared.open(url)
} catch {
print("Failed to get an IP for screen sharing: \(error)")
}
})
}
group.addTask {
do {
try await vm!.run(recovery)
Task {
do {
try await vm!.run(recovery)
Foundation.exit(0)
} catch {
if error.localizedDescription.contains("Failed to lock auxiliary storage.") {
print("Virtual machine \"\(name)\" is already running!")
} else {
print(error)
}
Foundation.exit(1)
Foundation.exit(0)
} catch {
if error.localizedDescription.contains("Failed to lock auxiliary storage.") {
print("Virtual machine \"\(name)\" is already running!")
} else {
print(error)
}
}
if noGraphics || vnc {
dispatchMain()
} else {
runUI()
Foundation.exit(1)
}
}
if vnc {
do {
print("Waiting for the VM to boot...")
let resolvedIP = try await IP.resolveIP(vm!.config, secondsToWait: 60)
guard let ip = resolvedIP else {
throw IPNotFound()
}
let url = URL(string: "vnc://\(ip)")!
print("Opening \(url)")
NSWorkspace.shared.open(url)
} catch {
print("Failed to get an IP for screen sharing: \(error)")
}
while !(vm?.inFinalState ?? false) {
try await Task.sleep(nanoseconds: 1_000_000)
}
} else if !noGraphics {
runUI()
}
}
private func runUI() {
+1 -1
View File
@@ -17,7 +17,7 @@ struct Set: AsyncParsableCommand {
var display: VMDisplayConfig?
@Option(help: .hidden)
var diskSize: UInt8?
var diskSize: UInt16?
func run() async throws {
do {
+23
View File
@@ -3,15 +3,38 @@ import Foundation
let ociManifestMediaType = "application/vnd.oci.image.manifest.v1+json"
let ociConfigMediaType = "application/vnd.oci.image.config.v1+json"
// Annotations
let uncompressedDiskSizeAnnotation = "org.cirruslabs.tart.uncompressed-disk-size"
struct OCIManifest: Codable, Equatable {
var schemaVersion: Int = 2
var mediaType: String = ociManifestMediaType
var config: OCIManifestConfig
var layers: [OCIManifestLayer] = Array()
var annotations: Dictionary<String, String> = Dictionary()
init(config: OCIManifestConfig, layers: [OCIManifestLayer], uncompressedDiskSize: UInt64? = nil) {
self.config = config
self.layers = layers
if let uncompressedDiskSize = uncompressedDiskSize {
annotations = [
uncompressedDiskSizeAnnotation: String(uncompressedDiskSize)
]
}
}
func digest() throws -> String {
try Digest.hash(JSONEncoder().encode(self))
}
func uncompressedDiskSize() -> UInt64? {
guard let value = annotations[uncompressedDiskSizeAnnotation] else {
return nil
}
return UInt64(value)
}
}
struct OCIManifestConfig: Codable, Equatable {
+2 -2
View File
@@ -184,8 +184,8 @@ class Registry {
body: fromData)
if putResponse.status != .created {
let body = try await postResponse.body.readTextResponse()
throw RegistryError.UnexpectedHTTPStatusCode(when: "pushing blob (PUT)", code: putResponse.status.code,
details: body ?? "")
throw RegistryError.UnexpectedHTTPStatusCode(when: "pushing blob (PUT) to \(uploadLocation)",
code: putResponse.status.code, details: body ?? "")
}
return digest
+10 -1
View File
@@ -77,8 +77,17 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
try data.write(to: expectedIPSWLocation, options: [.atomic])
return expectedIPSWLocation
}
var inFinalState: Bool {
get {
virtualMachine.state == VZVirtualMachine.State.stopped ||
virtualMachine.state == VZVirtualMachine.State.paused ||
virtualMachine.state == VZVirtualMachine.State.error
}
}
init(vmDir: VMDirectory, ipswURL: URL?, diskSizeGB: UInt8) async throws {
init(vmDir: VMDirectory, ipswURL: URL?, diskSizeGB: UInt16) async throws {
let ipswURL = ipswURL != nil ? ipswURL! : try await VM.retrieveLatestIPSW();
// Load the restore image and try to get the requirements
+4 -1
View File
@@ -117,8 +117,10 @@ extension VMDirectory {
// Read VM's compressed disk as chunks
// and sequentially upload them as blobs
let disk = try FileHandle(forReadingFrom: diskURL)
var diskReadBytes: UInt64 = 0
let compressingFilter = try InputFilter<Data>(.compress, using: .lz4, bufferCapacity: Self.bufferSizeBytes) { _ in
let data = try disk.read(upToCount: Self.bufferSizeBytes)
diskReadBytes += UInt64(data?.count ?? 0)
progress.completedUnitCount += Int64(data?.count ?? 0)
@@ -146,7 +148,8 @@ extension VMDirectory {
let ociConfigDigest = try await registry.pushBlob(fromData: ociConfigJSON)
let manifest = OCIManifest(
config: OCIManifestConfig(size: ociConfigJSON.count, digest: ociConfigDigest),
layers: layers
layers: layers,
uncompressedDiskSize: diskReadBytes
)
// Manifest
+1 -1
View File
@@ -68,7 +68,7 @@ struct VMDirectory {
try newVMConfig.save(toURL: to.configURL)
}
func resizeDisk(_ sizeGB: UInt8) throws {
func resizeDisk(_ sizeGB: UInt16) throws {
if !FileManager.default.fileExists(atPath: diskURL.path) {
FileManager.default.createFile(atPath: diskURL.path, contents: nil, attributes: nil)
}