Skip to content

Commit ecbeadf

Browse files
antonisclaude
andcommitted
fix(core): Resolve expo CLI directly instead of using npx in sourcemap upload
Replace `npx expo config --json` with direct `require.resolve('expo/bin/cli')` to avoid npm's `devEngines.packageManager` enforcement breaking the script for projects that restrict their package manager to pnpm or yarn. Fixes #6152 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a5d243c commit ecbeadf

2 files changed

Lines changed: 20 additions & 25 deletions

File tree

packages/core/test/scripts/expo-upload-sourcemaps.test.ts

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -295,19 +295,6 @@ process.exit(exitCode);
295295
});
296296

297297
describe('sentry.properties fallback', () => {
298-
let mockNpxScript: string;
299-
let mockBinDir: string;
300-
301-
beforeEach(() => {
302-
// Create a mock npx that makes `expo config --json` fail fast
303-
// so the script falls through to the sentry.properties fallback
304-
mockBinDir = path.join(tempDir, 'mock-bin');
305-
fs.mkdirSync(mockBinDir, { recursive: true });
306-
mockNpxScript = path.join(mockBinDir, 'npx');
307-
fs.writeFileSync(mockNpxScript, '#!/usr/bin/env node\nprocess.exit(1);\n');
308-
fs.chmodSync(mockNpxScript, '755');
309-
});
310-
311298
const createSentryProperties = (dir: string, content: string) => {
312299
fs.mkdirSync(dir, { recursive: true });
313300
fs.writeFileSync(path.join(dir, 'sentry.properties'), content);
@@ -320,8 +307,7 @@ process.exit(exitCode);
320307
const defaultEnv = {
321308
SENTRY_AUTH_TOKEN: 'test-token',
322309
SENTRY_CLI_EXECUTABLE: mockSentryCliScript,
323-
// Put mock npx first in PATH so expo config fails fast
324-
PATH: `${mockBinDir}:${process.env.PATH}`,
310+
NODE_PATH: path.join(tempDir, 'nonexistent_modules'),
325311
};
326312

327313
const result = spawnSync(process.execPath, [EXPO_UPLOAD_SCRIPT, outputDir], {
@@ -415,28 +401,31 @@ process.exit(exitCode);
415401
expoConfig: Record<string, unknown>,
416402
env: Record<string, string | undefined> = {},
417403
): { stdout: string; stderr: string; exitCode: number } => {
418-
// Create a mock npx that outputs the given expo config as JSON
419-
const mockBinDir = path.join(tempDir, 'mock-bin-expo');
420-
fs.mkdirSync(mockBinDir, { recursive: true });
421-
const mockNpxScript = path.join(mockBinDir, 'npx');
422-
// The mock npx script outputs the config JSON when called with 'expo config --json'
404+
// Create a mock expo/bin/cli that outputs the given expo config as JSON
405+
const mockExpoCliDir = path.join(tempDir, 'node_modules', 'expo', 'bin');
406+
fs.mkdirSync(mockExpoCliDir, { recursive: true });
423407
fs.writeFileSync(
424-
mockNpxScript,
408+
path.join(mockExpoCliDir, 'cli'),
425409
`#!/usr/bin/env node
426410
const args = process.argv.slice(2);
427-
if (args.includes('expo') && args.includes('config') && args.includes('--json')) {
411+
if (args.includes('config') && args.includes('--json')) {
428412
process.stdout.write(${JSON.stringify(JSON.stringify(expoConfig))});
429413
process.exit(0);
430414
}
431415
process.exit(1);
432416
`,
433417
);
434-
fs.chmodSync(mockNpxScript, '755');
418+
fs.chmodSync(path.join(mockExpoCliDir, 'cli'), '755');
419+
// require.resolve needs a package.json to resolve the package
420+
fs.writeFileSync(
421+
path.join(tempDir, 'node_modules', 'expo', 'package.json'),
422+
JSON.stringify({ name: 'expo', version: '0.0.0' }),
423+
);
435424

436425
const defaultEnv = {
437426
SENTRY_AUTH_TOKEN: 'test-token',
438427
SENTRY_CLI_EXECUTABLE: mockSentryCliScript,
439-
PATH: `${mockBinDir}:${process.env.PATH}`,
428+
NODE_PATH: path.join(tempDir, 'node_modules'),
440429
};
441430

442431
const result = spawnSync(process.execPath, [EXPO_UPLOAD_SCRIPT, outputDir], {

packages/expo-upload-sourcemaps/cli.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ function getEnvVar(varname) {
1616

1717
function getSentryPluginPropertiesFromExpoConfig() {
1818
try {
19-
const result = spawnSync('npx', ['expo', 'config', '--json'], { encoding: 'utf8' });
19+
let expoCli;
20+
try {
21+
expoCli = require.resolve('expo/bin/cli');
22+
} catch {
23+
expoCli = require.resolve('expo/bin/cli', { paths: [process.cwd()] });
24+
}
25+
const result = spawnSync(process.execPath, [expoCli, 'config', '--json'], { encoding: 'utf8' });
2026
if (result.error || result.status !== 0) {
2127
throw result.error || new Error(`expo config exited with status ${result.status}`);
2228
}

0 commit comments

Comments
 (0)