Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin-expo-eas-sourcemaps is not compatible with monorepos #46

Open
thorbenprimke opened this issue Aug 2, 2022 · 14 comments
Open

plugin-expo-eas-sourcemaps is not compatible with monorepos #46

thorbenprimke opened this issue Aug 2, 2022 · 14 comments
Assignees
Labels
awaiting feedback Awaiting a response from a customer. Will be automatically closed after approximately 2 weeks. bug Confirmed bug feature request Request for a new feature released This feature/bug fix has been released

Comments

@thorbenprimke
Copy link

Describe the bug

Add the plugin-expo-eas-sourcemaps config plugin to an expo project in a monorepo. It does not work due to the node_modules path not being what is expected in the scripts.

Steps to reproduce

  1. Set up a monorepo with workspaces
  2. Add the app at like packages/app
  3. Kick off an EAS build and see it fail

Environment

  • Bugsnag version: 45.0.0
  • Expo SDK version: SDK 45

Example code snippet - patch packages that resolves it (but not in a dynamic manner)

diff --git a/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh b/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh
index cc52c28..bce3d79 100755
--- a/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh
+++ b/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh
@@ -58,4 +58,4 @@ if [ ! -z "$ENDPOINT" ]; then
   ARGS+=("$ENDPOINT")
 fi
 
