Move flipper-doctor check running to flipper-server-core

Summary: Per title. Two new server API's: get-healthchecks, and run-healtcheck. Types have all been moved to flipper-common, so that they can be used by doctor, server-core and ui-core packages. Since it were quite some, moved them into a FlipperDoctor namespace.

Reviewed By: nikoant

Differential Revision: D32720510

fbshipit-source-id: 37aa35cde6ebd58479cf0dffec5b7b2da6d22198
This commit is contained in:
Michel Weststrate
2021-12-08 04:25:28 -08:00
committed by Facebook GitHub Bot
parent 2a4fe77404
commit 2480ed30c5
19 changed files with 373 additions and 276 deletions

View File

@@ -25,7 +25,7 @@ import {getEnvInfo} from './environmentInfo';
category.healthchecks.map(async ({key, label, run}) => ({
key,
label,
result: await run(environmentInfo),
result: await run!(environmentInfo),
})),
),
},

View File

@@ -8,28 +8,7 @@
*/
import {run} from 'envinfo';
export type EnvironmentInfo = {
SDKs: {
'iOS SDK': {
Platforms: string[];
};
'Android SDK':
| {
'API Levels': string[] | 'Not Found';
'Build Tools': string[] | 'Not Found';
'System Images': string[] | 'Not Found';
'Android NDK': string | 'Not Found';
}
| 'Not Found';
};
IDEs: {
Xcode: {
version: string;
path: string;
};
};
};
import {FlipperDoctor} from 'flipper-common';
async function retrieveAndParseEnvInfo(): Promise<any> {
return JSON.parse(
@@ -43,6 +22,6 @@ async function retrieveAndParseEnvInfo(): Promise<any> {
);
}
export async function getEnvInfo(): Promise<EnvironmentInfo> {
export async function getEnvInfo(): Promise<FlipperDoctor.EnvironmentInfo> {
return await retrieveAndParseEnvInfo();
}

View File

@@ -9,65 +9,15 @@
import {exec} from 'child_process';
import {promisify} from 'util';
import {EnvironmentInfo, getEnvInfo} from './environmentInfo';
export {EnvironmentInfo, getEnvInfo} from './environmentInfo';
import {getEnvInfo} from './environmentInfo';
export {getEnvInfo} from './environmentInfo';
import * as watchman from 'fb-watchman';
import * as fs from 'fs';
import * as path from 'path';
import {FlipperDoctor} from 'flipper-common';
export type HealthcheckCategory = {
label: string;
isSkipped: false;
isRequired: boolean;
healthchecks: Healthcheck[];
};
export type SkippedHealthcheckCategory = {
label: string;
isSkipped: true;
skipReason: string;
};
export type Healthchecks = {
common: HealthcheckCategory | SkippedHealthcheckCategory;
android: HealthcheckCategory | SkippedHealthcheckCategory;
ios: HealthcheckCategory | SkippedHealthcheckCategory;
};
export type Settings = {
idbPath: string;
enablePhysicalIOS: boolean;
};
export type Healthcheck = {
key: string;
label: string;
isRequired?: boolean;
run: (
env: EnvironmentInfo,
settings?: Settings,
) => Promise<HealthcheckRunResult>;
};
export type HealthcheckRunResult = {
hasProblem: boolean;
message: string;
};
export type CategoryResult = [
string,
{
label: string;
results: Array<{
key: string;
label: string;
isRequired: boolean;
result: {hasProblem: boolean};
}>;
},
];
export function getHealthchecks(): Healthchecks {
export function getHealthchecks(): FlipperDoctor.Healthchecks {
return {
common: {
label: 'Common',
@@ -77,7 +27,7 @@ export function getHealthchecks(): Healthchecks {
{
key: 'common.openssl',
label: 'OpenSSL Installed',
run: async (_: EnvironmentInfo) => {
run: async (_: FlipperDoctor.EnvironmentInfo) => {
const result = await tryExecuteCommand('openssl version');
const hasProblem = result.hasProblem;
const message = hasProblem
@@ -92,7 +42,7 @@ export function getHealthchecks(): Healthchecks {
{
key: 'common.watchman',
label: 'Watchman Installed',
run: async (_: EnvironmentInfo) => {
run: async (_: FlipperDoctor.EnvironmentInfo) => {
const isAvailable = await isWatchmanAvailable();
return {
hasProblem: !isAvailable,
@@ -113,11 +63,11 @@ export function getHealthchecks(): Healthchecks {
key: 'android.sdk',
label: 'SDK Installed',
isRequired: true,
run: async (_: EnvironmentInfo) => {
run: async (_: FlipperDoctor.EnvironmentInfo) => {
const androidHome = process.env.ANDROID_HOME;
const androidSdkRoot = process.env.ANDROID_SDK_ROOT;
let androidHomeResult: HealthcheckRunResult;
let androidHomeResult: FlipperDoctor.HealthcheckRunResult;
if (!androidHome) {
androidHomeResult = {
hasProblem: true,
@@ -145,7 +95,7 @@ export function getHealthchecks(): Healthchecks {
return androidHomeResult;
}
let androidSdkRootResult: HealthcheckRunResult;
let androidSdkRootResult: FlipperDoctor.HealthcheckRunResult;
if (!androidSdkRoot) {
androidSdkRootResult = {
hasProblem: true,
@@ -188,7 +138,7 @@ export function getHealthchecks(): Healthchecks {
key: 'ios.sdk',
label: 'SDK Installed',
isRequired: true,
run: async (e: EnvironmentInfo) => {
run: async (e: FlipperDoctor.EnvironmentInfo) => {
const hasProblem =
!e.SDKs['iOS SDK'] ||
!e.SDKs['iOS SDK'].Platforms ||
@@ -208,7 +158,7 @@ export function getHealthchecks(): Healthchecks {
key: 'ios.xcode',
label: 'XCode Installed',
isRequired: true,
run: async (e: EnvironmentInfo) => {
run: async (e: FlipperDoctor.EnvironmentInfo) => {
const hasProblem = e.IDEs == null || e.IDEs.Xcode == null;
const message = hasProblem
? 'Xcode (https://developer.apple.com/xcode/) is not installed.'
@@ -223,7 +173,7 @@ export function getHealthchecks(): Healthchecks {
key: 'ios.xcode-select',
label: 'xcode-select set',
isRequired: true,
run: async (_: EnvironmentInfo) => {
run: async (_: FlipperDoctor.EnvironmentInfo) => {
const result = await tryExecuteCommand('xcode-select -p');
const hasProblem = result.hasProblem;
const message = hasProblem
@@ -239,7 +189,7 @@ export function getHealthchecks(): Healthchecks {
key: 'ios.xctrace',
label: 'xctrace exists',
isRequired: true,
run: async (_: EnvironmentInfo) => {
run: async (_: FlipperDoctor.EnvironmentInfo) => {
const result = await tryExecuteCommand(
'xcrun xctrace version',
);
@@ -258,7 +208,7 @@ export function getHealthchecks(): Healthchecks {
label: 'IDB installed',
isRequired: false,
run: async (
_: EnvironmentInfo,
_: FlipperDoctor.EnvironmentInfo,
settings?: {enablePhysicalIOS: boolean; idbPath: string},
) => {
if (!settings) {
@@ -299,50 +249,48 @@ export function getHealthchecks(): Healthchecks {
}
export async function runHealthchecks(): Promise<
Array<CategoryResult | SkippedHealthcheckCategory>
Array<FlipperDoctor.CategoryResult | FlipperDoctor.SkippedHealthcheckCategory>
> {
const environmentInfo = await getEnvInfo();
const healthchecks: Healthchecks = getHealthchecks();
const results: Array<CategoryResult | SkippedHealthcheckCategory> =
await Promise.all(
Object.entries(healthchecks).map(async ([key, category]) => {
if (category.isSkipped) {
return category;
}
const categoryResult: CategoryResult = [
key,
{
label: category.label,
results: await Promise.all(
category.healthchecks.map(
async ({key, label, run, isRequired}) => ({
key,
label,
isRequired: isRequired ?? true,
result: await run(environmentInfo).catch((e) => {
console.warn(
`Health check ${key}/${label} failed with:`,
e,
);
// TODO Improve result type to be: OK | Problem(message, fix...)
return {
hasProblem: true,
};
}),
const healthchecks: FlipperDoctor.Healthchecks = getHealthchecks();
const results: Array<
FlipperDoctor.CategoryResult | FlipperDoctor.SkippedHealthcheckCategory
> = await Promise.all(
Object.entries(healthchecks).map(async ([key, category]) => {
if (category.isSkipped) {
return category;
}
const categoryResult: FlipperDoctor.CategoryResult = [
key,
{
label: category.label,
results: await Promise.all(
category.healthchecks.map(
async ({key, label, run, isRequired}) => ({
key,
label,
isRequired: isRequired ?? true,
result: await run!(environmentInfo).catch((e) => {
console.warn(`Health check ${key}/${label} failed with:`, e);
// TODO Improve result type to be: OK | Problem(message, fix...)
return {
hasProblem: true,
};
}),
),
}),
),
},
];
return categoryResult;
}),
);
),
},
];
return categoryResult;
}),
);
return results;
}
async function tryExecuteCommand(
command: string,
): Promise<HealthcheckRunResult> {
): Promise<FlipperDoctor.HealthcheckRunResult> {
try {
const output = await promisify(exec)(command);
return {