## What does this PR do?
Password login can terminate with an illegal-instruction crash on Linux,
leaving the user logged out. Anisette client time was formatted with
DateFormatter and an explicitly assigned Gregorian calendar. That
formatting path reaches Foundation’s NSCalendar bridging code, where the
reported crash occurs.
AnisetteData now formats client time with Date.ISO8601FormatStyle,
avoiding the DateFormatter calendar bridge. The header retains its UTC
timestamp format with whole seconds and a trailing Z. New parameterized
tests check the complete header dictionary for the Unix epoch, a year
boundary, leap day, and a timestamp with fractional seconds.
Fixes#184
## How was it tested?
Ran `swift test` locally on macOS for the xtool AnisetteData change; it
fails the same way on the base branch, so the failure predates this
change (it fails the same way on upstream/main).
`swift build` failed because an async call in a defer body prevented
compilation.
`swift test` failed during compilation with the same async-in-defer
error, so the tests did not run.
`repo_lint` ran without reported output.
`make lint SWIFTLINT_FLAGS='--reporter=github-actions-logging' | tee
swiftlint.log` ran with no lint diagnostics shown in the transcript.
The tests in `Tests/XToolTests/AnisetteDataTests.swift` fail without the
fix in the xtool AnisetteData change (checked by reverting the source
changes).
## AI tool usage
How much of the code in this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI in writing code. I reviewed the
finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
AI was used for assistance.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* Client timestamps now use a consistent UTC ISO 8601 format with dashed
dates, colon-separated times, and no fractional seconds. This applies to
timestamps with or without fractional seconds in the source date.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
## What does this PR do?
When spawning a subprocess,
`currentStandardOutput`/`currentStandardError` bypass `d.out`. Add
`.xtoolOutput()` and `.xtoolError()` which respect the dependency.
## How was it tested?
Tested in a downstream PR to ensure that forwarding works
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Subprocess output can now be routed directly to XTool’s standard
output or standard error.
* Added a way to create a writable output descriptor that forwards data
to a selected stream.
* Asynchronous input reading is now available through a public
interface, including configurable read sizes and explicit closing.
* **Compatibility**
* Existing input-reading behavior and parameters remain unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## The bug
`DeveloperServicesAssignAppGroupsOperation` sends one
`assignApplicationGroupToAppId` request per app group, concurrently.
That endpoint replaces the App ID's groups with the ones in the request,
so an app whose entitlements list two or more groups ends up with only
one of them on the App ID: whichever request Apple handles last. The
provisioning profile then carries one group while the signed binary
claims two, and the device refuses the install with
`ApplicationVerificationFailed`.
Setting the groups by hand in the developer portal does not help,
because the next `xtool dev build --sign` sends the per-group requests
again.
## The fix
- `DeveloperServicesAssignAppGroupRequest` takes `groupIDs:
[DeveloperServicesAppGroup.ID]` and sends `applicationGroups` as an
array. The endpoint accepts an array; it was being given a single string
before.
- `DeveloperServicesAssignAppGroupsOperation.perform()` still finds or
creates each group concurrently, then sends a single assign request
carrying every group ID, and returns the group IDs in the order the
entitlements listed them, so the rewritten entitlement is stable between
builds.
The only caller, `DeveloperServicesAddAppOperation`, is unchanged.
## Related
#324 would report the resulting mismatch between the entitlements and
the profile as a clear error at signing time; this change removes the
cause, so an app with several groups gets a profile that grants all of
them.
## Checked
Built on Linux with the Swift 6.4 toolchain and used to sign an iOS app
whose main target has two app groups and a Share extension. The app's
`embedded.mobileprovision` now carries both groups and the device
installs the app. The same app signed with the unpatched build got one
group in the profile and `ApplicationVerificationFailed` on install.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01UKzxgxaLwK7Ap5LMwj9MxH
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Apps can now be assigned multiple app groups in a single operation.
The assignment replaces the app’s existing group associations with the
selected groups, so any groups not included in the new assignment are no
longer associated with the app.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: David Reinecke <david.reinecke@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Kabir Oberai <oberai.kabir@gmail.com>
## What does this PR do?
Adds `Dependency`s for console IO. Also fixes#268 by explicitly
handling non-pipe input.
## How was it tested?
Ran on macOS + Linux in Docker. I have a future PR that overrides the
dependencies in certain cases, which also appears to work correctly.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
Did not scrutinize the tests too much tbh but they look reasonable. Easy
enough to update/remove them in the future if needed.
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **New Features**
* Added support for reading command-line input, including asynchronous
prompts, line-based responses, and graceful handling of end-of-input.
* Added coordinated cleanup when commands finish.
* **Improvements**
* Command messages across authentication, device management,
installation, SDK, and other workflows now use consistent output
handling; displayed messages and command behavior remain unchanged.
* Improved handling of complete output when the system temporarily
cannot accept more data.
* **Tests**
* Added coverage for command input and output, including stream
behavior, cancellation, and shutdown.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Fixes#305 (thanks @0x53 for the detailed diagnosis).
`Certificate.serialNumber()` stripped leading `0` *characters* from the
hex string, so a serial like `0337…` became `337…` and never matched the
`serialNumber` returned by the API. xtool then treated its stored
certificate as missing: free teams are prompted to revoke a valid
certificate on every run, and paid teams take the same path and try to
create another certificate, which @McoreD confirmed fails with `409 …
You already have a current Development certificate or a pending
certificate request.`
The character-level `drop` predates b6a40ca, which switched to
fixed-width `%02hhX` formatting; since then it removes the significant
zero of any first byte below `0x10`. swift-certificates already strips
leading zero bytes (including DER sign padding) when parsing, so the
`drop` isn't needed for padding.
- `serialNumber()` now drops only leading zero *bytes*, matching the API
and `openssl x509 -serial`.
- The stored-certificate lookup uses a new
`Certificate.hasSerialNumber(_:)`, which compares serials numerically
(ignoring case and leading zeros), so it doesn't depend on Apple's exact
formatting.
- Profile creation no longer finds the signing certificate with
`filter[serialNumber]`. The API lists serials with the leading zero
(`0E41…`), but the filter only matches them without it (`E41…`), so once
`serialNumber()` kept the zero the filter matched nothing and profile
creation failed with a 409
([details](https://github.com/xtool-org/xtool/pull/306#issuecomment-6181157860)).
It now fetches all pages of certificates and matches them with
`hasSerialNumber(_:)`. Fix by @McoreD, with pagination added so a
certificate on a later page isn't missed.
## How was it tested?
- Added `Tests/XToolTests/CertificateTests.swift`: builds a cert with
the serial from #305, round-trips it through DER, and checks
`serialNumber()` and `hasSerialNumber(_:)`, plus a high-bit serial so
DER sign padding doesn't leak into the string. The leading-zero test
fails on `main` (returns `33742570D6F9684A23311711956A084`) and passes
with this change.
- `swift test` (17 tests) and `make lint` pass on macOS (Xcode 27, Swift
6.4, `XTL_CI=1`).
- @McoreD tested on Linux (Arch, Swift 6.4.1, Xcode 27 SDK, on top of
1.22.2) with a paid team whose certificate serial starts with `0`: the
stored certificate is reused, and provisioning, signing and install to
an iPhone (iOS 27) succeed end to end (tested before pagination was
added). `CertificateTests` also pass on Linux.
- Not tested with a free team.
To generate test certificates, the test target now depends on `Crypto`
and `X509`, which XKit already uses.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Certificate serial numbers now preserve leading zero nibbles while
omitting unnecessary padding.
* Certificate matching now ignores letter case and leading zeros, and
profile lookups find matching certificates even when the API’s
serial-number filter treats leading-zero values differently.
* Certificate lookups continue to use the existing fallback when no
matching certificate is found.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: McoreD <McoreD@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## What does this PR do?
Drops `mxcl/Version` in favour of a lighter parser that's specifically
designed to parse the Swift version.
## How was it tested?
- [x] Unit tests
- [x] CI
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Swift versions can now be compared by major, minor, and patch number,
and displayed in a consistent three-part format.
* Version parsing now accepts two- or three-number versions, including
supported compiler-output formats with optional prefixes or a `-dev`
suffix.
* Versions with an omitted patch number are treated as equivalent to the
same version with patch `0`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Application packing now sets CFBundleSupportedPlatforms from
Packer.cfBundleSupportedPlatforms(for:). The helper splits the triple on
hyphens, takes the OS token after apple with any numeric version
stripped, and returns iPhoneSimulator for an ios triple that also
contains simulator, iPhoneOS for other ios triples, MacOSX for macos or
macosx, and iPhoneOS when the triple is missing or unrecognized.
PackerTests covers those triples. Package.resolved drops the
swift-issue-reporting pin and adds xctest-dynamic-overlay 1.13.1.
Application builds aimed at the iOS simulator still ship an Info.plist
whose CFBundleSupportedPlatforms value is iPhoneOS. Hosts that read that
key treat the archive as a physical-device build and refuse to launch it
on a simulator. While packing an application, Packer assigns
CFBundleSupportedPlatforms the fixed array iPhoneOS. The build triple is
already available on buildSettings, but that assignment never reads it,
so a simulator triple is packed the same way as a device triple.
Fixes#191
## How was it tested?
The package builds successfully.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
AI was used for assistance.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Application metadata now reports the supported platform based on the
build target, including iOS devices, iOS simulators, and macOS. Unknown
targets retain the iOS device platform as a fallback.
* **Tests**
* Added coverage for platform metadata across supported and unknown
build targets.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
## What does this PR do?
Bumps pointfree deps and explicitly disables OpenCombine so that we
don't have different dependency graphs when building xtool on Darwin vs
Linux hosts.
## How was it tested?
A bunch of `swift package resolve`s
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Updated supporting package versions and refreshed command-line and
file-operation handling.
* These changes do not add or remove app features or alter its public
interface. No action is needed to continue using the app.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Adds integration tests
## How was it tested?
CI
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [x] **0** - No AI was used to write code
- [ ] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Added integration smoke tests covering version checks, app creation,
iOS build metadata, and slim SDK update behavior.
* Added validation for locating Xcode and verifying the Swift toolchain.
* **Chores**
* Added automated integration-test execution for pushes and pull
requests.
* Added Docker and Compose support for running integration tests in a
consistent environment.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What does this PR do?
Allows xtool to auto-update the Darwin SDK format when there's a new
Darwin toolset, or when we change the SDK format. Downstream this also
allows us to auto-update when there's changes to OpenAppleMacros (#101).
One callout: the disk footprint of `darwin.artifactbundle` is now ~7 GB
instead of ~3 GB.
Reason: `xtool sdk install /path/to/Xcode.[app|xip]` now performs a
"normal" install by default, which preserves the entirety of
`Xcode.app/Contents/Developer` — even the bits we don't currently use,
like `AppleTVOS.platform`. This makes it so if we start using more
components in the future, the user doesn't need to download Xcode again
and rebuild the SDK themselves.
You can still build an old-style SDK (containing just the components we
need) using `xtool sdk install --slim` or the dedicated `xtool sdk
build`.
## How was it tested?
Tested all of the install modes. Confirmed that auto-updating works.
Added integration tests in #252.
## AI tool usage
How much of this PR was AI-assisted? (check one)
- [ ] **0** - No AI was used to write code
- [x] **1** - I was assisted by AI. I reviewed the finished result.
- [ ] **2** - I set the AI going and left it to it; nobody has read the
result - no review, or AI review only
<!-- If an AI agent is filling this in: declare the level honestly, and
open as a draft if it is #2. Do not lower the declared level to get the
PR reviewed. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added SDK flavor reporting for slim, normal, and legacy installations.
- Added `sdk update` support to rebuild and install SDKs from an
installed Xcode bundle.
- Added a `--slim` option for SDK installation.
- SDK status now displays installation path, flavor, and version.
- SDK setup and build operations now ensure a suitable SDK is available
automatically.
- **Bug Fixes**
- Improved temporary SDK bundle handling, validation, replacement, and
cross-filesystem copying.
- Copy failures now provide detailed diagnostic information.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->