-../node_modules/.bin/bugsnag-source-maps upload-react-native "${ARGS[@]}"
+../../../node_modules/.bin/bugsnag-source-maps upload-react-native "${ARGS[@]}"
diff --git a/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/src/ios.js b/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/src/ios.js
index e18e9c8..b01bd40 100644
--- a/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/src/ios.js
+++ b/node_modules/@bugsnag/plugin-expo-eas-sourcemaps/src/ios.js
@@ -31,7 +31,7 @@ function withIosPlugin (config, onPremConfig) {
     bundleReactNativePhase.shellScript = modifiedScript
 
     // 03. Configure the new build phase
-    const shellScript = 'SOURCE_MAP="$TMPDIR/$(md5 -qs "$CONFIGURATION_BUILD_DIR")-main.jsbundle.map" ../node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh'
+    const shellScript = 'SOURCE_MAP="$TMPDIR/$(md5 -qs "$CONFIGURATION_BUILD_DIR")-main.jsbundle.map" ../../../node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh'
 
     xcodeProject.addBuildPhase([], buildPhaseName, 'Upload source maps to Bugsnag', null, {
       shellPath: '/bin/sh',

@gingerbenw
Copy link
Member

gingerbenw commented Aug 3, 2022

Hi @thorbenprimke, thanks for raising this one - I'm glad the plugin is working otherwise!

Could I confirm what your eas build command looks like, and which directory you are running it from? Are you hoisting the dependencies for your expo app?

Thanks!

@gingerbenw gingerbenw self-assigned this Aug 3, 2022
@thorbenprimke
Copy link
Author

thorbenprimke commented Aug 3, 2022

Sure.

Commend: STAGE=production eas build --profile production --platform ios --auto-submit

I run this from the folder: /Users/thorben/projects/backstage-app/packages/expo.
The node_modules folder is at the project root: /Users/thorben/projects/backstage-app/

I believe the answer is yes to are you hoisting the dependencies. It is set up as workspaces in the root package.json and all dependencies are installed at the root but defined in the expo folder's package.json.

Let me know if I can provide anything else or you want to me to test a change.

@luke-belton luke-belton added bug Confirmed bug backlog We hope to fix this feature/bug in the future labels Aug 4, 2022
@gingerbenw
Copy link
Member

gingerbenw commented Aug 9, 2022

Hi @thorbenprimke - Apologies for the delay on this, could you please try the following:

Install @bugsnag/source-maps as a dev dependency to your expo application, and add the following to the package.json (also in the expo app):

"workspaces": {
    "nohoist": [
      "@bugsnag/plugin-expo-eas-sourcemaps",
      "@bugsnag/source-maps"
    ]
  }

This will prevent the dependencies being hoisted, and allow the plugin to execute the necessary binaries from the node_modules.

@phillipsam phillipsam added the awaiting feedback Awaiting a response from a customer. Will be automatically closed after approximately 2 weeks. label Aug 16, 2022
@evelant
Copy link

evelant commented Sep 5, 2022

@gingerbenw That won't work for people running modern yarn. nohoist only works on yarn classic (1.x versions).

@gingerbenw
Copy link
Member

Hi @evelant! Apologies for the slow response, I needed to do some homework on this one.

nohoist works when using the node modules plugin which is required when using expo with yarn 2 and above.

we're looking to automate adding this nohoist option in our CLI shortly.

@evelant
Copy link

evelant commented Sep 14, 2022

@gingerbenw Unfortunately it's not that simple.

IIRC when I went though this with yarn (it is confusing!) I found out that won't do it either. I'm pretty sure nohoist does not work with when using nodeLinker: node-modules. There is no way to do per-package hoisting in yarn v2+.

You first have to add nodeLinker: node-modules which turns off "plug n' play" mode to give you a node_modules folder again. Then if you add nohoist to your package.json it will not do anything as it's only supported in yarn v1. In v2+ the only option is install config hoisting limits.

Unfortunately with installConfig: { hoistingLimits: "" } the only options are none (hoist everything) dependencies (hoist everything except direct dependencies) and workspaces (don't hoist anything inside a workspace, treat it as if it were not a workspace).

I have no idea why the yarn team decided to remove per-package hoisting control. It makes things significantly more difficult.

Personally I've moved on to https://pnpm.io as it's far more consistent, fast, configurable, and reliable. Pnpm works well with react-native if you use @rnx-kit/metro-resolver-symlinks to work around metro's 5+ years unresolved lack of symlink support 🤦

@gingerbenw
Copy link
Member

Thanks for the useful breakdown @evelant! I think I had misunderstood what was going on in my local testing as I had hoistingLimits: "workspace" set as well as using nohoist.

I wonder whether the best thing to do would be to symlink the necessary dependencies into node_modules in a similar fashion to the expo-yarn-workspaces plugin... 🤔

@evelant
Copy link

evelant commented Sep 14, 2022

@gingerbenw I think the way a lot of packages and gradle scripts are doing it now is to use node to resolve the path, such as node --print require.resolve('react-native/package.json') although that assumes node is available on $PATH. That has the benefit of (mostly, looking at you metro) resolving files in the same way all the other tools in the stack are doing it.

@gingerbenw
Copy link
Member

After some consideration we’re going to include installConfig.hoistingLimits on our CLI and documentation to get this functional for yarn 2+ in the short term and I will include making our source map upload compatible with hoisting for consideration on our roadmap.

@johnkiely1
Copy link
Member

Just to let you know we have released the short term changes mentioned above as of v45.1.2 (for Expo 45) and v46.0.1 (for Expo 46) and updated the documentation to match https://docs.bugsnag.com/platforms/react-native/expo/manual-setup/#eas-build

@johnkiely1 johnkiely1 added feature request Request for a new feature released This feature/bug fix has been released and removed awaiting feedback Awaiting a response from a customer. Will be automatically closed after approximately 2 weeks. backlog We hope to fix this feature/bug in the future labels Oct 21, 2022
@kennethlynne
Copy link
Contributor

This is still a problem for us. We're using PNPM in a monorepo with a managed expo app using SDK 49, I have not been able to get this to work yet

@johnkiely1
Copy link
Member

Hi @kennethlynne,

PNPM is not something we are particularly familiar with as we don't directly support it. As a suggestion have you tried modifying hoist-pattern or public-hoist-pattern config settings? https://pnpm.io/npmrc#hoist-pattern.

This looks to be somewhat similar to Yarn's nohoist option to avoid node_modules being hoisted to the top-level of the monorepo so that it can find the bugsnag-expo-xcode.sh script.

@johnkiely1 johnkiely1 added the awaiting feedback Awaiting a response from a customer. Will be automatically closed after approximately 2 weeks. label Aug 9, 2023
@ricardoribas
Copy link

ricardoribas commented Aug 29, 2023

Hey guys, I was able to make it work on the project i am working on. In order to make it work without nohoist, I had to make a few changes:

  1. Add a custom expo plugin replicating the original dependency
  2. Create a file with the following contents
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

SOURCE_MAP="$TMPDIR/$(md5 -qs "$CONFIGURATION_BUILD_DIR")-main.jsbundle.map" `$NODE_BINARY --print "require('path').dirname(require.resolve('@app/my-package/package.json')) + '/lib/bugsnag-expo-xcode.sh'"`
  1. Create a bugsnag-expo-xcode.sh inside the package lib folder. This will have the same contents as @bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh, except the call to the bugsnag-source-maps to upload a react native, which instead will call to a local script
$NODE_BINARY ../lib/bugsnag-expo-node upload-react-native "${ARGS[@]}"

The node executable is attached on the build phase script
5. This new nodeJS script will not use relative paths. Instead it will:

#!/usr/bin/env node

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { dirname, join } = require('path');

// eslint-disable-next-line @typescript-eslint/no-var-requires
require(join(
  dirname(require.resolve('@bugsnag/source-maps/package.json')),
  'dist/bin/cli.js'
)).default(process.argv);

I created two PRs to address this issue under @bugsnag/source-maps and @bugsnag/expo

@GSTJ
Copy link

GSTJ commented May 19, 2024

I'm using this patch based on the open PR, which stands stale.

It's a little long as prettier also did style changes automatically - though, it does the trick.

diff --git a/lib/bugsnag-expo-xcode.sh b/lib/bugsnag-expo-xcode.sh
index 3345711939266156933d19c58cf1c3082b5b3e06..af1ef16f56fe590129b8807a5831c033b68190ea 100755
--- a/lib/bugsnag-expo-xcode.sh
+++ b/lib/bugsnag-expo-xcode.sh
@@ -60,4 +60,11 @@ if [ ! -z "$ENDPOINT" ]; then
   ARGS+=("$ENDPOINT")
 fi
 
-../node_modules/.bin/bugsnag-source-maps upload-react-native "${ARGS[@]}"
+# Make sure that node binary exists
+source `$NODE_BINARY --print "require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/node-binary.sh'"`
+
+# Retrieve the expo node binary
+BUGSNAG_EXPO_NODE_BINARY=$($NODE_BINARY --print "require('path').dirname(require.resolve('@bugsnag/source-maps/bin/cli.js'))")
+
+# Start upload the source maps to bugsnag
+$BUGSNAG_EXPO_NODE_BINARY upload-react-native "${ARGS[@]}"
diff --git a/src/ios.js b/src/ios.js
index 5150364c16229665014fa26d4c298558d1ef26ad..659133f8a6a8874444841cc9185956bf0df90655 100644
--- a/src/ios.js
+++ b/src/ios.js
@@ -1,54 +1,73 @@
-const {
-  withInfoPlist,
-  withXcodeProject
-} = require('@expo/config-plugins')
+const { withInfoPlist, withXcodeProject } = require("@expo/config-plugins");
 
-const buildPhaseName = 'PBXShellScriptBuildPhase'
-const buildPhaseComment = 'Bundle React Native code and images'
+const buildPhaseName = "PBXShellScriptBuildPhase";
+const buildPhaseComment = "Bundle React Native code and images";
 
-function withIosPlugin (config, onPremConfig) {
+function withIosPlugin(config, onPremConfig) {
   // 01. Update InfoPlist with apiKey
-  config = withInfoPlist(config, config => {
-    const apiKey = config?.extra?.bugsnag?.apiKey
+  config = withInfoPlist(config, (config) => {
+    const apiKey = config?.extra?.bugsnag?.apiKey;
     config.modResults.bugsnag = {
-      apiKey: apiKey
-    }
-    return config
-  })
+      apiKey: apiKey,
+    };
+    return config;
+  });
 
-  config = withXcodeProject(config, config => {
-    const xcodeProject = config.modResults
+  config = withXcodeProject(config, (config) => {
+    const xcodeProject = config.modResults;
 
     // 02. Update react native bundle phase with sourcemap filepath
-    const bundleReactNativePhase = xcodeProject.pbxItemByComment(buildPhaseComment, buildPhaseName)
+    const bundleReactNativePhase = xcodeProject.pbxItemByComment(
+      buildPhaseComment,
+      buildPhaseName
+    );
 
-    const initialScript = bundleReactNativePhase.shellScript
+    const initialScript = bundleReactNativePhase.shellScript;
 
-    const additionalExports = '"export EXTRA_PACKAGER_ARGS=\\"--sourcemap-output $TMPDIR/$(md5 -qs \\"$CONFIGURATION_BUILD_DIR\\")-main.jsbundle.map\\"\\n'
+    const additionalExports =
+      '"export EXTRA_PACKAGER_ARGS=\\"--sourcemap-output $TMPDIR/$(md5 -qs \\"$CONFIGURATION_BUILD_DIR\\")-main.jsbundle.map\\"\\n';
 
     if (initialScript.indexOf(additionalExports) < 0) {
-      const modifiedScript = additionalExports + initialScript.substr(1)
-      bundleReactNativePhase.shellScript = modifiedScript
+      const modifiedScript = additionalExports + initialScript.substr(1);
+      bundleReactNativePhase.shellScript = modifiedScript;
     }
 
     // 03. Configure the new build phase
-    const uploadBuildPhaseComment = 'Upload source maps to Bugsnag'
+    const uploadBuildPhaseComment = "Upload source maps to Bugsnag";
 
-    const uploadBuildPhase = xcodeProject.pbxItemByComment(uploadBuildPhaseComment, buildPhaseName)
+    const uploadBuildPhase = xcodeProject.pbxItemByComment(
+      uploadBuildPhaseComment,
+      buildPhaseName
+    );
 
     if (!uploadBuildPhase) {
-      const shellScript = 'SOURCE_MAP="$TMPDIR/$(md5 -qs "$CONFIGURATION_BUILD_DIR")-main.jsbundle.map" ../node_modules/@bugsnag/plugin-expo-eas-sourcemaps/lib/bugsnag-expo-xcode.sh'
+      const shellScript = `
+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
+
+SOURCE_MAP="$TMPDIR/$(md5 -qs "$CONFIGURATION_BUILD_DIR")-main.jsbundle.map" \`$NODE_BINARY --print "require('path').dirname(require.resolve('@bugsnag/plugin-expo-eas-sourcemaps/package.json')) + '/lib/bugsnag-expo-xcode.sh'"\`
+      `;
 
-      xcodeProject.addBuildPhase([], buildPhaseName, uploadBuildPhaseComment, null, {
-        shellPath: '/bin/sh',
-        shellScript: shellScript
-      })
+      xcodeProject.addBuildPhase(
+        [],
+        buildPhaseName,
+        uploadBuildPhaseComment,
+        null,
+        {
+          shellPath: "/bin/sh",
+          shellScript: shellScript,
+        }
+      );
     }
 
-    return config
-  })
+    return config;
+  });
 
-  return config
+  return config;
 }
 
-module.exports = { withIosPlugin }
+module.exports = { withIosPlugin };
``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting feedback Awaiting a response from a customer. Will be automatically closed after approximately 2 weeks. bug Confirmed bug feature request Request for a new feature released This feature/bug fix has been released
Projects
None yet
Development

No branches or pull requests

9 participants