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 extra?: { 18 expoClient?: { 19 name: string; 20 }; 21 }; 22} 23 24// some files are absent on turtle builders and we don't want log errors there 25const isTurtle = !!process.env.TURTLE_WORKING_DIR_PATH; 26 27const EXPO_DIR = getExpoRepositoryRootDir(); 28 29async function getManifestAsync( 30 url: string, 31 platform: string, 32 sdkVersion: string | null 33): Promise<Manifest> { 34 const headers = { 35 'Exponent-Platform': platform, 36 Accept: 'application/expo+json,application/json', 37 }; 38 if (sdkVersion) { 39 headers['Exponent-SDK-Version'] = sdkVersion; 40 } 41 return await ExponentTools.getManifestAsync(url, headers, { 42 logger: { 43 log: () => {}, 44 error: () => {}, 45 info: () => {}, 46 }, 47 }); 48} 49 50async function getSavedDevHomeUrlAsync(): Promise<string> { 51 const devHomeConfig = await new JsonFile(path.join(EXPO_DIR, 'dev-home-config.json')).readAsync(); 52 return devHomeConfig.url as string; 53} 54 55function kernelManifestObjectToJson(manifest) { 56 if (!manifest.id) { 57 // hack for now because unsigned manifest won't have an id 58 manifest.id = '@exponent/home'; 59 } 60 manifest.sdkVersion = 'UNVERSIONED'; 61 return JSON.stringify(manifest); 62} 63 64export default { 65 async TEST_APP_URI() { 66 if (process.env.TEST_SUITE_URI) { 67 return process.env.TEST_SUITE_URI; 68 } else { 69 try { 70 const testSuitePath = path.join(__dirname, '..', '..', '..', 'apps', 'test-suite'); 71 const status = await Project.currentStatus(testSuitePath); 72 if (status === 'running') { 73 return await UrlUtils.constructManifestUrlAsync(testSuitePath); 74 } else { 75 return ''; 76 } 77 } catch { 78 return ''; 79 } 80 } 81 }, 82 83 async TEST_CONFIG() { 84 if (process.env.TEST_CONFIG) { 85 return process.env.TEST_CONFIG; 86 } else { 87 return ''; 88 } 89 }, 90 91 async TEST_SERVER_URL() { 92 let url = 'TODO'; 93 94 try { 95 const lanAddress = ip.address(); 96 const localServerUrl = `http://${lanAddress}:3013`; 97 const response = await fetch(`${localServerUrl}/expo-test-server-status`, { timeout: 500 }); 98 const data = await response.text(); 99 if (data === 'running!') { 100 url = localServerUrl; 101 } 102 } catch {} 103 104 return url; 105 }, 106 107 async TEST_RUN_ID() { 108 return process.env.UNIVERSE_BUILD_ID || crypto.randomUUID(); 109 }, 110 111 async BUILD_MACHINE_LOCAL_HOSTNAME() { 112 if (process.env.SHELL_APP_BUILDER) { 113 return ''; 114 } 115 116 try { 117 const 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 BUILD_MACHINE_KERNEL_MANIFEST(platform) { 144 if (process.env.SHELL_APP_BUILDER) { 145 return ''; 146 } 147 148 if (process.env.CI) { 149 console.log('Skip fetching local manifest on CI.'); 150 return ''; 151 } 152 153 const pathToHome = 'home'; 154 const url = await UrlUtils.constructManifestUrlAsync(path.join(EXPO_DIR, pathToHome)); 155 156 try { 157 const manifest = await getManifestAsync(url, platform, null); 158 159 if (manifest.extra?.expoClient?.name !== 'expo-home') { 160 console.log( 161 `Manifest at ${url} is not expo-home; using published kernel manifest instead...` 162 ); 163 return ''; 164 } 165 return kernelManifestObjectToJson(manifest); 166 } catch { 167 console.error( 168 chalk.red( 169 `Unable to generate manifest from ${chalk.cyan( 170 pathToHome 171 )}: Failed to fetch manifest from ${chalk.cyan(url)}` 172 ) 173 ); 174 return ''; 175 } 176 }, 177 178 async TEMPORARY_SDK_VERSION(): Promise<string> { 179 return await getHomeSDKVersionAsync(); 180 }, 181 182 INITIAL_URL() { 183 return null; 184 }, 185}; 186