diff --git a/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme b/examples/bare/ios/BareExample.xcodeproj/xcshareddata/xcschemes/BareExample.xcscheme index f73aa503..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', + 'fi', +]; /** * Relays bugsee-cli output with Xcode's `error:` diagnostics turned into * `warning: bugsee: ...`, and returns the CLI's exit status. A failed upload @@ -92,6 +114,7 @@ function dsymPostActionScript(bakedToken, bakedEndpoint) { 'if [ -z "$TOKEN" ]; then', ' TOKEN="$BUGSEE_TOKEN_IOS"', 'fi', + ...exports.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/build/dsym-script.js.map b/packages/react-native/plugin/build/dsym-script.js.map index c674e350..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/build/index.d.ts b/packages/react-native/plugin/build/index.d.ts index df2d85ae..ade76ca6 100644 --- a/packages/react-native/plugin/build/index.d.ts +++ b/packages/react-native/plugin/build/index.d.ts @@ -51,6 +51,18 @@ 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"). 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; /** 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 ad2a8fca..e829c774 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 b2d15635..e766f6cf 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..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 @@ -6,6 +6,16 @@ fi 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 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..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 @@ -3,6 +3,16 @@ 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 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 04005d46..a6d56070 100644 --- a/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts +++ b/packages/react-native/plugin/src/__tests__/with-bugsee.test.ts @@ -320,6 +320,25 @@ 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 () => { + 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'], + [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..a7b0dc52 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,29 @@ 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. 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', + ' 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', +]; + /** * 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 +119,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 1e5d159e..15aec9c2 100644 --- a/packages/react-native/plugin/src/index.ts +++ b/packages/react-native/plugin/src/index.ts @@ -84,6 +84,18 @@ 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"). 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; /** Defaults to native-versions.json `android.gradlePlugin`. */ gradlePluginVersion?: string; /** Defaults off. Writes `com.bugsee.app-token` manifest meta-data. */ @@ -112,13 +124,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 +201,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 +211,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 +238,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__/compose-then-inject.test.js b/packages/react-native/scripts/__tests__/compose-then-inject.test.js index 5bde7e27..cdd9f07e 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,15 @@ 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 = []; + 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]); + }); + 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 9da6fd39..952ba878 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,90 @@ 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('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'); + // 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', () => { + 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(stderr.mock.calls.join('')).toContain('bugsee: source map upload skipped: could not remove sourcesContent from the map ('); + 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); + 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 868d6480..03f11c5d 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'); @@ -47,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; @@ -91,7 +92,7 @@ function run(argv, env) { credentialsPath: env.BUGSEE_CREDENTIALS_FILE, env, }); - uploadComposedSourceMap({ + upload({ composedMapPath: output, enabled: !uploadDisabled(undefined, env), configuration: env.CONFIGURATION, @@ -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 cce35a28..d2f45bd9 100644 --- a/packages/react-native/scripts/hermes-sourcemaps.js +++ b/packages/react-native/scripts/hermes-sourcemaps.js @@ -425,6 +425,32 @@ 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); + 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. */ +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 +477,7 @@ function uploadComposedSourceMap({ cliPath, nodePath, projectRoot, + stripSources = false, }) { if (!enabled) { say('source map upload skipped: uploadSourcemaps is off'); @@ -490,19 +517,26 @@ function uploadComposedSourceMap({ if (endpoint) env.BUGSEE_ENDPOINT = endpoint; let stage = null; let uploadPath = composedMapPath; + let original; try { - const original = fs.readFileSync(composedMapPath, 'utf8'); - const rewritten = relativizeSourceMap(original, projectRoot); + original = fs.readFileSync(composedMapPath, 'utf8'); + 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)); 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 { @@ -545,6 +579,7 @@ function uploadFromArgs(args) { appVersion: args['app-version'], appBuild: args['app-build'], projectRoot: args['project-root'], + stripSources: stripSourcesContentRequested(process.env), }); } @@ -617,6 +652,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..1dddea1c 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,85 @@ 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('survives a file with a syntax error or an exit, and one that sets a bad NODE_BINARY or endpoint', () => { + // 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`); + 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`); + }); + + 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/);