Skip to content

Commit ab730d9

Browse files
authored
chore: improve error messages around installing pods; run codegen (#289)
* chore: improve error messages around installing pods * remove unnecessary retry since we already know whether bundler is in use * add codegen step * changeset
1 parent 0be7484 commit ab730d9

File tree

8 files changed

+144
-57
lines changed

8 files changed

+144
-57
lines changed

.changeset/tough-yaks-cry.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@rnef/platform-apple-helpers': patch
3+
'@rnef/plugin-brownfield-ios': patch
4+
'@rnef/platform-ios': patch
5+
---
6+
7+
chore: improve error messages around installing pods; run codegen

packages/platform-apple-helpers/src/lib/commands/build/createBuild.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@ import {
2121
import type { BuildFlags } from './buildOptions.js';
2222
import { exportArchive } from './exportArchive.js';
2323

24-
export const createBuild = async (
25-
platformName: BuilderCommand['platformName'],
26-
projectConfig: ProjectConfig,
27-
args: BuildFlags,
28-
projectRoot: string
29-
) => {
24+
export const createBuild = async ({
25+
platformName,
26+
projectConfig,
27+
args,
28+
projectRoot,
29+
reactNativePath,
30+
}: {
31+
platformName: BuilderCommand['platformName'];
32+
projectConfig: ProjectConfig;
33+
args: BuildFlags;
34+
projectRoot: string;
35+
reactNativePath: string;
36+
}) => {
3037
await validateArgs(args);
3138
let xcodeProject: XcodeProjectInfo;
3239
let sourceDir: string;
@@ -40,6 +47,7 @@ export const createBuild = async (
4047
? getSimulatorPlatformSDK(platformName)
4148
: getDevicePlatformSDK(platformName),
4249
args,
50+
reactNativePath,
4351
});
4452
const loader = spinner();
4553
loader.start('');

packages/platform-apple-helpers/src/lib/commands/run/createRun.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,23 @@ import { runOnMacCatalyst } from './runOnMacCatalyst.js';
3131
import { launchSimulator, runOnSimulator } from './runOnSimulator.js';
3232
import type { RunFlags } from './runOptions.js';
3333

34-
export const createRun = async (
35-
platformName: ApplePlatform,
36-
projectConfig: ProjectConfig,
37-
args: RunFlags,
38-
projectRoot: string,
39-
remoteCacheProvider: SupportedRemoteCacheProviders | undefined,
40-
fingerprintOptions: { extraSources: string[]; ignorePaths: string[] }
41-
) => {
34+
export const createRun = async ({
35+
platformName,
36+
projectConfig,
37+
args,
38+
projectRoot,
39+
remoteCacheProvider,
40+
fingerprintOptions,
41+
reactNativePath,
42+
}: {
43+
platformName: ApplePlatform;
44+
projectConfig: ProjectConfig;
45+
args: RunFlags;
46+
projectRoot: string;
47+
remoteCacheProvider: SupportedRemoteCacheProviders | undefined;
48+
fingerprintOptions: { extraSources: string[]; ignorePaths: string[] };
49+
reactNativePath: string;
50+
}) => {
4251
if (!args.binaryPath && args.remoteCache) {
4352
const artifactName = await formatArtifactName({
4453
platform: 'ios',
@@ -76,6 +85,7 @@ export const createRun = async (
7685
projectRoot,
7786
udid,
7887
deviceName,
88+
reactNativePath,
7989
});
8090
await runOnMac(appPath);
8191
return;
@@ -88,6 +98,7 @@ export const createRun = async (
8898
projectRoot,
8999
udid,
90100
deviceName,
101+
reactNativePath,
91102
});
92103
if (scheme) {
93104
await runOnMacCatalyst(appPath, scheme);
@@ -121,6 +132,7 @@ export const createRun = async (
121132
platformSDK: getSimulatorPlatformSDK(platformName),
122133
udid: device.udid,
123134
projectRoot,
135+
reactNativePath,
124136
}),
125137
]);
126138

@@ -133,6 +145,7 @@ export const createRun = async (
133145
platformSDK: getDevicePlatformSDK(platformName),
134146
udid: device.udid,
135147
projectRoot,
148+
reactNativePath,
136149
});
137150
await runOnDevice(device, appPath, projectConfig.sourceDir);
138151
}
@@ -173,6 +186,7 @@ export const createRun = async (
173186
platformSDK: getSimulatorPlatformSDK(platformName),
174187
udid: simulator.udid,
175188
projectRoot,
189+
reactNativePath,
176190
}),
177191
]);
178192
await runOnSimulator(simulator, appPath, infoPlistPath);

