Skip to content

Commit

Permalink
doctor: improve ios-deploy installation (#726)
Browse files Browse the repository at this point in the history
* Separate CocoaPods installation functions to be reused

* Improve the UI of CocoaPods installation on `doctor`

* Calculate the size of the cocoapods prompt question to remove it correctly

* Refactor install method declaration when installing cocoapods

* Use callbacks to clean `brewInstall` function

* Clean up question size calculation on cocoapods

* Move message removal functions to `common` in `doctor`

* Add prompt message to show options for method of installation for `ios-deploy`

* Fix unknown export

* Minor refactor on `ora` type import

* Remove unneeded `.toLowerCase()`

* Remove unused `try...catch` block

* Minor refactor

* Log error message when installing `cocoapods` & `ios-deploy`

* Revert `ios-deploy` installation command
  • Loading branch information
Lucas Bento authored and thymikee committed Sep 18, 2019
1 parent 908b34d commit c99dcf5
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 55 deletions.
10 changes: 5 additions & 5 deletions packages/cli/src/commands/doctor/checkInstallation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import semver from 'semver';
import commandExists from 'command-exists';

const PACKAGE_MANAGERS = {
YARN: 'YARN',
NPM: 'NPM',
};
export enum PACKAGE_MANAGERS {
YARN = 'YARN',
NPM = 'NPM',
}

const checkSoftwareInstalled = async (command: string) => {
try {
Expand Down Expand Up @@ -32,4 +32,4 @@ const doesSoftwareNeedToBeFixed = ({
);
};

export {PACKAGE_MANAGERS, checkSoftwareInstalled, doesSoftwareNeedToBeFixed};
export {checkSoftwareInstalled, doesSoftwareNeedToBeFixed};
23 changes: 3 additions & 20 deletions packages/cli/src/commands/doctor/healthchecks/cocoaPods.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import execa from 'execa';
import chalk from 'chalk';
import readline from 'readline';
import wcwidth from 'wcwidth';
import {logger} from '@react-native-community/cli-tools';
import {checkSoftwareInstalled} from '../checkInstallation';
import {
promptCocoaPodsInstallationQuestion,
runSudo,
} from '../../../tools/installPods';
import {removeMessage} from './common';
import {brewInstall} from '../../../tools/brewInstall';
import {HealthCheckInterface} from '../types';

function calculateQuestionSize(promptQuestion: string) {
return Math.max(
1,
Math.ceil(wcwidth(promptQuestion) / (process.stdout.columns || 80)),
);
}

function clearQuestion(promptQuestion: string) {
readline.moveCursor(
process.stdout,
0,
-calculateQuestionSize(promptQuestion),
);
readline.clearScreenDown(process.stdout);
}

export default {
label: 'CocoaPods',
getDiagnostics: async () => ({
Expand All @@ -49,7 +32,7 @@ export default {
const loaderSucceedMessage = `CocoaPods (installed with ${installMethodCapitalized})`;

// Remove the prompt after the question of how to install CocoaPods is answered
clearQuestion(promptQuestion);
removeMessage(promptQuestion);

if (installMethod === 'gem') {
loader.start(loaderInstallationMessage);
Expand All @@ -69,7 +52,7 @@ export default {
return loader.succeed(loaderSucceedMessage);
} catch (error) {
loader.fail();
logger.log(chalk.dim(`\n${error}`));
logger.log(chalk.dim(`\n${error.message}`));

return logger.log(
`An error occured while trying to install CocoaPods. Please try again manually: ${chalk.bold(
Expand Down
21 changes: 19 additions & 2 deletions packages/cli/src/commands/doctor/healthchecks/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {logger} from '@react-native-community/cli-tools';
import chalk from 'chalk';
import readline from 'readline';
import wcwidth from 'wcwidth';
import stripAnsi from 'strip-ansi';
import {logger} from '@react-native-community/cli-tools';

// Space is necessary to keep correct ordering on screen
const logMessage = (message: string) => logger.log(` ${message}`);
Expand Down Expand Up @@ -34,4 +37,18 @@ const logManualInstallation = ({
}
};

export {logManualInstallation};
// Calculate the size of a message on terminal based on rows
function calculateMessageSize(message: string) {
return Math.max(
1,
Math.ceil(wcwidth(stripAnsi(message)) / (process.stdout.columns || 80)),
);
}

// Clear the message from the terminal
function removeMessage(message: string) {
readline.moveCursor(process.stdout, 0, -calculateMessageSize(message));
readline.clearScreenDown(process.stdout);
}

export {logManualInstallation, removeMessage};
113 changes: 87 additions & 26 deletions packages/cli/src/commands/doctor/healthchecks/iosDeploy.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,119 @@
import execa from 'execa';
import chalk from 'chalk';
// @ts-ignore untyped
import inquirer from 'inquirer';
import {logger} from '@react-native-community/cli-tools';
import {checkSoftwareInstalled, PACKAGE_MANAGERS} from '../checkInstallation';
import {packageManager} from './packageManagers';
import {logManualInstallation} from './common';
import {logManualInstallation, removeMessage} from './common';
import {HealthCheckInterface} from '../types';
import {Ora} from 'ora';

const getInstallationCommand = () => {
const label = 'ios-deploy';

const installationWithYarn = 'yarn global add ios-deploy';
const installationWithNpm = 'npm install ios-deploy --global';

const identifyInstallationCommand = () => {
if (packageManager === PACKAGE_MANAGERS.YARN) {
return 'yarn global add ios-deploy';
return installationWithYarn;
}

if (packageManager === PACKAGE_MANAGERS.NPM) {
return 'npm install ios-deploy --global';
return installationWithNpm;
}

return undefined;
};

const installLibrary = async ({
installationCommand,
packageManagerToUse,
loader,
}: {
installationCommand: string;
packageManagerToUse: 'yarn' | 'npm';
loader: Ora;
}) => {
try {
loader.start(`${label} (installing with ${packageManagerToUse})`);

const installationCommandArgs = installationCommand.split(' ');

await execa(installationCommandArgs[0], installationCommandArgs.splice(1));

loader.succeed(`${label} (installed with ${packageManagerToUse})`);
} catch (error) {
loader.fail();
logger.log(chalk.dim(`\n${error.message}`));

logManualInstallation({
healthcheck: 'ios-deploy',
command: installationCommand,
});
}
};

export default {
label: 'ios-deploy',
label,
isRequired: false,
getDiagnostics: async () => ({
needsToBeFixed: await checkSoftwareInstalled('ios-deploy'),
}),
runAutomaticFix: async ({loader}) => {
const installationCommand = getInstallationCommand();
loader.stop();

const installationCommand = identifyInstallationCommand();

// This means that we couldn't "guess" the package manager
if (installationCommand === undefined) {
loader.fail();
const promptQuestion = `ios-deploy needs to be installed either by ${chalk.bold(
'yarn',
)} ${chalk.reset('or')} ${chalk.bold(
'npm',
)} ${chalk.reset()}, which one do you want to use?`;
const installWithYarn = 'yarn';
const installWithNpm = 'npm';
const skipInstallation = 'Skip installation';

// Then we just print out the URL that the user can head to download the library
logManualInstallation({
healthcheck: 'ios-deploy',
url: 'https://github.com/ios-control/ios-deploy#readme',
});
return;
}
const {chosenPackageManager} = await inquirer.prompt([
{
type: 'list',
name: 'chosenPackageManager',
message: promptQuestion,
choices: [installWithYarn, installWithNpm, skipInstallation],
},
]);

try {
const installationCommandArgs = installationCommand.split(' ');
removeMessage(`? ${promptQuestion} ${chosenPackageManager}`);

await execa(
installationCommandArgs[0],
installationCommandArgs.splice(1),
);
if (chosenPackageManager === skipInstallation) {
loader.fail();

loader.succeed();
} catch (_error) {
loader.fail();
// Then we just print out the URL that the user can head to download the library
logManualInstallation({
healthcheck: 'ios-deploy',
url: 'https://github.com/ios-control/ios-deploy#readme',
});

logManualInstallation({
healthcheck: 'ios-deploy',
command: installationCommand,
return;
}

const shouldInstallWithYarn = chosenPackageManager === installWithYarn;

return installLibrary({
installationCommand: shouldInstallWithYarn
? installationWithYarn
: installationWithNpm,
loader,
packageManagerToUse: chosenPackageManager,
});
}

return installLibrary({
installationCommand,
packageManagerToUse: packageManager!.toLowerCase() as 'yarn' | 'npm',
loader,
});
},
} as HealthCheckInterface;
3 changes: 1 addition & 2 deletions packages/cli/src/tools/installPods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import chalk from 'chalk';
import ora from 'ora';
// @ts-ignore untyped
import inquirer from 'inquirer';
import stripAnsi from 'strip-ansi';
import {logger} from '@react-native-community/cli-tools';
import {NoopLoader} from './loader';
// @ts-ignore untyped
Expand Down Expand Up @@ -74,7 +73,7 @@ async function promptCocoaPodsInstallationQuestion(): Promise<
return {
installMethod: shouldInstallWithGem ? 'gem' : 'homebrew',
// This is used for removing the message in `doctor` after it's answered
promptQuestion: `? ${stripAnsi(promptQuestion)} ${
promptQuestion: `? ${promptQuestion} ${
shouldInstallWithGem ? installWithGem : installWithHomebrew
}`,
};
Expand Down

0 comments on commit c99dcf5

Please sign in to comment.