Unregister the stdin readabilityHandler when availableData returns empty:
a closed pipe fd stays permanently readable, so Foundation re-invokes the
handler in a tight loop (fstat + zero-byte read) at 100% of one core for
the rest of the command's lifetime.
Fixes#1280
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Publish Tart to openai/homebrew-tools
* Write Tart formula under Formula directory
* Use macOS 26 runners
* docs: install Tart tools from OpenAI tap
* Add required GitHub Actions test check
* Fix hosted tests and notarization credentials
GoReleaser's Homebrew template always emits a bare `depends_on :macos`
for macOS-only formulae. Combining that with the `depends_on :macos =>
:ventura` line injected via custom_block triggers a Homebrew deprecation
warning on `brew upgrade`:
Warning: Calling `depends_on :macos` with `depends_on macos:` is deprecated! Use `depends_on :macos` with `depends_on macos:` inside an `on_macos` block instead.
Please report this issue to the cirruslabs/homebrew-cli tap (not Homebrew/* repositories), or even better, submit a PR to fix it:
/opt/homebrew/Library/Taps/cirruslabs/homebrew-cli/tart.rb:22
Declaring the version constraint inside an `on_macos` block is the form
Homebrew recommends and silences the warning without changing behavior
(still macOS-only, Ventura or newer).
* 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>
* [docs] publish press release about licensing violation
Highlighting that this is an exceptional case since the company did contact us about a license, and we explicitly declined due to conflict of interests.
* Fixed linting
* Use ghcr.io/cirruslabs/macos-tahoe-base:latest
* CI: "Test on Sequoia" can be named just "Test"
* integration-test: can use latest requests now that the bug is fixed
* 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>
"How Tart is different from Anka" is not a question, and thus should not have a question mark. This PR proposes to change it into a question, but an equally valid fix is to drop the question mark.
* 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>
* Iterate over Orchard Architecture description
* Document Orchard Controller customization (e.g. --listen-ssh)
* New section: "Using Orchard CLI"
* Fix Markdown unordered list indentation
* Fix "fenced code blocks should have a language specified"
* the context → a context
* Clarify different port
* Simplify labels explanation
* Studios → Studio
* Better explain resources
* crate → create
* only to place → only place
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Surround "Using resources when creating VMs" header by blank lines
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.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>
* docs(FAQ): document unsupported DHCP client identifiers
* New section "Resolving the VMs IP with bridged networking"
And a more clearer explanation of what "tart ip" does.
* Remove extraneous space in ` --resolver=arp`
* Better section name
* Add a note about Linux talkativeness
* Explain "talkativeness" a bit better
* Build universal binary instead of architecture dependent
* Added universal_binaries stage to goreleaser
* Fixed paths to compiled binary in .cirrus.yml
* Revert changes in .cirrus.yml, use builtin venv module instead of virtualenv only
* 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.
* Registry: limit the text output on unexpected status code
* pullBlob(): limit channel read-out on error to 4 KiB
* No need to always read channel until end
This was introduced in https://github.com/cirruslabs/tart/pull/284
because we were blocking in "urlSession(_ session: URLSession, dataTask:
URLSessionDataTask, didReceive data: Data)", which we don't do anymore.
* Fetcher.fetch(): remove "progress" argument as we don't need it anymore
Otherwise we start to periodically get RST's from GitHub, possibly
because of too many connection opens, which has an effect of cancelling
previously received bytes.
These RST's can be observed in tcpdump/Wireshark or Console, emitted
from the libusrtcp.dylib library, com.apple.network subsystem, for the
Tart process:
>tcp_input [C59.1.1.1:3] flags=[R] seq=1805021659, ack=0, win=0 state=CLOSED rcv_nxt=1805021659, snd_una=1752355607
You can also observe the "Received Bytes" in "Activity Monitor" for
the Tart process while pulling ghcr.io/cirruslabs/macos-runner:sequoia,
and this value will periodically decrease.
* DiskV2: avoid allocating zero chunk on each zeroSkippingWrite() call
* Increase hole granularity size from 64 KiB to 4 MiB
* Fetcher: never write to disk, thanks to URLSessionDataDelegate
* Get a fresh instance of executor for each benchmark invocation
And don't pre-initialize all of the executors at once, as this
might reach the maximum number of VMs limit in case we want to
test multiple Tart executors.
* Run benchmarks on Tart with different --root-disk-opts options
* Fix TestTart
* benchmark fio: introduce --prepare command-line argument
To be able to specify --prepare='sudo purge && sync', similarly to
Hyperfine[1].
[1]: https://github.com/sharkdp/hyperfine
* Benchmark Tart with --root-disk-opts=caching=cached separately too
* Add Ars Technica recommended benchmarks
* Tart executor: log SSH session standard output and standard error
* Reduce file I/O size from 16 to 10 GB to avoid "No space left on device"
* Remove random writing tests to make space for more read/read-write tests
* Add some "randrw"-style fio benchmarks
* Show latency in benchmark results
* Add sync benchmark and show read/write/sync latency
* README.md: add new benchmark results
Was looking into performance and was wondering about Game Mode on Sonoma.
This change is unrelated. Just found they have a category for tools like Tart.
To make sure we won't prune then immediately after. Useful for when scenarios similar to Cirrus CLI when we make sure that several images are up-to-date before every request for task execution.
* Revert "Lowercase `tart.app` (#751)"
This reverts commit a9e2a19015.
* Optimize DiskV2 deduplication logic
In case we cloned `disk.img` from a local image, check if data at offset has the expected contents already.
* Hole punch only if needed
* Calculate hash only if needed
* subdataChunks optimization
* Reapply "Lowercase `tart.app` (#751)"
This reverts commit e74e9c845a.
* format
* Save at least 1GB on deduplication logic
* Build separately
* Revert "subdataChunks optimization"
This reverts commit e59382aeba.
* Another optimization
* Removed debug log
* reformat
* Revert "Hole punch only if needed"
This reverts commit 8c569fc5
* VZDiskImageSynchronizationMode's "description" field is a dead code
* Re-use the VZDiskImageSynchronizationMode extension
* tart run: replace --sync with --root-disk-opts
* VM: support root disk synchronization mode on macOS
* Support setting root disk synchronization mode
Adds a new VMConfig parameter (tart get / tart set) called 'sync' which
can be set to 'full' (default), 'fsync', or 'none', corresponding with
the values of VZDiskImageSynchronizationMode and allowing a tradeoff
between data integrity and speed.
* Remove unused import
* Fix formatting
* Make root disk sync behaviour a commandline option
* tart pull: try to re-use APFS blocks by cloning the base image
* Punch a hole when a zero chunk is detected
* Properly retrieve errno when hole punching operation fails
* tart pull: do not retry on RuntimeError
* Ensure that the holes we're about to punch are FS block size-aligned
* VMDirectory: remove unused static variables
* tart pull: log if we've found an image to deduplicate against
* Do not prematurely read contents from disk
* Only consider candidates with deduplicatedBytes more than 0
* APFS reuse UX/DX improvements (#870)
* Show how much deduplication happening
Improvement to the APFS deduplication logic which checks whether a disk image file `mayShareFileContent` with some other file, and then we put a custom attribute to track the deduplication since there is no way to get this information from APFS itself.
It's not 100% accurate but given that OCI cache is immutable the actual disk usage can only be lover than that.
* Use string attribute
* Update Sources/tart/URL+Prunable.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Added SizeOnDisk colume
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
This is accomplished by saving the OCI VM image manifests on "tart pull"
in "manifest.json" file and then using them on successive "tart pull"'s
to find the best candidate that results in the most de-duplication,
measured in bytes.
* Support remote VM names in --disk command-line argument
* tart set: introduce "--disk" to support replacing VM's disk contents
* Complete the code comment
For macOS this brings up a dialog, asking the user if they are sure
they want to shut down, which makes this less useful for automated
graceful shutdowns, but it may behave better on Linux, and there
might be ways to instruct macOS to not ask the user, so it's still
a nice feature, and aligns with the SIGUSR1 for suspend, and SIGINT
for non-graceful shutdown.
* Drop Monterey Support
People will still be able to run and SSH into Monterey VMs or use VNC but pointing devices/keyboard won't work.
Fixes#841
* Fixed x86 build
The latest release has this error:
> error: Project not found. Please check that you entered the project and organization slugs correctly.
Which seems indicating that Auth is broken and we are getting 404? In the Sentry Settings I didn't find any token which I find strange. So I created one and re-encrypted.
* Give Virtualization.framework a chance to stop the VM on tart stop
We were letting the CancellationError bubble up all the way until
it terminated app, which meant we didn't hit the shutdown code
in run(), stopping the VM and the network.
We now catch CancellationError and proceed to gracefully shut down.
We only stop the VM if it's still running, as a VM that has been
stopped via the menu can't be stopped again.
* Gracefully shut down VM when Tart is quit via menu
Normally the quit action will result in AppKit calling exit(),
but we want to gracefully shut down the VM, so we use the same
path as for closing of the VM window, namely signal our own
process with SIGINT or SIGUSR1.
If that doesn't work we let AppKit terminate as before.
This fixes the "Warning: NSActivity <_NSActivityAssertion:
0x600001f785a0> was ended multiple times" warning seen on
the console when quitting Tart via the menu.
* Activate Tart after application finishes launching
This ensures that the VM window has been shown by the time we
activate, so that we consistently activate and bring the VM
window to the front.
* Document how to mount the shared directory on Linux at boot time
* Use admonition
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* add VM completion for run command
* add VM completion for stop command
* create ShellCompletions utilities
* add shell completions to some commands
* add shell completion for fqn command
* run command: fix tiny typo
* add shell completion for get command
* more shell completions
* remove unnecessary `try`
* refactor ShellCompletions file
The URLSession async/await functions do not report progress through
the normal URLSessionTaskDelegate callbacks, as reported in:
https://developer.apple.com/forums/thread/723015
We don't want to use URLSession.bytes, as that results in a much
slower download speed compared to URLSession.download, but we can
work around the lack of progress callbacks by observing the
progress on the URLSessionTask itself.
Fixes#767
We wrap the installation with a withTaskCancellationHandler, which
ensures that the SIGINT signal handling code in main() will trigger
a cancellation of the installer.
As the VZMacOSInstaller must be both created and interacted with
on the VM's queue, which in our case is the main queue, we need
to move the logic to a separate function tagged with @MainActor.
This makes sense either way, as it cleans up the code a bit.
* Improve macOS app integration
Tart is now a proper application bundle, with the name and icon
declared in the Info.plist, which we were missing.
This also allows us to declare the app as LSBackgroundOnly
as a default, which means that 'tart create' and similar
background commands will not show the application icon in
the dock, while 'tart run' will, thanks to it overriding
the activation policy of the app.
For now the logic of creating the Tart.app bundle is duplicated
between the CI packaging scripts and the run-signed.sh script.
Now that these scripts are growing, it makes sense to look
at whether we can share the logic somehow, e.g. by building
the application bundle directly during build, and packaging
that, instead of creating it as a post install step.
* Integration tests: fix DockerContainer import
To work around the breaking change in 4.0.0,
see https://github.com/testcontainers/testcontainers-python/commit/383b12e9d63b4b105c65e587e87c380355871a36.
* .cirrus.yml(Release (Dry Run)): no need to install Sentry CLI
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
Right now we show only actual size of files on disk which excludes empty blocks of the recently introduced sparced format in #671. This makes impossible to get info about disk size that we just set via `tart set`.
Here is an example of `tart list` output before the change:
```
Source Name Size State
local sonoma-base 22 stopped
local sonoma-vanilla 18 stopped
local sonoma-xcode 67 stopped
local ubuntu 1 stopped
oci ghcr.io/cirruslabs/macos-sonoma-base:latest 22 stopped
oci ghcr.io/cirruslabs/macos-sonoma-base@sha256:16c1593bbaf787b20b3c0bc094c5b6baf71c937d22c2e4596da85ac55c92e6cc 22 stopped
oci ghcr.io/cirruslabs/macos-sonoma-vanilla:14.3 17 stopped
oci ghcr.io/cirruslabs/macos-sonoma-vanilla@sha256:23c4e853d48d00a4333346d66a32b2b5aad900cc0dc10e7ecb9dbe67b6f587f4 17 stopped
oci ghcr.io/cirruslabs/macos-sonoma-xcode:latest 67 stopped
oci ghcr.io/cirruslabs/macos-sonoma-xcode@sha256:d0cb8d01424a68b89e0f16f5371bf2152b2c115bd886341a6ba8da42121d1f41 67 stopped
oci ghcr.io/cirruslabs/ubuntu:22.04 1 stopped
oci ghcr.io/cirruslabs/ubuntu@sha256:037763feb7a15d6077edeb7a097738c34313637d16036764b4c196d28d8b429c 1 stopped
```
And here is the output after the change:
```
Source Name Disk Size State
local sonoma-base 50 22 stopped
local sonoma-vanilla 50 18 stopped
local sonoma-xcode 90 67 stopped
local ubuntu 20 1 stopped
oci ghcr.io/cirruslabs/macos-sonoma-base:latest 50 22 stopped
oci ghcr.io/cirruslabs/macos-sonoma-base@sha256:16c1593bbaf787b20b3c0bc094c5b6baf71c937d22c2e4596da85ac55c92e6cc 50 22 stopped
oci ghcr.io/cirruslabs/macos-sonoma-vanilla:14.3 50 17 stopped
oci ghcr.io/cirruslabs/macos-sonoma-vanilla@sha256:23c4e853d48d00a4333346d66a32b2b5aad900cc0dc10e7ecb9dbe67b6f587f4 50 17 stopped
oci ghcr.io/cirruslabs/macos-sonoma-xcode:latest 90 67 stopped
oci ghcr.io/cirruslabs/macos-sonoma-xcode@sha256:d0cb8d01424a68b89e0f16f5371bf2152b2c115bd886341a6ba8da42121d1f41 90 67 stopped
oci ghcr.io/cirruslabs/ubuntu:22.04 20 1 stopped
oci ghcr.io/cirruslabs/ubuntu@sha256:037763feb7a15d6077edeb7a097738c34313637d16036764b4c196d28d8b429c 20 1 stopped
```
Additionally, `tart get` will print actual size with a 3 decimal point precision which will help to track growth in disk images for our templates.
`tart get` before:
```
CPU Memory Disk Display State
4 8192 67 1024x768 stopped
```
`tart get` after:
```
CPU Memory Disk Size Display State
4 8192 90 67.333 1024x768 stopped
```
5x jump in price from Gold to Platinum is a bit too high. Most of known large deployments target 200-300 hosts. Let's accommodate such users by lowering Platinum tier.
From a discussion in #728 it appeared that having both `--graphics` and `--no-graphics` is a bit confusing.
`--graphics` was introduced in #248 to support having both VNC and UI for debugging Packer plugin in cirruslabs/packer-plugin-tart#21. This is because `--vnc` flag has a side effect of hiding UI which I think was wrong in retrospective. One can run `tart run --vnc --no-graphics`. In most of the cases this is automated via Alfred or something like that.
Now we have so many arguments that IMO it's worth to remove `--graphics` for overall consistency in arguments: everything is enabled by default and can be disabled via `--no-*` flags.
* Build x86 binary
To support Linux VMs on Intel aka x86_64
* Fixed paths and formatting
* Unique IDs
* Fixed Goreleaser
* Skip creation integration test for now
* import
* Reenable create test
* Revert "Reenable create test"
This reverts commit 4c947c1f0e.
* Reenable create test
* tart create --linux: allow scaling VM down to 1 CPU and 256 MiB
* Revert "tart create --linux: allow scaling VM down to 1 CPU and 256 MiB"
This reverts commit 7a31443eea.
* Check minimum CPU and memory sizes in "tart set"
* Print put errors from Docker Helpers
* Update Sources/tart/Credentials/DockerConfigCredentialsProvider.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Check output data is not empty
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Package tart binary into app bundle when running via run-signed.sh
This is what happens when installing the tart application package
as built by CI. We should stay as close as possible to the install
situation during development, so that we get bug/behavior parity.
For example, an app bundle behaves differently than a standalone
executable when it comes to bringing up a Dock icon for the app.
* Set activation policy to prohibited when starting in no graphics mode
This ensures that the Dock icon is hidden.
* Support mounting remote archives
Allow to pass an HTTPS link instead of a local path to `tart run --dir` argument. HTTPS link should point to a gzipped Tar archive aka `*.tar.gz` file.
In this situation Tart will download an archive by the link if necessary, will cache it and will unarchive it into a temporary folder inside `$TART_HOME` to be mounted to the VM.
This use case is useful for mounting something external that updates faster than the VM itself. For example, GitHub Actions Runner installation.
* Don't use async/await APIs to prevent from deadlocks because of the MainActor thing
* Prefer cached data
* Moved comment
* Fix URLCache caching files in memory instead of on-disk (#622)
* Fix URLCache caching files in memory instead of on-disk
* Fix disk capacity typo
* Moved log
* Moved fetching logic to `DirectoryShare#createConfiguration` method
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Document XL Cirrus Runners
Also tried to put everything about Cirrus Runners in one place rather than having the information spreaded between https://tart.run and https://github.com/apps/cirrus-runners.
Plus updated docs to use Sonoma.
* Apply suggestions from code review
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Improved format for fast and efficient pulls from remote OCI-registry
* Tests: introduce fileWithRandomData() helper function
* Remove useless continuation
* --concurrency should be an option, not an argument
* --v2-disk-format → --old-disk-format and use the new V2 by default
* Reduce LZ4 buffer size from 64 to 4 MiB
* --old-disk-format → --disk-format=...
* Support wildcards in `credHelpers`
With #591 `tart pull` fails when for example you have `ecr-login` set as the default `credsStore` but you try to pull our images from `ghcr.io`.
This change reverts #591 and instead supports regex in `credHelpers`. This is not supported by Docker itself but highly demanded in https://github.com/docker/cli/issues/2928
I think it's fine to support it for Tart.
Additionally this change bumps the minimum host macOS version to Ventura in order to bring `Regex`. Yes, `Regex` only supported in Swift for macOS 13+ 🤦♂️I think it's fine in the light of Sonoma release and Tart 2.0.0.
* Removed Monterey mentions from docs
This way for #581 we don't need to specify a fully quialified URL and can simply use the following `~/.docker/config.json`:
```json
{
"credsStore": "ecr-login"
}
```
Related to https://github.com/docker/cli/issues/2928
* GC avoidance and tmpDeterminstic
* change tmpDeterministic to use hashing
- temporaryDeterministic() now takes in a key and hashes it
- creates directory with the hash
* Update Sources/tart/VMStorageOCI.swift
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
Seems in 2.0.0 release they broke looping. Right now animation on https://tart.run/ is always looping even without `loop` property. I was able to disable it, so I just downgraded to the last known working version.
* Don't delete an initiator of pruning
Sometimes people have an image that is greater than half of the disk itself. In that case such image will be pulled and prunned right away.
This change makes sure that an image that is being cloned from is not pruned right away.
* Resolve symbolic links
* Allow mounting a single directory without a name
To utilize `VZSingleDirectoryShare` which seems more stable than `VZMultipleDirectoryShare`.
We've been having reports from users that mounted directories occasionally return "no such file" errors when building large projects. I took a stab at reproducing the issue by running https://github.com/devMEremenko/XcodeBenchmark in a mounted directory:
```bash
tart run --dir=workdir-test:~/workspace-temp/XcodeBenchmark ventura-xcode
```
And I was able to reproduce the "no such file" error on the first try! After looking into the issue I decided to try `VZSingleDirectoryShare` as this PR changes and to my pleasant surprise it all worked like a charm the next run. So it seems there is a bug in `VZMultipleDirectoryShare` integration with virtiofs. Since in most cases users only mount a single directory it makes sense to allow doing it wihtout providing a `name`.
So now it will be possible to run the following command:
```bash
tart run --dir=~/workspace-temp/XcodeBenchmark ventura-xcode
```
Which will make `~/workspace-temp/XcodeBenchmark` available under `/Volumes/My Shared Files/` without any intermediate directories.
* Reformat
* Updated description
* Upload debug files to both Sentry projects
Apparently it's not possible to share debug files cross projects. So let's upload to both of them. To one we use in production and one we use for testing.
* Use environment variable
* Suspend/resume support
* Use RuntimeError.SuspendFailed for consistency's sake
* Add a comment about "Running" field deprecation
* Use compute credits
* Use Mac-specific input devices and remove --no-{audio,entropy}
* Suspend the VM when closing window and running with --suspendable
* Snapshotting Improvements (#539)
* Don't use static field for arguments
It throws a runtime error
* Fixed suspendability
* Lazy generation of new MAC addresses
To support cloning on suspended VMs
* Refactored
* formatted
* Configurable signal for window closing
* reformatted
* Don't generate MAC only for suspended VMs
* Removed misleading comment
* Reverted
* Lock while a suspendable VM is starting
* Lock on TART_HOME
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* Optimistically check if we need to do anything on a pull
Right now on a pull we always acquire a lock for a registry host. This is problematic because, for example, host can be pulling `ghcr.io/cirruslabs/macos-ventura-xcode:15-beta-2` image when a new request will come to pull `ghcr.io/cirruslabs/macos-ventura-xcode:latest` if needed.
In this situation, even though `ghcr.io/cirruslabs/macos-ventura-xcode:latest` is already cached and linked, `tart pull` will wait for a lock.
This change optimistically check if there is something to do at all before acquiring a lock.
* Fix linter errors
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Replace mentions of Monteray with Ventura in quickstart guide
They seem to just be leftovers from previous versions of the docs, so a small cleanup could be helpful.
* Update quick-start.md
* Update quick-start.md
* Update quick-start.md
* Add PITS Global Data Recovery Services to the list of companies
* Remove unnecessary change at line 48
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* Update PITS Global Data Recovery logo location
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
Converting all the images to WebP reduced the size more than 2x.
Plus enabled `privacy` plugin for mkdocs so the site will bundle remote resources. It appeared that loading Roboto font dynamically was adding 700ms to the page load.
* tart push: allow pushing OCI VMs from the cache too
* Check for RemoteName earlier
* Refactored pushing of OCI images under new tag (#466)
* Refactored pushing of OCI images under new tag
* Fixed compilation
---------
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* Build .pkg again
Last time it broke in #441. The theory is that notarization of the `.pkg` before after notarizaation of the binary was breaking validation on Apple side.
This attempt does build the .pkg before we do all the dance with gon ang goreleaser.
* codesign deep
* Move back to before hooks
* tart list: show if the VM is running or not
* Boolean "running" field instead of "state", similarly to "tart get"
* Re-use VMDirectory.running() in "tart get"
* support ZVirtioConsoleDevice for linux vm, user can control the vm via serial port with screen command
* cleanup code, fix indent
* add --serial option to tart
* remove serial in vmconfig, fix Serial.swift
---------
Co-authored-by: peternguyen93 <peternguyen9321@gmail.com>
Co-authored-by: peter <peter@starlabs.sg>
Logic for building .tar.gz should be the same as for 1.0.0 and only after that a .pkg is build to make sure it's not interfering. Let's try this before disabling building .pkg all together.
* Remove all binary attributes
To fix "code has no resources but signature indicates they must be present" which is caused by attributes
* Also verify signature for testing
* Do everything at once
* Relicensed under Fair Source License
As announced in https://tart.run/blog/2023/02/11/changing-tart-license/
* Proper encoding
* Terms of service and subscription agreement template
* No need for an announcement
* Update license in brew
* Show installation count on landing page
Fixes#410
* Update docs/theme/overrides/home.html
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
---------
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* JSON output for `get` and `list` commands
In the light of the upcoming `1.0.0` release and stabilizing of the API, let's introduce some breaking changes for the good.
Removed all the `--cpu`, `--memory`, `--disk` and `--display` flags and replaced with a single `--json` flag for machine-readable output.
Added `--json` option to the `list` command to output a single JSON list. Notably removed `--quite` flag since it seemed unnecessary.
Fixes#297
* Added Size to `list` output
Fixes#379
* Added running state to `get`
Fixes#393
* Better signature
* Updated tests
* More test fixes
* Introduce "tart import" and "tart export" commands
* Use AppleArchive instead of ZIP and simply {ar,un}chive the VM dir
* Fix formatting
* Link to Apple's docs
* Print "importing..." and "exporting..." lines
* Draft
* Keep media queries at the end + get rid of dduplicate values
* Open external links in a separate window
* Minors
* Remove paragraph from Hero + add links
* Update img
* Remove unnecessary
* Layout adjustment
* Change animated logo position
* Shrink space between sections in mobile
* Move to mkdocs for docs
* Deploy task
* Custom landing page
* Setup Google Analytics
* Cropped animation
* Update docs/theme/overrides/home.html
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Direct to website and discussions
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Ditch Foundation.exit()'s where feasible
* Sentry integration
* SwiftFormat
* Upload symbols and sources to Sentry
* Use Sentry Releases
* Do not use ExitCode exceptions
* Clarify why we need CustomNSError extension
* Adds Option to filter VMs by 'source'
* Tidy up columns for table on some Terminals
* Don't show headers if there's an error.
* Fixes linting errors.
* Update Sources/tart/Commands/List.swift
- removing the short name for now, as we don't know if we may need -s in the future
- removing the capitalization for the word "source" and adding an example instead
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* source should be an Optional flag. Reverts header change.
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* OCI: pull blobs via file
* Explain why we delete the downloaded file after opening a handle to it
* Further abstract away ways to fetch a URLRequest
* No need to cast HTTPURLResponse to HTTPURLResponse
* Fetcher: no need to be a delegate anymore
* Fetcher.fetch() can be made static
* Improve RemoteName parser
* Remove Parsing import
* Permit namespace components to contain separators, but no more than one
* Add testNoPathTraversal
* Set line-buffered output for stdout
* tart run: introduce --graphics
* Update Sources/tart/Commands/Run.swift
Co-authored-by: Pete Goldsmith <peter.n.goldsmith@gmail.com>
* Update Sources/tart/Commands/Run.swift
Co-authored-by: Pete Goldsmith <peter.n.goldsmith@gmail.com>
Co-authored-by: Pete Goldsmith <peter.n.goldsmith@gmail.com>
* tart clone: clone VM and generate MAC under a file lock
* Lock concurrent "tart pull"'s for the same host
* Config: ensure Tart's home and cache directories always exist
* Improve RemoteName parser
* Remove Parsing import
* Permit namespace components to contain separators, but no more than one
* Add testNoPathTraversal
* OCI blob compression: fix Data memory leak when using InputFilter
* Rename mappedDiskOffset to mappedDiskReadOffset
* Update progress.completedUnitCount differently
* Do not include Content-Range for monolithic uploads
Some registries still assumes it's a chunked upload and verifies the "chunk" size which is too big.
* Update Sources/tart/OCI/Registry.swift
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com>
* Softnet: an alternative to built-in NAT with better isolation
* Softnet: increase socketpair(2) socket buffer sizes to 1 MiB
* Pass VM's FD and MAC address to the Softnet
* Softnet: implement graceful shutdown
* Bring back the dispatchMain() and task cancellation
* tart pull: check for cancellation when pulling response body
* Don't dispatchMain() in withTaskCancellationHandler()
* Move VNC URL opening logic into VNCWrapper.open()
* 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
* tart clone: always re-generate MAC-address
This is not really an issue for non-enterprise users[1].
[1]: https://github.com/cirruslabs/tart/issues/20#issuecomment-1136944455
* Atomic tart {create,clone,pull} operations using rename(2)
* Print a nicer error message when attempting to double-run a VM
* tart clone: bring back the old MAC-address generation logic
* Ensure VMDirectory.temporary() will be deleted on failure
* tart pull: introduce --populate-cache flag
* VMStorageOCI: introduce cache() method
* Review comments (#107)
* Rename SetCommand back to Set
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* Terminate when Ctrl+C is encountered when entering credentials
* Handle Ctrl+C by catching SIGINT and converting it to task cancellation
* maxCharacters instead of (buf.count - 2)
* readStdinCredential: fix maxCharacters to be 255
* "user" variable should be named "credential"
* tart pull: be more liberal when accepting local image as argument
* Update Sources/tart/Commands/Pull.swift
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
* Single sentence and consistent capitalization
Co-authored-by: Fedor Korotkov <fedor.korotkov@gmail.com>
- [How to Create an Issue/Enhancement](#how-to-create-an-issueenhancement)
- [Style Guidelines](#style-guidelines)
- [Pull Requests](#Pull-Requests)
## How to Build
1. Fork the repository to your own GitHub account
2. Clone the forked repository to your local machine
3. If using Xcode, use from Xcode 15 or newer
4. Run ./scripts/run-signed.sh from the root of your repository
```bash
./scripts/run-signed.sh list
```
## How to Create an Issue/Enhancement
1. Go to the [Issue page](https://github.com/openai/tart/issues) of the repository
2. Click on the "New Issue" button
3. Provide a descriptive title and detailed description of the issue or enhancement you're suggesting
4. Submit the issue
## Style Guidelines
1. Code should follow camel case
2. Code should follow [SwiftFormat](https://github.com/nicklockwood/SwiftFormat#swift-package-manager-plugin) guidelines. You can auto-format the code by running the following command:
```bash
swift package plugin --allow-writing-to-package-directory swiftformat --cache ignore .
```
## Pull Requests
1. Provide a detailed description of the changes you made in the pull request
Perhaps, the easiest, but not the most comprehensive way to tell what's going on with Tart is to use the [`time(1)`](https://ss64.com/mac/time.html) command.
In the example below, you will run `tart pull` via `time(1)` to gather generalized CPU, I/O and memory usage metrics:
```shell
/usr/bin/time -l tart pull ghcr.io/cirruslabs/macos-tahoe-base:latest
```
**Note:** you need to specify a full path to `time(1)` binary, otherwise the shell's built-in `time` command will be invoked, which doesn't have the `-l` command-line argument.
**Note:** The `-l` command-line argument makes `time(1)` return much more useful information, for example, maximum memory usage.
When running the command above, you'll see the `tart pull` output first as it pulls the image, and then the `time(1)` output, which will be printed once the Tart process finishes:
```
172.17 real 10.29 user 8.36 sys
353796096 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
23838 page reclaims
35 page faults
0 swaps
0 block input operations
0 block output operations
8 messages sent
8 messages received
0 signals received
146 voluntary context switches
222950 involuntary context switches
39683070975 instructions retired
27562035252 cycles elapsed
170920448 peak memory footprint
```
From the output above, you can tell that `tart pull` spent nearly 90% of time off-CPU (`real` > `user` + `sys`), which means that Tart was mostly waiting for the I/O (be it a network or disk), instead of decompressing disk layers or doing other useful computations.
## Using `xctrace(1)`
[`xctrace(1)`](https://keith.github.io/xcode-man-pages/xctrace.1.html) is a `.trace` format recorder for the [Instruments](https://en.wikipedia.org/wiki/Instruments_(software)) app, which yields much more powerful insights compared to `time(1)`. For example, it can tell which Tart functions spent the most time on the CPU, thus allowing the Tart developers to further optimize these functions.
To use it, make sure that [Xcode](https://developer.apple.com/xcode/resources/) is installed. If you're installing Xcode for the first time on the machine, you'll need to launch it once and click the blue "Install" button. There's no need to choose any platforms except for the macOS.
Once done, you can create a CPU profile of `tart pull`:
Now that `xctrace(1)` is running, you'll see the `tart pull`-related output first, and once finished, the following line will appear:
```
Output file saved as: Launch_[...].trace
```
To view this trace in the Instruments app, simply find this directory in Finder and double-click it. Instruments app will appear:

To send this trace, right-click its directory in Finder and choose "Compress [...]". This will result in a similarly named file with a `.zip` at the end, which can now be conveniently sent via email or uploaded.
*Tart* is a virtualization toolset to build, run and manage virtual machines on Apple Silicon.
*Tart* is a virtualization toolset to build, run and manage macOS and Linux virtual machines (VMs) on Apple Silicon.
Built by CI engineers for your automation needs. Here are some highlights of Tart:
* Tart uses Apple's own `Virtualization.Framework` for near-native performance.
* Tart uses Apple's own `Virtualization.Framework` for [near-native performance](https://browser.geekbench.com/v5/cpu/compare/20382844?baseline=20382722).
* Push/Pull virtual machines from any OCI-compatible container registry.
* Use Tart Packer Plugin to automate VM creation.
*Built-in CI integration.
*Easily integrates with any CI system.
Try running a Tart VM on your Apple Silicon device (will download a 25 GB image):
Many companies are using Tart in their internal setups. Here are just a few of them:
```shell
brew install cirruslabs/cli/tart
tart clone ghcr.io/cirruslabs/macos-monterey-base:latest monterey-base
Run it locally or in CI with the following command:
```shell
brew install cirruslabs/cli/cirrus
cirrus run
```

[Cirrus CI](https://cirrus-ci.org/) already leverages Tart to power its macOS cloud infrastructure. The `.cirrus.yml`
config from above will just work in Cirrus CI and your tasks will be executed inside Tart VMs in our cloud.
**Note:** Cirrus CI only allows [images managed and regularly updated by us](https://github.com/orgs/cirruslabs/packages?tab=packages&q=macos).
## Virtual Machine Management
### Creating from scratch
Tart can create VMs from `*.ipsw` files. You can download a specific `*.ipsw` file [here](https://ipsw.me/) or you can
use `latest` instead of a path to `*.ipsw` to download the latest available version:
```shell
tart create --from-ipsw=latest monterey-vanilla
tart run monterey-vanilla
```
After the initial booting of the VM you'll need to manually go through the macOS installation process. As a convention we recommend creating an `admin` user with an `admin` password. After the regular installation please do some additional modifications in the VM:
5. Run `sudo visudo` in Terminal, find `%admin ALL=(ALL) ALL` add `admin ALL=(ALL) NOPASSWD: ALL` to allow sudo without a password.
### Configuring a VM
By default, a tart VM uses 2 CPUs and 4 GB of memory with a `1024x768` display. This can be changed with `tart set` command.
Please refer to `tart set --help` for additional details.
### Building with Packer
Please refer to [Tart Packer Plugin reposiotry](https://github.com/cirruslabs/packer-plugin-tart) for setup instructions.
Here is an example of a template to build `monterey-base` local image based of a remote image:
```json
{
"builders":[
{
"name":"tart",
"type":"tart-cli",
"vm_base_name":"tartvm/vanilla:latest",
"vm_name":"monterey-base",
"cpu_count":4,
"memory_gb":8,
"disk_size_gb":32,
"ssh_username":"admin",
"ssh_password":"admin",
"ssh_timeout":"120s"
}
],
"provisioners":[
{
"inline":[
"echo 'Disabling spotlight indexing...'",
"sudo mdutil -a -i off"
],
"type":"shell"
},
#moreprovisioners
]
}
```
Here is a [repository with Packer templates](https://github.com/cirruslabs/macos-image-templates) used to build [all the images managed by us](https://github.com/orgs/cirruslabs/packages?tab=packages&q=macos).
### Working with a Remote OCI Container Registry
For example, let's say you want to push/pull images to a registry hosted at https://acme.io/.
#### Registry Authorization
First, you need to log in and save credential for `acme.io` host via `tart login` command:
```shell
tart login acme.io
```
Credentials are securely stored in Keychain.
#### Pushing a Local Image
Once credentials are saved for `acme.io`, run the following command to push a local images remotely with two tags:
```shell
tart push my-local-vm-name acme.io/remoteorg/name:latest acme.io/remoteorg/name:v1.0.0
```
#### Pulling a Remote Image
```shell
tart pull acme.io/remoteorg/name:latest my-local-vm-name
```
## FAQ
<details>
<summary>How Tart is different from Anka</summary>
Under the hood Tart is using the same technology as Anka 3.0 so there should be no real difference in performance
or features supported. If there is some feature missing please don't hesitate to [create a feature request](https://github.com/cirruslabs/tart/issues).
Instead of Anka Registry, Tart can work with any OCI-compatible container registry.
Tart doesn't yet have an analogue of Anka Controller for managing long living VMs. Please take a look at [CI integration](#ci-integration)
section for an option to run ephemeral VMs for your needs.
</details>
<details>
<summary>Why Tart is free and open sourced?</summary>
Tart is a relatively small project, and it didn't feel right to try to monetize it.
Apple did all the heavy lifting with their `Virtualization.Framework`.
</details>
Please check the [official documentation](https://tart.run) for more information and/or feel free to use [discussions](https://github.com/openai/tart/discussions)
@Option(help:ArgumentHelp("Path to the IPSW file (or \"latest\") to fetch the latest appropriate IPSW",valueName:"path"))
@Option(help:ArgumentHelp("create a macOS VM using path to the IPSW file or URL (or \"latest\", to fetch the latest supported IPSW automatically)",valueName:"path"),completion:.file())
varfromIPSW:String?
@Option(help:ArgumentHelp("Disk size in Gb"))
vardiskSize:UInt8=32
@Flag(help:"create a Linux VM")
varlinux:Bool=false
@Option(help:ArgumentHelp("Disk size in GB"))
vardiskSize:UInt16=50
@Option(help:ArgumentHelp("Disk image format",discussion:"ASIF format provides better performance but requires macOS 26 Tahoe or later"))
vardiskFormat:DiskImageFormat=.raw
funcvalidate()throws{
iffromIPSW==nil{
throwValidationError("Please specify a --from-ipsw option!")
iffromIPSW==nil&&!linux{
throwValidationError("Please specify either a --from-ipsw or --linux option!")
}
#ifarch(x86_64)
iffromIPSW!=nil{
throwValidationError("Only Linux VMs are supported on Intel!")
}
#endif
//Validatediskformatsupport
if!diskFormat.isSupported{
throwValidationError("Disk format '\(diskFormat.rawValue)' is not supported on this system.")
@Option(help:"Number of seconds to wait for a potential VM booting")
varwait:UInt16=0
@Option(help:ArgumentHelp("Strategy for resolving IP address",
discussion:"""
By default, Tart is using a "dhcp" resolver which parses the DHCP lease file on host and tries to find an entry containing the VM's MAC address. This method is fast and the most reliable, but only works for VMs are not using the bridged networking.\n
Alternatively, Tart has an "arp" resolver which calls an external "arp" executable and parses it's output. This works for VMs using bridged networking and returns their IP, but when they generate enough network activity to populate the host's ARP table. Note that "arp" strategy won't work for VMs using the Softnet networking.\n
A third strategy, "agent" works in all cases reliably, but requires Guest agent for Tart VMs (https://github.com/cirruslabs/tart-guest-agent) to be installed inside of a VM.
staticvarconfiguration=CommandConfiguration(abstract:"Prune OCI and IPSW caches or local VMs")
@Option(help:ArgumentHelp("Entries to remove: \"caches\" targets OCI and IPSW caches and \"vms\" targets local VMs."),completion:.list(["caches","vms"]))
varentries:String="caches"
@Option(help:ArgumentHelp("Remove entries that were last accessed more than n days ago",
discussion:"For example, --older-than=7 will remove entries that weren't accessed by Tart in the last 7 days.",
valueName:"n"))
varolderThan:UInt?
@Option(help:.hidden)
varcacheBudget:UInt?
@Option(help:ArgumentHelp("Remove the least recently used entries that do not fit the specified space size budget n, expressed in gigabytes",
discussion:"For example, --space-budget=50 will effectively shrink all entries to a total size of 50 gigabytes.",
valueName:"n"))
varspaceBudget:UInt?
@Flag(help:.hidden)
vargc:Bool=false
mutatingfuncvalidate()throws{
//--cache-budgetdeprecationlogic
ifletcacheBudget=cacheBudget{
fputs("--cache-budget is deprecated, please use --space-budget\n",stderr)
ifspaceBudget!=nil{
throwValidationError("--cache-budget is deprecated, please use --space-budget")
}
spaceBudget=cacheBudget
}
ifolderThan==nil&&spaceBudget==nil&&!gc{
throwValidationError("at least one pruning criteria must be specified")
staticvarconfiguration=CommandConfiguration(abstract:"Push a VM to a registry")
@Argument(help:"local VM name")
@Argument(help:"local or remote VM name",completion:.custom(completeMachines))
varlocalName:String
@Argument(help:"remote VM name(s)")
varremoteNames:[String]
@Flag(help:"connect to the OCI registry via insecure HTTP protocol")
varinsecure:Bool=false
@Option(help:"network concurrency to use when pushing a local VM to the OCI-compatible registry")
varconcurrency:UInt=4
@Option(help:ArgumentHelp("chunk size in MB if registry supports chunked uploads",
discussion:"""
By default monolithic method is used for uploading blobs to the registry but some registries support a more efficient chunked method.
For example, AWS Elastic Container Registry supports only chunks larger than 5MB but GitHub Container Registry supports only chunks smaller than 4MB. Google Container Registry on the other hand doesn't support chunked uploads at all.
Please refer to the documentation of your particular registry in order to see if this option is suitable for you and what's the recommended chunk size.
"""))
varchunkSize:Int=0
@Option(name:[.customLong("label")],help:ArgumentHelp("additional metadata to attach to the OCI image configuration in key=value format",
discussion:"Can be specified multiple times to attach multiple labels."))
@Option(help:"VM display settings in a format of <width>x<height>(x<dpi>)?. For example, 1200x800 or 1200x800x72")
@Option(help:"VM display resolution in a format of WIDTHxHEIGHT[pt|px]. For example, 1200x800, 1200x800pt or 1920x1080px. Units are treated as hints and default to \"pt\" (points) for macOS VMs and \"px\" (pixels) for Linux VMs when not specified.")
vardisplay:VMDisplayConfig?
@Option(help:.hidden)
vardiskSize:UInt8?
@Flag(inversion:.prefixedNo,help:ArgumentHelp("Whether to automatically reconfigure the VM's display to fit the window"))
vardisplayRefit:Bool?=nil
@Flag(help:ArgumentHelp("Generate a new random MAC address for the VM."))
varrandomMAC:Bool=false
#ifarch(arm64)
@Flag(help:ArgumentHelp("Generate a new random serial number for the macOS VM."))
#endif
varrandomSerial:Bool=false
@Option(help:ArgumentHelp("Replace the VM's disk contents with the disk contents at path.",valueName:"path"))
vardisk:String?
@Option(help:ArgumentHelp("Resize the VMs disk to the specified size in GB (note that the disk size can only be increased to avoid losing data)",
discussion:"""
See https://tart.run/faq/#disk-resizing for more details.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.