Skip to content

Commit ce0a4aa

Browse files
fix(apple): include check if Podfile and Podfile.lock changed when deciding to install Cocoapods (#2443)
* fix(apple): compare also `Podfile` and `Podfile.lock` when deciding to install Cocoapods * fix: save lock checksum instead of hash * Update packages/cli-platform-apple/src/tools/pods.ts Co-authored-by: Riccardo Cipolleschi <[email protected]> * fix: simplify receiving checksum from `Podfile` * fix: return undefined if `Podfile.lock` not found * fix: sync unit tests with current impl --------- Co-authored-by: Riccardo Cipolleschi <[email protected]>
1 parent 8c8c0ae commit ce0a4aa

File tree

5 files changed

+98
-25
lines changed

5 files changed

+98
-25
lines changed

packages/cli-config-apple/src/__tests__/pods.test.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import {writeFiles, getTempDirectory, cleanup} from '../../../../jest/helpers';
23
import installPods from '../tools/installPods';
34
import resolvePods, {
@@ -51,6 +52,7 @@ const DIR = getTempDirectory('root_test');
5152
const createTempFiles = (rest?: Record<string, string>) => {
5253
writeFiles(DIR, {
5354
'package.json': JSON.stringify(packageJson),
55+
'ios/Podfile': '',
5456
...rest,
5557
});
5658
};
@@ -83,25 +85,27 @@ describe('getPlatformDependencies', () => {
8385

8486
describe('resolvePods', () => {
8587
it('should install pods if they are not installed', async () => {
86-
createTempFiles({'ios/Podfile/Manifest.lock': ''});
88+
createTempFiles();
8789

88-
await resolvePods(DIR, {}, 'ios');
90+
await resolvePods(DIR, path.join(DIR, 'ios'), {}, 'ios');
8991

9092
expect(installPods).toHaveBeenCalled();
9193
});
9294

9395
it('should install pods when force option is set to true', async () => {
9496
createTempFiles();
9597

96-
await resolvePods(DIR, {}, 'ios', {forceInstall: true});
98+
await resolvePods(DIR, path.join(DIR, 'ios'), {}, 'ios', {
99+
forceInstall: true,
100+
});
97101

98102
expect(installPods).toHaveBeenCalled();
99103
});
100104

101105
it('should install pods when there is no cached hash of dependencies', async () => {
102106
createTempFiles();
103107

104-
await resolvePods(DIR, {}, 'ios');
108+
await resolvePods(DIR, path.join(DIR, 'ios'), {}, 'ios');
105109

106110
expect(mockSet).toHaveBeenCalledWith(
107111
packageJson.name,
@@ -111,22 +115,26 @@ describe('resolvePods', () => {
111115
});
112116

113117
it('should skip pods installation if the cached hash and current hash are the same', async () => {
114-
createTempFiles({'ios/Pods/Manifest.lock': ''});
118+
createTempFiles({
119+
'ios/Pods/Manifest.lock': '',
120+
'ios/Podfile.lock': `PODFILE CHECKSUM: ${dependencyHash}`,
121+
});
115122

116123
mockGet.mockImplementation(() => dependencyHash);
117124

118-
await resolvePods(DIR, {}, 'ios');
125+
await resolvePods(DIR, path.join(DIR, 'ios'), {}, 'ios');
119126

120127
expect(installPods).not.toHaveBeenCalled();
121128
});
122129

123130
it('should install pods if the cached hash and current hash are different', async () => {
124-
createTempFiles({'ios/Pods/Manifest.lock': ''});
131+
createTempFiles();
125132

126133
mockGet.mockImplementation(() => dependencyHash);
127134

128135
await resolvePods(
129136
DIR,
137+
path.join(DIR, 'ios'),
130138
{
131139
dep1: {
132140
name: 'dep1',

packages/cli-config-apple/src/tools/pods.ts

+61-10
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
getLoader,
99
} from '@react-native-community/cli-tools';
1010
import installPods from './installPods';
11-
import findPodfilePath from '../config/findPodfilePath';
1211
import {
1312
DependencyConfig,
1413
IOSDependencyConfig,
@@ -61,10 +60,30 @@ export function generateMd5Hash(text: string) {
6160
return createHash('md5').update(text).digest('hex');
6261
}
6362

64-
export function compareMd5Hashes(hash1: string, hash2: string) {
63+
export function compareMd5Hashes(hash1?: string, hash2?: string) {
6564
return hash1 === hash2;
6665
}
6766

67+
async function getChecksum(
68+
podfileLockPath: string,
69+
): Promise<string | undefined> {
70+
try {
71+
const file = fs.readFileSync(podfileLockPath, 'utf8');
72+
73+
const checksumLine = file
74+
.split('\n')
75+
.find((line) => line.includes('PODFILE CHECKSUM'));
76+
77+
if (checksumLine) {
78+
return checksumLine.split(': ')[1];
79+
}
80+
81+
return undefined;
82+
} catch {
83+
return undefined;
84+
}
85+
}
86+
6887
async function install(
6988
packageJson: Record<string, any>,
7089
cachedDependenciesHash: string | undefined,
@@ -79,24 +98,28 @@ async function install(
7998
});
8099
cacheManager.set(packageJson.name, 'dependencies', currentDependenciesHash);
81100
loader.succeed();
82-
} catch {
101+
} catch (error) {
83102
loader.fail();
84103
throw new CLIError(
85104
`Something when wrong while installing CocoaPods. Please run ${chalk.bold(
86105
'pod install',
87106
)} manually`,
107+
error as Error,
88108
);
89109
}
90110
}
91111

92112
export default async function resolvePods(
93113
root: string,
114+
sourceDir: string,
94115
nativeDependencies: NativeDependencies,
95116
platformName: ApplePlatform,
96117
options?: ResolvePodsOptions,
97118
) {
98119
const packageJson = getPackageJson(root);
99-
const podfilePath = findPodfilePath(root, platformName);
120+
const podfilePath = path.join(sourceDir, 'Podfile'); // sourceDir is calculated based on Podfile location, see getProjectConfig()
121+
122+
const podfileLockPath = path.join(sourceDir, 'Podfile.lock');
100123
const platformFolderPath = podfilePath
101124
? podfilePath.slice(0, podfilePath.lastIndexOf('/'))
102125
: path.join(root, platformName);
@@ -108,6 +131,18 @@ export default async function resolvePods(
108131
);
109132
const dependenciesString = dependenciesToString(platformDependencies);
110133
const currentDependenciesHash = generateMd5Hash(dependenciesString);
134+
// Users can manually add dependencies to Podfile, so we can't entirely rely on `dependencies` from `config`'s output.
135+
const currentPodfileHash = generateMd5Hash(
136+
fs.readFileSync(podfilePath, 'utf8'),
137+
);
138+
let currentPodfileLockChecksum = await getChecksum(podfileLockPath);
139+
140+
const cachedPodfileHash = cacheManager.get(packageJson.name, 'podfile');
141+
const cachedPodfileLockChecksum = cacheManager.get(
142+
packageJson.name,
143+
'podfileLock',
144+
);
145+
111146
const cachedDependenciesHash = cacheManager.get(
112147
packageJson.name,
113148
'dependencies',
@@ -120,13 +155,20 @@ export default async function resolvePods(
120155
currentDependenciesHash,
121156
platformFolderPath,
122157
);
123-
} else if (arePodsInstalled && cachedDependenciesHash === undefined) {
124-
cacheManager.set(packageJson.name, 'dependencies', currentDependenciesHash);
125158
} else if (
126-
!cachedDependenciesHash ||
127-
!compareMd5Hashes(currentDependenciesHash, cachedDependenciesHash) ||
128-
!arePodsInstalled
159+
arePodsInstalled &&
160+
compareMd5Hashes(currentDependenciesHash, cachedDependenciesHash) &&
161+
compareMd5Hashes(currentPodfileHash, cachedPodfileHash) &&
162+
compareMd5Hashes(currentPodfileLockChecksum, cachedPodfileLockChecksum)
129163
) {
164+
cacheManager.set(packageJson.name, 'dependencies', currentDependenciesHash);
165+
cacheManager.set(packageJson.name, 'podfile', currentPodfileHash);
166+
cacheManager.set(
167+
packageJson.name,
168+
'podfileLock',
169+
currentPodfileLockChecksum ?? '',
170+
);
171+
} else {
130172
const loader = getLoader('Installing CocoaPods...');
131173
try {
132174
await installPods(loader, {
@@ -139,13 +181,22 @@ export default async function resolvePods(
139181
'dependencies',
140182
currentDependenciesHash,
141183
);
184+
cacheManager.set(packageJson.name, 'podfile', currentPodfileHash);
185+
// We need to read again the checksum because value changed after running `pod install`
186+
currentPodfileLockChecksum = await getChecksum(podfileLockPath);
187+
cacheManager.set(
188+
packageJson.name,
189+
'podfileLock',
190+
currentPodfileLockChecksum ?? '',
191+
);
142192
loader.succeed();
143-
} catch {
193+
} catch (error) {
144194
loader.fail();
145195
throw new CLIError(
146196
`Something when wrong while installing CocoaPods. Please run ${chalk.bold(
147197
'pod install',
148198
)} manually`,
199+
error as Error,
149200
);
150201
}
151202
}

packages/cli-platform-apple/src/commands/buildCommand/createBuild.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ const createBuild =
2929
? await getArchitecture(platformConfig.sourceDir)
3030
: undefined;
3131

32-
await resolvePods(ctx.root, ctx.dependencies, platformName, {
33-
forceInstall: args.forcePods,
34-
newArchEnabled: isAppRunningNewArchitecture,
35-
});
32+
await resolvePods(
33+
ctx.root,
34+
platformConfig.sourceDir,
35+
ctx.dependencies,
36+
platformName,
37+
{
38+
forceInstall: args.forcePods,
39+
newArchEnabled: isAppRunningNewArchitecture,
40+
},
41+
);
3642

3743
installedPods = true;
3844
}

packages/cli-platform-apple/src/commands/runCommand/createRun.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,16 @@ const createRun =
8484
? await getArchitecture(platformConfig.sourceDir)
8585
: undefined;
8686

87-
await resolvePods(ctx.root, ctx.dependencies, platformName, {
88-
forceInstall: args.forcePods,
89-
newArchEnabled: isAppRunningNewArchitecture,
90-
});
87+
await resolvePods(
88+
ctx.root,
89+
platformConfig.sourceDir,
90+
ctx.dependencies,
91+
platformName,
92+
{
93+
forceInstall: args.forcePods,
94+
newArchEnabled: isAppRunningNewArchitecture,
95+
},
96+
);
9197

9298
installedPods = true;
9399
}

packages/cli-tools/src/cacheManager.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ type CacheKey =
1010
| 'lastChecked'
1111
| 'latestVersion'
1212
| 'dependencies'
13+
| 'podfile'
14+
| 'podfileLock'
1315
| 'lastUsedIOSDeviceId';
1416
type Cache = {[key in CacheKey]?: string};
1517

0 commit comments

Comments
 (0)