diff --git a/packages/build-tools/src/android/__tests__/gradle.test.ts b/packages/build-tools/src/android/__tests__/gradle.test.ts new file mode 100644 index 0000000000..fb52f19894 --- /dev/null +++ b/packages/build-tools/src/android/__tests__/gradle.test.ts @@ -0,0 +1,50 @@ +import { Platform } from '@expo/eas-build-job'; +import spawn from '@expo/turtle-spawn'; +import fs from 'fs-extra'; + +import { runGradleCommand } from '../gradle'; + +jest.mock('@expo/turtle-spawn', () => ({ + __esModule: true, + default: jest.fn(), +})); + +jest.mock('fs-extra', () => ({ + chmod: jest.fn().mockResolvedValue(undefined), +})); + +describe(runGradleCommand, () => { + beforeEach(() => { + const spawnPromise = Promise.resolve(undefined) as any; + spawnPromise.child = { pid: 123 }; + (spawn as jest.Mock).mockReturnValue(spawnPromise); + (fs.chmod as unknown as jest.Mock).mockClear(); + (spawn as jest.Mock).mockClear(); + }); + + it('allows Sentry upload failures by default', async () => { + await runGradleCommand( + { + env: { EAS_BUILD_RUNNER: 'eas-build' }, + job: { + platform: Platform.ANDROID, + }, + } as any, + { + logger: { info: jest.fn() } as any, + gradleCommand: ':app:bundleRelease', + androidDir: '/app/android', + } + ); + + expect(spawn).toHaveBeenCalledWith( + 'bash', + ['-c', './gradlew :app:bundleRelease --profile '], + expect.objectContaining({ + env: expect.objectContaining({ + SENTRY_ALLOW_FAILURE: 'true', + }), + }) + ); + }); +}); diff --git a/packages/build-tools/src/android/gradle.ts b/packages/build-tools/src/android/gradle.ts index 9f616ad1c5..cf90524302 100644 --- a/packages/build-tools/src/android/gradle.ts +++ b/packages/build-tools/src/android/gradle.ts @@ -7,6 +7,7 @@ import path from 'path'; import { BuildContext } from '../context'; import { getParentAndDescendantProcessPidsAsync } from '../utils/processes'; +import { resolveSentryUploadEnv } from '../utils/buildEnv'; export async function ensureLFLineEndingsInGradlewScript( ctx: BuildContext @@ -31,6 +32,7 @@ export async function runGradleCommand( logger.info(`Running 'gradlew ${gradleCommand}' in ${androidDir}`); await fs.chmod(path.join(androidDir, 'gradlew'), 0o755); const verboseFlag = ctx.env['EAS_VERBOSE'] === '1' ? '--info' : ''; + const env = { ...ctx.env, ...extraEnv }; const spawnPromise = spawn( 'bash', @@ -46,8 +48,8 @@ export async function runGradleCommand( } }, env: { - ...ctx.env, - ...extraEnv, + ...resolveSentryUploadEnv(env), + ...env, ...resolveVersionOverridesEnvs(ctx), LC_ALL: 'C.UTF-8', }, diff --git a/packages/build-tools/src/ios/__tests__/fastlane.test.ts b/packages/build-tools/src/ios/__tests__/fastlane.test.ts index c066c78cf5..ff3c1ffe89 100644 --- a/packages/build-tools/src/ios/__tests__/fastlane.test.ts +++ b/packages/build-tools/src/ios/__tests__/fastlane.test.ts @@ -23,13 +23,19 @@ const WORKING_DIR = '/workingdir'; // BuildContext.getReactNativeProjectDirectory() nests the RN project under `${workingdir}/build`. const IOS_DIR = path.join(WORKING_DIR, 'build', 'ios'); -function makeIosBuildContext({ simulator }: { simulator: boolean }): BuildContext { +function makeIosBuildContext({ + simulator, + env = {}, +}: { + simulator: boolean; + env?: Record; +}): BuildContext { const job: Ios.Job = { ...createTestIosJob(), simulator }; return new BuildContext(job, { workingdir: WORKING_DIR, logger: createMockLogger(), logBuffer: { getLogs: () => [], getPhaseLogs: () => [] }, - env: { __API_SERVER_URL: 'http://api.expo.test' }, + env: { __API_SERVER_URL: 'http://api.expo.test', ...env }, uploadArtifact: jest.fn(), }); } @@ -125,4 +131,27 @@ describe(runFastlaneGym, () => { workspacePath: IOS_DIR, }); }); + + it('allows Sentry upload failures by default', async () => { + const ctx = makeIosBuildContext({ + simulator: false, + env: { EAS_BUILD_RUNNER: 'eas-build' }, + }); + + await runFastlaneGym(ctx, { + scheme: 'App', + credentials: ARCHIVE_CREDENTIALS, + entitlements: null, + }); + + expect(spawn).toHaveBeenCalledWith( + 'fastlane', + ['gym'], + expect.objectContaining({ + env: expect.objectContaining({ + SENTRY_ALLOW_FAILURE: 'true', + }), + }) + ); + }); }); diff --git a/packages/build-tools/src/ios/fastlane.ts b/packages/build-tools/src/ios/fastlane.ts index 771cc902c3..43e5cca0c3 100644 --- a/packages/build-tools/src/ios/fastlane.ts +++ b/packages/build-tools/src/ios/fastlane.ts @@ -13,6 +13,7 @@ import { isTVOS } from './tvos'; import { XcodeBuildLogger } from './xcpretty'; import { COMMON_FASTLANE_ENV } from '../common/fastlane'; import { BuildContext, SkipNativeBuildError } from '../context'; +import { resolveSentryUploadEnv } from '../utils/buildEnv'; export async function runFastlaneGym( ctx: BuildContext, @@ -48,11 +49,12 @@ export async function runFastlaneGym( } const buildLogger = new XcodeBuildLogger(ctx.logger, ctx.getReactNativeProjectDirectory()); void buildLogger.watchLogFiles(ctx.buildLogsDirectory); + const env = { ...ctx.env, ...extraEnv }; try { await runFastlane(['gym'], { cwd: workspacePath, logger: ctx.logger, - env: { ...ctx.env, ...extraEnv }, + env: { ...resolveSentryUploadEnv(env), ...env }, }); } finally { await buildLogger.flush(); diff --git a/packages/build-tools/src/utils/__tests__/buildEnv.test.ts b/packages/build-tools/src/utils/__tests__/buildEnv.test.ts new file mode 100644 index 0000000000..16be4053d4 --- /dev/null +++ b/packages/build-tools/src/utils/__tests__/buildEnv.test.ts @@ -0,0 +1,22 @@ +import { resolveSentryUploadEnv } from '../buildEnv'; + +describe(resolveSentryUploadEnv, () => { + it('allows Sentry upload failures by default', () => { + expect(resolveSentryUploadEnv({ EAS_BUILD_RUNNER: 'eas-build' })).toEqual({ + SENTRY_ALLOW_FAILURE: 'true', + }); + }); + + it('does not change Sentry upload behavior outside hosted EAS Build', () => { + expect(resolveSentryUploadEnv({})).toEqual({}); + }); + + it('preserves explicit Sentry upload behavior', () => { + expect( + resolveSentryUploadEnv({ EAS_BUILD_RUNNER: 'eas-build', SENTRY_ALLOW_FAILURE: 'false' }) + ).toEqual({}); + expect( + resolveSentryUploadEnv({ EAS_BUILD_RUNNER: 'eas-build', SENTRY_DISABLE_AUTO_UPLOAD: 'true' }) + ).toEqual({}); + }); +}); diff --git a/packages/build-tools/src/utils/buildEnv.ts b/packages/build-tools/src/utils/buildEnv.ts new file mode 100644 index 0000000000..c29c566772 --- /dev/null +++ b/packages/build-tools/src/utils/buildEnv.ts @@ -0,0 +1,13 @@ +import type { Env } from '@expo/eas-build-job'; + +export function resolveSentryUploadEnv(env: Env): Env { + if ( + env.EAS_BUILD_RUNNER !== 'eas-build' || + env.SENTRY_ALLOW_FAILURE || + env.SENTRY_DISABLE_AUTO_UPLOAD + ) { + return {}; + } + + return { SENTRY_ALLOW_FAILURE: 'true' }; +}