Skip to content

Commit

Permalink
feat: allow platforms and community packages to provide additional he…
Browse files Browse the repository at this point in the history
…alth checks (#1231)

* Allow platforms and community packages to provide additional health checks

* Adding some documentation

* Update tests

* remove todo

* Add a couple of examples for RunAutomaticFix implementations

* update config snapshot

* Use real ora project for types

* lint fix

Co-authored-by: Andrew Coates <acoates-ms@noreply.github.com>
  • Loading branch information
acoates-ms and Andrew Coates authored Sep 21, 2020
1 parent 652ab45 commit be37d64
Show file tree
Hide file tree
Showing 31 changed files with 454 additions and 181 deletions.
1 change: 1 addition & 0 deletions __e2e__/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ exports[`shows up current config without unnecessary output 1`] = `
}
],
"assets": [],
"healthChecks": [],
"platforms": {
"ios": {},
"android": {}
Expand Down
175 changes: 175 additions & 0 deletions docs/healthChecks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Health Check Plugins

Plugins can be used to extend the health checks that `react-native doctor` runs. This can be used to add additional checks for out of tree platforms, or other checks that are specific to a community module.

See [`Plugins`](./plugins.md) for information about how plugins work.


## How does it work?

To provide additional health checks, a package needs to have a `react-native.config.js` at the root folder in order to be discovered by the CLI as a plugin.

```js
module.exports = {
healthChecks: [
{
label: 'Foo',
healthchecks: [
label: 'bar-installed',
getDiagnostics: async () => ({
needsToBeFixed: !isBarInstalled()
}),
runAutomaticFix: async ({loader}) => {
await installBar();
loader.succeed();
},
}
],
};
```

> Above is an example of a plugin that extends the healthChecks performed by `react-native doctor` to check if `bar` is installed.
At the startup, React Native CLI reads configuration from all dependencies listed in `package.json` and reduces them into a single configuration.

At the end, an array of health check categories is concatenated to be checked when `react-native doctor` is run.


## HealthCheckCategory interface

```ts
type HealthCheckCategory = {
label: string;
healthchecks: HealthCheckInterface[];
};
```

##### `label`

Name of the category for this health check. This will be used to group health checks in doctor.

##### `healthChecks`

Array of health checks to perorm in this category


## HealthCheckInterface interface

```ts
type HealthCheckInterface = {
label: string;
visible?: boolean | void;
isRequired?: boolean;
description?: string;
getDiagnostics: (
environmentInfo: EnvironmentInfo,
) => Promise<{
version?: string;
versions?: [string];
versionRange?: string;
needsToBeFixed: boolean | string;
}>;
win32AutomaticFix?: RunAutomaticFix;
darwinAutomaticFix?: RunAutomaticFix;
linuxAutomaticFix?: RunAutomaticFix;
runAutomaticFix: RunAutomaticFix;
};
```

##### `label`

Name of this health check

##### `visible`

If set to false, doctor will ignore this health check

##### `isRequired`

Is this health check required or optional?

##### `description`

Longer description of this health check


##### `getDiagnostics`

Functions which performs the actual check. Simple checks can just return `needsToBeFixed`. Checks which are looking at versions of an installed component (such as the version of node), can also return `version`, `versions` and `versionRange` to provide better information to be displayed in `react-native doctor` when running the check

##### `win32AutomaticFix`

This function will be used to try to fix the issue when `react-native doctor` is run on a windows machine. If this is not specified, `runAutomaticFix` will be run instead.

##### `darwinAutomaticFix`

This function will be used to try to fix the issue when `react-native doctor` is run on a macOS machine. If this is not specified, `runAutomaticFix` will be run instead.

##### `linuxAutomaticFix`

This function will be used to try to fix the issue when `react-native doctor` is run on a linux machine. If this is not specified, `runAutomaticFix` will be run instead.

##### `runAutomaticFix`

This function will be used to try to fix the issue when `react-native doctor` is run and no more platform specific automatic fix function was provided.


## RunAutomaticFix interface

```ts
type RunAutomaticFix = (args: {
loader: Ora;
logManualInstallation: ({
healthcheck,
url,
command,
message,
}: {
healthcheck?: string;
url?: string;
command?: string;
message?: string;
}) => void;
environmentInfo: EnvironmentInfo;
}) => Promise<void> | void;
```

##### `loader`

A reference to a [`ora`](https://www.npmjs.com/package/ora) instance which should be used to report success / failure, and progress of the fix. The fix function should always call either `loader.succeed()` or `loader.fail()` before returning.

##### `logManualInstallation`

If an automated fix cannot be performed, this function should be used to provide instructions to the user on how to manually fix the issue.

##### `environmentInfo`

Provides information about the current system


### Examples of RunAutomaticFix implementations

A health check that requires the user to manually go download/install something. This check will immediately display a message to notify the user how to fix the issue.

```ts
async function needToInstallFoo({loader, logManualInstallation}) {
loader.fail();

return logManualInstallation({
healthcheck: 'Foo',
url: 'https:/foo.com/download',
});
}
```

A health check that runs some commands locally which may fix the issue. This check will display a spinner while the exec commands are running. Then once the commands are complete, the spinner will change to a checkmark.

```ts

import { exec } from 'promisify-child-process';
async function fixFoo({loader}) {
await exec(`foo --install`);
await exec(`foo --fix`);

loader.succeed();
}
3 changes: 3 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ At the startup, React Native CLI reads configuration from all dependencies liste

At the end, an array of commands concatenated from all plugins is passed on to the CLI to be loaded after built-in commands.

> See [`healthChecks`](./healthChecks.md) for information on how plugins can provide additional health checks for `react-native doctor`.
## Command interface

```ts
Expand Down Expand Up @@ -107,6 +109,7 @@ String that describes this particular usage.

A command with arguments and options (if applicable) that can be run in order to achieve the desired goal.


## Migrating from `rnpm` configuration

The changes are mostly cosmetic so the migration should be pretty straight-forward.
Expand Down
3 changes: 3 additions & 0 deletions packages/cli-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"publishConfig": {
"access": "public"
},
"dependencies": {
"ora": "^3.4.0"
},
"files": [
"build",
"!*.map"
Expand Down
99 changes: 99 additions & 0 deletions packages/cli-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AndroidDependencyConfig,
AndroidDependencyParams,
} from './android';
import {Ora} from 'ora';

export type InquirerPrompt = any;

Expand Down Expand Up @@ -120,6 +121,100 @@ export type ProjectConfig = {
[key: string]: any;
};

export type NotFound = 'Not Found';
type AvailableInformation = {
version: string;
path: string;
};

type Information = AvailableInformation | NotFound;

export type EnvironmentInfo = {
System: {
OS: string;
CPU: string;
Memory: string;
Shell: AvailableInformation;
};
Binaries: {
Node: AvailableInformation;
Yarn: AvailableInformation;
npm: AvailableInformation;
Watchman: AvailableInformation;
};
SDKs: {
'iOS SDK': {
Platforms: string[];
};
'Android SDK':
| {
'API Levels': string[] | NotFound;
'Build Tools': string[] | NotFound;
'System Images': string[] | NotFound;
'Android NDK': string | NotFound;
}
| NotFound;
};
IDEs: {
'Android Studio': AvailableInformation | NotFound;
Emacs: AvailableInformation;
Nano: AvailableInformation;
VSCode: AvailableInformation;
Vim: AvailableInformation;
Xcode: AvailableInformation;
};
Languages: {
Java: Information;
Python: Information;
};
};

export type HealthCheckCategory = {
label: string;
healthchecks: HealthCheckInterface[];
};

export type Healthchecks = {
common: HealthCheckCategory;
android: HealthCheckCategory;
ios?: HealthCheckCategory;
};

export type RunAutomaticFix = (args: {
loader: Ora;
logManualInstallation: ({
healthcheck,
url,
command,
message,
}: {
healthcheck?: string;
url?: string;
command?: string;
message?: string;
}) => void;
environmentInfo: EnvironmentInfo;
}) => Promise<void> | void;

export type HealthCheckInterface = {
label: string;
visible?: boolean | void;
isRequired?: boolean;
description?: string;
getDiagnostics: (
environmentInfo: EnvironmentInfo,
) => Promise<{
version?: string;
versions?: [string];
versionRange?: string;
needsToBeFixed: boolean | string;
}>;
win32AutomaticFix?: RunAutomaticFix;
darwinAutomaticFix?: RunAutomaticFix;
linuxAutomaticFix?: RunAutomaticFix;
runAutomaticFix: RunAutomaticFix;
};

/**
* @property root - Root where the configuration has been resolved from
* @property reactNativePath - Path to React Native source
Expand All @@ -128,6 +223,7 @@ export type ProjectConfig = {
* @property dependencies - Map of the dependencies that are present in the project
* @property platforms - Map of available platforms (build-ins and dynamically loaded)
* @property commands - An array of commands that are present in 3rd party packages
* @property healthChecks - An array of health check categories to add to doctor command
*/
export interface Config extends IOSNativeModulesConfig {
root: string;
Expand All @@ -151,6 +247,7 @@ export interface Config extends IOSNativeModulesConfig {
[name: string]: PlatformConfig<any, any, any, any>;
};
commands: Command[];
healthChecks: HealthCheckCategory[];
}

/**
Expand All @@ -175,6 +272,8 @@ export type UserDependencyConfig = {
commands: Command[];
// An array of extra platforms to load
platforms: Config['platforms'];
// Additional health checks
healthChecks: HealthCheckCategory[];
};

export {
Expand Down
7 changes: 3 additions & 4 deletions packages/cli/src/commands/doctor/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import {getHealthchecks, HEALTHCHECK_TYPES} from './healthchecks';
import {getLoader} from '../../tools/loader';
import printFixOptions, {KEYS} from './printFixOptions';
import runAutomaticFix, {AUTOMATIC_FIX_LEVELS} from './runAutomaticFix';
import {DetachedCommandFunction} from '@react-native-community/cli-types';
import {
DetachedCommandFunction,
HealthCheckCategory,
HealthCheckCategoryResult,
HealthCheckResult,
HealthCheckInterface,
} from './types';
} from '@react-native-community/cli-types';
import {HealthCheckCategoryResult, HealthCheckResult} from './types';
import getEnvironmentInfo from '../../tools/envinfo';
import {logMessage} from './healthchecks/common';

Expand Down
Loading

0 comments on commit be37d64

Please sign in to comment.