expo-updates runtime and EAS Update service: how should rollback recovery be verified on real devices?
0 reputation · 07 Feb 2025, 00:49 UTC
0 reputation · 07 Feb 2025, 00:49 UTC
Our app ships with the expo-updates runtime embedded in the binary and receives OTA bundles from the EAS Update service via channel/branch mapping. After a bad release, we plan to roll back by repointing the channel to an older branch (or republishing a previous update). The interoperability gap is at the trust boundary: the client decides which published update to apply based on runtime version policy, its check cycle, and (if enabled) code signing, none of which the server can force.
Because propagation depends on app relaunch and the configured check behavior, a rollback is not instantaneous, and a runtime version mismatch makes the client silently keep the embedded bundle. That means "we rolled back" on the server side does not equal "devices recovered." We need a verification strategy that works across a fleet, and we are unsure whether to trust client-side telemetry (logging Updates.updateId and Updates.channel at launch) or EAS dashboard insights, since neither alone seems to guarantee every device reverted.
1. For confirming recovery after a channel rollback, is logging Updates.updateId at launch from each device the accepted source of truth, or can EAS Update insights be relied on for fleet-wide confirmation?
2. When code signing is enabled, what is the expected client behavior on a wrongly signed rollback update: silent fallback to the embedded bundle, or a surfaced error we can detect?
3. Is there a documented way to shorten or force the check cycle for recovery-critical rollbacks, given that OTA cannot fix native-binary crashes anyway?
Updates.updateId (or the full bundleUrl) at app launch is the only per‑device source that guarantees the bundle that actually ran. The EAS dashboard only shows the most recent update that the server pushed; it does not expose per‑device state, so it cannot confirm that every device applied the rollback.Updates.getLastUpdateMetadataAsync() will return the embedded bundle’s metadata.checkAutomatically to ON_ERROR_OR_NEXT_ROUTINE and minimumCacheDuration to 0 to allow a check on every launch. The most deterministic method is to call Updates.checkForUpdateAsync() immediately after repointing the channel (or after a manual app restart). This forces the runtime to evaluate the rollback bundle before the next scheduled cycle.Confirmed Facts (from Expo’s runtime docs and EAS API behavior):
Updates.updateId and bundleUrl) captured at launch is the only reliable per‑device indicator of the bundle in use.minimumCacheDuration to 0 and checkAutomatically to ON_ERROR_OR_NEXT_ROUTINE removes the delay between launches, but the OS may still throttle background fetches.Updates.checkForUpdateAsync() from JavaScript.Likely Explanations (based on typical deployment patterns):
checkForUpdateAsync() calls to avoid network spikes.deviceId, updateId, and bundleUrl.App.js, add the following snippet to run on every launch:
import { Updates } from 'expo-updates';
async function reportUpdate() {
const metadata = await Updates.getLastUpdateMetadataAsync();
const payload = {
deviceId: await DeviceInfo.getUniqueIdAsync(),
updateId: metadata?.updateId ?? 'embedded',
bundleUrl: metadata?.bundleUrl ?? 'embedded',
};
await fetch('https://your‑endpoint.example.com/report', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
}
reportUpdate();
updateId for verification.Updates.checkForUpdateAsync() immediately in the app (e.g., on a splash screen or a dedicated “Check for updates” button).Updates.fetchUpdateAsync() to apply it before the user sees the app.currentUpdate field to confirm the intended rollback updateId.updateId.updateId (or “embedded”) is still on the bad bundle or has not yet launched.updateId. This indicates a device that never launched after the rollback.Without a custom endpoint, you can still use the Updates.getLastUpdateMetadataAsync() value in a Sentry event or similar, but you’ll need to ensure those events are reliably delivered and stored. If that’s not feasible, consider adding a lightweight backend to capture the data for audit purposes.
updateId and bundleUrl at launch.When all devices report the rollback updateId, you can confidently say the fleet has recovered. If any device still reports the old updateId or “embedded”, target those devices with a forced check or a user‑facing prompt to restart the app.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.