Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This is the log of notable changes to EAS CLI and related packages.

### 🐛 Bug fixes

- [build-tools] Regenerate Android autolinking metadata after cache restore so paths from another operating system cannot break Gradle project discovery. ([#4288](https://github.com/expo/eas-cli/pull/4288) by [@AbbanMustafa](https://github.com/AbbanMustafa))

- [build-tools] Preapprove custom URL schemes before opening them in iOS Simulator sessions to avoid the first-use confirmation prompt. ([#4274](https://github.com/expo/eas-cli/pull/4274) by [@szdziedzic](https://github.com/szdziedzic))

### 🧹 Chores
Expand Down
30 changes: 30 additions & 0 deletions packages/build-tools/src/android/__tests__/autolinking.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import fs from 'fs-extra';
import path from 'path';

import { clearReactNativeAutolinkingCacheAsync } from '../autolinking';

describe(clearReactNativeAutolinkingCacheAsync, () => {
it('removes the generated autolinking cache without removing other Android build output', async () => {
const projectDirectory = '/workingdir/build';
const autolinkingDirectory = path.join(projectDirectory, 'android/build/generated/autolinking');
const unrelatedBuildOutput = path.join(projectDirectory, 'android/build/outputs/keep.txt');

await fs.outputJson(path.join(autolinkingDirectory, 'autolinking.json'), {
dependencies: {
'react-native-screens': {
platforms: {
android: {
sourceDir: 'C:\\Users\\expo\\app\\node_modules\\react-native-screens\\android',
},
},
},
},
});
await fs.outputFile(unrelatedBuildOutput, 'keep');

await clearReactNativeAutolinkingCacheAsync(projectDirectory);

await expect(fs.pathExists(autolinkingDirectory)).resolves.toBe(false);
await expect(fs.readFile(unrelatedBuildOutput, 'utf8')).resolves.toBe('keep');
});
});
13 changes: 13 additions & 0 deletions packages/build-tools/src/android/autolinking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import fs from 'fs-extra';
import path from 'path';

/**
* React Native's generated autolinking state contains absolute paths. A user cache created on a
* different operating system can therefore link Gradle projects to directories that do not exist
* on the worker. Always regenerate this small cache after restoring user-controlled build output.
*/
export async function clearReactNativeAutolinkingCacheAsync(
projectDirectory: string
): Promise<void> {
await fs.remove(path.join(projectDirectory, 'android/build/generated/autolinking'));
}
27 changes: 27 additions & 0 deletions packages/build-tools/src/builders/__tests__/android.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createMockLogger } from '../../__tests__/utils/logger';
import { BuildContext } from '../../context';
import { Datadog } from '../../datadog';
import { restoreCredentials } from '../../android/credentials';
import { clearReactNativeAutolinkingCacheAsync } from '../../android/autolinking';
import { uploadEmbeddedBundleAsync } from '../../utils/expoUpdatesEmbedded';
import androidBuilder from '../android';
import { runBuilderWithHooksAsync } from '../common';
Expand All @@ -25,6 +26,7 @@ jest.mock('../custom', () => ({
runCustomBuildAsync: jest.fn(),
}));
jest.mock('../../android/credentials');
jest.mock('../../android/autolinking');
jest.mock('../../android/gradle', () => ({
ensureLFLineEndingsInGradlewScript: jest.fn(),
resolveGradleCommand: jest.fn(() => ':app:bundleRelease'),
Expand All @@ -43,9 +45,11 @@ jest.mock('../../common/setup', () => ({
jest.mock('../../steps/functions/restoreBuildCache', () => ({
cacheStatsAsync: jest.fn(),
restoreCcacheAsync: jest.fn(),
restoreGradleCacheAsync: jest.fn(),
}));
jest.mock('../../steps/functions/saveBuildCache', () => ({
saveCcacheAsync: jest.fn(),
saveGradleCacheAsync: jest.fn(),
}));
jest.mock('../../steps/utils/android/gradleConfig', () => ({
...jest.requireActual('../../steps/utils/android/gradleConfig'),
Expand Down Expand Up @@ -90,6 +94,29 @@ describe(androidBuilder, () => {
);
});

it('clears generated autolinking state after restoring the user cache', async () => {
const restoreCache = jest.fn();
const ctx = new BuildContext(createTestAndroidJob(), {
workingdir: '/workingdir',
logBuffer: { getLogs: () => [], getPhaseLogs: () => [] },
logger: createMockLogger(),
env: {
__API_SERVER_URL: 'http://api.expo.test',
EAS_BUILD_RUNNER: 'eas-build',
},
uploadArtifact: jest.fn(),
cacheManager: { restoreCache, saveCache: jest.fn() },
});

await androidBuilder(ctx);

expect(restoreCache).toHaveBeenCalled();
expect(clearReactNativeAutolinkingCacheAsync).toHaveBeenCalledWith('/workingdir/build');
expect(restoreCache.mock.invocationCallOrder[0]).toBeLessThan(
jest.mocked(clearReactNativeAutolinkingCacheAsync).mock.invocationCallOrder[0]
);
});

it('passes the install_node_modules anchor and a hooks ref to setupAsync', async () => {
const ctx = new BuildContext(createTestAndroidJob(), {
workingdir: '/workingdir',
Expand Down
2 changes: 2 additions & 0 deletions packages/build-tools/src/builders/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from 'path';
import { runBuilderWithHooksAsync } from './common';
import { runCustomBuildAsync } from './custom';
import { restoreCredentials } from '../android/credentials';
import { clearReactNativeAutolinkingCacheAsync } from '../android/autolinking';
import {
ensureLFLineEndingsInGradlewScript,
resolveGradleCommand,
Expand Down Expand Up @@ -95,6 +96,7 @@ async function buildInnerAsync(
return;
}
await ctx.cacheManager?.restoreCache(ctx);
await clearReactNativeAutolinkingCacheAsync(workingDirectory);
await restoreCcacheAsync({
logger: ctx.logger,
workingDirectory,
Expand Down
Loading