* fix(control-socket): recover from transient accept errors
Keep the control socket alive when Darwin reports a transient fcntl failure while accepting a client connection.
Refs #1346
* test(control-socket): use synchronous pipeline lookup
* test(control-socket): query handler on event loop
* fix(control-socket): preserve channel backpressure
* test(control-socket): exercise accept error recovery
* test(control-socket): verify accepts continue after transient errors
Check that the real server inbound stream yields a client connection after
each simulated accept error. Keep the read-routing and unrelated-error
checks, bound the wait, and close the test connections.
Adapted from the regression test suggested in:
https://github.com/openai/tart/pull/1351#issuecomment-5851285933
---------
Co-authored-by: Yibo Zhuang <yzhuang@openai.com>
* fix(storage): preserve running VM delete errors
Do not reinterpret RuntimeError.VMIsRunning as a missing VM when the storage wrapper bridges errors through NSError.
Refs #1345
* test(storage): initialize running VM lock file
* test(storage): hold VM lock in a child process
* fix(storage): narrow file-not-found error matching
* test(storage): use Swift error-domain regression coverage
* Allow HumanReadableByteCount to represent an unknown byte count
Some byte counts, such as the capacity of an ASIF disk image, can't
always be determined. Accept an optional byte count and render an
unknown value as "-" in text output and as null in JSON output.
* Fix listing and getting VMs when disk capacity is unavailable
For ASIF disk images, the disk capacity is read with "diskutil image
info". The command fails with "Resource temporarily unavailable" while
a running VM holds the disk image open. As a result, "tart list" and
"tart get" fail entirely when any such VM exists.
Treat the disk capacity as unknown when it can't be determined, so
that both commands still show the remaining information.
Fixes#1344
* Remove unused VMDirectory.diskSizeGB()
The method was added together with diskSizeBytes() but has never been
used. "tart list" and "tart get" use diskSizeBytes() directly.
* Use registry-1.docker.io for Docker Hub's docker.io host
docker.io doesn't serve the registry API: https://docker.io/v2/ redirects
to https://www.docker.com/, which URLSession follows, getting back an HTML
page with HTTP 200. As a result, pushing fails with
UnexpectedHTTPStatusCode("pushing blob (POST)", 200, ...), pulling fails
to parse the manifest and "tart login docker.io" accepts any credentials,
because ping() never gets an authentication challenge.
Send the API requests for docker.io to registry-1.docker.io instead, while
still using docker.io for the pushed image names and for the credentials
lookup, so that credentials saved with "tart login docker.io" keep working.
Fixes#1275
* Match docker.io case insensitively
* Recognize Docker Hub with an explicit port
* Parse the registry host once and keep it normalized
Using the host exactly as specified for naming and credentials lookup
changed the behavior for other registries too. For example,
"127.0.0.1:05000" used to find credentials stored for "127.0.0.1:5000",
but didn't anymore.
Parse the URL once instead, take the normalized host and port from it
like before, and only replace the URL's host with registry-1.docker.io
for Docker Hub.
This is first of several changes to add support for the new
DiskImageKit ASIF layers to tart VM images.
This change is focused on laying down the OCI media type for
ASIF layers, content addressable store structure, as well
as the VMDirectory structure for supporting layers.
Add DiskImageStack type to model VM image using DiskImage APIs.
* 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
* 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>
* ASIF is available only starting from macOS 26 (Tahoe)
* Remove testRawFormatIsAlwaysSupported() test
* Fix testASIFFormatSupport() test to check for macOS 26+
* 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>
* 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
* 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
* Improve RemoteName parser
* Remove Parsing import
* Permit namespace components to contain separators, but no more than one
* Add testNoPathTraversal
* 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
* 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>