expo / turtle

Standalone app builder service
MIT License
385 stars 29 forks source link

Why does Turtle push assets to the Cloudfront CDN when building locally? #302

Closed charles-m-knox closed 3 years ago

charles-m-knox commented 3 years ago

Question Checklist

Question Subject

My goal: Package an IPA file for distribution with the App Store. My assets directory contains 3 60 MB files, which are getting rejected by both Cloudfront and Expo since the assets are too big.

So, I decided to try and build it locally using Turtle. I have successfully followed the steps outlined in the Expo docs here: https://docs.expo.io/distribution/hosting-your-app/

My question: Turtle is also failing to build, because for some reason, it's trying to push all of the files to the Expo CDN (which is Cloudfront). Why is this happening, and how can it be disabled, especially given that I'm hosting all of the files that Turtle should need for its build locally?

Question Description

Digging into the Expo source code, we can see the code in Turtle where it makes a call to IosShellApp. configureAndCopyArchiveAsync:

https://github.com/expo/turtle/blob/d2eb447ebcbc4e2303659e93bb45feb614ec1d40/src/builders/utils/ios/shellAppBuilder.ts#L54

The Expo CDN is confirmed by doing some digging:

const EXPO_CDN = 'https://d1wp6m56sqw74a.cloudfront.net';

https://github.com/expo/xdl/blob/6f2eec2252ff2c35ddb58dbd89ee4c4662907690/packages/xdl/src/ProjectAssets.ts#L21

Setup

I am running Mac OS Big Sur, with a completely fresh setup and everything up to date as of late last week. Expo CLI version 4.3.2, Turtle version 0.20.7, Expo version 40.0.0.

I start by exporting my project:

expo export --dev --public-url http://localhost:12001 --force

I'm running Nginx locally:

CONTAINER_NAME=nginx-expo-local
docker rm -f "${CONTAINER_NAME}" || true
docker run \
    -it \
    -d \
    --name "${CONTAINER_NAME}" \
    --restart unless-stopped \
    -v $(pwd)/dist:/usr/share/nginx/html/ \
    -v $(pwd)/nginx-default.conf:/etc/nginx/conf.d/default.conf \
    -p "127.0.0.1:12001:80" \
    nginx:alpine

Note: The nginx-default.conf is a stock nginx.conf with a simple autoindex on; flag added to the root location to fix 403 forbidden errors

