Move app/server to flipper-server-core
Summary: moved `app/src/server` to `flipper-server-core/src` and fixed any fallout from that (aka integration points I missed on the preparing diffs). Reviewed By: passy Differential Revision: D31541378 fbshipit-source-id: 8a7e0169ebefa515781f6e5e0f7b926415d4b7e9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3e7a6b1b4b
commit
d88b28330a
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {Mutex} from 'async-mutex';
|
||||
import {exec as unsafeExec, Output} from 'promisify-child-process';
|
||||
import {reportPlatformFailures} from 'flipper-common';
|
||||
import {promises, constants} from 'fs';
|
||||
import memoize from 'lodash.memoize';
|
||||
import {notNull} from '../../utils/typeUtils';
|
||||
import {promisify} from 'util';
|
||||
import child_process from 'child_process';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
const exec = promisify(child_process.exec);
|
||||
|
||||
// Use debug to get helpful logs when idb fails
|
||||
const idbLogLevel = 'DEBUG';
|
||||
const operationPrefix = 'iosContainerUtility';
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
type IdbTarget = {
|
||||
name: string;
|
||||
udid: string;
|
||||
state: 'Booted' | 'Shutdown';
|
||||
type: string | DeviceType;
|
||||
target_type?: string | DeviceType;
|
||||
os_version: string;
|
||||
architecture: string;
|
||||
};
|
||||
|
||||
export type DeviceType = 'physical' | 'emulator';
|
||||
|
||||
export type DeviceTarget = {
|
||||
udid: string;
|
||||
type: DeviceType;
|
||||
name: string;
|
||||
};
|
||||
|
||||
function isAvailable(idbPath: string): Promise<boolean> {
|
||||
if (!idbPath) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return promises
|
||||
.access(idbPath, constants.X_OK)
|
||||
.then((_) => true)
|
||||
.catch((_) => false);
|
||||
}
|
||||
|
||||
function safeExec(
|
||||
command: string,
|
||||
): Promise<{stdout: string; stderr: string} | Output> {
|
||||
return mutex
|
||||
.acquire()
|
||||
.then((release) => unsafeExec(command).finally(release));
|
||||
}
|
||||
|
||||
export async function queryTargetsWithoutXcodeDependency(
|
||||
idbCompanionPath: string,
|
||||
isPhysicalDeviceEnabled: boolean,
|
||||
isAvailableFunc: (idbPath: string) => Promise<boolean>,
|
||||
safeExecFunc: (
|
||||
command: string,
|
||||
) => Promise<{stdout: string; stderr: string} | Output>,
|
||||
): Promise<Array<DeviceTarget>> {
|
||||
if (await isAvailableFunc(idbCompanionPath)) {
|
||||
return safeExecFunc(`${idbCompanionPath} --list 1 --only device`)
|
||||
.then(({stdout}) => parseIdbTargets(stdout!.toString()))
|
||||
.then((devices) => {
|
||||
if (devices.length > 0 && !isPhysicalDeviceEnabled) {
|
||||
// TODO: Show a notification to enable the toggle or integrate Doctor to better suggest this advice.
|
||||
console.warn(
|
||||
'You are trying to connect Physical Device. Please enable the toggle "Enable physical iOS device" from the setting screen.',
|
||||
);
|
||||
}
|
||||
return devices;
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
console.warn(
|
||||
'Failed to query idb_companion --list 1 --only device for physical targets:',
|
||||
e,
|
||||
);
|
||||
return [];
|
||||
});
|
||||
} else {
|
||||
console.warn(
|
||||
`Unable to locate idb_companion in ${idbCompanionPath}. Try running sudo yum install -y fb-idb`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function parseIdbTargets(lines: string): Array<DeviceTarget> {
|
||||
return lines
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => JSON.parse(line))
|
||||
.filter(({state}: IdbTarget) => state.toLocaleLowerCase() === 'booted')
|
||||
.map<IdbTarget>(({type, target_type, ...rest}: IdbTarget) => ({
|
||||
type: (type || target_type) === 'simulator' ? 'emulator' : 'physical',
|
||||
...rest,
|
||||
}))
|
||||
.map<DeviceTarget>((target: IdbTarget) => ({
|
||||
udid: target.udid,
|
||||
type: target.type as DeviceType,
|
||||
name: target.name,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function idbListTargets(
|
||||
idbPath: string,
|
||||
safeExecFunc: (
|
||||
command: string,
|
||||
) => Promise<{stdout: string; stderr: string} | Output> = safeExec,
|
||||
): Promise<Array<DeviceTarget>> {
|
||||
return safeExecFunc(`${idbPath} list-targets --json`)
|
||||
.then(({stdout}) =>
|
||||
// See above.
|
||||
parseIdbTargets(stdout!.toString()),
|
||||
)
|
||||
.catch((e: Error) => {
|
||||
console.warn('Failed to query idb for targets:', e);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
async function targets(
|
||||
idbPath: string,
|
||||
isPhysicalDeviceEnabled: boolean,
|
||||
): Promise<Array<DeviceTarget>> {
|
||||
if (process.platform !== 'darwin') {
|
||||
return [];
|
||||
}
|
||||
const isXcodeInstalled = await isXcodeDetected();
|
||||
if (!isXcodeInstalled) {
|
||||
if (!isPhysicalDeviceEnabled) {
|
||||
// TODO: Show a notification to enable the toggle or integrate Doctor to better suggest this advice.
|
||||
console.warn(
|
||||
'You are trying to connect Physical Device. Please enable the toggle "Enable physical iOS device" from the setting screen.',
|
||||
);
|
||||
}
|
||||
const idbCompanionPath = path.dirname(idbPath) + '/idb_companion';
|
||||
return queryTargetsWithoutXcodeDependency(
|
||||
idbCompanionPath,
|
||||
isPhysicalDeviceEnabled,
|
||||
isAvailable,
|
||||
safeExec,
|
||||
);
|
||||
}
|
||||
|
||||
// Not all users have idb installed because you can still use
|
||||
// Flipper with Simulators without it.
|
||||
// But idb is MUCH more CPU efficient than xcrun, so
|
||||
// when installed, use it. This still holds true
|
||||
// with the move from instruments to xcrun.
|
||||
// TODO: Move idb availability check up.
|
||||
if (await memoize(isAvailable)(idbPath)) {
|
||||
return await idbListTargets(idbPath);
|
||||
} else {
|
||||
return safeExec('xcrun xctrace list devices')
|
||||
.then(({stdout}) =>
|
||||
stdout!
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => /(.+) \([^(]+\) \[(.*)\]( \(Simulator\))?/.exec(line))
|
||||
.filter(notNull)
|
||||
.filter(([_match, _name, _udid, isSim]) => !isSim)
|
||||
.map<DeviceTarget>(([_match, name, udid]) => {
|
||||
return {udid, type: 'physical', name};
|
||||
}),
|
||||
)
|
||||
.catch((e) => {
|
||||
console.warn('Failed to query for devices using xctrace:', e);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function push(
|
||||
udid: string,
|
||||
src: string,
|
||||
bundleId: string,
|
||||
dst: string,
|
||||
idbPath: string,
|
||||
): Promise<void> {
|
||||
await memoize(checkIdbIsInstalled)(idbPath);
|
||||
return wrapWithErrorMessage(
|
||||
reportPlatformFailures(
|
||||
safeExec(
|
||||
`${idbPath} --log ${idbLogLevel} file push --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`,
|
||||
)
|
||||
.then(() => {
|
||||
return;
|
||||
})
|
||||
.catch((e) => handleMissingIdb(e, idbPath)),
|
||||
`${operationPrefix}:push`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async function pull(
|
||||
udid: string,
|
||||
src: string,
|
||||
bundleId: string,
|
||||
dst: string,
|
||||
idbPath: string,
|
||||
): Promise<void> {
|
||||
await memoize(checkIdbIsInstalled)(idbPath);
|
||||
return wrapWithErrorMessage(
|
||||
reportPlatformFailures(
|
||||
safeExec(
|
||||
`${idbPath} --log ${idbLogLevel} file pull --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`,
|
||||
)
|
||||
.then(() => {
|
||||
return;
|
||||
})
|
||||
.catch((e) => handleMissingIdb(e, idbPath))
|
||||
.catch((e) => handleMissingPermissions(e)),
|
||||
`${operationPrefix}:pull`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export async function checkIdbIsInstalled(idbPath: string): Promise<void> {
|
||||
const isInstalled = await isAvailable(idbPath);
|
||||
if (!isInstalled) {
|
||||
throw new Error(
|
||||
`idb is required to use iOS devices. Install it with instructions from https://github.com/facebook/idb and set the installation path in Flipper settings.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The fb-internal idb binary is a shim that downloads the proper one on first run. It requires sudo to do so.
|
||||
// If we detect this, Tell the user how to fix it.
|
||||
function handleMissingIdb(e: Error, idbPath: string): void {
|
||||
if (
|
||||
e.message &&
|
||||
e.message.includes('sudo: no tty present and no askpass program specified')
|
||||
) {
|
||||
console.warn(e);
|
||||
throw new Error(
|
||||
`idb doesn't appear to be installed. Run "${idbPath} list-targets" to fix this.`,
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
function handleMissingPermissions(e: Error): void {
|
||||
if (
|
||||
e.message &&
|
||||
e.message.includes('Command failed') &&
|
||||
e.message.includes('file pull') &&
|
||||
e.message.includes('sonar/app.csr')
|
||||
) {
|
||||
console.warn(e);
|
||||
throw new Error(
|
||||
'Cannot connect to iOS application. idb_certificate_pull_failed' +
|
||||
'Idb lacks permissions to exchange certificates. Did you install a source build ([FB] or enable certificate exchange)? ' +
|
||||
e,
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
function wrapWithErrorMessage<T>(p: Promise<T>): Promise<T> {
|
||||
return p.catch((e: Error) => {
|
||||
console.warn(e);
|
||||
// Give the user instructions. Don't embed the error because it's unique per invocation so won't be deduped.
|
||||
throw new Error(
|
||||
"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 Flipper Support.",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function isXcodeDetected(): Promise<boolean> {
|
||||
return exec('xcode-select -p')
|
||||
.then(({stdout}) => {
|
||||
return fs.pathExists(stdout.trim());
|
||||
})
|
||||
.catch((_) => false);
|
||||
}
|
||||
|
||||
export default {
|
||||
isAvailable,
|
||||
targets,
|
||||
push,
|
||||
pull,
|
||||
isXcodeDetected,
|
||||
};
|
||||
Reference in New Issue
Block a user