1import { Command } from '@expo/commander'; 2import spawnAsync from '@expo/spawn-async'; 3import { Android, Config, Simulator, Versions } from '@expo/xdl'; 4import chalk from 'chalk'; 5 6import { STAGING_API_HOST } from '../Constants'; 7import { Platform, getNewestSDKVersionAsync } from '../ProjectVersions'; 8import askForPlatformAsync from '../utils/askForPlatformAsync'; 9import askForSDKVersionAsync from '../utils/askForSDKVersionAsync'; 10 11type ActionOptions = { 12 platform?: Platform; 13 sdkVersion?: string; 14}; 15 16async function downloadAndInstallOnIOSAsync(clientUrl: string): Promise<void> { 17 if (!(await Simulator.isSimulatorInstalledAsync())) { 18 console.error(chalk.red('iOS simulator is not installed!')); 19 return; 20 } 21 22 console.log('Booting up iOS simulator...'); 23 24 const simulator = await Simulator.ensureSimulatorOpenAsync(); 25 26 console.log('Uninstalling previously installed Expo client...'); 27 28 await Simulator.uninstallExpoAppFromSimulatorAsync(simulator); 29 30 console.log(`Installing Expo client from ${chalk.blue(clientUrl)} on iOS simulator...`); 31 32 const installResult = await Simulator.installExpoOnSimulatorAsync({ url: clientUrl, simulator }); 33 34 if (installResult.status !== 0) { 35 throw new Error('Installing Expo client simulator failed!'); 36 } 37 38 const appIdentifier = 'host.exp.Exponent'; 39 40 console.log(`Launching Expo client with identifier ${chalk.blue(appIdentifier)}...`); 41 42 await spawnAsync('xcrun', ['simctl', 'launch', 'booted', appIdentifier]); 43} 44 45async function downloadAndInstallOnAndroidAsync(clientUrl: string): Promise<void> { 46 try { 47 console.log('Checking if the are any Android devices or emulators connected...'); 48 49 const devices = await Android.getAttachedDevicesAsync(); 50 if (devices.length === 0) { 51 throw new Error('No connected devices or emulators found.'); 52 } 53 54 const device = devices[0]; 55 if (devices.length > 1) { 56 console.log( 57 `More than one Android device found. Installing on the first one found, ${device.name}.` 58 ); 59 } 60 61 if (!device.isAuthorized) { 62 throw new Error( 63 `This computer is not authorized for developing on ${device.name}. See https://expo.fyi/authorize-android-device.` 64 ); 65 } 66 67 console.log('Uninstalling previously installed Expo client...'); 68 69 await Android.uninstallExpoAsync(device); 70 71 console.log( 72 `Installing Expo client from ${chalk.blue(clientUrl)} on Android ${device.type}...` 73 ); 74 75 await Android.installExpoAsync({ url: clientUrl, device }); 76 77 console.log('Launching application...'); 78 79 await Android.getAdbOutputAsync([ 80 'shell', 81 'am', 82 'start', 83 '-n', 84 `host.exp.exponent/.LauncherActivity`, 85 ]); 86 } catch (error) { 87 console.error(chalk.red(`Unable to install Expo client: ${error.message}`)); 88 } 89} 90 91async function action(options: ActionOptions) { 92 const platform = options.platform || (await askForPlatformAsync()); 93 const sdkVersion = 94 options.sdkVersion || 95 (await askForSDKVersionAsync(platform, await getNewestSDKVersionAsync(platform))); 96 97 if (!sdkVersion) { 98 throw new Error(`Unable to find SDK version. Try to use ${chalk.yellow('--sdkVersion')} flag.`); 99 } 100 101 // Set XDL config to use staging 102 Config.api.host = STAGING_API_HOST; 103 104 const versions = await Versions.versionsAsync(); 105 const sdkConfiguration = versions?.sdkVersions?.[sdkVersion]; 106 107 if (!sdkConfiguration) { 108 throw new Error(`Versions configuration for SDK ${chalk.cyan(sdkVersion)} not found!`); 109 } 110 111 const tarballKey = `${platform}ClientUrl`; 112 const clientUrl = sdkConfiguration[tarballKey]; 113 114 if (!clientUrl) { 115 throw new Error(`Client url not found at ${chalk.yellow(tarballKey)} key of versions config!`); 116 } 117 118 switch (platform) { 119 case 'ios': { 120 await downloadAndInstallOnIOSAsync(clientUrl); 121 break; 122 } 123 case 'android': { 124 await downloadAndInstallOnAndroidAsync(clientUrl); 125 break; 126 } 127 default: { 128 throw new Error(`Platform "${platform}" not implemented!`); 129 } 130 } 131 console.log(chalk.green('Successfully installed and launched staging version of the client ')); 132} 133 134export default (program: Command) => { 135 program 136 .command('client-install') 137 .alias('ci') 138 .description( 139 'Installs staging version of the client on iOS simulator, Android emulator or connected Android device.' 140 ) 141 .option('-p, --platform [string]', 'Platform for which the client will be installed.') 142 .option('-s, --sdkVersion [string]', 'SDK version of the client to install.') 143 .asyncAction(action); 144}; 145