Skip to content

Commit

Permalink
[menu-bar][cli] Automatically select correct iOS device depending on …
Browse files Browse the repository at this point in the history
…app type (#200)

* [menu-bar][cli] Automatically select correct iOS device depending on app type

* Add changelog entry
  • Loading branch information
gabrieldonadel committed May 27, 2024
1 parent db2cf0b commit a7054c1
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### 🎉 New features

- Add experimental support for Windows and Linux. ([#152](https://github.com/expo/orbit/pull/152), [#157](https://github.com/expo/orbit/pull/157), [#158](https://github.com/expo/orbit/pull/158), [#160](https://github.com/expo/orbit/pull/160), [#161](https://github.com/expo/orbit/pull/161), [#165](https://github.com/expo/orbit/pull/165), [#170](https://github.com/expo/orbit/pull/170), [#171](https://github.com/expo/orbit/pull/171), [#172](https://github.com/expo/orbit/pull/172), [#173](https://github.com/expo/orbit/pull/173), [#174](https://github.com/expo/orbit/pull/174), [#175](https://github.com/expo/orbit/pull/175), [#177](https://github.com/expo/orbit/pull/177), [#178](https://github.com/expo/orbit/pull/178), [#180](https://github.com/expo/orbit/pull/180), [#181](https://github.com/expo/orbit/pull/181), [#182](https://github.com/expo/orbit/pull/182), [#185](https://github.com/expo/orbit/pull/185), [#191](https://github.com/expo/orbit/pull/191) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Automatically select the correct iOS device depending on app type. ([#200](https://github.com/expo/orbit/pull/200) by [@gabrieldonadel](https://github.com/gabrieldonadel))

### 🐛 Bug fixes

Expand Down
11 changes: 11 additions & 0 deletions apps/cli/src/commands/DetectIOSAppType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { extractAppFromLocalArchiveAsync, detectIOSAppType } from 'eas-shared';

export async function detectIOSAppTypeAsync(appPath: string) {
if (!appPath.endsWith('.app') && !appPath.endsWith('.ipa')) {
appPath = await extractAppFromLocalArchiveAsync(appPath);
}

const appType = await detectIOSAppType(appPath);

return appType;
}
6 changes: 6 additions & 0 deletions apps/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { installAndLaunchAppAsync } from './commands/InstallAndLaunchApp';
import { launchSnackAsync } from './commands/LaunchSnack';
import { checkToolsAsync } from './commands/CheckTools';
import { setSessionAsync } from './commands/SetSession';
import { detectIOSAppTypeAsync } from './commands/DetectIOSAppType';
import { returnLoggerMiddleware } from './utils';

const program = new Command();
Expand Down Expand Up @@ -66,6 +67,11 @@ program
.argument('<string>', 'Session secret')
.action(returnLoggerMiddleware(setSessionAsync));

program
.command('detect-ios-app-type')
.argument('<string>', 'Local path of the app')
.action(returnLoggerMiddleware(detectIOSAppTypeAsync));

if (process.argv.length < 3) {
program.help();
}
Expand Down
11 changes: 11 additions & 0 deletions apps/menu-bar/src/commands/detectIOSAppTypeAsync'.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Device } from 'common-types/build/devices';

import MenuBarModule from '../modules/MenuBarModule';

export const detectIOSAppTypeAsync = async (appPath: string) => {
return (await MenuBarModule.runCli(
'detect-ios-app-type',
[appPath],
console.log
)) as Device['deviceType'];
};
25 changes: 20 additions & 5 deletions apps/menu-bar/src/popover/Core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SECTION_HEADER_HEIGHT } from './SectionHeader';
import { Analytics, Event } from '../analytics';
import { withApolloProvider } from '../api/ApolloClient';
import { bootDeviceAsync } from '../commands/bootDeviceAsync';
import { detectIOSAppTypeAsync } from "../commands/detectIOSAppTypeAsync'";
import { downloadBuildAsync } from '../commands/downloadBuildAsync';
import { installAndLaunchAppAsync } from '../commands/installAndLaunchAppAsync';
import { launchSnackAsync } from '../commands/launchSnackAsync';
Expand Down Expand Up @@ -146,21 +147,29 @@ function Core(props: Props) {
);

const getDeviceByPlatform = useCallback(
(platform: 'android' | 'ios') => {
(platform: 'android' | 'ios', deviceType?: Device['deviceType']) => {
const devices = devicesPerPlatform[platform].devices;
const selectedDevicesId = selectedDevicesIds[platform];
if (selectedDevicesId && devices.has(selectedDevicesId)) {
return devices.get(selectedDevicesId);
const device = devices.get(selectedDevicesId);
if (!deviceType || device?.deviceType === deviceType) {
return devices.get(selectedDevicesId);
}
}

for (const device of devices.values()) {
if (isVirtualDevice(device) && device.state === 'Booted') {
if (
(deviceType === 'device' && device.deviceType === deviceType) ||
(deviceType !== 'device' && isVirtualDevice(device) && device.state === 'Booted')
) {
setSelectedDevicesIds((prev) => ({ ...prev, [platform]: getDeviceId(device) }));
return device;
}
}

const [firstDevice] = devices.values();
const firstDevice = [...devices.values()].find(
(d) => !deviceType || d.deviceType === deviceType
);
if (!firstDevice) {
return;
}
Expand Down Expand Up @@ -302,7 +311,13 @@ function Core(props: Props) {
}

const platform = getPlatformFromURI(appURI);
const device = getDeviceByPlatform(platform);

let appType: Device['deviceType'] | undefined;
if (platform === 'ios') {
appType = await detectIOSAppTypeAsync(localFilePath);
}

const device = getDeviceByPlatform(platform, appType);
if (!device) {
Alert.alert(
`You don't have any ${platform} device available to run this build, please make sure your environment is configured correctly and try again.`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import os from 'os';
import path from 'path';

import * as AppleDevice from './AppleDevice';
import * as devicectl from '../devicectl';
import { ensureDirectory } from '../../../utils/dir';
import { InternalError } from 'common-types';

Expand Down Expand Up @@ -39,6 +40,13 @@ export async function installOnDeviceAsync(props: {
},
});
} catch (error: any) {
if (error.code === 'APPLE_DEVICE_USBMUXD') {
// Usbmux can only find wireless devices if they are unlocked. Fallback on much slower devicectl.
if (devicectl.hasDevicectlEverBeenInstalled()) {
return await devicectl.installAndLaunchAppAsync({ bundle, bundleIdentifier, udid });
}
}

if (error.code === 'APPLE_DEVICE_LOCKED') {
// Get the app name from the binary path.
const appName = path.basename(bundle).split('.')[0] ?? 'app';
Expand Down
41 changes: 41 additions & 0 deletions packages/eas-shared/src/run/ios/devicectl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,44 @@ function isDevicectlInstalled() {
return false;
}
}

/**
* Wraps the apple device method for installing and running an app
*/
export async function installAndLaunchAppAsync(props: {
bundle: string;
bundleIdentifier: string;
udid: string;
}): Promise<void> {
const { bundle, bundleIdentifier, udid } = props;

await installAppWithDeviceCtlAsync(udid, bundle);

async function launchAppOptionally() {
try {
await launchAppWithDeviceCtl(udid, bundleIdentifier);
} catch (error: any) {
if (error.code === 'APPLE_DEVICE_LOCKED') {
// Get the app name from the binary path.
const appName = path.basename(bundle).split('.')[0] ?? 'app';
throw new CommandError(`Cannot launch ${appName} because the device is locked.`);
}
if (error.message.includes('BSErrorCodeDescription = fairplay')) {
throw new CommandError(
`Unable to launch app due to a FairPlay failure. Ensure this device is included in your provisioning profile`
);
}

throw error;
}
}

await launchAppOptionally();
}

async function installAppWithDeviceCtlAsync(
uuid: string,
bundleIdOrAppPath: string
): Promise<void> {
await xcrunAsync(['devicectl', 'device', 'install', 'app', '--device', uuid, bundleIdOrAppPath]);
}

0 comments on commit a7054c1

Please sign in to comment.