* Use let for the immutable disk image storage attachment
* Don't bind the unused error when catching connection-pool failures
* Report errors thrown inside tart run's fire-and-forget tasks
We were discarding any error thrown inside these unstructured tasks,
which silently hid failures to run the control socket or to start and
stop the VM, and which the compiler now warns about.
Wrap them in an ErrorReportingTask, which spawns the task and reports
any thrown error to stderr, rather than repeating a do/catch at every
call site. An unstructured task spawned from a synchronous context (a
signal handler or SwiftUI action) has no parent to propagate the error
to, so reporting it is the best we can do.
* Avoid blocking SwiftNIO calls in async guest agent connections
The gRPC channel setup in "tart exec" and the MAC address resolver
created a dedicated event loop group and tore both it and the channel
down with the blocking syncShutdownGracefully() and wait(), which are
unavailable from async contexts (the former is an error in the Swift 6
language mode).
Factor the connection out into a withGuestAgentChannel() helper that
uses the process-wide singleton event loop group, so there is no group
to shut down, and closes the channel with the async close().get().
Exposes Apple's macOS 27 guest provisioning API
(VZMacGuestProvisioningOptions) so a macOS guest can be set up
automatically on the first boot after restore.
The flag takes a comma-separated list of key=value pairs mapping 1:1 to
the API properties (fullName, username, password, logsInAutomatically,
enablesRemoteLogin). It is validated to require a macOS 27+ host and a
macOS VM.
The entire user-facing surface is gated behind
'#if arch(arm64) && compiler(>=6.4)' so the flag doesn't appear in help
on toolchains that lack the macOS 27 SDK, while the runtime
'#available(macOS 27, *)' check gates actual use against the host OS.
When built against the macOS 27 (Xcode 27, Swift 6.4) SDK, "tart run"
brings up the VM window but the guest never boots.
Swift's asynchronous main() entry point implicitly starts an executor
that owns the main thread, and as of Swift 6.4 that executor is no
longer backed by the Dispatch main queue. Running an AppKit/SwiftUI
run loop nested inside it via MainApp.main() leaves the main run loop
unable to drain Swift tasks or DispatchQueue.main, so the task that
starts the VM is never scheduled, even though the window itself
(driven directly by AppKit during launch) still appears.
We now keep Root.main() synchronous, so that a command driving a run
loop can own the main thread at the top level, exactly like a plain
SwiftUI app. With AppKit owning the loop again, MainActor tasks and
the Dispatch main queue drain as before. Such commands opt in through
a new MainThreadCommand protocol; everything else keeps running
asynchronously via a detached task and dispatchMain().
Verified that the guest boots again, and that Ctrl+C still stops the
VM gracefully.
* Remove disk v1 support
* fix: address PR review feedback
- add explicit error for legacy disk.v1 media type during pull
- include actionable re-push guidance in runtime error
🤖 Generated with [Codex](https://chatgpt.com/codex)
Co-Authored-By: Codex <codex@openai.com>
* Re-use legacyDiskV1MediaType in error message
---------
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
Restore the applicationDidFinishLaunching method that was accidentally
removed in commit b1e88e1 ("tart run: do not remove 'Edit' menu as its
not present anymore").
That commit intended to remove the Edit menu removal code (since the
menu no longer exists), but also removed the crucial activation code:
- setActivationPolicy(.regular) - tells macOS this is a GUI app
- activate(ignoringOtherApps:) - brings the window to the foreground
Without these calls, the VM runs fine (SSH works) but no window appears
on screen.
Fixes#1181
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Avoid duplicate progress updates in CI logs
* Update Sources/tart/Logging/ProgressObserver.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* ASIF is available only starting from macOS 26 (Tahoe)
* Remove testRawFormatIsAlwaysSupported() test
* Fix testASIFFormatSupport() test to check for macOS 26+
* feat: prioritize pruning of old SHA when pulling updated tags
When pulling a new version of a tagged image (e.g., ghcr.io/cirruslabs/macos-runner:sonoma),
set the access date of the previous SHA to epoch time (1970-01-01). This ensures that the
old SHA will be prioritized for pruning, even if it was accessed more recently than other
cached images.
This helps manage disk space more efficiently by automatically cleaning up superseded
versions of frequently-updated tagged images.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* format
* Review comments
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: Add disk image format selection with ASIF support
* fixed goreleaser-pro
* Fix ASIF disk format compatibility issues
- Use .uncached caching mode for ASIF disks to avoid Virtualization framework compatibility issues
- Improve caching mode selection logic for better maintainability
- Fix compiler warning by changing var to let for attachment variable
This resolves VM startup failures when using ASIF disk format by ensuring proper disk attachment configuration.
* Update goreleaser installation to use tap-specific formula
Change from 'brew install --cask goreleaser-pro' to 'brew install --cask goreleaser/tap/goreleaser-pro' for proper installation from the official goreleaser tap.
* Remove VS Code configuration and add to gitignore
- Remove .vscode/launch.json from repository
- Add .vscode/ to .gitignore to prevent VS Code settings from being tracked
* Implement ASIF disk resize using diskutil
- Add support for resizing ASIF disk images using diskutil image resize
- Detect disk format from VM config and route to appropriate resize method
- Use diskutil image info to get current ASIF disk size and validate resize
- Remove restriction that prevented ASIF disk resizing in Set command
- Add FailedToResizeDisk error case for proper error handling
- Maintain backward compatibility with raw disk resizing
- Add comprehensive size validation to prevent data loss
* Update Sources/tart/Commands/Create.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/DiskImageFormat.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/DiskImageFormat.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Fix test naming and remove redundant test cases
- Rename testFormatArgument to testCaseInsensitivity for clarity
- Remove redundant 'raw' and 'invalid' test cases already covered in testFormatFromString
- Remove testFormatDescriptions test as it's not very useful
Addresses review comment: https://github.com/cirruslabs/tart/pull/1094#discussion_r2152093510
* Remove canCreate property and simplify DiskImageFormat
- Remove canCreate property since it's the same as isSupported
- Remove description property entirely as it's not used
- Fix displayName for RAW format (remove UDIF reference)
- Remove checkDiskutilASIFSupport helper function
Addresses review comments:
- https://github.com/cirruslabs/tart/pull/1094#discussion_r2152109450
- https://github.com/cirruslabs/tart/pull/1094#discussion_r2152115610
- https://github.com/cirruslabs/tart/pull/1094#discussion_r2152124330
* Update Create command validation and help text
- Simplify ArgumentParser help text to let it show possible values automatically
- Remove canCreate validation since property was removed
- Simplify error message for unsupported disk formats
Addresses review comment: https://github.com/cirruslabs/tart/pull/1094#discussion_r2152113480
* Add disk format validation to Run command
- Add validation to ensure ASIF disk format is supported on current system
- Check disk format compatibility before attempting to run VM
Addresses review comment: https://github.com/cirruslabs/tart/pull/1094#discussion_r2152109450
* Use proper namespaced constant for OCI label
- Add diskFormatLabelAnnotation constant in Manifest.swift
- Use org.cirruslabs.tart.disk.format namespace for consistency
- Use variable shadowing instead of new variable name for labels
Addresses review comment: https://github.com/cirruslabs/tart/pull/1094#discussion_r2152163515
* Remove special ASIF caching mode
- Remove .uncached caching mode for ASIF disks
- Use default caching logic for all disk formats
- Testing shows .cached mode works fine on macOS 26.0
Addresses review comment: https://github.com/cirruslabs/tart/pull/1094#discussion_r2152133589
* Improve code structure in VMDirectory
- Use guard let instead of nested if let for better readability
- Reduce nesting in resizeASIFDisk function
- Improve error handling flow
Addresses review comment: https://github.com/cirruslabs/tart/pull/1094#discussion_r2152141916
* diskFormatLabel
* reverted caching mode
* Use PropertyListDecoder
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* tart exec: explain that Tart Guest Agent is required
Also handle decrease the connection timeout to 1 second
and provide a hint to the user.
* execute() can be made private
* Include error.localizedDescription
* Introduce "tart exec" command as an alternative to SSH
* Simplify control socket machinery by using NIO async/await primitives
* No reason to print the "vm" object directly, just refer to it as "VM"
* Log to Apple’s Unified Logging System
* Enable clipboard sharing on macOS too
And document which packages need to be installed on these operating
systems.
* We now use Tart Guest Agent
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* Posibility to add Labels when pushing OCI Image
Example running:
tart push $image ${registry}/org/${image}-testing --labels com.org.revision=testing --labels com.org.repo.buildid=123456
* Fix Linting
Run swift package plugin --allow-writing-to-package-directory swiftformat --cache ignore
* Update Sources/tart/Commands/Push.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/Commands/Push.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/Commands/Push.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/OCI/Manifest.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/Commands/Push.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Update Sources/tart/Commands/Push.swift
* Do not use a variable to store parseLabels() results
* Trim spaces before splitting labels and support empty values
---------
Co-authored-by: Victor Serbu <victors@4psa.com>
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* tart run: introduce --net-softnet-expose
* --net-softnet-expose: add discussion
* --net-softnet-expose: add a note about Softnet restrictions
...and how to disable them.
* LAN → local network
* Better clarify what --net-softnet does
And how --net-softnet-allow can change that behavior.