1import JsonFile from '@expo/json-file'; 2import spawnAsync from '@expo/spawn-async'; 3import { ExponentTools, Project, UrlUtils } from '@expo/xdl'; 4import chalk from 'chalk'; 5import ip from 'ip'; 6import os from 'os'; 7import path from 'path'; 8import request from 'request-promise-native'; 9import { v4 as uuidv4 } from 'uuid'; 10 11import { getExpoRepositoryRootDir } from '../Directories'; 12import { getHomeSDKVersionAsync } from '../ProjectVersions'; 13 14interface Manifest { 15 id: string; 16 name: string; 17} 18 19// some files are absent on turtle builders and we don't want log errors there 20const isTurtle = !!process.env.TURTLE_WORKING_DIR_PATH; 21 22const dogfoodingHomeUrl = 'exp://expo.io/@expo-dogfooding/home'; 23 24const EXPO_DIR = getExpoRepositoryRootDir(); 25 26async function getManifestAsync( 27 url: string, 28 platform: string, 29 sdkVersion: string | null 30): Promise<Manifest> { 31 const headers = { 32 'Exponent-Platform': platform, 33 Accept: 'application/expo+json,application/json', 34 }; 35 if (sdkVersion) { 36 headers['Exponent-SDK-Version'] = sdkVersion; 37 } 38 return await ExponentTools.getManifestAsync(url, headers, { 39 logger: { 40 log: () => {}, 41 error: () => {}, 42 info: () => {}, 43 }, 44 }); 45} 46 47async function getSavedDevHomeUrlAsync(): Promise<string> { 48 const devHomeConfig = await new JsonFile(path.join(EXPO_DIR, 'dev-home-config.json')).readAsync(); 49 return devHomeConfig.url as string; 50} 51 52function kernelManifestObjectToJson(manifest) { 53 if (!manifest.id) { 54 // hack for now because unsigned manifest won't have an id 55 manifest.id = '@exponent/home'; 56 } 57 manifest.sdkVersion = 'UNVERSIONED'; 58 return JSON.stringify(manifest); 59} 60 61export default { 62 async TEST_APP_URI() { 63 if (process.env.TEST_SUITE_URI) { 64 return process.env.TEST_SUITE_URI; 65 } else { 66 try { 67 let testSuitePath = path.join(__dirname, '..', 'apps', 'test-suite'); 68 let status = await Project.currentStatus(testSuitePath); 69 if (status === 'running') { 70 return await UrlUtils.constructManifestUrlAsync(testSuitePath); 71 } else { 72 return ''; 73 } 74 } catch (e) { 75 return ''; 76 } 77 } 78 }, 79 80 async TEST_CONFIG() { 81 if (process.env.TEST_CONFIG) { 82 return process.env.TEST_CONFIG; 83 } else { 84 return ''; 85 } 86 }, 87 88 async TEST_SERVER_URL() { 89 let url = 'TODO'; 90 91 try { 92 let lanAddress = ip.address(); 93 let localServerUrl = `http://${lanAddress}:3013`; 94 let result = await request.get({ 95 url: `${localServerUrl}/expo-test-server-status`, 96 timeout: 500, // ms 97 resolveWithFullResponse: true, 98 }); 99 if (result.body === 'running!') { 100 url = localServerUrl; 101 } 102 } catch (e) {} 103 104 return url; 105 }, 106 107 async TEST_RUN_ID() { 108 return process.env.UNIVERSE_BUILD_ID || uuidv4(); 109 }, 110 111 async BUILD_MACHINE_LOCAL_HOSTNAME() { 112 if (process.env.SHELL_APP_BUILDER) { 113 return ''; 114 } 115 116 try { 117 let result = await spawnAsync('scutil', ['--get', 'LocalHostName']); 118 return `${result.stdout.trim()}.local`; 119 } catch (e) { 120 if (e.code !== 'ENOENT') { 121 console.error(e.stack); 122 } 123 return os.hostname(); 124 } 125 }, 126 127 async DEV_PUBLISHED_KERNEL_MANIFEST(platform) { 128 let manifest, savedDevHomeUrl; 129 try { 130 savedDevHomeUrl = await getSavedDevHomeUrlAsync(); 131 const sdkVersion = await this.TEMPORARY_SDK_VERSION(); 132 133 manifest = await getManifestAsync(savedDevHomeUrl, platform, sdkVersion); 134 } catch (e) { 135 const msg = `Unable to download manifest from ${savedDevHomeUrl}: ${e.message}`; 136 console[isTurtle ? 'debug' : 'error'](msg); 137 return ''; 138 } 139 140 return kernelManifestObjectToJson(manifest); 141 }, 142 143 async DOGFOODING_PUBLISHED_KERNEL_MANIFEST(platform) { 144 let manifest: Manifest; 145 try { 146 const sdkVersion = await this.TEMPORARY_SDK_VERSION(); 147 manifest = await getManifestAsync(dogfoodingHomeUrl, platform, sdkVersion); 148 } catch (e) { 149 const msg = `Unable to download manifest from ${dogfoodingHomeUrl}: ${e.message}`; 150 console[isTurtle ? 'debug' : 'error'](msg); 151 return ''; 152 } 153 154 return kernelManifestObjectToJson(manifest); 155 }, 156 157 async BUILD_MACHINE_KERNEL_MANIFEST(platform) { 158 if (process.env.SHELL_APP_BUILDER) { 159 return ''; 160 } 161 162 const pathToHome = 'home'; 163 const url = await UrlUtils.constructManifestUrlAsync(path.join(EXPO_DIR, pathToHome)); 164 165 try { 166 const manifest = await getManifestAsync(url, platform, null); 167 168 if (manifest.name !== 'expo-home') { 169 console.log( 170 `Manifest at ${url} is not expo-home; using published kernel manifest instead...` 171 ); 172 return ''; 173 } 174 return kernelManifestObjectToJson(manifest); 175 } catch (e) { 176 console.error( 177 chalk.red( 178 `Unable to generate manifest from ${chalk.cyan( 179 pathToHome 180 )}: Failed to fetch manifest from ${chalk.cyan(url)}` 181 ) 182 ); 183 return ''; 184 } 185 }, 186 187 async TEMPORARY_SDK_VERSION(): Promise<string> { 188 return await getHomeSDKVersionAsync(); 189 }, 190 191 INITIAL_URL() { 192 return null; 193 }, 194}; 195