xref: /expo/tools/src/dynamic-macros/macros.ts (revision 8285c032)
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