microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ffdf159274093d12e32a09aafae6d0de3099bb4a

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/ios/iOSContainerUtility.ts

192lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import { ChildProcess } from "../../common/node/childProcess";
5import { notNullOrUndefined } from "../../common/utils";
6import { PromiseUtil } from "../../common/node/promise";
7import { IVirtualDevice } from "../VirtualDeviceManager";
8import { DeviceType } from "../launchArgs";
9import { OutputChannelLogger } from "../log/OutputChannelLogger";
10import { promises, constants } from "fs";
11
12/**
13 * @preserve
14 * Start region: the code is borrowed from https://github.com/facebook/flipper/blob/v0.79.1/desktop/app/src/utils/iOSContainerUtility.tsx
15 *
16 * Copyright (c) Facebook, Inc. and its affiliates.
17 *
18 * This source code is licensed under the MIT license found in the
19 * LICENSE file in the root directory of this source tree.
20 *
21 * @format
22 */
23
24export const idbPath = "/usr/local/bin/idb";
25// Use debug to get helpful logs when idb fails
26const idbLogLevel = "DEBUG";
27
28type IdbTarget = {
29 name: string;
30 udid: string;
31 state: string;
32 type: string;
33 os_version: string;
34 architecture: string;
35};
36
37export interface DeviceTarget extends IVirtualDevice {
38 type: DeviceType;
39 state: string;
40}
41
42const isIdbAvailable = PromiseUtil.promiseCacheDecorator<boolean>(isAvailable);
43
44function isAvailable(): Promise<boolean> {
45 if (!idbPath) {
46 return Promise.resolve(false);
47 }
48 return promises
49 .access(idbPath, constants.X_OK)
50 .then(() => true)
51 .catch(() => false);
52}
53
54async function targets(): Promise<Array<DeviceTarget>> {
55 const cp = new ChildProcess();
56 if (process.platform !== "darwin") {
57 return [];
58 }
59
60 // Not all users have idb installed because you can still use
61 // Flipper with Simulators without it.
62 // But idb is MUCH more CPU efficient than instruments, so
63 // when installed, use it.
64 if (await isIdbAvailable()) {
65 return cp.execToString(`${idbPath} list-targets --json`).then(stdout =>
66 // It is safe to assume this to be non-null as it only turns null
67 // if the output redirection is misconfigured:
68 // https://stackoverflow.com/questions/27786228/node-child-process-spawn-stdout-returning-as-null
69 stdout!
70 .trim()
71 .split("\n")
72 .map(line => line.trim())
73 .filter(Boolean)
74 .map(line => JSON.parse(line))
75 .filter(({ type }: IdbTarget) => type !== "simulator")
76 .map((target: IdbTarget) => {
77 return {
78 id: target.udid,
79 type: "device",
80 name: target.name,
81 state: target.state,
82 };
83 }),
84 );
85 } else {
86 await cp.killOrphanedInstrumentsProcesses();
87 return cp.execToString("instruments -s devices").then(stdout =>
88 stdout!
89 .toString()
90 .split("\n")
91 .map(line => line.trim())
92 .filter(Boolean)
93 .map(line => /(.+) \([^(]+\) \[(.*)\]( \(Simulator\))?/.exec(line))
94 .filter(notNullOrUndefined)
95 .filter(([_match, _name, _udid, isSim]) => !isSim)
96 .map(([_match, name, udid]) => {
97 return {
98 id: udid,
99 type: "device",
100 name,
101 state: "active",
102 };
103 }),
104 );
105 }
106}
107
108async function push(
109 udid: string,
110 src: string,
111 bundleId: string,
112 dst: string,
113 logger?: OutputChannelLogger,
114): Promise<void> {
115 const cp = new ChildProcess();
116 await checkIdbIsInstalled();
117 return wrapWithErrorMessage(
118 cp
119 .execToString(
120 `${idbPath} --log ${idbLogLevel} file push --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`,
121 )
122 .then(() => {
123 return;
124 })
125 .catch(e => handleMissingIdb(e)),
126 logger,
127 );
128}
129
130async function pull(
131 udid: string,
132 src: string,
133 bundleId: string,
134 dst: string,
135 logger?: OutputChannelLogger,
136): Promise<void> {
137 const cp = new ChildProcess();
138 await checkIdbIsInstalled();
139 return wrapWithErrorMessage(
140 cp
141 .execToString(
142 `${idbPath} --log ${idbLogLevel} file pull --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`,
143 )
144 .then(() => {
145 return;
146 })
147 .catch(e => handleMissingIdb(e)),
148 logger,
149 );
150}
151
152export async function checkIdbIsInstalled(): Promise<void> {
153 const isInstalled = await isIdbAvailable();
154 if (!isInstalled) {
155 throw new Error(
156 `idb is required to use iOS devices. Please install it with instructions from https://github.com/facebook/idb.`,
157 );
158 }
159}
160
161// The fb-internal idb binary is a shim that downloads the proper one on first run. It requires sudo to do so.
162// If we detect this, Tell the user how to fix it.
163function handleMissingIdb(e: Error): void {
164 if (e.message && e.message.includes("sudo: no tty present and no askpass program specified")) {
165 throw new Error(
166 `idb doesn't appear to be installed. Run "${idbPath} list-targets" to fix this.`,
167 );
168 }
169 throw e;
170}
171
172function wrapWithErrorMessage<T>(p: Promise<T>, logger?: OutputChannelLogger): Promise<T> {
173 return p.catch((e: Error) => {
174 logger?.error(e.message);
175 // Give the user instructions. Don't embed the error because it's unique per invocation so won't be deduped.
176 throw new Error(
177 "A problem with idb has ocurred. Please run `sudo rm -rf /tmp/idb*` and `sudo yum install -y fb-idb` to update it, if that doesn't fix it, post in https://github.com/microsoft/vscode-react-native.",
178 );
179 });
180}
181
182export default {
183 isAvailable,
184 targets,
185 push,
186 pull,
187};
188
189/**
190 * @preserve
191 * End region: https://github.com/facebook/flipper/blob/v0.79.1/desktop/app/src/utils/iOSContainerUtility.tsx
192 */
193