Skip to content

fix(apple): read Gemfile & fallback to globally installed pod #2629

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
17 changes: 9 additions & 8 deletions packages/cli-config-apple/src/tools/installPods.ts
Original file line number Diff line number Diff line change
@@ -12,14 +12,16 @@ import {
import runBundleInstall from './runBundleInstall';

interface PodInstallOptions {
skipBundleInstall?: boolean;
newArchEnabled?: boolean;
iosFolderPath?: string;
useBundler?: boolean;
root?: string;
}

interface RunPodInstallOptions {
shouldHandleRepoUpdate?: boolean;
newArchEnabled?: boolean;
useBundler?: boolean;
}

async function runPodInstall(loader: Ora, options: RunPodInstallOptions) {
@@ -30,8 +32,10 @@ async function runPodInstall(loader: Ora, options: RunPodInstallOptions) {
options?.newArchEnabled ? 'with New Architecture' : '',
)} ${chalk.dim('(this may take a few minutes)')}`,
);
const command = options.useBundler ? 'bundle' : 'pod';
const args = options.useBundler ? ['exec', 'pod', 'install'] : ['install'];

await execa('bundle', ['exec', 'pod', 'install'], {
await execa(command, args, {
env: {
RCT_NEW_ARCH_ENABLED: options?.newArchEnabled ? '1' : '0',
RCT_IGNORE_PODS_DEPRECATION: '1', // From React Native 0.79 onwards, users shouldn't install CocoaPods manually.
@@ -139,12 +143,8 @@ async function installPods(loader?: Ora, options?: PodInstallOptions) {
return;
}

if (fs.existsSync('../Gemfile') && !options?.skipBundleInstall) {
if (options?.useBundler) {
await runBundleInstall(loader);
} else if (!fs.existsSync('../Gemfile')) {
throw new CLIError(
'Could not find the Gemfile. Currently the CLI requires to have this file in the root directory of the project to install CocoaPods. If your configuration is different, please install the CocoaPods manually.',
);
}

try {
@@ -157,10 +157,11 @@ async function installPods(loader?: Ora, options?: PodInstallOptions) {
await installCocoaPods(loader);
}
await runPodInstall(loader, {
useBundler: options?.useBundler,
newArchEnabled: options?.newArchEnabled,
});
} finally {
process.chdir('..');
process.chdir(options?.root ?? '..');
}
}

49 changes: 35 additions & 14 deletions packages/cli-config-apple/src/tools/pods.ts
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import {
CLIError,
cacheManager,
getLoader,
logger,
} from '@react-native-community/cli-tools';
import installPods from './installPods';
import {
@@ -24,6 +25,18 @@ interface NativeDependencies {
[key: string]: DependencyConfig;
}

function checkGemfileForCocoaPods(gemfilePath: string): boolean {
try {
const gemfileContent = fs.readFileSync(gemfilePath, 'utf-8');
// Check for common CocoaPods gem declarations, because some projects might have Gemfile but for other purposes
return /^\s*gem\s+['"]cocoapods['"]/.test(gemfileContent);
} catch (error) {
logger.debug(`Failed to read Gemfile at: ${gemfilePath}`);
logger.debug(String(error));
return false;
}
}

function getPackageJson(root: string) {
try {
return require(path.join(root, 'package.json'));
@@ -87,7 +100,6 @@ async function getChecksum(

async function install(
packageJson: Record<string, any>,
cachedDependenciesHash: string | undefined,
currentDependenciesHash: string,
iosFolderPath: string,
platform: string,
@@ -101,9 +113,24 @@ async function install(
platform,
reactNativePath,
});
const gemfilePath = path.join(root, 'Gemfile');
let useBundler = checkGemfileForCocoaPods(gemfilePath);

if (!useBundler) {
logger.debug(
`Could not find the Gemfile at: ${chalk.cyan(gemfilePath)}
The default React Native Template uses Gemfile to leverage Ruby Bundler and we advice the same.
If you use Gemfile, make sure it's ${chalk.bold(
'in the project root directory',
)}.
Falling back to installing CocoaPods using globally installed "pod".`,
);
}

await installPods(loader, {
skipBundleInstall: !!cachedDependenciesHash,
useBundler,
iosFolderPath,
root,
});
cacheManager.set(packageJson.name, 'dependencies', currentDependenciesHash);
loader.succeed();
@@ -161,7 +188,6 @@ export default async function resolvePods(
if (options?.forceInstall) {
await install(
packageJson,
cachedDependenciesHash,
currentDependenciesHash,
platformFolderPath,
platformName,
@@ -182,17 +208,14 @@ export default async function resolvePods(
currentPodfileLockChecksum ?? '',
);
} else {
const loader = getLoader('Installing CocoaPods...');
try {
await installPods(loader, {
skipBundleInstall: !!cachedDependenciesHash,
newArchEnabled: options?.newArchEnabled,
iosFolderPath: platformFolderPath,
});
cacheManager.set(
packageJson.name,
'dependencies',
await install(
packageJson,
currentDependenciesHash,
platformFolderPath,
platformName,
root,
reactNativePath,
);
cacheManager.set(packageJson.name, 'podfile', currentPodfileHash);
// We need to read again the checksum because value changed after running `pod install`
@@ -202,9 +225,7 @@ export default async function resolvePods(
'podfileLock',
currentPodfileLockChecksum ?? '',
);
loader.succeed();
} catch (error) {
loader.fail();
throw new CLIError(
`Something when wrong while installing CocoaPods. Please run ${chalk.bold(
'pod install',
4 changes: 3 additions & 1 deletion packages/cli-config/src/schema.ts
Original file line number Diff line number Diff line change
@@ -158,7 +158,9 @@ export const projectConfig = t
automaticPodsInstallation: t.bool().default(true),
assets: t.array().items(t.string()).default([]),
})
.default({}),
.default({
automaticPodsInstallation: true,
}),
android: t
// AndroidProjectParams
.object({
8 changes: 6 additions & 2 deletions packages/cli/src/commands/init/init.ts
Original file line number Diff line number Diff line change
@@ -293,7 +293,9 @@ async function createFromTemplate({
platform: 'ios',
reactNativePath,
});
await installPods(loader, {});
await installPods(loader, {
useBundler: true,
});
loader.succeed();
setEmptyHashForCachedDependencies(projectName);
} else if (installPodsValue === 'undefined') {
@@ -312,7 +314,9 @@ async function createFromTemplate({
platform: 'ios',
reactNativePath,
});
await installPods(loader, {});
await installPods(loader, {
useBundler: true,
});
loader.succeed();
setEmptyHashForCachedDependencies(projectName);
}

Unchanged files with check annotations Beta

});
// react-native-macos stopped shipping `template.config.js` for 0.75, so this test is disabled. in future releases we should re-enable once `template.config.js` will be there again.
test.skip('init --platform-name should work for out of tree platform', () => {

Check warning on line 156 in __e2e__/init.test.ts

GitHub Actions / Lint

Disabled test
createCustomTemplateFiles();
const outOfTreePlatformName = 'react-native-macos';