diff --git a/src/plugin/__tests__/__snapshots__/withIosSourcemaps.test.ts.snap b/src/plugin/__tests__/__snapshots__/withIosSourcemaps.test.ts.snap new file mode 100644 index 0000000..6aa2c72 --- /dev/null +++ b/src/plugin/__tests__/__snapshots__/withIosSourcemaps.test.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`withIosSourcemaps on pristine project adds script to upload sourcemaps to Datadog 1`] = `""if [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\nexport SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map\\n \\n\\nif [[ -z \\"$DATADOG_CI_EXEC\\" ]]; then\\n DATADOG_CI_EXEC=\\"$(\\"$NODE_BINARY\\" --print \\"require('path').resolve(require('path').dirname(require.resolve('@datadog/datadog-ci/package.json')), '../../.bin/datadog-ci')\\")\\";\\n \\n # Check if the file exists and is executable\\n if [[ -x \\"$DATADOG_CI_EXEC\\" ]]; then\\n export DATADOG_CI_EXEC;\\n else\\n echo \\"Error: DATADOG_CI_EXEC does not exist or is not executable\\";\\n exit 1;\\n fi\\nfi\\n\\n\\n$DATADOG_CI_EXEC react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n\\n\\n""`; + +exports[`withIosSourcemaps on pristine project adds script to upload sourcemaps to Datadog with custom service name 1`] = `""if [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\nexport SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map\\n \\n\\nif [[ -z \\"$DATADOG_CI_EXEC\\" ]]; then\\n DATADOG_CI_EXEC=\\"$(\\"$NODE_BINARY\\" --print \\"require('path').resolve(require('path').dirname(require.resolve('@datadog/datadog-ci/package.json')), '../../.bin/datadog-ci')\\")\\";\\n \\n # Check if the file exists and is executable\\n if [[ -x \\"$DATADOG_CI_EXEC\\" ]]; then\\n export DATADOG_CI_EXEC;\\n else\\n echo \\"Error: DATADOG_CI_EXEC does not exist or is not executable\\";\\n exit 1;\\n fi\\nfi\\n\\n\\n$DATADOG_CI_EXEC react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n --service com.company.app\\n\\n""`; + +exports[`withIosSourcemaps on projects implementing Sentry adds script to upload sourcemaps to Datadog 1`] = `""export SENTRY_PROPERTIES=sentry.properties\\nexport EXTRA_PACKAGER_ARGS=\\"--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map\\"\\nif [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\n\`node --print \\"require.resolve('@sentry/cli/package.json').slice(0, -13) + '/bin/sentry-cli'\\"\` react-native xcode --force-foreground \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n\\n\\n \\n\\nif [[ -z \\"$DATADOG_CI_EXEC\\" ]]; then\\n DATADOG_CI_EXEC=\\"$(\\"$NODE_BINARY\\" --print \\"require('path').resolve(require('path').dirname(require.resolve('@datadog/datadog-ci/package.json')), '../../.bin/datadog-ci')\\")\\";\\n \\n # Check if the file exists and is executable\\n if [[ -x \\"$DATADOG_CI_EXEC\\" ]]; then\\n export DATADOG_CI_EXEC;\\n else\\n echo \\"Error: DATADOG_CI_EXEC does not exist or is not executable\\";\\n exit 1;\\n fi\\nfi\\n\\n\\n$DATADOG_CI_EXEC react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n""`; + +exports[`withIosSourcemaps on projects implementing Sentry adds script to upload sourcemaps to Datadog with custom service name 1`] = `""export SENTRY_PROPERTIES=sentry.properties\\nexport EXTRA_PACKAGER_ARGS=\\"--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map\\"\\nif [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\n\`node --print \\"require.resolve('@sentry/cli/package.json').slice(0, -13) + '/bin/sentry-cli'\\"\` react-native xcode --force-foreground \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n\\n\\n \\n\\nif [[ -z \\"$DATADOG_CI_EXEC\\" ]]; then\\n DATADOG_CI_EXEC=\\"$(\\"$NODE_BINARY\\" --print \\"require('path').resolve(require('path').dirname(require.resolve('@datadog/datadog-ci/package.json')), '../../.bin/datadog-ci')\\")\\";\\n \\n # Check if the file exists and is executable\\n if [[ -x \\"$DATADOG_CI_EXEC\\" ]]; then\\n export DATADOG_CI_EXEC;\\n else\\n echo \\"Error: DATADOG_CI_EXEC does not exist or is not executable\\";\\n exit 1;\\n fi\\nfi\\n\\n\\n$DATADOG_CI_EXEC react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n --service com.company.app""`; diff --git a/src/plugin/__tests__/getErrorTrackingPluginsFromOptions.test.ts b/src/plugin/__tests__/getErrorTrackingPluginsFromOptions.test.ts index 3362aaa..c1c9a38 100644 --- a/src/plugin/__tests__/getErrorTrackingPluginsFromOptions.test.ts +++ b/src/plugin/__tests__/getErrorTrackingPluginsFromOptions.test.ts @@ -22,7 +22,7 @@ const ALL_PLUGINS = [ describe("getErrorTrackingPluginsFromOptions", () => { it("returns all plugins if no option is provided", () => { expect(getErrorTrackingPluginsFromOptions()).toHaveLength( - ALL_PLUGINS.length, + ALL_PLUGINS.length ); }); @@ -31,7 +31,7 @@ describe("getErrorTrackingPluginsFromOptions", () => { getErrorTrackingPluginsFromOptions({ iosDsyms: true, androidProguardMappingFiles: false, - }).length, + }).length ).toEqual(ALL_PLUGINS.filter((_, index) => index !== 3).length); }); }); diff --git a/src/plugin/__tests__/withAndroidConfiguration.test.ts b/src/plugin/__tests__/withAndroidConfiguration.test.ts index c4cc58d..7f2a77e 100644 --- a/src/plugin/__tests__/withAndroidConfiguration.test.ts +++ b/src/plugin/__tests__/withAndroidConfiguration.test.ts @@ -6,8 +6,8 @@ import { withAppBuildGradle } from "@expo/config-plugins"; -import buildGradle from "./__fixtures__/build.gradle"; import withAndroidConfiguration from "../withAndroidConfiguration/withAndroidConfiguration"; +import buildGradle from "./__fixtures__/build.gradle"; jest.mock("@expo/config-plugins", () => { return { @@ -46,10 +46,10 @@ describe("withAndroidConfiguration", () => { it("adds a datadog config block", async () => { mockAppBuildGradle(buildGradle); const result = (await withAndroidConfiguration({})( - createFakeConfig(), + createFakeConfig() )) as any; expect(result.modResults.contents).toMatch( - 'id("com.datadoghq.dd-sdk-android-gradle-plugin") version "1.14.0"', + 'id("com.datadoghq.dd-sdk-android-gradle-plugin") version "1.14.0"' ); expect(result.modResults.contents).toMatchSnapshot(); }); @@ -70,7 +70,7 @@ describe("withAndroidConfiguration", () => { datadogGradlePluginVersion: "1.9.0", })(createFakeConfig())) as any; expect(result.modResults.contents).toMatch( - 'id("com.datadoghq.dd-sdk-android-gradle-plugin") version "1.9.0"', + 'id("com.datadoghq.dd-sdk-android-gradle-plugin") version "1.9.0"' ); }); }); diff --git a/src/plugin/__tests__/withAndroidSourcemaps.test.ts b/src/plugin/__tests__/withAndroidSourcemaps.test.ts index 0248fc9..e26242d 100644 --- a/src/plugin/__tests__/withAndroidSourcemaps.test.ts +++ b/src/plugin/__tests__/withAndroidSourcemaps.test.ts @@ -6,8 +6,8 @@ import { withAppBuildGradle } from "@expo/config-plugins"; -import buildGradle from "./__fixtures__/build.gradle"; import withAndroidSourcemaps from "../withAndroidSourcemaps/withAndroidSourcemaps"; +import buildGradle from "./__fixtures__/build.gradle"; jest.mock("@expo/config-plugins", () => { return { diff --git a/src/plugin/__tests__/withIosSourcemaps.test.ts b/src/plugin/__tests__/withIosSourcemaps.test.ts index 720d198..51d6524 100644 --- a/src/plugin/__tests__/withIosSourcemaps.test.ts +++ b/src/plugin/__tests__/withIosSourcemaps.test.ts @@ -6,9 +6,9 @@ import { withXcodeProject } from "@expo/config-plugins"; +import withIosSourcemaps from "../withIosSourcemaps/withIosSourcemaps"; import pristineProject from "./__fixtures__/pristineProjectPbxproj.json"; import sentryProject from "./__fixtures__/sentryProjectPbxproj.json"; -import withIosSourcemaps from "../withIosSourcemaps/withIosSourcemaps"; jest.mock("@expo/config-plugins", () => { return { @@ -49,36 +49,28 @@ describe("withIosSourcemaps", () => { it("adds script to upload sourcemaps to Datadog", async () => { mockXcodeProject(pristineProject); const result = (await withIosSourcemaps({})(createFakeConfig())) as any; - expect(result.xcodeProject.shellScript).toMatchInlineSnapshot( - `""if [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\nexport SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map\\n ../node_modules/.bin/datadog-ci react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n\\n""`, - ); + expect(result.xcodeProject.shellScript).toMatchSnapshot(); }); it("adds script to upload sourcemaps to Datadog with custom service name", async () => { mockXcodeProject(pristineProject); const result = (await withIosSourcemaps({ serviceName: "com.company.app", })(createFakeConfig())) as any; - expect(result.xcodeProject.shellScript).toMatchInlineSnapshot( - `""if [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\nexport SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map\\n ../node_modules/.bin/datadog-ci react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\` --service com.company.app\\n\\n""`, - ); + expect(result.xcodeProject.shellScript).toMatchSnapshot(); }); }); describe("on projects implementing Sentry", () => { it("adds script to upload sourcemaps to Datadog", async () => { mockXcodeProject(sentryProject); const result = (await withIosSourcemaps({})(createFakeConfig())) as any; - expect(result.xcodeProject.shellScript).toMatchInlineSnapshot( - `""export SENTRY_PROPERTIES=sentry.properties\\nexport EXTRA_PACKAGER_ARGS=\\"--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map\\"\\nif [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\n\`node --print \\"require.resolve('@sentry/cli/package.json').slice(0, -13) + '/bin/sentry-cli'\\"\` react-native xcode --force-foreground \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n\\n\\n ../node_modules/.bin/datadog-ci react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`""`, - ); + expect(result.xcodeProject.shellScript).toMatchSnapshot(); }); it("adds script to upload sourcemaps to Datadog with custom service name", async () => { mockXcodeProject(sentryProject); const result = (await withIosSourcemaps({ serviceName: "com.company.app", })(createFakeConfig())) as any; - expect(result.xcodeProject.shellScript).toMatchInlineSnapshot( - `""export SENTRY_PROPERTIES=sentry.properties\\nexport EXTRA_PACKAGER_ARGS=\\"--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map\\"\\nif [[ -f \\"$PODS_ROOT/../.xcode.env\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env\\"\\nfi\\nif [[ -f \\"$PODS_ROOT/../.xcode.env.local\\" ]]; then\\n source \\"$PODS_ROOT/../.xcode.env.local\\"\\nfi\\n\\n# The project root by default is one level up from the ios directory\\nexport PROJECT_ROOT=\\"$PROJECT_DIR\\"/..\\n\\nif [[ \\"$CONFIGURATION\\" = *Debug* ]]; then\\n export SKIP_BUNDLING=1\\nfi\\n\`node --print \\"require.resolve('@sentry/cli/package.json').slice(0, -13) + '/bin/sentry-cli'\\"\` react-native xcode --force-foreground \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`\\n\\n\\n ../node_modules/.bin/datadog-ci react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\` --service com.company.app""`, - ); + expect(result.xcodeProject.shellScript).toMatchSnapshot(); }); }); }); diff --git a/src/plugin/common/config.ts b/src/plugin/common/config.ts new file mode 100644 index 0000000..52ba6d8 --- /dev/null +++ b/src/plugin/common/config.ts @@ -0,0 +1,2 @@ +export const DEFAULT_DATADOG_GRADLE_PLUGIN_VERSION = "1.14.0"; +export const IOS_DSYMS_BUILD_PHASE_NAME = "Upload dSYMs to Datadog"; diff --git a/src/plugin/common/exports.ts b/src/plugin/common/exports.ts new file mode 100644 index 0000000..df55478 --- /dev/null +++ b/src/plugin/common/exports.ts @@ -0,0 +1,16 @@ +export const IOS_SOURCEMAP_FILE_EXPORT = + "export SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map"; + +export const IOS_DATADOG_CI_EXPORT = ` +if [[ -z "$DATADOG_CI_EXEC" ]]; then + DATADOG_CI_EXEC="$("$NODE_BINARY" --print "require('path').resolve(require('path').dirname(require.resolve('@datadog/datadog-ci/package.json')), '../../.bin/datadog-ci')")"; + + # Check if the file exists and is executable + if [[ -x "$DATADOG_CI_EXEC" ]]; then + export DATADOG_CI_EXEC; + else + echo "Error: DATADOG_CI_EXEC does not exist or is not executable"; + exit 1; + fi +fi +`; diff --git a/src/plugin/common/utils.ts b/src/plugin/common/utils.ts new file mode 100644 index 0000000..59aeedb --- /dev/null +++ b/src/plugin/common/utils.ts @@ -0,0 +1,6 @@ +export const escapeStringForIOSBuildPhase = (str: string) => { + return str + .replace(/\\/g, "\\\\") // Escape backslashes + .replace(/"/g, '\\"') // Escape double quotes + .replace(/\n/g, "\\n"); // Replace newlines with \n +}; diff --git a/src/plugin/getErrorTrackingPluginsFromOptions.ts b/src/plugin/getErrorTrackingPluginsFromOptions.ts index 5571aec..da9149e 100644 --- a/src/plugin/getErrorTrackingPluginsFromOptions.ts +++ b/src/plugin/getErrorTrackingPluginsFromOptions.ts @@ -57,7 +57,7 @@ export type ErrorTrackingOptions = FileUploadOptions & * to `false`. */ export const getErrorTrackingPluginsFromOptions = ( - options?: ErrorTrackingOptions, + options?: ErrorTrackingOptions ): (ConfigPlugin | StaticPlugin)[] => { const ERROR_TRACKING_CONFIG_PLUGINS_MAP: Record< keyof FileUploadOptions, @@ -73,7 +73,7 @@ export const getErrorTrackingPluginsFromOptions = ( const configPluginsKeys = ( Object.keys( - ERROR_TRACKING_CONFIG_PLUGINS_MAP, + ERROR_TRACKING_CONFIG_PLUGINS_MAP ) as (keyof FileUploadOptions)[] ).filter((option) => !options || options[option] !== false); diff --git a/src/plugin/index.ts b/src/plugin/index.ts index 586f191..b4c28ee 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -21,7 +21,7 @@ type PluginConfiguration = const withDatadog: ConfigPlugin = (config, options) => { return withPlugins( config, - getErrorTrackingPluginsFromOptions(options && options.errorTracking), + getErrorTrackingPluginsFromOptions(options && options.errorTracking) ); }; diff --git a/src/plugin/withAndroidConfiguration/withAndroidConfiguration.ts b/src/plugin/withAndroidConfiguration/withAndroidConfiguration.ts index 373a23a..f342558 100644 --- a/src/plugin/withAndroidConfiguration/withAndroidConfiguration.ts +++ b/src/plugin/withAndroidConfiguration/withAndroidConfiguration.ts @@ -7,16 +7,15 @@ import type { ConfigPlugin } from "@expo/config-plugins"; import { withAppBuildGradle } from "@expo/config-plugins"; +import { DEFAULT_DATADOG_GRADLE_PLUGIN_VERSION } from "../common/config"; import { AndroidProguardMappingFilesOptions, SourceMapUploadOptions, } from "../getErrorTrackingPluginsFromOptions"; -const DEFAULT_DATADOG_GRADLE_PLUGIN_VERSION = "1.14.0"; - const withAndroidConfiguration = ( - options: SourceMapUploadOptions & AndroidProguardMappingFilesOptions, + options: SourceMapUploadOptions & AndroidProguardMappingFilesOptions ): ConfigPlugin => (config) => { return withAppBuildGradle(config, async (config) => { diff --git a/src/plugin/withAndroidSourcemaps/withAndroidSourcemaps.ts b/src/plugin/withAndroidSourcemaps/withAndroidSourcemaps.ts index b724f62..8db0201 100644 --- a/src/plugin/withAndroidSourcemaps/withAndroidSourcemaps.ts +++ b/src/plugin/withAndroidSourcemaps/withAndroidSourcemaps.ts @@ -17,8 +17,8 @@ const withAndroidSourcemaps: ConfigPlugin = (config) => { appBuildGradle.contents = appBuildGradle.contents.replace( /apply plugin: "com\.facebook\.react"/, `apply plugin: "com.facebook.react"\napply from: "${require("path").dirname( - require.resolve("@datadog/mobile-react-native/package.json"), - )}/datadog-sourcemaps.gradle"`, + require.resolve("@datadog/mobile-react-native/package.json") + )}/datadog-sourcemaps.gradle"` ); return config; diff --git a/src/plugin/withIosDsyms/withIosDsyms.ts b/src/plugin/withIosDsyms/withIosDsyms.ts index 82b1e41..a6cdd7a 100644 --- a/src/plugin/withIosDsyms/withIosDsyms.ts +++ b/src/plugin/withIosDsyms/withIosDsyms.ts @@ -7,14 +7,15 @@ import type { ConfigPlugin } from "@expo/config-plugins"; import { withXcodeProject } from "@expo/config-plugins"; -const BUILD_PHASE_NAME = "Upload dSYMs to Datadog"; +import { IOS_DSYMS_BUILD_PHASE_NAME } from "../common/config"; +import { IOS_DATADOG_CI_EXPORT } from "../common/exports"; const withIosDsyms: ConfigPlugin = (config) => { return withXcodeProject(config, async (config) => { const xcodeProject = config.modResults; const buildPhase = xcodeProject.pbxItemByComment( - BUILD_PHASE_NAME, - "PBXShellScriptBuildPhase", + IOS_DSYMS_BUILD_PHASE_NAME, + "PBXShellScriptBuildPhase" ); if (buildPhase) { return config; @@ -23,12 +24,27 @@ const withIosDsyms: ConfigPlugin = (config) => { xcodeProject.addBuildPhase( [], "PBXShellScriptBuildPhase", - BUILD_PHASE_NAME, + IOS_DSYMS_BUILD_PHASE_NAME, null /* target */, { - shellScript: `set -e\\n ../node_modules/.bin/datadog-ci dsyms upload $DWARF_DSYM_FOLDER_PATH`, + shellScript: `set -e + +if [[ -f "$PODS_ROOT/../.xcode.env" ]]; then + source "$PODS_ROOT/../.xcode.env" +fi + +if [[ -f "$PODS_ROOT/../.xcode.env.local" ]]; then + source "$PODS_ROOT/../.xcode.env.local" +fi + +if [[ -z "$NODE_BINARY" ]]; then + echo "ERROR: NODE_BINARY env variable is not set" +fi +${IOS_DATADOG_CI_EXPORT} +$DATADOG_CI_EXEC dsyms upload $DWARF_DSYM_FOLDER_PATH + `, shellPath: "/bin/sh", - }, + } ); return config; diff --git a/src/plugin/withIosSourcemaps/withIosSourcemaps.ts b/src/plugin/withIosSourcemaps/withIosSourcemaps.ts index aefb024..f40d783 100644 --- a/src/plugin/withIosSourcemaps/withIosSourcemaps.ts +++ b/src/plugin/withIosSourcemaps/withIosSourcemaps.ts @@ -7,16 +7,22 @@ import type { ConfigPlugin } from "@expo/config-plugins"; import { withXcodeProject } from "@expo/config-plugins"; +import { + IOS_DATADOG_CI_EXPORT, + IOS_SOURCEMAP_FILE_EXPORT, +} from "../common/exports"; +import { escapeStringForIOSBuildPhase } from "../common/utils"; import { SourceMapUploadOptions } from "../getErrorTrackingPluginsFromOptions"; -const SOURCEMAP_FILE_COMMAND = - "export SOURCEMAP_FILE=$DERIVED_FILE_DIR/main.jsbundle.map"; +const DATADOG_XCODE_COMMAND = escapeStringForIOSBuildPhase(` +${IOS_DATADOG_CI_EXPORT} + +$DATADOG_CI_EXEC react-native xcode \`"$NODE_BINARY" --print "require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'"\` +`); const getDatadogXCodeCommand = ( - serviceName: SourceMapUploadOptions["serviceName"], + serviceName: SourceMapUploadOptions["serviceName"] ) => - `../node_modules/.bin/datadog-ci react-native xcode \`\\"$NODE_BINARY\\" --print \\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\"\`${ - serviceName ? ` --service ${serviceName}` : "" - }`; + `${DATADOG_XCODE_COMMAND}${serviceName ? ` --service ${serviceName}` : ""}`; const withIosSourcemaps = (options: SourceMapUploadOptions): ConfigPlugin => @@ -25,7 +31,7 @@ const withIosSourcemaps = const xcodeProject = config.modResults; const bundlePhase = xcodeProject.pbxItemByComment( "Bundle React Native code and images", - "PBXShellScriptBuildPhase", + "PBXShellScriptBuildPhase" ); if (bundlePhase.shellScript.match("datadog-ci react-native xcode")) { return config; @@ -42,17 +48,17 @@ const withIosSourcemaps = */ bundlePhase.shellScript = `${bundlePhase.shellScript.replace( /.$/, - "", + "" )}\\n ${getDatadogXCodeCommand(options.serviceName)}"`; return config; } const [beforeScript, afterScript] = bundlePhase.shellScript.split( - "`\\\"$NODE_BINARY\\\" --print \\\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\\"`", + "`\\\"$NODE_BINARY\\\" --print \\\"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\\\"`" ); - const datadogScript = `${SOURCEMAP_FILE_COMMAND}\\n ${getDatadogXCodeCommand( - options.serviceName, + const datadogScript = `${IOS_SOURCEMAP_FILE_EXPORT}\\n ${getDatadogXCodeCommand( + options.serviceName )}`; bundlePhase.shellScript = `${beforeScript}${datadogScript}${afterScript}`;