I then run the following to build for a simulator: (I also have this working for archive, let's ignore those flags for this discussion):

EXPO_IOS_DIST_P12_PASSWORD=${EXPO_PASSWORD} \
    turtle build:ios \
    --public-url http://localhost:12001/ios-index.json \
    --allow-non-https-public-url \
    -t simulator

Extra log output

Here is the truncated stack trace from the Turtle build command, which shows

Mar 28 19:50:14 turtle[2511] INFO:  Modifying NSBundle configuration at /private/tmp/turtle/8d74ad14-e54c-44e8-9823-a458451e2993/archive/Release/ExpoKitApp.app...
  platform: "ios"
  buildPhase: "configuring NSBundle"
Mar 28 19:50:14 turtle[2511] INFO:  Using standalone config: {
  isShell: true,
  manifestUrl: 'http://localhost:12001/ios-index.json',
  releaseChannel: 'default',
  testEnvironment: 'none',
  updatesFallbackToCacheTimeout: 0
}
  platform: "ios"
  buildPhase: "configuring NSBundle"
Mar 28 19:50:14 turtle[2511] INFO:  Configuring iOS Launch Screen...
  platform: "ios"
  buildPhase: "configuring NSBundle"
Mar 28 19:50:15 turtle[2511] INFO:  Bundling assets...
  platform: "ios"
  buildPhase: "configuring NSBundle"

...

https://d1wp6m56sqw74a.cloudfront.net/~assets/baaa18930b970fb55f296faf5d06078d
https://d1wp6m56sqw74a.cloudfront.net/~assets/84a93218c2680bec41f68ba56a8a0005
https://d1wp6m56sqw74a.cloudfront.net/~assets/ef3001877969374cf649bc2c80894ef1
Error: Asset bundling failed: Error: Request failed with status code 403
    at Object.configureAsync (/@expo/xdl@59.0.17/src/detach/IosNSBundle.ts:611:15)
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
    at Object.configureAndCopyArchiveAsync (/@expo/xdl@59.0.17/src/detach/IosShellApp.js:251:3)
    at Object.runShellAppBuilder [as default] (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/utils/ios/shellAppBuilder.ts:54:10)
    at Object.buildSimulator [as default] (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/ios/simulator.ts:9:3)
    at iosBuilder (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/ios/index.ts:35:7)
    at /opt/homebrew/lib/node_modules/turtle-cli/src/bin/utils/builder.ts:87:7
    at Command.<anonymous> (/opt/homebrew/lib/node_modules/turtle-cli/src/bin/index.ts:23:12)
Mar 28 19:50:24 turtle[2511] INFO:  Cleaning up iOS...
  platform: "ios"
  buildPhase: "configuring NSBundle"
Mar 28 19:50:24 turtle[2511] ERROR: Error: Asset bundling failed: Error: Request failed with status code 403
    at Object.configureAsync (/@expo/xdl@59.0.17/src/detach/IosNSBundle.ts:611:15)
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
    at Object.configureAndCopyArchiveAsync (/@expo/xdl@59.0.17/src/detach/IosShellApp.js:251:3)
    at Object.runShellAppBuilder [as default] (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/utils/ios/shellAppBuilder.ts:54:10)
    at Object.buildSimulator [as default] (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/ios/simulator.ts:9:3)
    at iosBuilder (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/ios/index.ts:35:7)
    at /opt/homebrew/lib/node_modules/turtle-cli/src/bin/utils/builder.ts:87:7
    at Command.<anonymous> (/opt/homebrew/lib/node_modules/turtle-cli/src/bin/index.ts:23:12)
  platform: "ios"
Mar 28 19:50:24 turtle[2511] ERROR: Failed to build standalone app
  err: Error: Asset bundling failed: Error: Request failed with status code 403
      at Object.configureAsync (/@expo/xdl@59.0.17/src/detach/IosNSBundle.ts:611:15)
      at processTicksAndRejections (node:internal/process/task_queues:94:5)
      at Object.configureAndCopyArchiveAsync (/@expo/xdl@59.0.17/src/detach/IosShellApp.js:251:3)
      at Object.runShellAppBuilder [as default] (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/utils/ios/shellAppBuilder.ts:54:10)
      at Object.buildSimulator [as default] (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/ios/simulator.ts:9:3)
      at iosBuilder (/opt/homebrew/lib/node_modules/turtle-cli/src/builders/ios/index.ts:35:7)
      at /opt/homebrew/lib/node_modules/turtle-cli/src/bin/utils/builder.ts:87:7
      at Command.<anonymous> (/opt/homebrew/lib/node_modules/turtle-cli/src/bin/index.ts:23:12)
  platform: "ios"

Thank you for the help! I hope I have provided sufficient information for answering the question -- which, to reiterate, is basically... what's causing Turtle to push everything to a CDN even though I'm hosting the manifest, assets, and bundle files locally via an Nginx server?

wkozyra95 commented 3 years ago

It seems to be an issue with localhost urls there, cli is trying to download assets from cdn instead of the localhost.

--allow-non-https-public-url is a bit of an experimental option, that was added by an external contributor, but it's not sth we want to officially support. You can get a public https URL for your local server with e.g. ngrok

charles-m-knox commented 3 years ago

Hi @wkozyra95 , thank you SO much for responding. This was very helpful, and it appears to have worked. It looks like I'll need to figure out a TLS solution aside from ngrok though, because I'm getting HTTP 429 (which is expected given ngrok's rate limits for free users). I'll keep grinding away at this one with my own TLS solution (maybe Caddy or something) and will post an update here for other readers.

charles-m-knox commented 3 years ago

After grinding through it and finally succeeding, I'll share my results.

I ended up using one of my domains, e.g. turtlebuilder.domain.com, to do a DNS acme challenge with Lets Encrypt+Certbot, and then ran a file server (using the TLS certs) via Caddy on my system to host all the files.

The last networking trick was to set my local DNS for turtlebuilder.domain.com to 192.168.x.x, which was the IP of my Mac.

The other key point was to remove the --dev flag from the expo export --public-url https://turtlebuilder.domain.com command. When the --dev flag is enabled, even when deployed through TestFlight, the app will try to pull down all of the assets from the configured URL in the iOS/Expo manifest JSON file. Once I fixed this, it no longer tried to pull down assets, and things worked more perfectly.

Here's the full guide: https://github.com/charles-m-knox/space-codes#building-locally

The other issues I encountered were basically the standard certificate management headaches that come with Xcode/Apple Developer/provisioning profiles.

vladev commented 2 years ago

An update - replacing localhost with 127.0.0.1 in both export and build commands fixed it.