xref: /expo/docs/pages/eas-update/debug.mdx (revision 6f07fc9e)
1---
2title: Debug EAS Update
3sidebar_title: Debug
4description: Learn how to debug EAS Update.
5---
6
7import ImageSpotlight from '~/components/plugins/ImageSpotlight';
8import { Terminal } from '~/ui/components/Snippet';
9
10It's important to tell the current state of our app at any given time. We built EAS Update with this in mind. Once we know which updates are running on which builds, we can make changes so that our apps are in the state we expect and desire. This guide sets out to show how we can verify our EAS Update and expo-updates configuration so that we can find the source of problems like an app not showing a published update.
11
12## expo-updates configuration
13
14The expo-updates library runs inside an end-user's app and makes requests to an update server to get the latest update.
15
16### Verifying app configuration
17
18When we set up EAS Update, we likely ran `eas update:configure` to configure expo-updates to work with EAS Update. This command makes changes to our app config (**app.json**/**app.config.js**). Here are the fields we'd expect to see:
19
20- `runtimeVersion` should be set. By default, it is `{ "policy": "sdkVersion" }`. If our project has **android** and **ios** directories, we'll have to set the `runtimeVersion` manually.
21- `updates.url` should be a value like `https://u.expo.dev/your-project-id`, where `your-project-id` matches the ID of our project. We can see this ID on [our website](https://expo.dev/accounts/[account]/projects/[project]).
22- `updates.enabled` should not be `false`. It's `true` by default if it is not specified.
23
24Finally, make sure that `expo-updates` is included in **package.json**. If it's not, run:
25
26<Terminal cmd={['$ npx expo install expo-updates']} />
27
28### Inspecting expo-updates configuration after prebuild
29
30Whenever we run `eas build`, the `npx expo prebuild` command is run on our project on EAS' servers to unpack the **android** and **ios** directories that contain native files. This makes it so EAS Build can build any project, whether it includes the native files or not.
31
32If our project does not have **android** or **ios** directories, we can make commit any existing changes, then run `npx expo prebuild` to inspect the project state that EAS Build will act on. After running this, look for the following files: **android/app/src/main/AndroidManifest.xml** and **ios/your-project-name/Supporting/Expo.plist**.
33
34In each, we expect to see configuration for the EAS Update URL and the runtime version. Here are the properties we'd expect to see in each file:
35
36**AndroidManifest.xml**
37
38```xml
39...
40<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="your-runtime-version-here"/>
41<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://u.expo.dev/your-project-id-here"/>
42...
43```
44
45**Expo.plist**
46
47```xml
48...
49<key>EXUpdatesRuntimeVersion</key>
50<string>your-runtime-version-here</string>
51<key>EXUpdatesURL</key>
52<string>https://u.expo.dev/your-project-id-here</string>
53...
54```
55
56## EAS Update configuration
57
58To debug the state of EAS Update in our project, we'll need to look at multiple spots in the system. Below is a diagram of how EAS Update works and the spots that are useful to inspect when finding the root cause of an issue. In the sections following, we'll inspect and verify these spots and more.
59
60<ImageSpotlight alt="Map of debugging spots" src="/static/images/eas-update/debug-map.png" />
61
62### 1. Verify a channel
63
64Builds have a property named `channel`, which EAS Update uses to link to a branch. A channel is often given to multiple platform-specific builds. For instance, we might have an Android build and an iOS build, both with a channel named `"production"`.
65
66#### Configuring channels
67
68To verify that a build has a specific channel, make sure that in **eas.json**, there is a channel property:
69
70```json eas.json
71{
72  "build": {
73    "preview": {
74      "distribution": "internal",
75      "channel": "preview"
76    },
77    "production": {
78      "channel": "production"
79    }
80  }
81}
82```
83
84Then, we can run a command like `eas build --profile preview` to create a build with a channel named "preview".
85
86#### Inspecting channels on EAS
87
88Once a build has a channel name, we can make sure that EAS' servers know about the channel by running the following commands:
89
90<Terminal cmd={['$ eas channel:list']} />
91
92or
93
94<Terminal
95  cmd={['# eas channel:view [channel-name]', '', '', '# Example', '$ eas channel:view production']}
96/>
97
98We'd expect the output of these commands to display the same channel name that our build has. If it's not there, we can create the channel on EAS' servers with:
99
100<Terminal
101  cmd={[
102    '# eas channel:create [channel-name]',
103    '',
104    '',
105    '# Example',
106    'eas channel:create production',
107  ]}
108/>
109
110### 2. Verify the channel/branch mapping
111
112There is a link that is defined by the developer between a channel and a branch. When a channel and branch are linked, an app with a channel will get the most recent compatible update on the linked branch.
113
114To verify which branch is linked to a channel, we can run:
115
116<Terminal
117  cmd={['# eas channel:view [channel-name]', '', '', '# Example', 'eas channel:view production']}
118/>
119
120If the channel is not linked to the branch we expect, we can change the link with:
121
122<Terminal
123  cmd={[
124    '# eas channel:edit [channel-name] --branch [branch-name]',
125    '',
126    '',
127    '# Example',
128    'eas channel:edit production --branch release-1.0',
129  ]}
130/>
131
132### 3. Verify the update
133
134Every branch contains a list of updates. When a build makes a call for an update, we find the channel of the build, then the branch linked to that channel. Once the branch is found, EAS will return the most recent compatible update on that branch. A build and an update are compatible when they share the same runtime version and platform.
135
136To inspect which updates are on a branch, we can run:
137
138<Terminal
139  cmd={['# eas branch:view [branch-name]', '', '', '# Example', 'eas branch:view production']}
140/>
141
142The output of this command will show us a list of updates and their runtime versions and platforms. From this list, we should be able to figure out which update should apply to a given build, by matching the build's runtime version and platform to update's runtime version and platform. The most recent update that is compatible will be available for a build to download and execute.
143
144### Verify the update command
145
146To create and publish an update, we can run the following command:
147
148<Terminal cmd={['$ eas update']} />
149
150After publishing, the output will display the branch and the runtime version. This info can help us verify that we're creating an update with the configuration we expect.
151
152### Viewing deployments
153
154If we've made builds and updates with EAS, we can view the state of our project on our website on our project's deployments tab. We use the term _deployments_ to refer to the entire system of builds and their updates. The system includes builds, channels, branches, updates, runtime versions, and platforms.
155
156The EAS website has a page that shows the current state of our apps. We can view it at [https://expo.dev/accounts/[account]/projects/[project]/deployments](https://expo.dev/accounts/[account]/projects/[project]/deployments).
157
158## Debugging EAS Update
159
160After verifying `expo-updates` and EAS Update configurations, we can move on to debugging how our project is interacting with updates.
161
162### In-app debugging
163
164The `expo-updates` library exports a variety of functions to interact with updates once the app is already running. In certain cases, making a call to fetch an update and seeing an error message can help us narrow down the root cause. We can make a simulator build of the project and manually check to see if updates are available or if there are errors when fetching updates. See the code example to [check for updates manually](/versions/latest/sdk/updates/#use-expo-updates-with-a-custom-server).
165
166### Viewing network requests
167
168Another way to identify the root cause of an issue is to look at the network requests that the app is making to EAS servers, then viewing the responses. We recommend using a program like [Proxyman](https://proxyman.io/) or [Charles Proxy](https://www.charlesproxy.com/) to watch network requests from our app.
169
170With either program, we'll need to follow their instructions for installing an SSL certificate, so that the program can decode HTTPS requests. Once that's set up in a simulator or on an actual device, we can open our app and watch requests.
171
172The requests we're interested in are from https://u.expo.dev and https://assets.eascdn.net. Responses from https://u.expo.dev will contain an update manifest, which specifies which assets the app will need to fetch to run the update. Responses from https://assets.eascdn.net will contain assets, like images, font files, etc that are required for the update to run.
173
174When inspecting the request to https://u.expo.dev, we can look for the following request headers:
175
176- `Expo-Runtime-Version`: this should make the runtime version we made our build and update with.
177- `expo-channel-name`: this should be the channel name specified in the **eas.json** build profile.
178- `Expo-Platform`: this should be either "android" or "ios".
179
180As for all requests, we expect to see either `200` response codes, or `304` if nothing has changed.
181
182Below is a screenshot showing the request of a successful update manifest request:
183
184<ImageSpotlight
185  alt="Successful manifest request"
186  src="/static/images/eas-update/network-request.png"
187/>
188
189### Inspecting a build manually
190
191When building a project into an app, there can be multiple steps that alter the output of `npx expo prebuild`. After making a build, it is possible to open the build's contents and inspect native files to see its final configuration.
192
193Here are the steps for inspecting an iOS Simulator build on macOS:
194
1951. Create an iOS Simulator build of the app using EAS Build. This is done by adding `"ios": { "simulator": true }` to a build profile.
1962. Once the build is finished, download the result and unzip it.
1973. Then, right click on the app and select "Show Package Contents".
1984. From there, we can inspect the **Expo.plist** file.
199
200Inside the **Expo.plist** file, we expect to see the following configurations:
201
202```xml
203...
204<key>EXUpdatesRequestHeaders</key>
205<dict>
206  <key>expo-channel-name</key>
207  <string>your-channel-name</string>
208</dict>
209<key>EXUpdatesRuntimeVersion</key>
210<string>your-runtime-version</string>
211<key>EXUpdatesURL</key>
212<string>https://u.expo.dev/your-project-id</string>
213...
214```
215
216### Inspecting the latest update locally
217
218When we publish an update with EAS Update, it creates a **/dist** folder in the root of our project locally, which includes the assets that were uploaded as a part of the update.
219
220<ImageSpotlight alt="Dist directory" src="/static/images/eas-update/dist.png" />
221
222### Inspecting manifests manually
223
224When an update is published with EAS Update, we create a manifest that end-user app's request. The manifest has information like which assets and versions are needed for an update to load. We can inspect the manifest by going to a specific URL in a browser or by using `curl`.
225
226Inside our project's app config (**app.json**/**app.config.json**), the URL we can GET is under `updates.url`.
227
228This `url` is EAS' "https://u.expo.dev" domain, followed by the project's ID on EAS' servers. If we go to the URL directly, we'll see an error about missing a header. We can view a manifest by adding three query parameters to the URL: `runtime-version`, `channel-name`, and `platform`. If we published an update with a runtime version of `1.0.0`, a channel of `production` and a platform of `android`, the full URL you could visit would be similar to this:
229
230```
231https://u.expo.dev/your-project-id?runtime-version=1.0.0&channel-name=production&platform=android
232```
233
234### Viewing all assets included in an update
235
236It may be helpful to see which assets are included in our update bundle. We can see a list of named assets by running:
237
238<Terminal cmd={['$ npx expo export']} />
239
240### Debugging of native code while loading the app through expo-updates
241
242By default, we need to make a release build for `expo-updates` to be enabled and to load updates rather than reading from a development server. This is because debug builds behave like normal React Native project debug builds.
243
244To make it easier to test and debug native code in an environment that is closer to production, follow the steps below to create a debug build of the app with `expo-updates` enabled.
245
246We also provide a [step-by-step guide to try out EAS Update quickly](/eas-update/build-locally) in a local development environment using Android Studio or Xcode, with either release or debug builds of the app.
247
248#### iOS local builds
249
250- Set the debug environment variable: `export EX_UPDATES_NATIVE_DEBUG=1`
251- Reinstall pods with `npx pod-install`. The `expo-updates` podspec now detects this environment variable, and makes changes so that the debug code that would normally load from the Metro packager is bypassed, and the app is built with the EXUpdates bundle and other dependencies needed to load updates from EAS.
252- [Ensure the desired channel is set in your **Expo.plist**](/bare/updating-your-app/#configuring-the-channel-manually)
253- Modify the application Xcode project file to force bundling of the application JavaScript for both release and debug builds:
254
255```
256sed -i '' 's/SKIP_BUNDLING/FORCE_BUNDLING/g;' ios/&lt;project name&gt;.xcodeproj/project.pbxproj
257```
258
259- Execute a [debug build](/debugging/runtime-issues/#native-debugging) of the app with Xcode or from the command line.
260
261#### Android local builds
262
263- Set the debug environment variable: `export EX_UPDATES_NATIVE_DEBUG=1`
264- [Ensure the desired channel is set in your **AndroidManifest.xml**](/bare/updating-your-app/#configuring-the-channel-manually)
265- Execute a [debug build](/debugging/runtime-issues/#native-debugging) of the app with Android Studio or from the command line.
266
267#### EAS Build
268
269Alternatively, we can use EAS to create a debug build where `expo-updates` is enabled. The environment variable is set in **eas.json**, as shown in the example below:
270
271```json eas.json
272{
273  "build": {
274    "preview_debug": {
275      "env": {
276        "EX_UPDATES_NATIVE_DEBUG": "1"
277      },
278      "android": {
279        "distribution": "internal",
280        "withoutCredentials": true,
281        "gradleCommand": ":app:assembleDebug"
282      },
283      "ios": {
284        "simulator": true,
285        "buildConfiguration": "Debug"
286      },
287      "channel": "preview_debug"
288    }
289  }
290}
291```
292
293## Mitigation steps
294
295Once we've found the root cause of the issue, there are various mitigation steps we might want to take. One of the most common problems is pushing an update that has a bug inside it. When this happens, we can re-publish a previous update to resolve the issue.
296
297### Re-publishing a previous update
298
299The fastest way to "undo" a bad publish is to re-publish a known good update. Imagine we have a branch with two updates:
300
301```bash
302branch: "production"
303updates: [
304  update 2 (id: xyz2) "fixes typo"     // bad update
305  update 1 (id: abc1) "updates color"  // good update
306]
307```
308
309If "update 2" turned out to be a bad update, we can re-publish "update 1" with a command like this:
310
311<Terminal
312  cmd={[
313    '# eas update:republish --group [update-group-id]',
314    '',
315    '# eas update:republish --branch [branch-name]',
316    '',
317    '',
318    '# Example',
319    '$ eas update:republish --group abc1',
320    '$ eas update:republish --branch production',
321  ]}
322/>
323
324The example command above would result in a branch that now appears like this:
325
326```bash
327branch: "production"
328updates: [
329  update 3 (id: def3) "updates color"  // re-publish of update 1 (id: abc1)
330  update 2 (id: xyz2) "fixes typo"     // bad update
331  update 1 (id: abc1) "updates color"  // good update
332]
333```
334
335Since "update 3" is now the most recent update on the "production" branch, all users who query for an update in the future will receive "update 3" instead of the bad update, "update 2".
336
337While this will prevent all new users from seeing the bad update, users who've already received the bad update will run it until they can download the latest update. Since mobile networks are not always able to download the most recent update, sometimes users may run a bad update for a long time. When viewing error logs for our app, it's normal to see a lingering long tail of errors as our users' apps get the most recent update or build. We'll know we solved the bug when we see the error rate decline dramatically; however, it likely will not disappear completely if we have a diverse user base across many locations and mobile networks.
338
339## Wrap up
340
341Still having issues with EAS Update? Provide us with a reproduction repo in our [forums](https://forums.expo.dev/c/expo-application-services/56). Also, feel free to ask in the **#update** channel on our [community Discord](https://chat.expo.dev/), or [contact us](https://expo.dev/contact) directly.
342