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