Skip to content

Commit 6c36ba5

Browse files
antonisclaude
andauthored
fix(ios): Respect SENTRY_ALLOW_FAILURE in Xcode build scripts (#5616)
Both sentry-xcode.sh and sentry-xcode-debug-files.sh now properly check the SENTRY_ALLOW_FAILURE environment variable. When set to true, upload failures print warnings instead of errors, allowing builds to continue. This fixes a regression from v5.22.0 (PR #3887) where the flag was mentioned in error messages but never actually implemented. Fixes #5507 Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 6f5e35c commit 6c36ba5

File tree

4 files changed

+287
-6
lines changed

4 files changed

+287
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
- [changelog](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/main/CHANGELOG.md#490)
5454
- [diff](https://github.com/getsentry/sentry-javascript-bundler-plugins/compare/4.8.0...4.9.0)
5555

56+
### Fixes
57+
58+
- Fix `SENTRY_ALLOW_FAILURE` environment variable not being respected in Xcode build scripts ([#5616](https://github.com/getsentry/sentry-react-native/pull/5616))
59+
5660
## 7.11.0
5761

5862
### Features

packages/core/scripts/sentry-xcode-debug-files.sh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,17 @@ else
7070
# 'warning:' triggers a warning in Xcode, 'error:' triggers an error
7171
set +x +e # disable printing commands otherwise we might print `error:` by accident and allow continuing on error
7272
SENTRY_UPLOAD_COMMAND_OUTPUT=$(/bin/sh -c "\"$LOCAL_NODE_BINARY\" $UPLOAD_DEBUG_FILES" 2>&1)
73-
if [ $? -eq 0 ]; then
73+
UPLOAD_EXIT_CODE=$?
74+
if [ $UPLOAD_EXIT_CODE -eq 0 ]; then
7475
echo "$SENTRY_UPLOAD_COMMAND_OUTPUT" | awk '{print "output: sentry-cli - " $0}'
7576
else
76-
echo "error: sentry-cli - To disable native debug files auto upload, set SENTRY_DISABLE_AUTO_UPLOAD=true in your environment variables. Or to allow failing upload, set SENTRY_ALLOW_FAILURE=true"
77-
echo "error: sentry-cli - $SENTRY_UPLOAD_COMMAND_OUTPUT"
77+
if [ "$SENTRY_ALLOW_FAILURE" == true ]; then
78+
echo "warning: sentry-cli - Debug files upload failed, but continuing build because SENTRY_ALLOW_FAILURE=true"
79+
echo "warning: sentry-cli - $SENTRY_UPLOAD_COMMAND_OUTPUT"
80+
else
81+
echo "error: sentry-cli - To disable native debug files auto upload, set SENTRY_DISABLE_AUTO_UPLOAD=true in your environment variables. Or to allow failing upload, set SENTRY_ALLOW_FAILURE=true"
82+
echo "error: sentry-cli - $SENTRY_UPLOAD_COMMAND_OUTPUT"
83+
fi
7884
fi
7985
set -x -e # re-enable
8086
fi

packages/core/scripts/sentry-xcode.sh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,14 @@ if [ "$SENTRY_DISABLE_AUTO_UPLOAD" != true ]; then
5656
if [ $? -eq 0 ]; then
5757
echo "$SENTRY_XCODE_COMMAND_OUTPUT" | awk '{print "output: sentry-cli - " $0}'
5858
else
59-
echo "error: sentry-cli - To disable source maps auto upload, set SENTRY_DISABLE_AUTO_UPLOAD=true in your environment variables. Or to allow failing upload, set SENTRY_ALLOW_FAILURE=true"
60-
echo "error: sentry-cli - $SENTRY_XCODE_COMMAND_OUTPUT"
61-
exitCode=1
59+
if [ "$SENTRY_ALLOW_FAILURE" == true ]; then
60+
echo "warning: sentry-cli - Source maps upload failed, but continuing build because SENTRY_ALLOW_FAILURE=true"
61+
echo "warning: sentry-cli - $SENTRY_XCODE_COMMAND_OUTPUT"
62+
else
63+
echo "error: sentry-cli - To disable source maps auto upload, set SENTRY_DISABLE_AUTO_UPLOAD=true in your environment variables. Or to allow failing upload, set SENTRY_ALLOW_FAILURE=true"
64+
echo "error: sentry-cli - $SENTRY_XCODE_COMMAND_OUTPUT"
65+
exitCode=1
66+
fi
6267
fi
6368
set -x -e # re-enable
6469
else
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
import { execSync } from 'child_process';
2+
import * as fs from 'fs';
3+
import * as os from 'os';
4+
import * as path from 'path';
5+
6+
const SCRIPTS_DIR = path.resolve(__dirname, '../../scripts');
7+
const DEBUG_FILES_SCRIPT = path.join(SCRIPTS_DIR, 'sentry-xcode-debug-files.sh');
8+
const XCODE_SCRIPT = path.join(SCRIPTS_DIR, 'sentry-xcode.sh');
9+
10+
describe('sentry-xcode-debug-files.sh', () => {
11+
let tempDir: string;
12+
let mockSentryCliScript: string;
13+
14+
beforeEach(() => {
15+
// Create a temporary directory for test artifacts
16+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sentry-xcode-test-'));
17+
18+
// Create a mock sentry-cli script that can simulate success or failure
19+
mockSentryCliScript = path.join(tempDir, 'mock-sentry-cli.js');
20+
fs.writeFileSync(
21+
mockSentryCliScript,
22+
`
23+
const exitCode = process.env.MOCK_CLI_EXIT_CODE || '0';
24+
const output = process.env.MOCK_CLI_OUTPUT || 'Mock upload output';
25+
console.log(output);
26+
process.exit(parseInt(exitCode));
27+
`,
28+
);
29+
});
30+
31+
afterEach(() => {
32+
// Clean up temp directory
33+
if (tempDir && fs.existsSync(tempDir)) {
34+
fs.rmSync(tempDir, { recursive: true, force: true });
35+
}
36+
});
37+
38+
const runScript = (env: Record<string, string> = {}): { stdout: string; stderr: string; exitCode: number } => {
39+
const defaultEnv = {
40+
NODE_BINARY: process.execPath,
41+
SENTRY_CLI_EXECUTABLE: mockSentryCliScript,
42+
DWARF_DSYM_FOLDER_PATH: tempDir,
43+
CONFIGURATION: 'Release',
44+
PROJECT_DIR: tempDir,
45+
DERIVED_FILE_DIR: tempDir,
46+
};
47+
48+
try {
49+
const stdout = execSync(`bash "${DEBUG_FILES_SCRIPT}"`, {
50+
env: { ...process.env, ...defaultEnv, ...env },
51+
encoding: 'utf8',
52+
stdio: 'pipe',
53+
});
54+
return { stdout, stderr: '', exitCode: 0 };
55+
} catch (error: any) {
56+
return {
57+
stdout: error.stdout?.toString() || '',
58+
stderr: error.stderr?.toString() || '',
59+
exitCode: error.status || 1,
60+
};
61+
}
62+
};
63+
64+
it('exits with 0 when upload succeeds', () => {
65+
const result = runScript({
66+
MOCK_CLI_EXIT_CODE: '0',
67+
MOCK_CLI_OUTPUT: 'Upload successful',
68+
});
69+
70+
expect(result.exitCode).toBe(0);
71+
expect(result.stdout).toContain('Upload successful');
72+
});
73+
74+
it('exits with 0 when SENTRY_ALLOW_FAILURE=true and upload fails', () => {
75+
const result = runScript({
76+
MOCK_CLI_EXIT_CODE: '1',
77+
MOCK_CLI_OUTPUT: 'Upload failed: API error',
78+
SENTRY_ALLOW_FAILURE: 'true',
79+
});
80+
81+
expect(result.exitCode).toBe(0);
82+
expect(result.stdout).toContain('warning: sentry-cli');
83+
expect(result.stdout).toContain('continuing build because SENTRY_ALLOW_FAILURE=true');
84+
expect(result.stdout).toContain('Upload failed: API error');
85+
});
86+
87+
it('exits with 0 but prints error when SENTRY_ALLOW_FAILURE not set and upload fails', () => {
88+
const result = runScript({
89+
MOCK_CLI_EXIT_CODE: '1',
90+
MOCK_CLI_OUTPUT: 'Upload failed: API error',
91+
});
92+
93+
// Original behavior: script exits 0, but Xcode fails build due to "error:" prefix
94+
expect(result.exitCode).toBe(0);
95+
expect(result.stdout).toContain('error: sentry-cli');
96+
expect(result.stdout).toContain('SENTRY_ALLOW_FAILURE=true');
97+
expect(result.stdout).toContain('Upload failed: API error');
98+
});
99+
100+
it('exits with 0 but prints error when SENTRY_ALLOW_FAILURE=false and upload fails', () => {
101+
const result = runScript({
102+
MOCK_CLI_EXIT_CODE: '1',
103+
MOCK_CLI_OUTPUT: 'Upload failed: Network error',
104+
SENTRY_ALLOW_FAILURE: 'false',
105+
});
106+
107+
// Original behavior: script exits 0, but Xcode fails build due to "error:" prefix
108+
expect(result.exitCode).toBe(0);
109+
expect(result.stdout).toContain('error: sentry-cli');
110+
});
111+
112+
it('skips upload when SENTRY_DISABLE_AUTO_UPLOAD=true', () => {
113+
const result = runScript({
114+
SENTRY_DISABLE_AUTO_UPLOAD: 'true',
115+
});
116+
117+
expect(result.exitCode).toBe(0);
118+
expect(result.stdout).toContain('SENTRY_DISABLE_AUTO_UPLOAD=true');
119+
expect(result.stdout).toContain('skipping debug files upload');
120+
});
121+
122+
it('skips upload for Debug configuration', () => {
123+
const result = runScript({
124+
CONFIGURATION: 'Debug',
125+
});
126+
127+
expect(result.exitCode).toBe(0);
128+
expect(result.stdout).toContain('Skipping debug files upload for *Debug* configuration');
129+
});
130+
131+
it('skips upload for debug configuration (case insensitive)', () => {
132+
const result = runScript({
133+
CONFIGURATION: 'debug',
134+
});
135+
136+
expect(result.exitCode).toBe(0);
137+
expect(result.stdout).toContain('Skipping debug files upload for *Debug* configuration');
138+
});
139+
});
140+
141+
describe('sentry-xcode.sh', () => {
142+
let tempDir: string;
143+
let mockSentryCliScript: string;
144+
let mockReactNativeScript: string;
145+
146+
beforeEach(() => {
147+
// Create a temporary directory for test artifacts
148+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sentry-xcode-test-'));
149+
150+
// Create a mock sentry-cli script
151+
mockSentryCliScript = path.join(tempDir, 'mock-sentry-cli.js');
152+
fs.writeFileSync(
153+
mockSentryCliScript,
154+
`
155+
const exitCode = process.env.MOCK_CLI_EXIT_CODE || '0';
156+
const output = process.env.MOCK_CLI_OUTPUT || 'Mock upload output';
157+
console.log(output);
158+
process.exit(parseInt(exitCode));
159+
`,
160+
);
161+
162+
// Create a mock react-native-xcode.sh script
163+
mockReactNativeScript = path.join(tempDir, 'react-native-xcode.sh');
164+
fs.writeFileSync(
165+
mockReactNativeScript,
166+
`#!/bin/bash
167+
echo "Mock React Native bundle"
168+
exit 0
169+
`,
170+
);
171+
fs.chmodSync(mockReactNativeScript, '755');
172+
});
173+
174+
afterEach(() => {
175+
// Clean up temp directory
176+
if (tempDir && fs.existsSync(tempDir)) {
177+
fs.rmSync(tempDir, { recursive: true, force: true });
178+
}
179+
});
180+
181+
const runScript = (env: Record<string, string> = {}): { stdout: string; stderr: string; exitCode: number } => {
182+
// Create a mock collect-modules.sh script to prevent script failure
183+
const mockCollectModulesScript = path.join(tempDir, 'collect-modules.sh');
184+
fs.writeFileSync(mockCollectModulesScript, '#!/bin/bash\nexit 0\n');
185+
fs.chmodSync(mockCollectModulesScript, '755');
186+
187+
const defaultEnv = {
188+
NODE_BINARY: process.execPath,
189+
SENTRY_CLI_EXECUTABLE: mockSentryCliScript,
190+
PROJECT_DIR: tempDir,
191+
DERIVED_FILE_DIR: tempDir,
192+
SENTRY_COLLECT_MODULES: mockCollectModulesScript, // Set this to avoid package resolution failure
193+
};
194+
195+
try {
196+
const stdout = execSync(`bash "${XCODE_SCRIPT}" "${mockReactNativeScript}"`, {
197+
env: { ...process.env, ...defaultEnv, ...env },
198+
encoding: 'utf8',
199+
stdio: 'pipe',
200+
});
201+
return { stdout, stderr: '', exitCode: 0 };
202+
} catch (error: any) {
203+
return {
204+
stdout: error.stdout?.toString() || '',
205+
stderr: error.stderr?.toString() || '',
206+
exitCode: error.status || 1,
207+
};
208+
}
209+
};
210+
211+
it('exits with 0 when upload succeeds', () => {
212+
const result = runScript({
213+
MOCK_CLI_EXIT_CODE: '0',
214+
MOCK_CLI_OUTPUT: 'Source maps uploaded successfully',
215+
});
216+
217+
expect(result.exitCode).toBe(0);
218+
expect(result.stdout).toContain('Source maps uploaded successfully');
219+
});
220+
221+
it('exits with 0 when SENTRY_ALLOW_FAILURE=true and upload fails', () => {
222+
const result = runScript({
223+
MOCK_CLI_EXIT_CODE: '1',
224+
MOCK_CLI_OUTPUT: 'Upload failed: Connection timeout',
225+
SENTRY_ALLOW_FAILURE: 'true',
226+
});
227+
228+
expect(result.exitCode).toBe(0);
229+
expect(result.stdout).toContain('warning: sentry-cli');
230+
expect(result.stdout).toContain('continuing build because SENTRY_ALLOW_FAILURE=true');
231+
expect(result.stdout).toContain('Upload failed: Connection timeout');
232+
});
233+
234+
it('exits with 1 when SENTRY_ALLOW_FAILURE not set and upload fails', () => {
235+
const result = runScript({
236+
MOCK_CLI_EXIT_CODE: '1',
237+
MOCK_CLI_OUTPUT: 'Upload failed: Invalid auth token',
238+
});
239+
240+
expect(result.exitCode).toBe(1);
241+
expect(result.stdout).toContain('error: sentry-cli');
242+
expect(result.stdout).toContain('SENTRY_ALLOW_FAILURE=true');
243+
expect(result.stdout).toContain('Upload failed: Invalid auth token');
244+
});
245+
246+
it('exits with 1 when SENTRY_ALLOW_FAILURE=false and upload fails', () => {
247+
const result = runScript({
248+
MOCK_CLI_EXIT_CODE: '1',
249+
MOCK_CLI_OUTPUT: 'Upload failed',
250+
SENTRY_ALLOW_FAILURE: 'false',
251+
});
252+
253+
expect(result.exitCode).toBe(1);
254+
expect(result.stdout).toContain('error: sentry-cli');
255+
});
256+
257+
it('skips upload when SENTRY_DISABLE_AUTO_UPLOAD=true', () => {
258+
const result = runScript({
259+
SENTRY_DISABLE_AUTO_UPLOAD: 'true',
260+
});
261+
262+
expect(result.exitCode).toBe(0);
263+
expect(result.stdout).toContain('SENTRY_DISABLE_AUTO_UPLOAD=true');
264+
expect(result.stdout).toContain('skipping sourcemaps upload');
265+
});
266+
});

0 commit comments

Comments
 (0)