Switch to using settings for android sdk location
Summary: A settings screen has been added where android home can be set. This changes the downstream code to use this value rather than the `env.PATH` variable. Reviewed By: passy Differential Revision: D17713288 fbshipit-source-id: 51551652c9c2f468e1117c18785123348e4b4576
This commit is contained in:
committed by
Facebook Github Bot
parent
85c0ec0d13
commit
729e74f2fc
@@ -281,7 +281,7 @@ async function startFlipper(userArguments: UserArguments) {
|
||||
> = [
|
||||
async (userArguments: UserArguments) => {
|
||||
if (userArguments.listDevices) {
|
||||
const devices = await listDevices();
|
||||
const devices = await listDevices(store);
|
||||
const mapped = devices.map(device => {
|
||||
return {
|
||||
os: device.os,
|
||||
@@ -306,7 +306,7 @@ async function startFlipper(userArguments: UserArguments) {
|
||||
async (userArguments: UserArguments, store: Store) => {
|
||||
const {device: selectedDeviceID} = userArguments;
|
||||
if (selectedDeviceID) {
|
||||
const devices = await listDevices();
|
||||
const devices = await listDevices(store);
|
||||
const matchedDevice = devices.find(
|
||||
device => device.serial === selectedDeviceID,
|
||||
);
|
||||
|
||||
@@ -47,7 +47,6 @@ class DevicesButton extends Component<Props> {
|
||||
// On Linux, you must run the emulator from the directory it's in because
|
||||
// reasons ...
|
||||
whichPromise('emulator')
|
||||
.catch(() => `${process.env.ANDROID_HOME || ''}/tools/emulator`)
|
||||
.then(emulatorPath => {
|
||||
if (emulatorPath) {
|
||||
const child = spawn(emulatorPath, [`@${name}`], {
|
||||
|
||||
@@ -48,8 +48,10 @@ function createDevice(
|
||||
});
|
||||
}
|
||||
|
||||
export async function getActiveAndroidDevices(): Promise<Array<BaseDevice>> {
|
||||
const client = await getAdbClient();
|
||||
export async function getActiveAndroidDevices(
|
||||
store: Store,
|
||||
): Promise<Array<BaseDevice>> {
|
||||
const client = await getAdbClient(store);
|
||||
const androidDevices = await client.listDevices();
|
||||
return await Promise.all(
|
||||
androidDevices.map(device => createDevice(client, device)),
|
||||
@@ -102,7 +104,7 @@ export default (store: Store, logger: Logger) => {
|
||||
);
|
||||
});
|
||||
|
||||
getAdbClient()
|
||||
getAdbClient(store)
|
||||
.then(client => {
|
||||
client
|
||||
.trackDevices()
|
||||
@@ -233,7 +235,7 @@ export default (store: Store, logger: Logger) => {
|
||||
|
||||
// cleanup method
|
||||
return () =>
|
||||
getAdbClient().then(client => {
|
||||
getAdbClient(store).then(client => {
|
||||
client.kill();
|
||||
});
|
||||
};
|
||||
|
||||
20
src/init.tsx
20
src/init.tsx
@@ -16,7 +16,7 @@ import BugReporter from './fb-stubs/BugReporter';
|
||||
import setupPrefetcher from './fb-stubs/Prefetcher';
|
||||
import {createStore} from 'redux';
|
||||
import {persistStore} from 'redux-persist';
|
||||
import reducers, {Actions, State as StoreState} from './reducers/index';
|
||||
import reducers, {Store, Actions, State as StoreState} from './reducers/index';
|
||||
import dispatcher from './dispatcher/index';
|
||||
import TooltipProvider from './ui/components/TooltipProvider';
|
||||
import config from './utils/processConfig';
|
||||
@@ -27,6 +27,7 @@ import fbConfig from './fb-stubs/config';
|
||||
import {isFBEmployee} from './utils/fbEmployee';
|
||||
import WarningEmployee from './chrome/WarningEmployee';
|
||||
import React from 'react';
|
||||
import path from 'path';
|
||||
|
||||
const store = createStore<StoreState, Actions, any, any>(
|
||||
reducers,
|
||||
@@ -72,6 +73,21 @@ const AppFrame = () => {
|
||||
);
|
||||
};
|
||||
|
||||
function setProcessState(store: Store) {
|
||||
const androidHome = store.getState().settingsState.androidHome;
|
||||
|
||||
if (!process.env.ANDROID_HOME) {
|
||||
process.env.ANDROID_HOME = androidHome;
|
||||
}
|
||||
|
||||
// emulator/emulator is more reliable than tools/emulator, so prefer it if
|
||||
// it exists
|
||||
process.env.PATH =
|
||||
['emulator', 'tools', 'platform-tools']
|
||||
.map(directory => path.resolve(androidHome, directory))
|
||||
.join(':') + `:${process.env.PATH}`;
|
||||
}
|
||||
|
||||
function init() {
|
||||
ReactDOM.render(<AppFrame />, document.getElementById('root'));
|
||||
initLauncherHooks(config(), store);
|
||||
@@ -85,6 +101,8 @@ function init() {
|
||||
|
||||
// rehydrate app state before exposing init
|
||||
persistStore(store, undefined, () => {
|
||||
// Make sure process state is set before dispatchers run
|
||||
setProcessState(store);
|
||||
dispatcher(store, logger);
|
||||
// make init function callable from outside
|
||||
window.Flipper.init = init;
|
||||
|
||||
@@ -64,7 +64,7 @@ class Server extends EventEmitter {
|
||||
super();
|
||||
this.logger = logger;
|
||||
this.connections = new Map();
|
||||
this.certificateProvider = new CertificateProvider(this, logger);
|
||||
this.certificateProvider = new CertificateProvider(this, logger, store);
|
||||
this.connectionTracker = new ConnectionTracker(logger);
|
||||
this.secureServer = null;
|
||||
this.insecureServer = null;
|
||||
|
||||
@@ -21,6 +21,7 @@ import {getAdbClient} from './adbClient';
|
||||
import * as androidUtil from './androidContainerUtility';
|
||||
import os from 'os';
|
||||
import {Client as ADBClient} from 'adbkit';
|
||||
import {Store} from '../reducers/index';
|
||||
|
||||
const tmpFile = promisify(tmp.file) as (
|
||||
options?: FileOptions,
|
||||
@@ -77,9 +78,9 @@ export default class CertificateProvider {
|
||||
certificateSetup: Promise<void>;
|
||||
server: Server;
|
||||
|
||||
constructor(server: Server, logger: Logger) {
|
||||
constructor(server: Server, logger: Logger, store: Store) {
|
||||
this.logger = logger;
|
||||
this.adb = getAdbClient();
|
||||
this.adb = getAdbClient(store);
|
||||
this.certificateSetup = reportPlatformFailures(
|
||||
this.ensureServerCertExists(),
|
||||
'ensureServerCertExists',
|
||||
@@ -200,9 +201,15 @@ export default class CertificateProvider {
|
||||
const deviceIdPromise = appNamePromise.then(app =>
|
||||
this.getTargetAndroidDeviceId(app, destination, csr),
|
||||
);
|
||||
return Promise.all([deviceIdPromise, appNamePromise]).then(
|
||||
([deviceId, appName]) =>
|
||||
androidUtil.push(deviceId, appName, destination + filename, contents),
|
||||
return Promise.all([deviceIdPromise, appNamePromise, this.adb]).then(
|
||||
([deviceId, appName, adbClient]) =>
|
||||
androidUtil.push(
|
||||
adbClient,
|
||||
deviceId,
|
||||
appName,
|
||||
destination + filename,
|
||||
contents,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (os === 'iOS' || os === 'windows' || os == 'MacOS') {
|
||||
@@ -343,8 +350,15 @@ export default class CertificateProvider {
|
||||
processName: string,
|
||||
csr: string,
|
||||
): Promise<boolean> {
|
||||
return androidUtil
|
||||
.pull(deviceId, processName, directory + csrFileName)
|
||||
return this.adb
|
||||
.then(adbClient =>
|
||||
androidUtil.pull(
|
||||
adbClient,
|
||||
deviceId,
|
||||
processName,
|
||||
directory + csrFileName,
|
||||
),
|
||||
)
|
||||
.then(deviceCsr => {
|
||||
// Santitize both of the string before comparation
|
||||
// The csr string extraction on client side return string in both way
|
||||
|
||||
@@ -10,13 +10,15 @@ import child_process from 'child_process';
|
||||
import promiseRetry from 'promise-retry';
|
||||
import adbConfig from '../utils/adbConfig';
|
||||
import adbkit, {Client} from 'adbkit';
|
||||
import {Store} from '../reducers/index';
|
||||
import path from 'path';
|
||||
|
||||
const MAX_RETRIES = 5;
|
||||
let instance: Promise<Client>;
|
||||
|
||||
export function getAdbClient(): Promise<Client> {
|
||||
export function getAdbClient(store: Store): Promise<Client> {
|
||||
if (!instance) {
|
||||
instance = reportPlatformFailures(createClient(), 'createADBClient');
|
||||
instance = reportPlatformFailures(createClient(store), 'createADBClient');
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
@@ -24,10 +26,9 @@ export function getAdbClient(): Promise<Client> {
|
||||
/* Adbkit will attempt to start the adb server if it's not already running,
|
||||
however, it sometimes fails with ENOENT errors. So instead, we start it
|
||||
manually before requesting a client. */
|
||||
function createClient(): Promise<Client> {
|
||||
const adbPath = process.env.ANDROID_HOME
|
||||
? `${process.env.ANDROID_HOME}/platform-tools/adb`
|
||||
: 'adb';
|
||||
function createClient(store: Store): Promise<Client> {
|
||||
const androidHome = store.getState().settingsState.androidHome;
|
||||
const adbPath = path.resolve(androidHome, 'platform-tools/adb');
|
||||
return reportPlatformFailures<Client>(
|
||||
promisify(child_process.exec)(`${adbPath} start-server`).then(() =>
|
||||
adbkit.createClient(adbConfig()),
|
||||
|
||||
@@ -11,8 +11,10 @@ import {
|
||||
_push,
|
||||
_pull,
|
||||
} from './androidContainerUtilityInternal';
|
||||
import {Client} from 'adbkit';
|
||||
|
||||
export function push(
|
||||
client: Client,
|
||||
deviceId: string,
|
||||
app: string,
|
||||
filepath: string,
|
||||
@@ -21,20 +23,21 @@ export function push(
|
||||
return validateAppName(app).then(validApp =>
|
||||
validateFilePath(filepath).then(validFilepath =>
|
||||
validateFileContent(contents).then(validContent =>
|
||||
_push(deviceId, validApp, validFilepath, validContent),
|
||||
_push(client, deviceId, validApp, validFilepath, validContent),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function pull(
|
||||
client: Client,
|
||||
deviceId: string,
|
||||
app: string,
|
||||
path: string,
|
||||
): Promise<string> {
|
||||
return validateAppName(app).then(validApp =>
|
||||
validateFilePath(path).then(validPath =>
|
||||
_pull(deviceId, validApp, validPath),
|
||||
_pull(client, deviceId, validApp, validPath),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
* opaque types will ensure the commands are only ever run on validated
|
||||
* arguments.
|
||||
*/
|
||||
import {getAdbClient} from './adbClient';
|
||||
import {UnsupportedError} from './metrics';
|
||||
import adbkit from 'adbkit';
|
||||
import adbkit, {Client} from 'adbkit';
|
||||
|
||||
const allowedAppNameRegex = /^[a-zA-Z0-9._\-]+$/;
|
||||
const appNotDebuggableRegex = /debuggable/;
|
||||
@@ -48,6 +47,7 @@ export function validateFileContent(content: string): Promise<FileContent> {
|
||||
}
|
||||
|
||||
export function _push(
|
||||
client: Client,
|
||||
deviceId: string,
|
||||
app: AppName,
|
||||
filename: FilePath,
|
||||
@@ -55,6 +55,7 @@ export function _push(
|
||||
): Promise<void> {
|
||||
console.debug(`Deploying ${filename} to ${deviceId}:${app}`, logTag);
|
||||
return executeCommandAsApp(
|
||||
client,
|
||||
deviceId,
|
||||
app,
|
||||
`echo "${contents}" > '${filename}' && chmod 644 '${filename}'`,
|
||||
@@ -62,23 +63,23 @@ export function _push(
|
||||
}
|
||||
|
||||
export function _pull(
|
||||
client: Client,
|
||||
deviceId: string,
|
||||
app: AppName,
|
||||
path: FilePath,
|
||||
): Promise<string> {
|
||||
return executeCommandAsApp(deviceId, app, `cat '${path}'`);
|
||||
return executeCommandAsApp(client, deviceId, app, `cat '${path}'`);
|
||||
}
|
||||
|
||||
// Keep this method private since it relies on pre-validated arguments
|
||||
function executeCommandAsApp(
|
||||
client: Client,
|
||||
deviceId: string,
|
||||
app: string,
|
||||
command: string,
|
||||
): Promise<string> {
|
||||
return getAdbClient()
|
||||
.then(client =>
|
||||
client.shell(deviceId, `echo '${command}' | run-as '${app}'`),
|
||||
)
|
||||
return client
|
||||
.shell(deviceId, `echo '${command}' | run-as '${app}'`)
|
||||
.then(adbkit.util.readAll)
|
||||
.then(buffer => buffer.toString())
|
||||
.then(output => {
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
import {getActiveAndroidDevices} from '../dispatcher/androidDevice';
|
||||
import {getActiveDevicesAndSimulators} from '../dispatcher/iOSDevice';
|
||||
import BaseDevice from '../devices/BaseDevice';
|
||||
import {Store} from '../reducers/index';
|
||||
|
||||
export async function listDevices(): Promise<Array<BaseDevice>> {
|
||||
const androidDevices = await getActiveAndroidDevices();
|
||||
export async function listDevices(store: Store): Promise<Array<BaseDevice>> {
|
||||
const androidDevices = await getActiveAndroidDevices(store);
|
||||
const iOSDevices: BaseDevice[] = await getActiveDevicesAndSimulators();
|
||||
return iOSDevices.concat(androidDevices);
|
||||
}
|
||||
|
||||
@@ -10,17 +10,6 @@ const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
module.exports = function(argv) {
|
||||
if (!process.env.ANDROID_HOME) {
|
||||
process.env.ANDROID_HOME = '/opt/android_sdk';
|
||||
}
|
||||
|
||||
// emulator/emulator is more reliable than tools/emulator, so prefer it if
|
||||
// it exists
|
||||
process.env.PATH =
|
||||
['emulator', 'tools', 'platform-tools']
|
||||
.map(directory => `${process.env.ANDROID_HOME}/${directory}`)
|
||||
.join(':') + `:${process.env.PATH}`;
|
||||
|
||||
// ensure .flipper folder and config exist
|
||||
const flipperDir = path.join(os.homedir(), '.flipper');
|
||||
if (!fs.existsSync(flipperDir)) {
|
||||
|
||||
Reference in New Issue
Block a user