1import JsonFile from '@expo/json-file'; 2import spawnAsync from '@expo/spawn-async'; 3import { ExponentTools, Project, UrlUtils } from '@expo/xdl'; 4import chalk from 'chalk'; 5import crypto from 'crypto'; 6import ip from 'ip'; 7import fetch from 'node-fetch'; 8import os from 'os'; 9import path from 'path'; 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://exp.host/@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 const testSuitePath = path.join(__dirname, '..', '..', '..', 'apps', 'test-suite'); 68 const status = await Project.currentStatus(testSuitePath); 69 if (status === 'running') { 70 return await UrlUtils.constructManifestUrlAsync(testSuitePath); 71 } else { 72 return ''; 73 } 74 } catch { 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 const lanAddress = ip.address(); 93 const localServerUrl = `http://${lanAddress}:3013`; 94 const response = await fetch(`${localServerUrl}/expo-test-server-status`, { timeout: 500 }); 95 const data = await response.text(); 96 if (data === 'running!') { 97 url = localServerUrl; 98 } 99 } catch {} 100 101 return url; 102 }, 103 104 async TEST_RUN_ID() { 105 return process.env.UNIVERSE_BUILD_ID || crypto.randomUUID(); 106 }, 107 108 async BUILD_MACHINE_LOCAL_HOSTNAME() { 109 if (process.env.SHELL_APP_BUILDER) { 110 return ''; 111 } 112 113 try { 114 const result = await spawnAsync('scutil', ['--get', 'LocalHostName']); 115 return `${result.stdout.trim()}.local`; 116 } catch (e) { 117 if (e.code !== 'ENOENT') { 118 console.error(e.stack); 119 } 120 return os.hostname(); 121 } 122 }, 123 124 async DEV_PUBLISHED_KERNEL_MANIFEST(platform) { 125 let manifest, savedDevHomeUrl; 126 try { 127 savedDevHomeUrl = await getSavedDevHomeUrlAsync(); 128 const sdkVersion = await this.TEMPORARY_SDK_VERSION(); 129 130 manifest = await getManifestAsync(savedDevHomeUrl, platform, sdkVersion); 131 } catch (e) { 132 const msg = `Unable to download manifest from ${savedDevHomeUrl}: ${e.message}`; 133 console[isTurtle ? 'debug' : 'error'](msg); 134 return ''; 135 } 136 137 return kernelManifestObjectToJson(manifest); 138 }, 139 140 async DOGFOODING_PUBLISHED_KERNEL_MANIFEST(platform) { 141 let manifest: Manifest; 142 try { 143 const sdkVersion = await this.TEMPORARY_SDK_VERSION(); 144 manifest = await getManifestAsync(dogfoodingHomeUrl, platform, sdkVersion); 145 } catch (e) { 146 const msg = `Unable to download manifest from ${dogfoodingHomeUrl}: ${e.message}`; 147 console[isTurtle ? 'debug' : 'error'](msg); 148 return ''; 149 } 150 151 return kernelManifestObjectToJson(manifest); 152 }, 153 154 async BUILD_MACHINE_KERNEL_MANIFEST(platform) { 155 if (process.env.SHELL_APP_BUILDER) { 156 return ''; 157 } 158 159 const pathToHome = 'home'; 160 const url = await UrlUtils.constructManifestUrlAsync(path.join(EXPO_DIR, pathToHome)); 161 162 try { 163 const manifest = await getManifestAsync(url, platform, null); 164 165 if (manifest.name !== 'expo-home') { 166 console.log( 167 `Manifest at ${url} is not expo-home; using published kernel manifest instead...` 168 ); 169 return ''; 170 } 171 return kernelManifestObjectToJson(manifest); 172 } catch { 173 console.error( 174 chalk.red( 175 `Unable to generate manifest from ${chalk.cyan( 176 pathToHome 177 )}: Failed to fetch manifest from ${chalk.cyan(url)}` 178 ) 179 ); 180 return ''; 181 } 182 }, 183 184 async TEMPORARY_SDK_VERSION(): Promise<string> { 185 return await getHomeSDKVersionAsync(); 186 }, 187 188 INITIAL_URL() { 189 return null; 190 }, 191}; 192