From 8e0c7d09ad7d662d888039b8da56ebc2b3015e66 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 20 Oct 2026 19:47:14 +0500 Subject: [PATCH 1/5] Build tooling: read .xcode.env(.local) for the dSYM token, editGradleFiles option for Kotlin projects, BUGSEE_STRIP_SOURCES_CONTENT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dSYM Archive post-action (plugin script and bare example scheme) sources ios/.xcode.env and .xcode.env.local for the token when none is set. - Expo plugin: the Kotlin DSL refusal now names an option that makes the suggested re-prebuild succeed: editGradleFiles: false. - Source map upload drops sourcesContent when BUGSEE_STRIP_SOURCES_CONTENT is on. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../xcschemes/BareExample.xcscheme | 2 +- packages/react-native/README.md | 15 +++- .../plugin/build/dsym-script.d.ts | 10 +++ .../react-native/plugin/build/dsym-script.js | 33 ++++++++- .../plugin/build/dsym-script.js.map | 2 +- packages/react-native/plugin/build/index.d.ts | 9 +++ packages/react-native/plugin/build/index.js | 37 ++++++---- .../react-native/plugin/build/index.js.map | 2 +- .../fixtures/dsym-post-action-baked.sh | 15 ++++ .../__tests__/fixtures/dsym-post-action.sh | 15 ++++ .../plugin/src/__tests__/plugin.test.ts | 4 +- .../plugin/src/__tests__/with-bugsee.test.ts | 18 +++++ .../react-native/plugin/src/dsym-script.ts | 32 ++++++++- packages/react-native/plugin/src/index.ts | 46 +++++++++---- .../sourcemap-relative-sources.test.js | 51 ++++++++++++++ .../react-native/scripts/hermes-sourcemaps.js | 24 ++++++- .../__tests__/dsym-scheme-post-action.test.ts | 69 ++++++++++++++++++- 17 files changed, 342 insertions(+), 42 deletions(-) diff --git a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme index f73aa503..5fc68bee 100644 --- a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme +++ b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme @@ -89,7 +89,7 @@ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> + scriptText = "CREDS="${PROJECT_DIR}/../credentials.json" TOKEN="$BUGSEE_APP_TOKEN" if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -z "$TOKEN" ]; then SAVED_NODE_BINARY="$NODE_BINARY" for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do if [ -f "$ENV_FILE" ]; then . "$ENV_FILE" fi done TOKEN="$BUGSEE_APP_TOKEN" if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -x "$SAVED_NODE_BINARY" ]; then NODE_BINARY="$SAVED_NODE_BINARY" fi fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) fi TOKEN_CHECK=$(printf '%s' "$TOKEN" | /usr/bin/tr -d ' \t\r\n-') if [ -z "$TOKEN_CHECK" ]; then echo "warning: bugsee: dSYM upload skipped: no app token is configured" exit 0 fi case "$TOKEN_CHECK" in 000000000000[0123456789abcdefABCDEF]000[0123456789abcdefABCDEF]000000000000000) echo "warning: bugsee: dSYM upload skipped: the app token is the placeholder" exit 0 ;; esac # An "error:" line reads as a build error to Xcode; a failed upload is a warning. run_cli() { CLI_OUT=$("$@" 2>&1) CLI_RC=$? if [ -n "$CLI_OUT" ]; then printf '%s\n' "$CLI_OUT" | /usr/bin/sed -E 's/^(.*: )?([Ff]atal [Ee]rror|[Ee]rror): (.*)$/warning: bugsee: \3/' fi return $CLI_RC } if [ -n "$TOKEN" ]; then export BUGSEE_APP_TOKEN="$TOKEN" fi if [ -z "$BUGSEE_ENDPOINT" ] && [ -f "$CREDS" ]; then ENDPOINT=$(/usr/bin/plutil -extract endpoint raw -o - "$CREDS" 2>/dev/null || true) if [ -n "$ENDPOINT" ]; then export BUGSEE_ENDPOINT="$ENDPOINT" fi fi try_bin() { if [ -x "$1" ]; then run_cli "$1" xcode post-action exit $? fi } for dir in \ "${PROJECT_DIR}/../node_modules" \ "${PROJECT_DIR}/../node_modules/@bugsee/react-native/node_modules" \ "${PROJECT_DIR}/../../../packages/react-native/node_modules" do if [ "$(uname -m)" = "x86_64" ]; then try_bin "${dir}/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli" else try_bin "${dir}/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli" fi done # Archive's PATH has no node, and the npm shim's shebang searches PATH. if [ ! -x "$NODE_BINARY" ]; then if [ -f "${PROJECT_DIR}/.xcode.env" ]; then . "${PROJECT_DIR}/.xcode.env" fi if [ -f "${PROJECT_DIR}/.xcode.env.local" ]; then . "${PROJECT_DIR}/.xcode.env.local" fi fi if [ ! -x "$NODE_BINARY" ]; then RN_FIND="" if [ -n "$REACT_NATIVE_PATH" ] && [ -f "$REACT_NATIVE_PATH/scripts/find-node-for-xcode.sh" ]; then RN_FIND="$REACT_NATIVE_PATH/scripts/find-node-for-xcode.sh" elif [ -f "${PROJECT_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh" ]; then RN_FIND="${PROJECT_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh" fi if [ -n "$RN_FIND" ]; then NODE_BINARY=$(/bin/bash -c '. "$1" >/dev/null 2>&1; command -v node' _ "$RN_FIND" || true) fi fi if [ -n "$NODE_BINARY" ] && [ -x "$NODE_BINARY" ]; then PATH="$(dirname "$NODE_BINARY"):$PATH" export PATH NODE_BINARY for dir in \ "${PROJECT_DIR}/../node_modules" \ "${PROJECT_DIR}/../node_modules/@bugsee/react-native/node_modules" \ "${PROJECT_DIR}/../../../packages/react-native/node_modules" do if [ -f "${dir}/@bugsee/cli/bin/bugsee-cli.js" ]; then run_cli "$NODE_BINARY" "${dir}/@bugsee/cli/bin/bugsee-cli.js" xcode post-action exit $? fi done fi echo "bugsee-cli: not found under node_modules" >&2 exit 1 "> /dev/null || true)', 'fi', diff --git a/packages/react-native/plugin/build/dsym-script.js.map b/packages/react-native/plugin/build/dsym-script.js.map index c674e350..c51050a5 100644 --- a/packages/react-native/plugin/build/dsym-script.js.map +++ b/packages/react-native/plugin/build/dsym-script.js.map @@ -1 +1 @@ -{"version":3,"file":"dsym-script.js","sourceRoot":"","sources":["../src/dsym-script.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dsym-script.js","sourceRoot":"","sources":["../src/dsym-script.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/react-native/plugin/build/index.d.ts b/packages/react-native/plugin/build/index.d.ts index df2d85ae..16b4ccf8 100644 --- a/packages/react-native/plugin/build/index.d.ts +++ b/packages/react-native/plugin/build/index.d.ts @@ -51,6 +51,15 @@ export interface BugseePluginProps { * versions wrote. JS source-map hooks stay either way. */ nativeCrashReporting?: boolean; + /** + * Defaults on. `false` stops the plugin editing android/settings.gradle, + * android/build.gradle and android/app/build.gradle at all, so a project + * whose Gradle files are Kotlin DSL (`.gradle.kts`), which the plugin + * refuses, can still prebuild. You then make those edits by hand (package + * README, "Android source maps"); everything else the plugin does + * (bugsee.properties, gradle.properties, the manifest, iOS) is unchanged. + */ + editGradleFiles?: boolean; /** Defaults to native-versions.json `android.gradlePlugin`. */ gradlePluginVersion?: string; /** Defaults off. Writes `com.bugsee.app-token` manifest meta-data. */ diff --git a/packages/react-native/plugin/build/index.js b/packages/react-native/plugin/build/index.js index ab527902..34ad094a 100644 --- a/packages/react-native/plugin/build/index.js +++ b/packages/react-native/plugin/build/index.js @@ -21,11 +21,13 @@ const scheme_1 = require("./scheme"); exports.IOS_ENDPOINT_PLIST_KEY = 'BugseeReactNativeEndpoint'; exports.APP_GRADLE_NOT_GROOVY = `${gradle_1.CANNOT_EDIT} android/app/build.gradle: it is written in the Kotlin DSL (build.gradle.kts), which ` + '@bugsee/react-native does not edit. Apply scripts/bugsee-sourcemaps.gradle and set react.hermesCommand by hand ' + - '(see the package README, "Android source maps"), then run expo prebuild again'; + '(see the package README, "Android source maps"), then set "editGradleFiles": false in the @bugsee/react-native ' + + 'plugin options and run expo prebuild again'; /** The settings or root Gradle file is Kotlin; the plugin edits Groovy only. */ function gradleNotGroovy(file) { return (`${gradle_1.CANNOT_EDIT} ${file}: it is written in the Kotlin DSL, which @bugsee/react-native does not edit. ` + - 'Make the Bugsee edits by hand (package README, "Android source maps"), then run expo prebuild again'); + 'Make the Bugsee edits by hand (package README, "Android source maps"), then set "editGradleFiles": false in the ' + + '@bugsee/react-native plugin options and run expo prebuild again'); } // One function per file, shared by the mods and by the check that runs them // all first: a refusal in any file then leaves every file as it was. @@ -72,6 +74,7 @@ const withBugsee = (config, props) => { const iosToken = platformToken(options.appToken, 'ios'); const uploadSourcemaps = options.uploadSourcemaps !== false; const uploadSymbols = options.uploadSymbols !== false; + const editGradleFiles = options.editGradleFiles !== false; const symbolicateReactNative = options.symbolicateReactNativeFrameworks === true; const endpoint = (0, endpoint_1.pluginEndpoint)(options.endpoint); const versions = (0, native_versions_1.loadNativeVersions)(__dirname); @@ -80,18 +83,20 @@ const withBugsee = (config, props) => { ndkVersion: options.nativeCrashReporting === false ? null : versions.sdk, uploadSymbols, }; - config = (0, config_plugins_1.withSettingsGradle)(config, (cfg) => { - cfg.modResults.contents = editSettingsGradle(cfg.modResults); - return cfg; - }); - config = (0, config_plugins_1.withProjectBuildGradle)(config, (cfg) => { - cfg.modResults.contents = editProjectBuildGradle(cfg.modResults, edits); - return cfg; - }); - config = (0, config_plugins_1.withAppBuildGradle)(config, (cfg) => { - cfg.modResults.contents = editAppBuildGradle(cfg.modResults, edits); - return cfg; - }); + if (editGradleFiles) { + config = (0, config_plugins_1.withSettingsGradle)(config, (cfg) => { + cfg.modResults.contents = editSettingsGradle(cfg.modResults); + return cfg; + }); + config = (0, config_plugins_1.withProjectBuildGradle)(config, (cfg) => { + cfg.modResults.contents = editProjectBuildGradle(cfg.modResults, edits); + return cfg; + }); + config = (0, config_plugins_1.withAppBuildGradle)(config, (cfg) => { + cfg.modResults.contents = editAppBuildGradle(cfg.modResults, edits); + return cfg; + }); + } config = (0, config_plugins_1.withGradleProperties)(config, (cfg) => { cfg.modResults = (0, gradle_1.applyUploadSourcemapsProperty)(cfg.modResults, uploadSourcemaps); return cfg; @@ -101,7 +106,9 @@ const withBugsee = (config, props) => { async (cfg) => { // Dangerous mods run first: a Gradle file the plugin cannot edit is // refused here, before any file is written. - await refuseUnlessEditable(cfg.modRequest.projectRoot, edits); + if (editGradleFiles) { + await refuseUnlessEditable(cfg.modRequest.projectRoot, edits); + } await (0, promises_1.writeFile)((0, node_path_1.join)(cfg.modRequest.platformProjectRoot, 'bugsee.properties'), (0, properties_1.bugseePropertiesText)({ appToken: androidToken, nativeCrashReporting: options.nativeCrashReporting, diff --git a/packages/react-native/plugin/build/index.js.map b/packages/react-native/plugin/build/index.js.map index d671f771..8b9b9af5 100644 --- a/packages/react-native/plugin/build/index.js.map +++ b/packages/react-native/plugin/build/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh index 969bc531..bd3699c4 100644 --- a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh +++ b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh @@ -6,6 +6,21 @@ fi if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi +if [ -z "$TOKEN" ]; then + SAVED_NODE_BINARY="$NODE_BINARY" + for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do + if [ -f "$ENV_FILE" ]; then + . "$ENV_FILE" + fi + done + TOKEN="$BUGSEE_APP_TOKEN" + if [ -z "$TOKEN" ]; then + TOKEN="$BUGSEE_TOKEN_IOS" + fi + if [ -x "$SAVED_NODE_BINARY" ]; then + NODE_BINARY="$SAVED_NODE_BINARY" + fi +fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) fi diff --git a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh index 1e7c4d95..33df8cda 100644 --- a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh +++ b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh @@ -3,6 +3,21 @@ TOKEN="$BUGSEE_APP_TOKEN" if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi +if [ -z "$TOKEN" ]; then + SAVED_NODE_BINARY="$NODE_BINARY" + for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do + if [ -f "$ENV_FILE" ]; then + . "$ENV_FILE" + fi + done + TOKEN="$BUGSEE_APP_TOKEN" + if [ -z "$TOKEN" ]; then + TOKEN="$BUGSEE_TOKEN_IOS" + fi + if [ -x "$SAVED_NODE_BINARY" ]; then + NODE_BINARY="$SAVED_NODE_BINARY" + fi +fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) fi diff --git a/packages/react-native/plugin/src/__tests__/plugin.test.ts b/packages/react-native/plugin/src/__tests__/plugin.test.ts index 378a42c0..da3c1a3c 100644 --- a/packages/react-native/plugin/src/__tests__/plugin.test.ts +++ b/packages/react-native/plugin/src/__tests__/plugin.test.ts @@ -25,6 +25,7 @@ import type { GradleProperty } from '../gradle'; import { insertDsymPostAction, removeDsymPostAction } from '../scheme'; import { DSYM_POST_ACTION_SCRIPT, + ENV_FILE_TOKEN, RESOLVE_BUGSEE_CLI_PACKAGE, dsymPostActionScript, resolveNativeCliPackageSource, @@ -1279,7 +1280,7 @@ describe('baked iOS token in the Archive post-action', () => { it('puts the baked token first and keeps the env and credentials fallbacks after it', () => { const script = dsymPostActionScript('tok-ios'); - expect(script.split('\n').slice(0, 9)).toEqual([ + expect(script.split('\n').slice(0, 8 + ENV_FILE_TOKEN.length + 1)).toEqual([ 'CREDS="${PROJECT_DIR}/../credentials.json"', "TOKEN='tok-ios'", 'if [ -z "$TOKEN" ]; then', @@ -1288,6 +1289,7 @@ describe('baked iOS token in the Archive post-action', () => { 'if [ -z "$TOKEN" ]; then', ' TOKEN="$BUGSEE_TOKEN_IOS"', 'fi', + ...ENV_FILE_TOKEN, 'if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then', ]); expect(script.slice(script.indexOf('if [ -n "$TOKEN" ]; then'))).toBe( diff --git a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts index 4b5c4740..c477c050 100644 --- a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts +++ b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts @@ -295,6 +295,24 @@ describe('withBugsee through the Expo mod compiler', () => { expect(APP_GRADLE_NOT_GROOVY.startsWith(`${CANNOT_EDIT} android/app/build.gradle:`)).toBe(true); }); + it('names editGradleFiles in the Kotlin refusals, and that option lets a Kotlin project prebuild', async () => { + expect(APP_GRADLE_NOT_GROOVY).toContain('"editGradleFiles": false'); + expect(gradleNotGroovy('android/build.gradle')).toContain('"editGradleFiles": false'); + const android = join(projectRoot, 'android'); + for (const [dir, name] of [ + [android, 'settings.gradle'], + [android, 'build.gradle'], + [join(android, 'app'), 'build.gradle'], + ] as const) { + renameSync(join(dir, name), join(dir, `${name}.kts`)); + } + await prebuild({ appToken: ANDROID_TOKEN, editGradleFiles: false }, ['android']); + expect(readFileSync(join(android, 'bugsee.properties'), 'utf8')).toContain(`app_token=${ANDROID_TOKEN}`); + for (const file of ['settings.gradle.kts', 'build.gradle.kts', 'app/build.gradle.kts']) { + expect(readFileSync(join(android, file), 'utf8')).not.toContain('bugsee'); + } + }); + it('refuses a Kotlin settings or root file at prebuild', async () => { const android = join(projectRoot, 'android'); renameSync(join(android, 'settings.gradle'), join(android, 'settings.gradle.kts')); diff --git a/packages/react-native/plugin/src/dsym-script.ts b/packages/react-native/plugin/src/dsym-script.ts index 25dd804d..630d39ec 100644 --- a/packages/react-native/plugin/src/dsym-script.ts +++ b/packages/react-native/plugin/src/dsym-script.ts @@ -5,8 +5,8 @@ // dependency from the CLI directory. Otherwise the JS entry comes from the // CLI package bin field. Without a real token (none, or the placeholder) the // script stops, exit 0, before any of that (TOKEN_GUARD). A token or endpoint -// the Expo plugin baked in comes first; the environment and credentials.json -// follow it. +// the Expo plugin baked in comes first; the environment, .xcode.env(.local) and +// credentials.json follow it. const locateBugseeCliPackage = [ "const path = require('path');", @@ -61,6 +61,33 @@ export const TOKEN_GUARD: readonly string[] = [ 'esac', ]; +/** + * Looks for the token in ios/.xcode.env and ios/.xcode.env.local, the files + * the README sends a bare app's `export BUGSEE_APP_TOKEN=...` to for the + * bundle phase. A GUI Archive does not inherit a shell, so the post-action + * reads them itself, and only when no earlier source gave a token. The files + * may also set NODE_BINARY; an executable one from Xcode's own environment + * wins, as it did before the files were read here. Shared with the bare + * example's scheme (scripts/__tests__/dsym-scheme-post-action.test.ts). + */ +export const ENV_FILE_TOKEN: readonly string[] = [ + 'if [ -z "$TOKEN" ]; then', + ' SAVED_NODE_BINARY="$NODE_BINARY"', + ' for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do', + ' if [ -f "$ENV_FILE" ]; then', + ' . "$ENV_FILE"', + ' fi', + ' done', + ' TOKEN="$BUGSEE_APP_TOKEN"', + ' if [ -z "$TOKEN" ]; then', + ' TOKEN="$BUGSEE_TOKEN_IOS"', + ' fi', + ' if [ -x "$SAVED_NODE_BINARY" ]; then', + ' NODE_BINARY="$SAVED_NODE_BINARY"', + ' fi', + 'fi', +]; + /** * Relays bugsee-cli output with Xcode's `error:` diagnostics turned into * `warning: bugsee: ...`, and returns the CLI's exit status. A failed upload @@ -96,6 +123,7 @@ export function dsymPostActionScript(bakedToken?: string, bakedEndpoint?: string 'if [ -z "$TOKEN" ]; then', ' TOKEN="$BUGSEE_TOKEN_IOS"', 'fi', + ...ENV_FILE_TOKEN, 'if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then', ' TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true)', 'fi', diff --git a/packages/react-native/plugin/src/index.ts b/packages/react-native/plugin/src/index.ts index 33a981b5..14e21191 100644 --- a/packages/react-native/plugin/src/index.ts +++ b/packages/react-native/plugin/src/index.ts @@ -84,6 +84,15 @@ export interface BugseePluginProps { * versions wrote. JS source-map hooks stay either way. */ nativeCrashReporting?: boolean; + /** + * Defaults on. `false` stops the plugin editing android/settings.gradle, + * android/build.gradle and android/app/build.gradle at all, so a project + * whose Gradle files are Kotlin DSL (`.gradle.kts`), which the plugin + * refuses, can still prebuild. You then make those edits by hand (package + * README, "Android source maps"); everything else the plugin does + * (bugsee.properties, gradle.properties, the manifest, iOS) is unchanged. + */ + editGradleFiles?: boolean; /** Defaults to native-versions.json `android.gradlePlugin`. */ gradlePluginVersion?: string; /** Defaults off. Writes `com.bugsee.app-token` manifest meta-data. */ @@ -112,13 +121,15 @@ export const IOS_ENDPOINT_PLIST_KEY = 'BugseeReactNativeEndpoint'; export const APP_GRADLE_NOT_GROOVY = `${CANNOT_EDIT} android/app/build.gradle: it is written in the Kotlin DSL (build.gradle.kts), which ` + '@bugsee/react-native does not edit. Apply scripts/bugsee-sourcemaps.gradle and set react.hermesCommand by hand ' + - '(see the package README, "Android source maps"), then run expo prebuild again'; + '(see the package README, "Android source maps"), then set "editGradleFiles": false in the @bugsee/react-native ' + + 'plugin options and run expo prebuild again'; /** The settings or root Gradle file is Kotlin; the plugin edits Groovy only. */ export function gradleNotGroovy(file: string): string { return ( `${CANNOT_EDIT} ${file}: it is written in the Kotlin DSL, which @bugsee/react-native does not edit. ` + - 'Make the Bugsee edits by hand (package README, "Android source maps"), then run expo prebuild again' + 'Make the Bugsee edits by hand (package README, "Android source maps"), then set "editGradleFiles": false in the ' + + '@bugsee/react-native plugin options and run expo prebuild again' ); } @@ -187,6 +198,7 @@ const withBugsee: ConfigPlugin = (config, props) => { const iosToken = platformToken(options.appToken, 'ios'); const uploadSourcemaps = options.uploadSourcemaps !== false; const uploadSymbols = options.uploadSymbols !== false; + const editGradleFiles = options.editGradleFiles !== false; const symbolicateReactNative = options.symbolicateReactNativeFrameworks === true; const endpoint = pluginEndpoint(options.endpoint); const versions = loadNativeVersions(__dirname); @@ -196,20 +208,22 @@ const withBugsee: ConfigPlugin = (config, props) => { uploadSymbols, }; - config = withSettingsGradle(config, (cfg) => { - cfg.modResults.contents = editSettingsGradle(cfg.modResults); - return cfg; - }); + if (editGradleFiles) { + config = withSettingsGradle(config, (cfg) => { + cfg.modResults.contents = editSettingsGradle(cfg.modResults); + return cfg; + }); - config = withProjectBuildGradle(config, (cfg) => { - cfg.modResults.contents = editProjectBuildGradle(cfg.modResults, edits); - return cfg; - }); + config = withProjectBuildGradle(config, (cfg) => { + cfg.modResults.contents = editProjectBuildGradle(cfg.modResults, edits); + return cfg; + }); - config = withAppBuildGradle(config, (cfg) => { - cfg.modResults.contents = editAppBuildGradle(cfg.modResults, edits); - return cfg; - }); + config = withAppBuildGradle(config, (cfg) => { + cfg.modResults.contents = editAppBuildGradle(cfg.modResults, edits); + return cfg; + }); + } config = withGradleProperties(config, (cfg) => { cfg.modResults = applyUploadSourcemapsProperty(cfg.modResults, uploadSourcemaps); @@ -221,7 +235,9 @@ const withBugsee: ConfigPlugin = (config, props) => { async (cfg) => { // Dangerous mods run first: a Gradle file the plugin cannot edit is // refused here, before any file is written. - await refuseUnlessEditable(cfg.modRequest.projectRoot, edits); + if (editGradleFiles) { + await refuseUnlessEditable(cfg.modRequest.projectRoot, edits); + } await writeFile( join(cfg.modRequest.platformProjectRoot, 'bugsee.properties'), bugseePropertiesText({ diff --git a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js index 9da6fd39..ff257254 100644 --- a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js +++ b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js @@ -239,4 +239,55 @@ fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({ p, text: fs.readF const { seen, mapPath } = upload([`${ROOT}/a.ts`], undefined); expect(seen.p).toBe(mapPath); }); + + describe('stripping sourcesContent', () => { + function uploadWithContent(stripSources) { + const mapPath = path.join(dir, 'main.jsbundle.composed.map'); + const original = JSON.stringify({ + version: 3, + sources: ['rel.ts'], + sourcesContent: ['const secret = 1;'], + names: [], + mappings: 'AAAA', + debug_id: 'd', + debugId: 'd', + }); + fs.writeFileSync(mapPath, original); + const result = uploadComposedSourceMap({ + composedMapPath: mapPath, + token: TOKEN, + appVersion: '1', + appBuild: '1', + cliPath: cli, + projectRoot: ROOT, + stripSources, + }); + return { result, original, mapPath, seen: JSON.parse(fs.readFileSync(captured, 'utf8')) }; + } + + it('uploads a copy without sourcesContent and leaves the map on disk alone', () => { + const { result, original, mapPath, seen } = uploadWithContent(true); + const uploaded = JSON.parse(seen.text); + expect(result.status).toBe('uploaded'); + expect(uploaded).not.toHaveProperty('sourcesContent'); + expect(uploaded.mappings).toBe('AAAA'); + expect(uploaded.debug_id).toBe('d'); + expect(seen.p).not.toBe(mapPath); + expect(fs.readFileSync(mapPath, 'utf8')).toBe(original); + }); + + it('keeps sourcesContent by default', () => { + const { seen, mapPath } = uploadWithContent(false); + expect(seen.p).toBe(mapPath); + expect(JSON.parse(seen.text).sourcesContent).toEqual(['const secret = 1;']); + }); + + it('is requested through BUGSEE_STRIP_SOURCES_CONTENT', () => { + const { stripSourcesContentRequested } = require('../hermes-sourcemaps'); + expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: 'true' })).toBe(true); + expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: ' 1 ' })).toBe(true); + expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: 'false' })).toBe(false); + expect(stripSourcesContentRequested({})).toBe(false); + }); + }); }); diff --git a/packages/react-native/scripts/hermes-sourcemaps.js b/packages/react-native/scripts/hermes-sourcemaps.js index cce35a28..5e37a892 100644 --- a/packages/react-native/scripts/hermes-sourcemaps.js +++ b/packages/react-native/scripts/hermes-sourcemaps.js @@ -425,6 +425,23 @@ function relativizeSourceMap(mapText, projectRoot) { return changed ? JSON.stringify(map) : mapText; } +/** + * Drops `sourcesContent` (the app's source code) from a map's JSON text. + * Returns the text unchanged when the map has none. The debug id and the + * mappings stay, so symbolication still works. + */ +function stripSourcesContent(mapText) { + const map = JSON.parse(mapText); + if (!('sourcesContent' in map)) return mapText; + delete map.sourcesContent; + return JSON.stringify(map); +} + +/** On when BUGSEE_STRIP_SOURCES_CONTENT says so. */ +function stripSourcesContentRequested(env) { + return ON_VALUES.has(String(env.BUGSEE_STRIP_SOURCES_CONTENT).trim().toLowerCase()); +} + const PLACEHOLDER_TOKEN = /^0{12}[0-9a-f]0{3}[0-9a-f]0{15}$/i; function readMapDebugId(mapPath) { @@ -451,6 +468,7 @@ function uploadComposedSourceMap({ cliPath, nodePath, projectRoot, + stripSources = false, }) { if (!enabled) { say('source map upload skipped: uploadSourcemaps is off'); @@ -492,7 +510,8 @@ function uploadComposedSourceMap({ let uploadPath = composedMapPath; try { const original = fs.readFileSync(composedMapPath, 'utf8'); - const rewritten = relativizeSourceMap(original, projectRoot); + let rewritten = relativizeSourceMap(original, projectRoot); + if (stripSources) rewritten = stripSourcesContent(rewritten); if (rewritten !== original) { stage = fs.mkdtempSync(path.join(os.tmpdir(), 'bugsee-upload-')); uploadPath = path.join(stage, path.basename(composedMapPath)); @@ -545,6 +564,7 @@ function uploadFromArgs(args) { appVersion: args['app-version'], appBuild: args['app-build'], projectRoot: args['project-root'], + stripSources: stripSourcesContentRequested(process.env), }); } @@ -617,6 +637,8 @@ module.exports = { uploadArgv, uploadComposedSourceMap, uploadDisabled, + stripSourcesContent, + stripSourcesContentRequested, }; /** Command-line entry: a failure prints its reason and sets exit code 1. */ diff --git a/scripts/__tests__/dsym-scheme-post-action.test.ts b/scripts/__tests__/dsym-scheme-post-action.test.ts index 4b997a43..8462d275 100644 --- a/scripts/__tests__/dsym-scheme-post-action.test.ts +++ b/scripts/__tests__/dsym-scheme-post-action.test.ts @@ -3,7 +3,7 @@ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, wr import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; -import { CLI_OUTPUT_FILTER, TOKEN_GUARD } from '../../packages/react-native/plugin/src/dsym-script'; +import { CLI_OUTPUT_FILTER, ENV_FILE_TOKEN, TOKEN_GUARD } from '../../packages/react-native/plugin/src/dsym-script'; // The shared scheme is what `xcodebuild -scheme BareExample` archives. // A user-specific scheme under xcuserdata would not. @@ -224,6 +224,73 @@ describe('BareExample archive post-action refuses a missing or placeholder token expect(result.invoked).toBe(`argv=xcode post-action\ntoken=${token}\nendpoint=https://127.0.0.1:9\n`); }); + // The README sends a bare app's token to ios/.xcode.env.local; a GUI Archive + // has no shell environment, so the post-action reads the files itself. + it("carries the plugin's .xcode.env token lookup, once, ahead of the guard", () => { + const lookup = ENV_FILE_TOKEN.join('\n'); + const at = script.indexOf(lookup); + expect(at).toBeGreaterThan(-1); + expect(script.indexOf(lookup, at + 1)).toBe(-1); + expect(at).toBeLessThan(script.indexOf(TOKEN_GUARD.join('\n'))); + }); + + describe('a token in .xcode.env.local', () => { + const token = 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee'; + + function withEnvFile(text: string, env: Record = {}) { + // PROJECT_DIR is /ios; the files sit in it. runScheme makes the dir + // itself, so the file is written through a shell prefix instead. + const dir = mkdtempSync(join(tmpdir(), 'bugsee-envfile-')); + try { + const ios = join(dir, 'ios'); + mkdirSync(ios); + writeFileSync(join(ios, '.xcode.env.local'), text); + const seen = join(dir, 'seen'); + const cli = join(dir, 'node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli'); + mkdirSync(dirname(cli), { recursive: true }); + writeFileSync(cli, `#!/bin/sh\nprintf 'token=%s\\n' "$BUGSEE_APP_TOKEN" > '${seen}'\n`); + chmodSync(cli, 0o755); + const x64 = join(dir, 'node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli'); + mkdirSync(dirname(x64), { recursive: true }); + writeFileSync(x64, readFileSync(cli)); + chmodSync(x64, 0o755); + const result = spawnSync('/bin/sh', ['-c', script], { + encoding: 'utf8', + env: { PATH: '/usr/bin:/bin', PROJECT_DIR: ios, NODE_BINARY: process.execPath, BUGSEE_ENDPOINT: 'https://127.0.0.1:9', ...env }, + }); + return { ...result, invoked: existsSync(seen) ? readFileSync(seen, 'utf8') : null }; + } finally { + rmSync(dir, { recursive: true, force: true }); + } + } + + it('is used when the environment has none', () => { + const result = withEnvFile(`export BUGSEE_APP_TOKEN=${token}\n`); + expect(result.stdout).toBe(''); + expect(result.invoked).toBe(`token=${token}\n`); + }); + + it('accepts BUGSEE_TOKEN_IOS too', () => { + expect(withEnvFile(`export BUGSEE_TOKEN_IOS=${token}\n`).invoked).toBe(`token=${token}\n`); + }); + + it('does not override a token the environment already has', () => { + const real = '11111111-2222-4333-8444-555555555555'; + expect(withEnvFile(`export BUGSEE_APP_TOKEN=${token}\n`, { BUGSEE_APP_TOKEN: real }).invoked).toBe(`token=${real}\n`); + }); + + it('still skips for the placeholder', () => { + const result = withEnvFile(`export BUGSEE_APP_TOKEN=${PLACEHOLDER}\n`); + expect(result.invoked).toBeNull(); + expect(result.stdout).toBe(SKIP_PLACEHOLDER); + }); + + it("keeps Xcode's executable NODE_BINARY over the file's", () => { + const result = withEnvFile(`export BUGSEE_APP_TOKEN=${token}\nexport NODE_BINARY=/nonexistent/node\n`); + expect(result.invoked).toBe(`token=${token}\n`); + }); + }); + it('a token one digit off the placeholder is not taken for it', () => { const result = runScheme({ BUGSEE_APP_TOKEN: '00000000-0000-4000-8000-000000000001', BUGSEE_ENDPOINT: 'https://127.0.0.1:9' }); expect(result.invoked).toMatch(/^argv=xcode post-action\n/); From 1f4c39856fba95abf77b295d683e7f55d48a5339 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 20 Oct 2026 19:52:09 +0500 Subject: [PATCH 2/5] Review fixes: strip sourcesContent on iOS+Hermes, skip upload when strip fails, keep endpoint, docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../xcschemes/BareExample.xcscheme | 2 +- packages/react-native/README.md | 23 ++++++++++++++++++- .../plugin/build/dsym-script.d.ts | 3 ++- .../react-native/plugin/build/dsym-script.js | 7 +++++- .../plugin/build/dsym-script.js.map | 2 +- packages/react-native/plugin/build/index.d.ts | 5 +++- .../react-native/plugin/build/index.js.map | 2 +- .../fixtures/dsym-post-action-baked.sh | 4 ++++ .../__tests__/fixtures/dsym-post-action.sh | 4 ++++ .../react-native/plugin/src/dsym-script.ts | 7 +++++- packages/react-native/plugin/src/index.ts | 5 +++- .../__tests__/compose-then-inject.test.js | 16 +++++++++++++ .../sourcemap-relative-sources.test.js | 15 ++++++++++++ .../scripts/compose-then-inject.js | 2 ++ .../react-native/scripts/hermes-sourcemaps.js | 10 ++++++-- 15 files changed, 96 insertions(+), 11 deletions(-) diff --git a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme index 5fc68bee..9064125b 100644 --- a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme +++ b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme @@ -89,7 +89,7 @@ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> + scriptText = "CREDS="${PROJECT_DIR}/../credentials.json" TOKEN="$BUGSEE_APP_TOKEN" if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -z "$TOKEN" ]; then SAVED_NODE_BINARY="$NODE_BINARY" SAVED_ENDPOINT="$BUGSEE_ENDPOINT" for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do if [ -f "$ENV_FILE" ]; then . "$ENV_FILE" fi done TOKEN="$BUGSEE_APP_TOKEN" if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -x "$SAVED_NODE_BINARY" ]; then NODE_BINARY="$SAVED_NODE_BINARY" fi if [ -n "$SAVED_ENDPOINT" ]; then BUGSEE_ENDPOINT="$SAVED_ENDPOINT" fi fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) fi TOKEN_CHECK=$(printf '%s' "$TOKEN" | /usr/bin/tr -d ' \t\r\n-') if [ -z "$TOKEN_CHECK" ]; then echo "warning: bugsee: dSYM upload skipped: no app token is configured" exit 0 fi case "$TOKEN_CHECK" in 000000000000[0123456789abcdefABCDEF]000[0123456789abcdefABCDEF]000000000000000) echo "warning: bugsee: dSYM upload skipped: the app token is the placeholder" exit 0 ;; esac # An "error:" line reads as a build error to Xcode; a failed upload is a warning. run_cli() { CLI_OUT=$("$@" 2>&1) CLI_RC=$? if [ -n "$CLI_OUT" ]; then printf '%s\n' "$CLI_OUT" | /usr/bin/sed -E 's/^(.*: )?([Ff]atal [Ee]rror|[Ee]rror): (.*)$/warning: bugsee: \3/' fi return $CLI_RC } if [ -n "$TOKEN" ]; then export BUGSEE_APP_TOKEN="$TOKEN" fi if [ -z "$BUGSEE_ENDPOINT" ] && [ -f "$CREDS" ]; then ENDPOINT=$(/usr/bin/plutil -extract endpoint raw -o - "$CREDS" 2>/dev/null || true) if [ -n "$ENDPOINT" ]; then export BUGSEE_ENDPOINT="$ENDPOINT" fi fi try_bin() { if [ -x "$1" ]; then run_cli "$1" xcode post-action exit $? fi } for dir in \ "${PROJECT_DIR}/../node_modules" \ "${PROJECT_DIR}/../node_modules/@bugsee/react-native/node_modules" \ "${PROJECT_DIR}/../../../packages/react-native/node_modules" do if [ "$(uname -m)" = "x86_64" ]; then try_bin "${dir}/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli" else try_bin "${dir}/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli" fi done # Archive's PATH has no node, and the npm shim's shebang searches PATH. if [ ! -x "$NODE_BINARY" ]; then if [ -f "${PROJECT_DIR}/.xcode.env" ]; then . "${PROJECT_DIR}/.xcode.env" fi if [ -f "${PROJECT_DIR}/.xcode.env.local" ]; then . "${PROJECT_DIR}/.xcode.env.local" fi fi if [ ! -x "$NODE_BINARY" ]; then RN_FIND="" if [ -n "$REACT_NATIVE_PATH" ] && [ -f "$REACT_NATIVE_PATH/scripts/find-node-for-xcode.sh" ]; then RN_FIND="$REACT_NATIVE_PATH/scripts/find-node-for-xcode.sh" elif [ -f "${PROJECT_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh" ]; then RN_FIND="${PROJECT_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh" fi if [ -n "$RN_FIND" ]; then NODE_BINARY=$(/bin/bash -c '. "$1" >/dev/null 2>&1; command -v node' _ "$RN_FIND" || true) fi fi if [ -n "$NODE_BINARY" ] && [ -x "$NODE_BINARY" ]; then PATH="$(dirname "$NODE_BINARY"):$PATH" export PATH NODE_BINARY for dir in \ "${PROJECT_DIR}/../node_modules" \ "${PROJECT_DIR}/../node_modules/@bugsee/react-native/node_modules" \ "${PROJECT_DIR}/../../../packages/react-native/node_modules" do if [ -f "${dir}/@bugsee/cli/bin/bugsee-cli.js" ]; then run_cli "$NODE_BINARY" "${dir}/@bugsee/cli/bin/bugsee-cli.js" xcode post-action exit $? fi done fi echo "bugsee-cli: not found under node_modules" >&2 exit 1 "> /dev/null || true) diff --git a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh index 33df8cda..c60e3539 100644 --- a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh +++ b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh @@ -5,6 +5,7 @@ if [ -z "$TOKEN" ]; then fi if [ -z "$TOKEN" ]; then SAVED_NODE_BINARY="$NODE_BINARY" + SAVED_ENDPOINT="$BUGSEE_ENDPOINT" for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do if [ -f "$ENV_FILE" ]; then . "$ENV_FILE" @@ -17,6 +18,9 @@ if [ -z "$TOKEN" ]; then if [ -x "$SAVED_NODE_BINARY" ]; then NODE_BINARY="$SAVED_NODE_BINARY" fi + if [ -n "$SAVED_ENDPOINT" ]; then + BUGSEE_ENDPOINT="$SAVED_ENDPOINT" + fi fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) diff --git a/packages/react-native/plugin/src/dsym-script.ts b/packages/react-native/plugin/src/dsym-script.ts index 630d39ec..3b75aa69 100644 --- a/packages/react-native/plugin/src/dsym-script.ts +++ b/packages/react-native/plugin/src/dsym-script.ts @@ -67,12 +67,14 @@ export const TOKEN_GUARD: readonly string[] = [ * bundle phase. A GUI Archive does not inherit a shell, so the post-action * reads them itself, and only when no earlier source gave a token. The files * may also set NODE_BINARY; an executable one from Xcode's own environment - * wins, as it did before the files were read here. Shared with the bare + * wins, as it did before the files were read here; so does a BUGSEE_ENDPOINT + * the plugin baked in or Xcode's environment already has. Shared with the bare * example's scheme (scripts/__tests__/dsym-scheme-post-action.test.ts). */ export const ENV_FILE_TOKEN: readonly string[] = [ 'if [ -z "$TOKEN" ]; then', ' SAVED_NODE_BINARY="$NODE_BINARY"', + ' SAVED_ENDPOINT="$BUGSEE_ENDPOINT"', ' for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do', ' if [ -f "$ENV_FILE" ]; then', ' . "$ENV_FILE"', @@ -85,6 +87,9 @@ export const ENV_FILE_TOKEN: readonly string[] = [ ' if [ -x "$SAVED_NODE_BINARY" ]; then', ' NODE_BINARY="$SAVED_NODE_BINARY"', ' fi', + ' if [ -n "$SAVED_ENDPOINT" ]; then', + ' BUGSEE_ENDPOINT="$SAVED_ENDPOINT"', + ' fi', 'fi', ]; diff --git a/packages/react-native/plugin/src/index.ts b/packages/react-native/plugin/src/index.ts index 14e21191..69d633c6 100644 --- a/packages/react-native/plugin/src/index.ts +++ b/packages/react-native/plugin/src/index.ts @@ -89,7 +89,10 @@ export interface BugseePluginProps { * android/build.gradle and android/app/build.gradle at all, so a project * whose Gradle files are Kotlin DSL (`.gradle.kts`), which the plugin * refuses, can still prebuild. You then make those edits by hand (package - * README, "Android source maps"); everything else the plugin does + * README, "Android source maps"). Edits an earlier prebuild already made + * stay as they are, and later option changes (`uploadSymbols`, + * `nativeCrashReporting`, `gradlePluginVersion`) no longer reach these + * files; change them by hand too. Everything else the plugin does * (bugsee.properties, gradle.properties, the manifest, iOS) is unchanged. */ editGradleFiles?: boolean; diff --git a/packages/react-native/scripts/__tests__/compose-then-inject.test.js b/packages/react-native/scripts/__tests__/compose-then-inject.test.js index 5bde7e27..a1dc55b5 100644 --- a/packages/react-native/scripts/__tests__/compose-then-inject.test.js +++ b/packages/react-native/scripts/__tests__/compose-then-inject.test.js @@ -187,6 +187,22 @@ fs.writeFileSync(out, JSON.stringify({ version: 3, mappings: 'CCCC' })); expect(written.join('')).not.toContain(TOKEN); }); + it('asks the upload to strip sourcesContent only when BUGSEE_STRIP_SOURCES_CONTENT is on', () => { + const calls = []; + jest.isolateModules(() => { + jest.doMock('../hermes-sourcemaps', () => { + const real = jest.requireActual('../hermes-sourcemaps'); + return { ...real, uploadComposedSourceMap: (options) => calls.push(options) }; + }); + const isolated = require('../compose-then-inject'); + const versions = { MARKETING_VERSION: '1', CURRENT_PROJECT_VERSION: '1', BUGSEE_PLUGIN_APP_TOKEN: TOKEN }; + expect(isolated.run(argv(), { ...env, ...versions })).toBe(0); + expect(isolated.run(argv(), { ...env, ...versions, BUGSEE_STRIP_SOURCES_CONTENT: 'true' })).toBe(0); + }); + jest.dontMock('../hermes-sourcemaps'); + expect(calls.map((call) => call.stripSources)).toEqual([false, true]); + }); + it('reads the iOS token from credentials.json and honours the off switch', () => { const credentials = path.join(dir, 'credentials.json'); fs.writeFileSync(credentials, JSON.stringify({ ios: '00000000-0000-4000-8000-000000000000' })); diff --git a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js index ff257254..fd6e3cfa 100644 --- a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js +++ b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js @@ -282,6 +282,21 @@ fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({ p, text: fs.readF expect(JSON.parse(seen.text).sourcesContent).toEqual(['const secret = 1;']); }); + it('skips the upload, saying why, when the stripped copy cannot be made', () => { + const mapPath = path.join(dir, 'main.jsbundle.composed.map'); + fs.writeFileSync(mapPath, 'not json'); + const result = uploadComposedSourceMap({ + composedMapPath: mapPath, + token: TOKEN, + appVersion: '1', + appBuild: '1', + cliPath: cli, + stripSources: true, + }); + expect(result).toEqual({ status: 'skipped', reason: 'strip-failed' }); + expect(fs.existsSync(captured)).toBe(false); + }); + it('is requested through BUGSEE_STRIP_SOURCES_CONTENT', () => { const { stripSourcesContentRequested } = require('../hermes-sourcemaps'); expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: 'true' })).toBe(true); diff --git a/packages/react-native/scripts/compose-then-inject.js b/packages/react-native/scripts/compose-then-inject.js index 868d6480..d5821b1d 100755 --- a/packages/react-native/scripts/compose-then-inject.js +++ b/packages/react-native/scripts/compose-then-inject.js @@ -17,6 +17,7 @@ const { debugUploadAllowed, finishAfterCompose, resolveUploadSettings, + stripSourcesContentRequested, uploadComposedSourceMap, uploadDisabled, } = require('./hermes-sourcemaps'); @@ -101,6 +102,7 @@ function run(argv, env) { appVersion: env.MARKETING_VERSION, appBuild: env.CURRENT_PROJECT_VERSION, projectRoot: projectRootFrom(env), + stripSources: stripSourcesContentRequested(env), }); } return 0; diff --git a/packages/react-native/scripts/hermes-sourcemaps.js b/packages/react-native/scripts/hermes-sourcemaps.js index 5e37a892..8892fb8d 100644 --- a/packages/react-native/scripts/hermes-sourcemaps.js +++ b/packages/react-native/scripts/hermes-sourcemaps.js @@ -508,8 +508,9 @@ function uploadComposedSourceMap({ if (endpoint) env.BUGSEE_ENDPOINT = endpoint; let stage = null; let uploadPath = composedMapPath; + let original; try { - const original = fs.readFileSync(composedMapPath, 'utf8'); + original = fs.readFileSync(composedMapPath, 'utf8'); let rewritten = relativizeSourceMap(original, projectRoot); if (stripSources) rewritten = stripSourcesContent(rewritten); if (rewritten !== original) { @@ -518,10 +519,15 @@ function uploadComposedSourceMap({ fs.writeFileSync(uploadPath, rewritten); } } catch (err) { - say(`could not make source map paths project-relative (${err.message}); uploading the map as it is`); if (stage) remove(stage); stage = null; uploadPath = composedMapPath; + if (stripSources) { + // The map as it is would carry the app's source code the build asked to keep out. + say(`source map upload skipped: could not remove sourcesContent from the map (${err.message})`); + return { status: 'skipped', reason: 'strip-failed' }; + } + say(`could not make source map paths project-relative (${err.message}); uploading the map as it is`); } let result; try { From 3b72aba01c665b15f703ff9fecc81bc3e915cae8 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 20 Oct 2026 20:14:25 +0500 Subject: [PATCH 3/5] Address review: read .xcode.env token in a subshell, strip sourcesContent from indexed map sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../xcschemes/BareExample.xcscheme | 2 +- .../plugin/build/dsym-script.d.ts | 10 +++---- .../react-native/plugin/build/dsym-script.js | 27 +++++++------------ .../plugin/build/dsym-script.js.map | 2 +- .../fixtures/dsym-post-action-baked.sh | 17 +++--------- .../__tests__/fixtures/dsym-post-action.sh | 17 +++--------- .../react-native/plugin/src/dsym-script.ts | 27 +++++++------------ .../sourcemap-relative-sources.test.js | 12 +++++++++ .../react-native/scripts/hermes-sourcemaps.js | 15 ++++++++--- .../__tests__/dsym-scheme-post-action.test.ts | 10 +++++++ 10 files changed, 67 insertions(+), 72 deletions(-) diff --git a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme index 9064125b..ef5d7ced 100644 --- a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme +++ b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme @@ -89,7 +89,7 @@ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction"> + scriptText = "CREDS="${PROJECT_DIR}/../credentials.json" TOKEN="$BUGSEE_APP_TOKEN" if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -z "$TOKEN" ]; then for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do if [ -f "$ENV_FILE" ]; then ENV_TOKEN=$( ( . "$ENV_FILE" >/dev/null 2>&1; if [ -n "$BUGSEE_APP_TOKEN" ]; then printf '%s' "$BUGSEE_APP_TOKEN"; else printf '%s' "$BUGSEE_TOKEN_IOS"; fi ) 2>/dev/null || true ) if [ -n "$ENV_TOKEN" ]; then TOKEN="$ENV_TOKEN" fi fi done fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) fi TOKEN_CHECK=$(printf '%s' "$TOKEN" | /usr/bin/tr -d ' \t\r\n-') if [ -z "$TOKEN_CHECK" ]; then echo "warning: bugsee: dSYM upload skipped: no app token is configured" exit 0 fi case "$TOKEN_CHECK" in 000000000000[0123456789abcdefABCDEF]000[0123456789abcdefABCDEF]000000000000000) echo "warning: bugsee: dSYM upload skipped: the app token is the placeholder" exit 0 ;; esac # An "error:" line reads as a build error to Xcode; a failed upload is a warning. run_cli() { CLI_OUT=$("$@" 2>&1) CLI_RC=$? if [ -n "$CLI_OUT" ]; then printf '%s\n' "$CLI_OUT" | /usr/bin/sed -E 's/^(.*: )?([Ff]atal [Ee]rror|[Ee]rror): (.*)$/warning: bugsee: \3/' fi return $CLI_RC } if [ -n "$TOKEN" ]; then export BUGSEE_APP_TOKEN="$TOKEN" fi if [ -z "$BUGSEE_ENDPOINT" ] && [ -f "$CREDS" ]; then ENDPOINT=$(/usr/bin/plutil -extract endpoint raw -o - "$CREDS" 2>/dev/null || true) if [ -n "$ENDPOINT" ]; then export BUGSEE_ENDPOINT="$ENDPOINT" fi fi try_bin() { if [ -x "$1" ]; then run_cli "$1" xcode post-action exit $? fi } for dir in \ "${PROJECT_DIR}/../node_modules" \ "${PROJECT_DIR}/../node_modules/@bugsee/react-native/node_modules" \ "${PROJECT_DIR}/../../../packages/react-native/node_modules" do if [ "$(uname -m)" = "x86_64" ]; then try_bin "${dir}/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli" else try_bin "${dir}/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-arm64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli-darwin-x64/bin/bugsee-cli" try_bin "${dir}/@bugsee/cli/node_modules/@bugsee/cli-darwin-x64/bin/bugsee-cli" fi done # Archive's PATH has no node, and the npm shim's shebang searches PATH. if [ ! -x "$NODE_BINARY" ]; then if [ -f "${PROJECT_DIR}/.xcode.env" ]; then . "${PROJECT_DIR}/.xcode.env" fi if [ -f "${PROJECT_DIR}/.xcode.env.local" ]; then . "${PROJECT_DIR}/.xcode.env.local" fi fi if [ ! -x "$NODE_BINARY" ]; then RN_FIND="" if [ -n "$REACT_NATIVE_PATH" ] && [ -f "$REACT_NATIVE_PATH/scripts/find-node-for-xcode.sh" ]; then RN_FIND="$REACT_NATIVE_PATH/scripts/find-node-for-xcode.sh" elif [ -f "${PROJECT_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh" ]; then RN_FIND="${PROJECT_DIR}/../node_modules/react-native/scripts/find-node-for-xcode.sh" fi if [ -n "$RN_FIND" ]; then NODE_BINARY=$(/bin/bash -c '. "$1" >/dev/null 2>&1; command -v node' _ "$RN_FIND" || true) fi fi if [ -n "$NODE_BINARY" ] && [ -x "$NODE_BINARY" ]; then PATH="$(dirname "$NODE_BINARY"):$PATH" export PATH NODE_BINARY for dir in \ "${PROJECT_DIR}/../node_modules" \ "${PROJECT_DIR}/../node_modules/@bugsee/react-native/node_modules" \ "${PROJECT_DIR}/../../../packages/react-native/node_modules" do if [ -f "${dir}/@bugsee/cli/bin/bugsee-cli.js" ]; then run_cli "$NODE_BINARY" "${dir}/@bugsee/cli/bin/bugsee-cli.js" xcode post-action exit $? fi done fi echo "bugsee-cli: not found under node_modules" >&2 exit 1 "> /dev/null 2>&1; if [ -n "$BUGSEE_APP_TOKEN" ]; then printf \'%s\' "$BUGSEE_APP_TOKEN"; else printf \'%s\' "$BUGSEE_TOKEN_IOS"; fi ) 2>/dev/null || true )', + ' if [ -n "$ENV_TOKEN" ]; then', + ' TOKEN="$ENV_TOKEN"', + ' fi', ' fi', ' done', - ' TOKEN="$BUGSEE_APP_TOKEN"', - ' if [ -z "$TOKEN" ]; then', - ' TOKEN="$BUGSEE_TOKEN_IOS"', - ' fi', - ' if [ -x "$SAVED_NODE_BINARY" ]; then', - ' NODE_BINARY="$SAVED_NODE_BINARY"', - ' fi', - ' if [ -n "$SAVED_ENDPOINT" ]; then', - ' BUGSEE_ENDPOINT="$SAVED_ENDPOINT"', - ' fi', 'fi', ]; /** diff --git a/packages/react-native/plugin/build/dsym-script.js.map b/packages/react-native/plugin/build/dsym-script.js.map index f419cf9a..36504137 100644 --- a/packages/react-native/plugin/build/dsym-script.js.map +++ b/packages/react-native/plugin/build/dsym-script.js.map @@ -1 +1 @@ -{"version":3,"file":"dsym-script.js","sourceRoot":"","sources":["../src/dsym-script.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dsym-script.js","sourceRoot":"","sources":["../src/dsym-script.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh index ea033de2..1621381e 100644 --- a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh +++ b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action-baked.sh @@ -7,23 +7,14 @@ if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -z "$TOKEN" ]; then - SAVED_NODE_BINARY="$NODE_BINARY" - SAVED_ENDPOINT="$BUGSEE_ENDPOINT" for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do if [ -f "$ENV_FILE" ]; then - . "$ENV_FILE" + ENV_TOKEN=$( ( . "$ENV_FILE" >/dev/null 2>&1; if [ -n "$BUGSEE_APP_TOKEN" ]; then printf '%s' "$BUGSEE_APP_TOKEN"; else printf '%s' "$BUGSEE_TOKEN_IOS"; fi ) 2>/dev/null || true ) + if [ -n "$ENV_TOKEN" ]; then + TOKEN="$ENV_TOKEN" + fi fi done - TOKEN="$BUGSEE_APP_TOKEN" - if [ -z "$TOKEN" ]; then - TOKEN="$BUGSEE_TOKEN_IOS" - fi - if [ -x "$SAVED_NODE_BINARY" ]; then - NODE_BINARY="$SAVED_NODE_BINARY" - fi - if [ -n "$SAVED_ENDPOINT" ]; then - BUGSEE_ENDPOINT="$SAVED_ENDPOINT" - fi fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) diff --git a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh index c60e3539..703d9510 100644 --- a/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh +++ b/packages/react-native/plugin/src/__tests__/fixtures/dsym-post-action.sh @@ -4,23 +4,14 @@ if [ -z "$TOKEN" ]; then TOKEN="$BUGSEE_TOKEN_IOS" fi if [ -z "$TOKEN" ]; then - SAVED_NODE_BINARY="$NODE_BINARY" - SAVED_ENDPOINT="$BUGSEE_ENDPOINT" for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do if [ -f "$ENV_FILE" ]; then - . "$ENV_FILE" + ENV_TOKEN=$( ( . "$ENV_FILE" >/dev/null 2>&1; if [ -n "$BUGSEE_APP_TOKEN" ]; then printf '%s' "$BUGSEE_APP_TOKEN"; else printf '%s' "$BUGSEE_TOKEN_IOS"; fi ) 2>/dev/null || true ) + if [ -n "$ENV_TOKEN" ]; then + TOKEN="$ENV_TOKEN" + fi fi done - TOKEN="$BUGSEE_APP_TOKEN" - if [ -z "$TOKEN" ]; then - TOKEN="$BUGSEE_TOKEN_IOS" - fi - if [ -x "$SAVED_NODE_BINARY" ]; then - NODE_BINARY="$SAVED_NODE_BINARY" - fi - if [ -n "$SAVED_ENDPOINT" ]; then - BUGSEE_ENDPOINT="$SAVED_ENDPOINT" - fi fi if [ -z "$TOKEN" ] && [ -f "$CREDS" ]; then TOKEN=$(/usr/bin/plutil -extract ios raw -o - "$CREDS" 2>/dev/null || true) diff --git a/packages/react-native/plugin/src/dsym-script.ts b/packages/react-native/plugin/src/dsym-script.ts index 3b75aa69..a7b0dc52 100644 --- a/packages/react-native/plugin/src/dsym-script.ts +++ b/packages/react-native/plugin/src/dsym-script.ts @@ -65,31 +65,22 @@ export const TOKEN_GUARD: readonly string[] = [ * Looks for the token in ios/.xcode.env and ios/.xcode.env.local, the files * the README sends a bare app's `export BUGSEE_APP_TOKEN=...` to for the * bundle phase. A GUI Archive does not inherit a shell, so the post-action - * reads them itself, and only when no earlier source gave a token. The files - * may also set NODE_BINARY; an executable one from Xcode's own environment - * wins, as it did before the files were read here; so does a BUGSEE_ENDPOINT - * the plugin baked in or Xcode's environment already has. Shared with the bare - * example's scheme (scripts/__tests__/dsym-scheme-post-action.test.ts). + * reads them itself, and only when no earlier source gave a token. Each file + * is sourced in a subshell that prints the token: a file written for bash, a + * syntax error or an `exit` in it cannot stop this script or change its + * NODE_BINARY or BUGSEE_ENDPOINT, and the later file (.local) wins. Shared + * with the bare example's scheme (scripts/__tests__/dsym-scheme-post-action.test.ts). */ export const ENV_FILE_TOKEN: readonly string[] = [ 'if [ -z "$TOKEN" ]; then', - ' SAVED_NODE_BINARY="$NODE_BINARY"', - ' SAVED_ENDPOINT="$BUGSEE_ENDPOINT"', ' for ENV_FILE in "${PROJECT_DIR}/.xcode.env" "${PROJECT_DIR}/.xcode.env.local"; do', ' if [ -f "$ENV_FILE" ]; then', - ' . "$ENV_FILE"', + ' ENV_TOKEN=$( ( . "$ENV_FILE" >/dev/null 2>&1; if [ -n "$BUGSEE_APP_TOKEN" ]; then printf \'%s\' "$BUGSEE_APP_TOKEN"; else printf \'%s\' "$BUGSEE_TOKEN_IOS"; fi ) 2>/dev/null || true )', + ' if [ -n "$ENV_TOKEN" ]; then', + ' TOKEN="$ENV_TOKEN"', + ' fi', ' fi', ' done', - ' TOKEN="$BUGSEE_APP_TOKEN"', - ' if [ -z "$TOKEN" ]; then', - ' TOKEN="$BUGSEE_TOKEN_IOS"', - ' fi', - ' if [ -x "$SAVED_NODE_BINARY" ]; then', - ' NODE_BINARY="$SAVED_NODE_BINARY"', - ' fi', - ' if [ -n "$SAVED_ENDPOINT" ]; then', - ' BUGSEE_ENDPOINT="$SAVED_ENDPOINT"', - ' fi', 'fi', ]; diff --git a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js index fd6e3cfa..9716be36 100644 --- a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js +++ b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js @@ -282,6 +282,18 @@ fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({ p, text: fs.readF expect(JSON.parse(seen.text).sourcesContent).toEqual(['const secret = 1;']); }); + it('strips the sections of an indexed map too', () => { + const { stripSourcesContent } = require('../hermes-sourcemaps'); + const indexed = JSON.stringify({ + version: 3, + sections: [{ offset: { line: 0, column: 0 }, map: { version: 3, sources: ['a.ts'], sourcesContent: ['x'], mappings: 'AAAA' } }], + }); + const out = JSON.parse(stripSourcesContent(indexed)); + expect(out.sections[0].map).not.toHaveProperty('sourcesContent'); + expect(out.sections[0].map.mappings).toBe('AAAA'); + expect(stripSourcesContent('{"version":3}')).toBe('{"version":3}'); + }); + it('skips the upload, saying why, when the stripped copy cannot be made', () => { const mapPath = path.join(dir, 'main.jsbundle.composed.map'); fs.writeFileSync(mapPath, 'not json'); diff --git a/packages/react-native/scripts/hermes-sourcemaps.js b/packages/react-native/scripts/hermes-sourcemaps.js index 8892fb8d..d2f45bd9 100644 --- a/packages/react-native/scripts/hermes-sourcemaps.js +++ b/packages/react-native/scripts/hermes-sourcemaps.js @@ -432,9 +432,18 @@ function relativizeSourceMap(mapText, projectRoot) { */ function stripSourcesContent(mapText) { const map = JSON.parse(mapText); - if (!('sourcesContent' in map)) return mapText; - delete map.sourcesContent; - return JSON.stringify(map); + let changed = false; + const strip = (node) => { + if (!node || typeof node !== 'object') return; + if ('sourcesContent' in node) { + delete node.sourcesContent; + changed = true; + } + // An indexed map keeps its sources in sections[].map. + if (Array.isArray(node.sections)) node.sections.forEach((section) => strip(section && section.map)); + }; + strip(map); + return changed ? JSON.stringify(map) : mapText; } /** On when BUGSEE_STRIP_SOURCES_CONTENT says so. */ diff --git a/scripts/__tests__/dsym-scheme-post-action.test.ts b/scripts/__tests__/dsym-scheme-post-action.test.ts index 8462d275..18711962 100644 --- a/scripts/__tests__/dsym-scheme-post-action.test.ts +++ b/scripts/__tests__/dsym-scheme-post-action.test.ts @@ -285,6 +285,16 @@ describe('BareExample archive post-action refuses a missing or placeholder token expect(result.stdout).toBe(SKIP_PLACEHOLDER); }); + it('survives a file with a syntax error or an exit, and one that sets a bad NODE_BINARY or endpoint', () => { + for (const bad of ['if [[ -n x ]]; then\n', 'exit 5\n', 'export BUGSEE_ENDPOINT=https://elsewhere.invalid\n']) { + const result = withEnvFile(`${bad}export BUGSEE_APP_TOKEN=${token}\n`); + // Statements after a bad one may not run; the script must still finish. + expect([0]).toContain(result.status); + } + const result = withEnvFile('export BUGSEE_ENDPOINT=https://elsewhere.invalid\nexport BUGSEE_APP_TOKEN=' + token + '\n'); + expect(result.invoked).toBe(`token=${token}\n`); + }); + it("keeps Xcode's executable NODE_BINARY over the file's", () => { const result = withEnvFile(`export BUGSEE_APP_TOKEN=${token}\nexport NODE_BINARY=/nonexistent/node\n`); expect(result.invoked).toBe(`token=${token}\n`); From 38ccc1017234c56a114bf6720485eaa3e079ba07 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 20 Oct 2026 20:53:52 +0500 Subject: [PATCH 4/5] Keep the strip tests static-safe for mutation scoring: inject the upload into run(), kill new survivors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../plugin/src/__tests__/with-bugsee.test.ts | 5 +++-- .../scripts/__tests__/compose-then-inject.test.js | 15 ++++----------- .../__tests__/sourcemap-relative-sources.test.js | 10 +++++++++- .../react-native/scripts/compose-then-inject.js | 4 ++-- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts index c477c050..0617e66f 100644 --- a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts +++ b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts @@ -296,8 +296,9 @@ describe('withBugsee through the Expo mod compiler', () => { }); it('names editGradleFiles in the Kotlin refusals, and that option lets a Kotlin project prebuild', async () => { - expect(APP_GRADLE_NOT_GROOVY).toContain('"editGradleFiles": false'); - expect(gradleNotGroovy('android/build.gradle')).toContain('"editGradleFiles": false'); + for (const message of [APP_GRADLE_NOT_GROOVY, gradleNotGroovy('android/build.gradle')]) { + expect(message).toContain('set "editGradleFiles": false in the @bugsee/react-native plugin options and run expo prebuild again'); + } const android = join(projectRoot, 'android'); for (const [dir, name] of [ [android, 'settings.gradle'], diff --git a/packages/react-native/scripts/__tests__/compose-then-inject.test.js b/packages/react-native/scripts/__tests__/compose-then-inject.test.js index a1dc55b5..cdd9f07e 100644 --- a/packages/react-native/scripts/__tests__/compose-then-inject.test.js +++ b/packages/react-native/scripts/__tests__/compose-then-inject.test.js @@ -189,17 +189,10 @@ fs.writeFileSync(out, JSON.stringify({ version: 3, mappings: 'CCCC' })); it('asks the upload to strip sourcesContent only when BUGSEE_STRIP_SOURCES_CONTENT is on', () => { const calls = []; - jest.isolateModules(() => { - jest.doMock('../hermes-sourcemaps', () => { - const real = jest.requireActual('../hermes-sourcemaps'); - return { ...real, uploadComposedSourceMap: (options) => calls.push(options) }; - }); - const isolated = require('../compose-then-inject'); - const versions = { MARKETING_VERSION: '1', CURRENT_PROJECT_VERSION: '1', BUGSEE_PLUGIN_APP_TOKEN: TOKEN }; - expect(isolated.run(argv(), { ...env, ...versions })).toBe(0); - expect(isolated.run(argv(), { ...env, ...versions, BUGSEE_STRIP_SOURCES_CONTENT: 'true' })).toBe(0); - }); - jest.dontMock('../hermes-sourcemaps'); + const upload = (options) => calls.push(options); + const versions = { MARKETING_VERSION: '1', CURRENT_PROJECT_VERSION: '1', BUGSEE_PLUGIN_APP_TOKEN: TOKEN }; + expect(run(argv(), { ...env, ...versions }, upload)).toBe(0); + expect(run(argv(), { ...env, ...versions, BUGSEE_STRIP_SOURCES_CONTENT: 'true' }, upload)).toBe(0); expect(calls.map((call) => call.stripSources)).toEqual([false, true]); }); diff --git a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js index 9716be36..952ba878 100644 --- a/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js +++ b/packages/react-native/scripts/__tests__/sourcemap-relative-sources.test.js @@ -291,7 +291,11 @@ fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({ p, text: fs.readF const out = JSON.parse(stripSourcesContent(indexed)); expect(out.sections[0].map).not.toHaveProperty('sourcesContent'); expect(out.sections[0].map.mappings).toBe('AAAA'); - expect(stripSourcesContent('{"version":3}')).toBe('{"version":3}'); + // Unchanged text comes back byte for byte, not re-serialised. + const plain = '{ "version": 3, "sections": [ null, {}, { "map": "x" }, { "map": { "version": 3 } } ] }'; + expect(stripSourcesContent(plain)).toBe(plain); + expect(stripSourcesContent('{ "version": 3 }')).toBe('{ "version": 3 }'); + expect(stripSourcesContent('{ "version": 3, "sourcesContent": [] }')).toBe('{"version":3}'); }); it('skips the upload, saying why, when the stripped copy cannot be made', () => { @@ -306,6 +310,7 @@ fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({ p, text: fs.readF stripSources: true, }); expect(result).toEqual({ status: 'skipped', reason: 'strip-failed' }); + expect(stderr.mock.calls.join('')).toContain('bugsee: source map upload skipped: could not remove sourcesContent from the map ('); expect(fs.existsSync(captured)).toBe(false); }); @@ -313,6 +318,9 @@ fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({ p, text: fs.readF const { stripSourcesContentRequested } = require('../hermes-sourcemaps'); expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: 'true' })).toBe(true); expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: ' 1 ' })).toBe(true); + for (const on of ['yes', 'ON', 'True', '1']) { + expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: on })).toBe(true); + } expect(stripSourcesContentRequested({ BUGSEE_STRIP_SOURCES_CONTENT: 'false' })).toBe(false); expect(stripSourcesContentRequested({})).toBe(false); }); diff --git a/packages/react-native/scripts/compose-then-inject.js b/packages/react-native/scripts/compose-then-inject.js index d5821b1d..03f11c5d 100755 --- a/packages/react-native/scripts/compose-then-inject.js +++ b/packages/react-native/scripts/compose-then-inject.js @@ -48,7 +48,7 @@ function fail(message) { } /** Returns the process exit code. */ -function run(argv, env) { +function run(argv, env, upload = uploadComposedSourceMap) { const realCompose = env.BUGSEE_REAL_COMPOSE; const bundlePath = env.BUGSEE_JS_BUNDLE; const bytecodePath = env.BUGSEE_BYTECODE_BUNDLE; @@ -92,7 +92,7 @@ function run(argv, env) { credentialsPath: env.BUGSEE_CREDENTIALS_FILE, env, }); - uploadComposedSourceMap({ + upload({ composedMapPath: output, enabled: !uploadDisabled(undefined, env), configuration: env.CONFIGURATION, From efbeeee65747f9f98141bec77dd01a681ea9c794 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Wed, 21 Oct 2026 10:20:49 +0500 Subject: [PATCH 5/5] Tighten the bad .xcode.env file test: assert the guard is reached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- scripts/__tests__/dsym-scheme-post-action.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/__tests__/dsym-scheme-post-action.test.ts b/scripts/__tests__/dsym-scheme-post-action.test.ts index 18711962..1dddea1c 100644 --- a/scripts/__tests__/dsym-scheme-post-action.test.ts +++ b/scripts/__tests__/dsym-scheme-post-action.test.ts @@ -286,10 +286,12 @@ describe('BareExample archive post-action refuses a missing or placeholder token }); it('survives a file with a syntax error or an exit, and one that sets a bad NODE_BINARY or endpoint', () => { - for (const bad of ['if [[ -n x ]]; then\n', 'exit 5\n', 'export BUGSEE_ENDPOINT=https://elsewhere.invalid\n']) { + // The file's own failure only costs the token: the script still reaches the guard. + for (const bad of ['if [[ -n x ]]; then\n', 'exit 5\n']) { const result = withEnvFile(`${bad}export BUGSEE_APP_TOKEN=${token}\n`); - // Statements after a bad one may not run; the script must still finish. - expect([0]).toContain(result.status); + expect(result.status).toBe(0); + expect(result.invoked).toBeNull(); + expect(result.stdout).toBe(SKIP_NONE); } const result = withEnvFile('export BUGSEE_ENDPOINT=https://elsewhere.invalid\nexport BUGSEE_APP_TOKEN=' + token + '\n'); expect(result.invoked).toBe(`token=${token}\n`);