packages/platform-apple-helpers/src/lib/utils/buildApp.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export async function buildApp({
2222
udid,
2323
projectRoot,
2424
deviceName,
25+
reactNativePath,
2526
}: {
2627
args: RunFlags | BuildFlags;
2728
projectConfig: ProjectConfig;
@@ -31,6 +32,7 @@ export async function buildApp({
3132
udid?: string;
3233
deviceName?: string;
3334
projectRoot: string;
35+
reactNativePath: string;
3436
}) {
3537
if ('binaryPath' in args && args.binaryPath) {
3638
return {
@@ -50,7 +52,8 @@ export async function buildApp({
5052
projectRoot,
5153
platformName,
5254
sourceDir,
53-
args.newArch
55+
args.newArch,
56+
reactNativePath
5457
);
5558
// When the project is not a workspace, we need to get the project config again,
5659
// because running pods install might have generated .xcworkspace project.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { SubprocessError } from '@rnef/tools';
2+
import { spawn } from '@rnef/tools';
3+
import { RnefError } from '@rnef/tools';
4+
import fs from 'fs';
5+
import path from 'path';
6+
import type { ApplePlatform } from '../types/index.js';
7+
8+
interface CodegenOptions {
9+
projectRoot: string;
10+
platformName: ApplePlatform;
11+
reactNativePath: string;
12+
}
13+
14+
async function runCodegen(options: CodegenOptions) {
15+
if (fs.existsSync('build')) {
16+
fs.rmSync('build', { recursive: true });
17+
}
18+
19+
const codegenScript = path.join(
20+
options.reactNativePath,
21+
'scripts/generate-codegen-artifacts.js'
22+
);
23+
24+
try {
25+
await spawn('node', [
26+
codegenScript,
27+
'-p',
28+
options.projectRoot,
29+
'-o',
30+
process.cwd(),
31+
'-t',
32+
options.platformName,
33+
]);
34+
} catch (error) {
35+
throw new RnefError('Failed to run React Native codegen script', {
36+
cause: (error as SubprocessError).stdout,
37+
});
38+
}
39+
}
40+
41+
export default runCodegen;

packages/platform-apple-helpers/src/lib/utils/pods.ts

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
spinner,
1313
} from '@rnef/tools';
1414
import type { ApplePlatform } from '../types/index.js';
15+
import runCodegen from './codegen.js';
1516

1617
const podErrorHelpMessage = `Please make sure your environment is correctly set up.
1718
Learn more at: ${color.dim('https://cocoapods.org/')}
@@ -21,7 +22,8 @@ export async function installPodsIfNeeded(
2122
projectRoot: string,
2223
platformName: ApplePlatform,
2324
sourceDir: string,
24-
newArch: boolean
25+
newArch: boolean,
26+
reactNativePath: string
2527
) {
2628
const podsPath = path.join(sourceDir, 'Pods');
2729
const podfilePath = path.join(sourceDir, 'Podfile');
@@ -40,6 +42,7 @@ export async function installPodsIfNeeded(
4042
: true;
4143

4244
if (!podsDirExists || hashChanged) {
45+
await runCodegen({ projectRoot, platformName, reactNativePath });
4346
await installPods({ projectRoot, sourceDir, podfilePath, newArch });
4447
cacheManager.set(
4548
cacheKey,
@@ -64,7 +67,8 @@ const calculateCurrentHash = ({
6467
podfile = fs.readFileSync(podfilePath, 'utf-8');
6568
} catch {
6669
throw new RnefError(
67-
`No Podfile found at: ${podfilePath}. ${podErrorHelpMessage}`
70+
`No Podfile found at: ${podfilePath}.
71+
${podErrorHelpMessage}`
6872
);
6973
}
7074

@@ -104,7 +108,6 @@ async function runPodInstall(options: {
104108

105109
const command = options.useBundler ? 'bundle' : 'pod';
106110
const args = options.useBundler ? ['exec', 'pod', 'install'] : ['install'];
107-
108111
try {
109112
await spawn(command, args, {
110113
env: {
@@ -117,8 +120,9 @@ async function runPodInstall(options: {
117120
cwd: options.sourceDir,
118121
});
119122
} catch (error) {
120-
const stderr = (error as SubprocessError).stderr;
121-
123+
loader.stop('Failed: Installing CocoaPods dependencies', 1);
124+
const stderr =
125+
(error as SubprocessError).stderr || (error as SubprocessError).output;
122126
/**
123127
* If CocoaPods failed due to repo being out of date, it will
124128
* include the update command in the error message.
@@ -135,26 +139,15 @@ async function runPodInstall(options: {
135139
useBundler: options.useBundler,
136140
});
137141
} else {
138-
if (options.useBundler) {
139-
// If for any reason the installing with bundler failed, try with pure `pod install`
140-
await runPodInstall({
141-
shouldHandleRepoUpdate: false,
142-
sourceDir: options.sourceDir,
143-
newArch: options.newArch,
144-
useBundler: false,
145-
});
146-
} else {
147-
loader.stop('CocoaPods installation failed. ', 1);
148-
149-
throw new RnefError(
150-
`CocoaPods installation failed. ${podErrorHelpMessage}`,
151-
{ cause: stderr }
152-
);
153-
}
142+
throw new RnefError(
143+
`CocoaPods installation failed.
144+
${podErrorHelpMessage}`,
145+
{ cause: stderr }
146+
);
154147
}
155148
}
156149

157-
loader.stop('CocoaPods installed successfully.');
150+
loader.stop('Installed CocoaPods dependencies successfully');
158151
}
159152

160153
async function runPodUpdate(cwd: string, useBundler: boolean) {
@@ -169,10 +162,11 @@ async function runPodUpdate(cwd: string, useBundler: boolean) {
169162
} catch (error) {
170163
const stderr =
171164
(error as SubprocessError).stderr || (error as SubprocessError).stdout;
172-
loader.stop();
165+
loader.stop('Failed: Updating CocoaPods repositories', 1);
173166

174167
throw new RnefError(
175-
`Failed to update CocoaPods repositories for iOS project. ${podErrorHelpMessage}`,
168+
`Failed to update CocoaPods repositories for iOS project.
169+
${podErrorHelpMessage}`,
176170
{ cause: stderr }
177171
);
178172
}
@@ -194,6 +188,9 @@ async function installPods(options: {
194188
options.sourceDir,
195189
options.projectRoot
196190
);
191+
if (!useBundler) {
192+
logger.info('Unable to use Ruby bundler, falling back to "pod install"');
193+
}
197194
await runPodInstall({
198195
sourceDir: options.sourceDir,
199196
newArch: options.newArch,
@@ -210,10 +207,18 @@ async function validatePodCommand(sourceDir: string) {
210207
try {
211208
await spawn('pod', ['--version'], { cwd: sourceDir });
212209
} catch (error) {
210+
const cause = (error as SubprocessError).cause;
211+
if (cause instanceof Error && cause.message.includes('ENOENT')) {
212+
throw new RnefError(
213+
`The "pod" command is not available.
214+
${podErrorHelpMessage}`
215+
);
216+
}
213217
const stderr =
214218
(error as SubprocessError).stderr || (error as SubprocessError).stdout;
215219
throw new RnefError(
216-
`CocoaPods "pod" command failed. ${podErrorHelpMessage}`,
220+
`CocoaPods "pod" command failed.
221+
${podErrorHelpMessage}`,
217222
{ cause: stderr }
218223
);
219224
}
@@ -260,14 +265,15 @@ skipping Ruby Gems installation.`
260265
} catch (error) {
261266
const stderr =
262267
(error as SubprocessError).stderr || (error as SubprocessError).stdout;
263-
loader.stop('Ruby Gems installation failed.', 1);
268+
loader.stop('Failed: Installing Ruby Gems', 1);
264269
throw new RnefError(
265-
`Failed to install Ruby Gems with "bundle install". ${podErrorHelpMessage}`,
270+
`Failed to install Ruby Gems with "bundle install".
271+
${podErrorHelpMessage}`,
266272
{ cause: stderr }
267273
);
268274
}
269275

270-
loader.stop('Installed Ruby Gems.');
276+
loader.stop('Installed Ruby Gems');
271277
return true;
272278
}
273279

packages/platform-ios/src/lib/platformIOS.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ export const platformIOS =
2424
description: 'Build iOS app.',
2525
action: async (args) => {
2626
intro('Building iOS app');
27-
await createBuild('ios', iosConfig, args as BuildFlags, projectRoot);
27+
await createBuild({
28+
platformName: 'ios',
29+
projectConfig: iosConfig,
30+
args: args as BuildFlags,
31+
projectRoot,
32+
reactNativePath: api.getReactNativePath(),
33+
});
2834
outro('Success 🎉.');
2935
},
3036
options: buildOptions,
@@ -35,14 +41,15 @@ export const platformIOS =
3541
description: 'Run iOS app.',
3642
action: async (args) => {
3743
intro('Running iOS app');
38-
await createRun(
39-
'ios',
40-
iosConfig,
41-
args as RunFlags,
44+
await createRun({
45+
platformName: 'ios',
46+
projectConfig: iosConfig,
47+
args: args as RunFlags,
4248
projectRoot,
43-
api.getRemoteCacheProvider(),
44-
api.getFingerprintOptions()
45-
);
49+
remoteCacheProvider: api.getRemoteCacheProvider(),
50+
fingerprintOptions: api.getFingerprintOptions(),
51+
reactNativePath: api.getReactNativePath(),
52+
});
4653
outro('Success 🎉.');
4754
},
4855
// @ts-expect-error: fix `simulator` is not defined in `RunFlags`

packages/plugin-brownfield-ios/src/lib/pluginBrownfieldIos.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ export const pluginBrownfieldIos =
3232
const buildFolder = args.buildFolder ?? derivedDataDir;
3333
const configuration = args.configuration ?? 'Debug';
3434

35-
await createBuild(
36-
'ios',
37-
iosConfig,
38-
{ ...args, destinations, buildFolder },
39-
projectRoot
40-
);
35+
await createBuild({
36+
platformName: 'ios',
37+
projectConfig: iosConfig,
38+
args: { ...args, destinations, buildFolder },
39+
projectRoot,
40+
reactNativePath: api.getReactNativePath(),
41+
});
4142

4243
if (!args.scheme) {
4344
throw new RnefError(

0 commit comments

Comments
 (0)