From d2af3f44567785f3644caab0a98c7d728d871110 Mon Sep 17 00:00:00 2001 From: PJ Date: Tue, 9 Jun 2026 16:51:03 +0530 Subject: [PATCH] feat(doctor): check usbmuxd socket instead of iproxy on PATH --- cmd/sanderling/doctor.go | 28 ++++++++++++++++++++++------ cmd/sanderling/doctor_test.go | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cmd/sanderling/doctor.go b/cmd/sanderling/doctor.go index c7226c5..41f8ef0 100644 --- a/cmd/sanderling/doctor.go +++ b/cmd/sanderling/doctor.go @@ -61,8 +61,9 @@ func androidChecks() []doctorCheck { // iosChecks covers the simulator path, which the native companion drives with // no JVM. A simulator host with no Java still passes. Physical-device runs -// additionally need devicectl, iproxy, a connected device, and signing -// credentials, covered by iosDeviceChecks and surfaced through the "all" union. +// additionally need devicectl, the usbmuxd socket, a connected device, and +// signing credentials, covered by iosDeviceChecks and surfaced through the +// "all" union. func iosChecks() []doctorCheck { return []doctorCheck{ {Name: "xcrun on PATH (ios simulator)", Run: checkExecutableOnPath("xcrun")}, @@ -71,13 +72,15 @@ func iosChecks() []doctorCheck { } // iosDeviceChecks covers the prerequisites a physical iOS device needs: the -// runner is built and driven over a usbmux tunnel, so devicectl installs the -// app, iproxy forwards the tunnel, a device must be connected and paired, and -// App Store Connect signing credentials must be present for the no-UI build. +// runner is built and driven over a native usbmux tunnel, so devicectl installs +// the app, the macOS usbmuxd socket carries the tunnel, a device must be +// connected and paired, and App Store Connect signing credentials must be +// present for the no-UI build. Everything here is part of macOS + Xcode; nothing +// is installed. func iosDeviceChecks() []doctorCheck { return []doctorCheck{ {Name: "devicectl available (ios physical device)", Run: checkDevicectl}, - {Name: "iproxy on PATH (ios physical device)", Run: checkExecutableOnPath("iproxy")}, + {Name: "usbmuxd socket present (ios physical device)", Run: checkUsbmuxd}, {Name: "an iOS device is connected and paired", Run: checkDeviceConnected}, {Name: "App Store Connect signing credentials present", Run: checkDeviceSigning}, } @@ -142,6 +145,19 @@ func checkDevicectl(ctx context.Context) error { return nil } +// usbmuxdSocketPath is the macOS usbmuxd unix socket. It ships with the OS, so +// the device tunnel needs nothing installed. +const usbmuxdSocketPath = "/var/run/usbmuxd" + +// checkUsbmuxd confirms the macOS usbmuxd socket is present: the native device +// tunnel speaks to it directly instead of shelling out to a third-party client. +func checkUsbmuxd(_ context.Context) error { + if _, err := os.Stat(usbmuxdSocketPath); err != nil { + return fmt.Errorf("usbmuxd socket not found at %s: %w", usbmuxdSocketPath, err) + } + return nil +} + // checkDeviceConnected confirms at least one physical iOS device is connected // and paired, the prerequisite for the tunnel and the install. func checkDeviceConnected(ctx context.Context) error { diff --git a/cmd/sanderling/doctor_test.go b/cmd/sanderling/doctor_test.go index 8e10d8e..18a41c5 100644 --- a/cmd/sanderling/doctor_test.go +++ b/cmd/sanderling/doctor_test.go @@ -153,7 +153,7 @@ func TestDoctorChecksFor_iOSDevice_CoversDevicePrereqs(t *testing.T) { t.Errorf("device checks must not include the retired %q", c.Name) } } - for _, want := range []string{"devicectl", "iproxy", "connected and paired", "signing credentials"} { + for _, want := range []string{"devicectl", "usbmuxd", "connected and paired", "signing credentials"} { found := false for _, c := range checks { if strings.Contains(c.Name, want) {