Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(doctor): Reuse installCocoaPods() and add Homebrew option #663

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions packages/cli/src/commands/doctor/doctor.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default (async function runDoctor(argv, ctx, options) {
),
);

const iterateOverHealthchecks = async ({label, healthchecks}) => ({
const iterateOverHealthChecks = async ({label, healthchecks}) => ({
label,
healthchecks: (await Promise.all(
healthchecks.map(async healthcheck => {
Expand All @@ -61,11 +61,7 @@ export default (async function runDoctor(argv, ctx, options) {
: await healthcheck.getDiagnosticsAsync(environmentInfo);

// Assume that it's required unless specified otherwise
const isRequired =
typeof healthcheck.isRequired === 'undefined'
? true
: healthcheck.isRequired;

const isRequired = healthcheck.isRequired !== false;
const isWarning = needsToBeFixed && !isRequired;

return {
Expand All @@ -80,20 +76,18 @@ export default (async function runDoctor(argv, ctx, options) {
: undefined,
};
}),
)).filter(healthcheck => !!healthcheck),
)).filter(Boolean),
});

// Remove all the categories that don't have any healthcheck with `needsToBeFixed`
// so they don't show when the user taps to fix encountered issues
const removeFixedCategories = categories =>
categories.filter(
category =>
category.healthchecks.filter(healthcheck => healthcheck.needsToBeFixed)
.length > 0,
categories.filter(category =>
category.healthchecks.some(healthcheck => healthcheck.needsToBeFixed),
);

const iterateOverCategories = categories =>
Promise.all(categories.map(iterateOverHealthchecks));
Promise.all(categories.map(iterateOverHealthChecks));

const healthchecksPerCategory = await iterateOverCategories(
Object.values(getHealthchecks(options)),
Expand Down
46 changes: 2 additions & 44 deletions packages/cli/src/commands/doctor/healthchecks/cocoaPods.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,10 @@
import execa from 'execa';
import {isSoftwareInstalled} from '../checkInstallation';

const hasSudoGranted = async () => {
try {
await execa('sudo', ['-nv']);

return true;
} catch (_ignored) {
return false;
}
};

// This is to show the loader but clear the `Password` prompt from the screen
const reportLoader = async ({loader, type, userHasSudoGranted}) => {
process.stdout.moveCursor(0, userHasSudoGranted ? -1 : -2);
process.stdout.clearScreenDown();

loader[type]();
};
import {installCocoaPods} from '../../../tools/installPods';

export default {
label: 'CocoaPods',
getDiagnosticsAsync: async () => ({
needsToBeFixed: !(await isSoftwareInstalled('pod')),
}),
runAutomaticFix: async ({loader}) => {
try {
// First attempt to install `cocoapods`
await execa('gem', ['install', 'cocoapods']);

loader.succeed();
} catch (_error) {
// This stops the loader so the user can provide a password to the `sudo` command
loader.stopAndPersist();

const userHasSudoGranted = await hasSudoGranted();

try {
await execa('sudo', ['gem', 'install', 'cocoapods']);

return await reportLoader({
loader,
type: 'succeed',
userHasSudoGranted,
});
} catch (_ignored) {
return await reportLoader({loader, type: 'fail', userHasSudoGranted});
}
}
},
runAutomaticFix: async ({loader}) => await installCocoaPods(loader),
};
92 changes: 53 additions & 39 deletions packages/cli/src/tools/installPods.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import inquirer from 'inquirer';
import {logger} from '@react-native-community/cli-tools';
import {NoopLoader} from './loader';
import sudo from 'sudo-prompt';
import {brewInstall} from './brewInstall';

async function updatePods(loader: typeof Ora) {
try {
Expand Down Expand Up @@ -41,6 +42,55 @@ function runSudo(command: string): Promise<void> {
});
}

async function installCocoaPods(loader: typeof Ora) {
loader.stop();

const withGem = 'Yes, with gem (may require sudo)';
const withHomebrew = 'Yes, with Homebrew';

const {shouldInstallCocoaPods} = await inquirer.prompt([
{
type: 'list',
name: 'shouldInstallCocoaPods',
message: `CocoaPods ${chalk.dim.underline(
'(https://cocoapods.org/)',
)} ${chalk.reset.bold(
"is not installed. It's necessary for iOS project to run correctly. Do you want to install it?",
)}`,
choices: [withGem, withHomebrew],
},
]);

switch (shouldInstallCocoaPods) {
case withGem:
const options = ['install', 'cocoapods', '--no-document'];
loader.start('Installing CocoaPods');
try {
// First attempt to install `cocoapods`
await execa('gem', options);
} catch (_error) {
// If that doesn't work then try with sudo
try {
await runSudo(`gem ${options.join(' ')}`);
} catch (error) {
loader.fail();
logger.error(error.stderr);

throw new Error(
`An error occured while trying to install CocoaPods, which is required by this template.\nPlease try again manually: sudo gem install cocoapods.\nCocoaPods documentation: ${chalk.dim.underline(
'https://cocoapods.org/',
)}`,
);
}
}
loader.succeed();
break;
case withHomebrew:
await brewInstall('cocoapods', loader);
break;
}
}

async function installPods({
projectName,
loader,
Expand Down Expand Up @@ -71,45 +121,7 @@ async function installPods({
await execa('pod', ['--version']);
} catch (e) {
loader.info();

const {shouldInstallCocoaPods} = await inquirer.prompt([
{
type: 'confirm',
name: 'shouldInstallCocoaPods',
message: `CocoaPods ${chalk.dim.underline(
'(https://cocoapods.org/)',
)} ${chalk.reset.bold(
"is not installed. It's necessary for iOS project to run correctly. Do you want to install it?",
)}`,
default: true,
},
]);

if (shouldInstallCocoaPods) {
loader.start('Installing CocoaPods');

try {
// First attempt to install `cocoapods`
await execa('gem', ['install', 'cocoapods', '--no-document']);
loader.succeed();
} catch (_error) {
try {
// If that doesn't work then try with sudo
await runSudo('gem install cocoapods --no-document');
} catch (error) {
loader.fail();
logger.log(error.stderr);

throw new Error(
`Error occured while trying to install CocoaPods, which is required by this template.\nPlease try again manually: "sudo gem install cocoapods".\nCocoaPods documentation: ${chalk.dim.underline(
'https://cocoapods.org/',
)}`,
);
}
}

await updatePods(loader);
}
installCocoaPods(loader);
}

if (shouldUpdatePods) {
Expand Down Expand Up @@ -141,4 +153,6 @@ async function installPods({
}
}

export {installCocoaPods};

export default installPods;