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:
committed by
Facebook GitHub Bot
parent
2a4fe77404
commit
2480ed30c5
@@ -31,7 +31,6 @@
|
||||
"expand-tilde": "^2.0.2",
|
||||
"flipper-client-sdk": "^0.0.3",
|
||||
"flipper-common": "0.0.0",
|
||||
"flipper-doctor": "0.0.0",
|
||||
"flipper-plugin": "0.0.0",
|
||||
"flipper-ui-core": "0.0.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
|
||||
@@ -24,9 +24,6 @@ import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {State as Store} from '../reducers';
|
||||
import {
|
||||
HealthcheckResult,
|
||||
HealthcheckReportCategory,
|
||||
HealthcheckReport,
|
||||
startHealthchecks,
|
||||
finishHealthchecks,
|
||||
updateHealthcheckResult,
|
||||
@@ -38,10 +35,10 @@ import runHealthchecks, {
|
||||
HealthcheckEventsHandler,
|
||||
} from '../utils/runHealthchecks';
|
||||
import {getFlipperLib} from 'flipper-plugin';
|
||||
import {reportUsage} from 'flipper-common';
|
||||
import {reportUsage, FlipperDoctor} from 'flipper-common';
|
||||
|
||||
type StateFromProps = {
|
||||
healthcheckReport: HealthcheckReport;
|
||||
healthcheckReport: FlipperDoctor.HealthcheckReport;
|
||||
} & HealthcheckSettings;
|
||||
|
||||
type DispatchFromProps = {
|
||||
@@ -123,7 +120,9 @@ function CenteredCheckbox(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function HealthcheckIcon(props: {checkResult: HealthcheckResult}) {
|
||||
function HealthcheckIcon(props: {
|
||||
checkResult: FlipperDoctor.HealthcheckResult;
|
||||
}) {
|
||||
const {checkResult: check} = props;
|
||||
switch (props.checkResult.status) {
|
||||
case 'IN_PROGRESS':
|
||||
@@ -170,7 +169,7 @@ function HealthcheckIcon(props: {checkResult: HealthcheckResult}) {
|
||||
|
||||
function HealthcheckDisplay(props: {
|
||||
label: string;
|
||||
result: HealthcheckResult;
|
||||
result: FlipperDoctor.HealthcheckResult;
|
||||
selected?: boolean;
|
||||
onClick?: () => void;
|
||||
}) {
|
||||
@@ -194,7 +193,7 @@ function SideMessageDisplay(props: {children: React.ReactNode}) {
|
||||
return <SideContainerText selectable>{props.children}</SideContainerText>;
|
||||
}
|
||||
|
||||
function ResultMessage(props: {result: HealthcheckResult}) {
|
||||
function ResultMessage(props: {result: FlipperDoctor.HealthcheckResult}) {
|
||||
if (status === 'IN_PROGRESS') {
|
||||
return <p>Doctor is running healthchecks...</p>;
|
||||
} else if (hasProblems(props.result)) {
|
||||
@@ -213,12 +212,12 @@ function ResultMessage(props: {result: HealthcheckResult}) {
|
||||
}
|
||||
}
|
||||
|
||||
function hasProblems(result: HealthcheckResult) {
|
||||
function hasProblems(result: FlipperDoctor.HealthcheckResult) {
|
||||
const {status} = result;
|
||||
return status === 'FAILED' || status === 'WARNING';
|
||||
}
|
||||
|
||||
function hasNewProblems(result: HealthcheckResult) {
|
||||
function hasNewProblems(result: FlipperDoctor.HealthcheckResult) {
|
||||
return hasProblems(result) && !result.isAcknowledged;
|
||||
}
|
||||
|
||||
@@ -321,7 +320,7 @@ class DoctorSheet extends Component<Props, State> {
|
||||
<FlexRow>
|
||||
<HealthcheckListContainer>
|
||||
{Object.values(this.props.healthcheckReport.categories).map(
|
||||
(category: HealthcheckReportCategory) => {
|
||||
(category: FlipperDoctor.HealthcheckReportCategory) => {
|
||||
return (
|
||||
<CategoryContainer key={category.key}>
|
||||
<HealthcheckDisplay
|
||||
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
updateHealthcheckResult,
|
||||
acknowledgeProblems,
|
||||
} from '../healthchecks';
|
||||
import {Healthchecks, EnvironmentInfo} from 'flipper-doctor';
|
||||
import type {FlipperDoctor} from 'flipper-common';
|
||||
|
||||
const HEALTHCHECKS: Healthchecks = {
|
||||
const HEALTHCHECKS: FlipperDoctor.Healthchecks = {
|
||||
ios: {
|
||||
label: 'iOS',
|
||||
isSkipped: false,
|
||||
@@ -25,7 +25,7 @@ const HEALTHCHECKS: Healthchecks = {
|
||||
{
|
||||
key: 'ios.sdk',
|
||||
label: 'SDK Installed',
|
||||
run: async (_env: EnvironmentInfo) => {
|
||||
run: async (_env: FlipperDoctor.EnvironmentInfo) => {
|
||||
return {hasProblem: false, message: ''};
|
||||
},
|
||||
},
|
||||
@@ -39,7 +39,7 @@ const HEALTHCHECKS: Healthchecks = {
|
||||
{
|
||||
key: 'android.sdk',
|
||||
label: 'SDK Installed',
|
||||
run: async (_env: EnvironmentInfo) => {
|
||||
run: async (_env: FlipperDoctor.EnvironmentInfo) => {
|
||||
return {hasProblem: true, message: 'Error'};
|
||||
},
|
||||
},
|
||||
@@ -53,7 +53,7 @@ const HEALTHCHECKS: Healthchecks = {
|
||||
{
|
||||
key: 'common.openssl',
|
||||
label: 'OpenSSL Istalled',
|
||||
run: async (_env: EnvironmentInfo) => {
|
||||
run: async (_env: FlipperDoctor.EnvironmentInfo) => {
|
||||
return {hasProblem: false, message: ''};
|
||||
},
|
||||
},
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
|
||||
import {Actions} from './';
|
||||
import {produce} from 'immer';
|
||||
import {Healthchecks} from 'flipper-doctor';
|
||||
import type {FlipperDoctor} from 'flipper-common';
|
||||
|
||||
export type State = {
|
||||
healthcheckReport: HealthcheckReport;
|
||||
healthcheckReport: FlipperDoctor.HealthcheckReport;
|
||||
acknowledgedProblems: string[];
|
||||
};
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: 'START_HEALTHCHECKS';
|
||||
payload: Healthchecks;
|
||||
payload: FlipperDoctor.Healthchecks;
|
||||
}
|
||||
| {
|
||||
type: 'FINISH_HEALTHCHECKS';
|
||||
@@ -29,7 +29,7 @@ export type Action =
|
||||
payload: {
|
||||
categoryKey: string;
|
||||
itemKey: string;
|
||||
result: HealthcheckResult;
|
||||
result: FlipperDoctor.HealthcheckResult;
|
||||
};
|
||||
}
|
||||
| {
|
||||
@@ -47,39 +47,6 @@ const INITIAL_STATE: State = {
|
||||
acknowledgedProblems: [],
|
||||
};
|
||||
|
||||
type Dictionary<T> = {[key: string]: T};
|
||||
|
||||
export type HealthcheckStatus =
|
||||
| 'IN_PROGRESS'
|
||||
| 'SUCCESS'
|
||||
| 'FAILED'
|
||||
| 'SKIPPED'
|
||||
| 'WARNING';
|
||||
|
||||
export type HealthcheckResult = {
|
||||
status: HealthcheckStatus;
|
||||
isAcknowledged?: boolean;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type HealthcheckReportItem = {
|
||||
key: string;
|
||||
label: string;
|
||||
result: HealthcheckResult;
|
||||
};
|
||||
|
||||
export type HealthcheckReportCategory = {
|
||||
key: string;
|
||||
label: string;
|
||||
result: HealthcheckResult;
|
||||
checks: Dictionary<HealthcheckReportItem>;
|
||||
};
|
||||
|
||||
export type HealthcheckReport = {
|
||||
result: HealthcheckResult;
|
||||
categories: Dictionary<HealthcheckReportCategory>;
|
||||
};
|
||||
|
||||
function recomputeHealthcheckStatus(draft: State): void {
|
||||
draft.healthcheckReport.result = computeAggregatedResult(
|
||||
Object.values(draft.healthcheckReport.categories).map((c) => c.result),
|
||||
@@ -87,8 +54,8 @@ function recomputeHealthcheckStatus(draft: State): void {
|
||||
}
|
||||
|
||||
function computeAggregatedResult(
|
||||
results: HealthcheckResult[],
|
||||
): HealthcheckResult {
|
||||
results: FlipperDoctor.HealthcheckResult[],
|
||||
): FlipperDoctor.HealthcheckResult {
|
||||
return results.some((r) => r.status === 'IN_PROGRESS')
|
||||
? {status: 'IN_PROGRESS'}
|
||||
: results.every((r) => r.status === 'SUCCESS')
|
||||
@@ -114,7 +81,7 @@ const updateCheckResult = produce(
|
||||
}: {
|
||||
categoryKey: string;
|
||||
itemKey: string;
|
||||
result: HealthcheckResult;
|
||||
result: FlipperDoctor.HealthcheckResult;
|
||||
},
|
||||
) => {
|
||||
const category = draft.healthcheckReport.categories[categoryKey];
|
||||
@@ -124,55 +91,57 @@ const updateCheckResult = produce(
|
||||
},
|
||||
);
|
||||
|
||||
function createDict<T>(pairs: [string, T][]): Dictionary<T> {
|
||||
const obj: Dictionary<T> = {};
|
||||
function createDict<T>(pairs: [string, T][]): FlipperDoctor.Dictionary<T> {
|
||||
const obj: FlipperDoctor.Dictionary<T> = {};
|
||||
for (const pair of pairs) {
|
||||
obj[pair[0]] = pair[1];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
const start = produce((draft: State, healthchecks: Healthchecks) => {
|
||||
draft.healthcheckReport = {
|
||||
result: {status: 'IN_PROGRESS'},
|
||||
categories: createDict<HealthcheckReportCategory>(
|
||||
Object.entries(healthchecks).map(([categoryKey, category]) => {
|
||||
if (category.isSkipped) {
|
||||
const start = produce(
|
||||
(draft: State, healthchecks: FlipperDoctor.Healthchecks) => {
|
||||
draft.healthcheckReport = {
|
||||
result: {status: 'IN_PROGRESS'},
|
||||
categories: createDict<FlipperDoctor.HealthcheckReportCategory>(
|
||||
Object.entries(healthchecks).map(([categoryKey, category]) => {
|
||||
if (category.isSkipped) {
|
||||
return [
|
||||
categoryKey,
|
||||
{
|
||||
key: categoryKey,
|
||||
result: {
|
||||
status: 'SKIPPED',
|
||||
message: category.skipReason,
|
||||
},
|
||||
label: category.label,
|
||||
checks: createDict<FlipperDoctor.HealthcheckReportItem>([]),
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
categoryKey,
|
||||
{
|
||||
key: categoryKey,
|
||||
result: {
|
||||
status: 'SKIPPED',
|
||||
message: category.skipReason,
|
||||
},
|
||||
result: {status: 'IN_PROGRESS'},
|
||||
label: category.label,
|
||||
checks: createDict<HealthcheckReportItem>([]),
|
||||
checks: createDict<FlipperDoctor.HealthcheckReportItem>(
|
||||
category.healthchecks.map((check) => [
|
||||
check.key,
|
||||
{
|
||||
key: check.key,
|
||||
result: {status: 'IN_PROGRESS'},
|
||||
label: check.label,
|
||||
},
|
||||
]),
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
categoryKey,
|
||||
{
|
||||
key: categoryKey,
|
||||
result: {status: 'IN_PROGRESS'},
|
||||
label: category.label,
|
||||
checks: createDict<HealthcheckReportItem>(
|
||||
category.healthchecks.map((check) => [
|
||||
check.key,
|
||||
{
|
||||
key: check.key,
|
||||
result: {status: 'IN_PROGRESS'},
|
||||
label: check.label,
|
||||
},
|
||||
]),
|
||||
),
|
||||
},
|
||||
];
|
||||
}),
|
||||
),
|
||||
};
|
||||
});
|
||||
}),
|
||||
),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const finish = produce((draft: State) => {
|
||||
Object.values(draft.healthcheckReport.categories)
|
||||
@@ -244,7 +213,7 @@ export default function reducer(
|
||||
export const updateHealthcheckResult = (
|
||||
categoryKey: string,
|
||||
itemKey: string,
|
||||
result: HealthcheckResult,
|
||||
result: FlipperDoctor.HealthcheckResult,
|
||||
): Action => ({
|
||||
type: 'UPDATE_HEALTHCHECK_RESULT',
|
||||
payload: {
|
||||
@@ -254,7 +223,9 @@ export const updateHealthcheckResult = (
|
||||
},
|
||||
});
|
||||
|
||||
export const startHealthchecks = (healthchecks: Healthchecks): Action => ({
|
||||
export const startHealthchecks = (
|
||||
healthchecks: FlipperDoctor.Healthchecks,
|
||||
): Action => ({
|
||||
type: 'START_HEALTHCHECKS',
|
||||
payload: healthchecks,
|
||||
});
|
||||
|
||||
@@ -18,12 +18,6 @@ import {
|
||||
LoadingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {Layout} from '../ui';
|
||||
import {
|
||||
HealthcheckReport,
|
||||
HealthcheckReportItem,
|
||||
HealthcheckStatus,
|
||||
HealthcheckResult,
|
||||
} from '../reducers/healthchecks';
|
||||
import {theme} from 'flipper-plugin';
|
||||
import {
|
||||
startHealthchecks,
|
||||
@@ -33,13 +27,14 @@ import {
|
||||
resetAcknowledgedProblems,
|
||||
} from '../reducers/healthchecks';
|
||||
import runHealthchecks from '../utils/runHealthchecks';
|
||||
import {Healthchecks} from 'flipper-doctor';
|
||||
import type {FlipperDoctor} from 'flipper-common';
|
||||
type Healthchecks = FlipperDoctor.Healthchecks;
|
||||
import {reportUsage} from 'flipper-common';
|
||||
|
||||
const {Title, Paragraph, Text} = Typography;
|
||||
|
||||
const statusTypeAndMessage: {
|
||||
[key in HealthcheckStatus]: {
|
||||
[key in FlipperDoctor.HealthcheckStatus]: {
|
||||
type: 'success' | 'info' | 'warning' | 'error';
|
||||
message: string;
|
||||
};
|
||||
@@ -67,15 +62,15 @@ const statusTypeAndMessage: {
|
||||
},
|
||||
};
|
||||
|
||||
function checkHasProblem(result: HealthcheckResult) {
|
||||
function checkHasProblem(result: FlipperDoctor.HealthcheckResult) {
|
||||
return result.status === 'FAILED' || result.status === 'WARNING';
|
||||
}
|
||||
|
||||
export function checkHasNewProblem(result: HealthcheckResult) {
|
||||
export function checkHasNewProblem(result: FlipperDoctor.HealthcheckResult) {
|
||||
return checkHasProblem(result) && !result.isAcknowledged;
|
||||
}
|
||||
|
||||
function ResultTopDialog(props: {status: HealthcheckStatus}) {
|
||||
function ResultTopDialog(props: {status: FlipperDoctor.HealthcheckStatus}) {
|
||||
const messages = statusTypeAndMessage[props.status];
|
||||
return (
|
||||
<Alert
|
||||
@@ -92,7 +87,7 @@ function ResultTopDialog(props: {status: HealthcheckStatus}) {
|
||||
);
|
||||
}
|
||||
|
||||
function CheckIcon(props: {status: HealthcheckStatus}) {
|
||||
function CheckIcon(props: {status: FlipperDoctor.HealthcheckStatus}) {
|
||||
switch (props.status) {
|
||||
case 'SUCCESS':
|
||||
return (
|
||||
@@ -119,7 +114,9 @@ function CheckIcon(props: {status: HealthcheckStatus}) {
|
||||
}
|
||||
}
|
||||
|
||||
function CollapsableCategory(props: {checks: Array<HealthcheckReportItem>}) {
|
||||
function CollapsableCategory(props: {
|
||||
checks: Array<FlipperDoctor.HealthcheckReportItem>;
|
||||
}) {
|
||||
return (
|
||||
<Collapse ghost>
|
||||
{props.checks.map((check) => (
|
||||
@@ -134,7 +131,7 @@ function CollapsableCategory(props: {checks: Array<HealthcheckReportItem>}) {
|
||||
);
|
||||
}
|
||||
|
||||
function HealthCheckList(props: {report: HealthcheckReport}) {
|
||||
function HealthCheckList(props: {report: FlipperDoctor.HealthcheckReport}) {
|
||||
useEffect(() => reportUsage('doctor:report:opened'), []);
|
||||
return (
|
||||
<Layout.Container gap>
|
||||
@@ -241,7 +238,7 @@ export default function SetupDoctorScreen(props: {
|
||||
updateHealthcheckResult: (
|
||||
categoryKey: string,
|
||||
itemKey: string,
|
||||
result: HealthcheckResult,
|
||||
result: FlipperDoctor.HealthcheckResult,
|
||||
) => dispatch(updateHealthcheckResult(categoryKey, itemKey, result)),
|
||||
finishHealthchecks: () => dispatch(finishHealthchecks()),
|
||||
});
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {HealthcheckResult} from '../reducers/healthchecks';
|
||||
import {getHealthchecks, getEnvInfo, Healthchecks} from 'flipper-doctor';
|
||||
import {logPlatformSuccessRate, reportPlatformFailures} from 'flipper-common';
|
||||
import {
|
||||
logPlatformSuccessRate,
|
||||
reportPlatformFailures,
|
||||
FlipperDoctor,
|
||||
} from 'flipper-common';
|
||||
import {getRenderHostInstance} from '../RenderHost';
|
||||
|
||||
let healthcheckIsRunning: boolean;
|
||||
let runningHealthcheck: Promise<void>;
|
||||
@@ -18,9 +21,9 @@ export type HealthcheckEventsHandler = {
|
||||
updateHealthcheckResult: (
|
||||
categoryKey: string,
|
||||
itemKey: string,
|
||||
result: HealthcheckResult,
|
||||
result: FlipperDoctor.HealthcheckResult,
|
||||
) => void;
|
||||
startHealthchecks: (healthchecks: Healthchecks) => void;
|
||||
startHealthchecks: (healthchecks: FlipperDoctor.Healthchecks) => void;
|
||||
finishHealthchecks: () => void;
|
||||
};
|
||||
|
||||
@@ -36,34 +39,25 @@ export type HealthcheckSettings = {
|
||||
export type HealthcheckOptions = HealthcheckEventsHandler & HealthcheckSettings;
|
||||
|
||||
async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
|
||||
const healthchecks = getHealthchecks();
|
||||
if (!options.settings.enableAndroid) {
|
||||
healthchecks.android = {
|
||||
label: healthchecks.android.label,
|
||||
isSkipped: true,
|
||||
skipReason:
|
||||
'Healthcheck is skipped, because "Android Development" option is disabled in the Flipper settings',
|
||||
};
|
||||
}
|
||||
if (!options.settings.enableIOS) {
|
||||
healthchecks.ios = {
|
||||
label: healthchecks.ios.label,
|
||||
isSkipped: true,
|
||||
skipReason:
|
||||
'Healthcheck is skipped, because "iOS Development" option is disabled in the Flipper settings',
|
||||
};
|
||||
}
|
||||
const {flipperServer} = getRenderHostInstance();
|
||||
const healthchecks = await flipperServer.exec('doctor-get-healthchecks', {
|
||||
settings: options.settings,
|
||||
});
|
||||
options.startHealthchecks(healthchecks);
|
||||
const environmentInfo = await getEnvInfo();
|
||||
let hasProblems = false;
|
||||
for (const [categoryKey, category] of Object.entries(healthchecks)) {
|
||||
if (category.isSkipped) {
|
||||
continue;
|
||||
}
|
||||
for (const h of category.healthchecks) {
|
||||
const checkResult = await h.run(environmentInfo, options.settings);
|
||||
const checkResult = await flipperServer.exec(
|
||||
'doctor-run-healthcheck',
|
||||
{settings: options.settings},
|
||||
categoryKey as keyof FlipperDoctor.Healthchecks,
|
||||
h.key,
|
||||
);
|
||||
const metricName = `doctor:${h.key.replace('.', ':')}.healthcheck`; // e.g. "doctor:ios:xcode-select.healthcheck"
|
||||
if (checkResult.hasProblem) {
|
||||
if (checkResult.status !== 'SUCCESS') {
|
||||
hasProblems = true;
|
||||
logPlatformSuccessRate(metricName, {
|
||||
kind: 'failure',
|
||||
@@ -75,19 +69,7 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
|
||||
kind: 'success',
|
||||
});
|
||||
}
|
||||
const result: HealthcheckResult =
|
||||
checkResult.hasProblem && h.isRequired
|
||||
? {
|
||||
status: 'FAILED',
|
||||
message: checkResult.message,
|
||||
}
|
||||
: checkResult.hasProblem && !h.isRequired
|
||||
? {
|
||||
status: 'WARNING',
|
||||
message: checkResult.message,
|
||||
}
|
||||
: {status: 'SUCCESS', message: checkResult.message};
|
||||
options.updateHealthcheckResult(categoryKey, h.key, result);
|
||||
options.updateHealthcheckResult(categoryKey, h.key, checkResult);
|
||||
}
|
||||
}
|
||||
options.finishHealthchecks();
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
"emitDeclarationOnly": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../doctor"
|
||||
},
|
||||
{
|
||||
"path": "../flipper-common"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user