expo-updates 29.0.8 → 29.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 29.0.10 — 2025-09-11
14
+
15
+ ### 💡 Others
16
+
17
+ - Surface iOS compilation errors in updates E2E tests. ([#39542](https://github.com/expo/expo/pull/39542) by [@douglowder](https://github.com/douglowder))
18
+
19
+ ## 29.0.9 — 2025-09-10
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - [Android] Propagate controller scope to state machine. ([#39526](https://github.com/expo/expo/pull/39526) by [@alanjhughes](https://github.com/alanjhughes))
24
+
13
25
  ## 29.0.8 — 2025-09-02
14
26
 
15
27
  _This version does not introduce any user-facing changes._
@@ -42,7 +42,7 @@ expoModule {
42
42
  }
43
43
 
44
44
  group = 'host.exp.exponent'
45
- version = '29.0.8'
45
+ version = '29.0.10'
46
46
 
47
47
  // Utility method to derive boolean values from the environment or from Java properties,
48
48
  // and return them as strings to be used in BuildConfig fields
@@ -88,7 +88,7 @@ android {
88
88
  namespace "expo.modules.updates"
89
89
  defaultConfig {
90
90
  versionCode 31
91
- versionName '29.0.8'
91
+ versionName '29.0.10'
92
92
  consumerProguardFiles("proguard-rules.pro")
93
93
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
94
94
 
@@ -19,6 +19,7 @@ import expo.modules.updates.statemachine.UpdatesStateValue
19
19
  import kotlinx.coroutines.CompletableDeferred
20
20
  import kotlinx.coroutines.CoroutineScope
21
21
  import kotlinx.coroutines.Dispatchers
22
+ import kotlinx.coroutines.SupervisorJob
22
23
  import kotlinx.coroutines.cancel
23
24
  import kotlinx.coroutines.launch
24
25
  import kotlinx.coroutines.runBlocking
@@ -45,13 +46,13 @@ class DisabledUpdatesController(
45
46
  ) : IUpdatesController {
46
47
  /** Keep the activity for [RecreateReactContextProcedure] to relaunch the app. */
47
48
  private var weakActivity: WeakReference<Activity>? = null
48
- private val controllerScope = CoroutineScope(Dispatchers.IO)
49
+ private val controllerScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
49
50
 
50
51
  private val logger = UpdatesLogger(context.filesDir)
51
52
  override val eventManager: IUpdatesEventManager = UpdatesEventManager(logger)
52
53
 
53
54
  // disabled controller state machine can only be idle or restarting
54
- private val stateMachine = UpdatesStateMachine(logger, eventManager, setOf(UpdatesStateValue.Idle, UpdatesStateValue.Restarting))
55
+ private val stateMachine = UpdatesStateMachine(logger, eventManager, setOf(UpdatesStateValue.Idle, UpdatesStateValue.Restarting), controllerScope)
55
56
 
56
57
  private var isStarted = false
57
58
  private var startupStartTimeMillis: Long? = null
@@ -106,7 +107,7 @@ class DisabledUpdatesController(
106
107
  isStarted = true
107
108
  startupStartTimeMillis = System.currentTimeMillis()
108
109
 
109
- launcher = NoDatabaseLauncher(context, logger, fatalException)
110
+ launcher = NoDatabaseLauncher(context, logger, fatalException, controllerScope)
110
111
 
111
112
  startupEndTimeMillis = System.currentTimeMillis()
112
113
  notifyController()
@@ -143,7 +144,8 @@ class DisabledUpdatesController(
143
144
  override fun onSuccess() {
144
145
  continuation.resume(Unit)
145
146
  }
146
- }
147
+ },
148
+ controllerScope
147
149
  )
148
150
  stateMachine.queueExecution(procedure)
149
151
  }
@@ -32,6 +32,7 @@ import expo.modules.updates.statemachine.UpdatesStateValue
32
32
  import kotlinx.coroutines.CompletableDeferred
33
33
  import kotlinx.coroutines.CoroutineScope
34
34
  import kotlinx.coroutines.Dispatchers
35
+ import kotlinx.coroutines.SupervisorJob
35
36
  import kotlinx.coroutines.cancel
36
37
  import kotlinx.coroutines.launch
37
38
  import kotlinx.coroutines.runBlocking
@@ -60,8 +61,8 @@ class EnabledUpdatesController(
60
61
 
61
62
  private val selectionPolicy: SelectionPolicy
62
63
  get() = SelectionPolicyFactory.createFilterAwarePolicy(updatesConfiguration.getRuntimeVersion(), updatesConfiguration)
63
- private val stateMachine = UpdatesStateMachine(logger, eventManager, UpdatesStateValue.entries.toSet())
64
- private val controllerScope = CoroutineScope(Dispatchers.IO)
64
+ private val controllerScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
65
+ private val stateMachine = UpdatesStateMachine(logger, eventManager, UpdatesStateValue.entries.toSet(), controllerScope)
65
66
  private val fileDownloader: FileDownloader
66
67
  get() = FileDownloader(context.filesDir, EASClientID(context).uuid.toString(), updatesConfiguration, logger)
67
68
  private val databaseHolder = DatabaseHolder(UpdatesDatabase.getInstance(context, Dispatchers.IO))
@@ -111,7 +112,8 @@ class EnabledUpdatesController(
111
112
  override fun onRequestRelaunch(shouldRunReaper: Boolean, callback: LauncherCallback) {
112
113
  relaunchReactApplication(shouldRunReaper, callback)
113
114
  }
114
- }
115
+ },
116
+ controllerScope
115
117
  )
116
118
 
117
119
  private val launchedUpdate
@@ -183,7 +185,8 @@ class EnabledUpdatesController(
183
185
  setCurrentLauncher = { currentLauncher -> startupProcedure.setLauncher(currentLauncher) },
184
186
  shouldRunReaper = shouldRunReaper,
185
187
  reloadScreenManager = reloadScreenManager,
186
- callback
188
+ callback,
189
+ controllerScope
187
190
  )
188
191
  stateMachine.queueExecution(procedure)
189
192
  }
@@ -33,6 +33,7 @@ import expo.modules.updatesinterface.UpdatesInterface
33
33
  import expo.modules.updatesinterface.UpdatesInterfaceCallbacks
34
34
  import kotlinx.coroutines.CoroutineScope
35
35
  import kotlinx.coroutines.Dispatchers
36
+ import kotlinx.coroutines.SupervisorJob
36
37
  import kotlinx.coroutines.launch
37
38
  import kotlinx.coroutines.suspendCancellableCoroutine
38
39
  import org.json.JSONObject
@@ -67,7 +68,7 @@ class UpdatesDevLauncherController(
67
68
  private var updatesConfiguration: UpdatesConfiguration? = initialUpdatesConfiguration
68
69
 
69
70
  private val databaseHolder = DatabaseHolder(UpdatesDatabase.getInstance(context, Dispatchers.IO))
70
- private val controllerScope = CoroutineScope(Dispatchers.IO)
71
+ private val controllerScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
71
72
 
72
73
  private var mSelectionPolicy: SelectionPolicy? = null
73
74
  private var defaultSelectionPolicy: SelectionPolicy = SelectionPolicy(
@@ -5,6 +5,7 @@ import expo.modules.updates.loader.EmbeddedLoader
5
5
  import expo.modules.updates.logging.UpdatesLogger
6
6
  import kotlinx.coroutines.CoroutineScope
7
7
  import kotlinx.coroutines.Dispatchers
8
+ import kotlinx.coroutines.SupervisorJob
8
9
  import kotlinx.coroutines.launch
9
10
  import java.io.File
10
11
 
@@ -20,7 +21,7 @@ class NoDatabaseLauncher @JvmOverloads constructor(
20
21
  private val context: Context,
21
22
  private val logger: UpdatesLogger,
22
23
  fatalException: Exception? = null,
23
- launcherScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
24
+ launcherScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
24
25
  ) : Launcher {
25
26
  override val bundleAssetName = EmbeddedLoader.BARE_BUNDLE_FILENAME
26
27
  override val launchedUpdate = null
@@ -19,6 +19,7 @@ import expo.modules.updates.manifest.ManifestMetadata
19
19
  import expo.modules.updates.manifest.Update
20
20
  import kotlinx.coroutines.CoroutineScope
21
21
  import kotlinx.coroutines.Dispatchers
22
+ import kotlinx.coroutines.SupervisorJob
22
23
  import java.io.File
23
24
  import java.io.IOException
24
25
  import java.util.*
@@ -38,7 +39,7 @@ abstract class Loader protected constructor(
38
39
  private val database: UpdatesDatabase,
39
40
  private val updatesDirectory: File,
40
41
  private val loaderFiles: LoaderFiles,
41
- private var scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
42
+ private var scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
42
43
  ) {
43
44
  private var updateResponse: UpdateResponse? = null
44
45
  private var updateEntity: UpdateEntity? = null
@@ -80,7 +80,7 @@ class StartupProcedure(
80
80
  object : LoaderTask.LoaderTaskCallback {
81
81
  override fun onFailure(e: Exception) {
82
82
  logger.error("UpdatesController loaderTask onFailure", e, UpdatesErrorCode.None)
83
- launcher = NoDatabaseLauncher(context, logger, e)
83
+ launcher = NoDatabaseLauncher(context, logger, e, procedureScope)
84
84
  emergencyLaunchException = e
85
85
  notifyController()
86
86
  }
@@ -4,6 +4,8 @@ import expo.modules.updates.events.IUpdatesEventManager
4
4
  import expo.modules.updates.logging.UpdatesLogger
5
5
  import expo.modules.updates.procedures.StateMachineProcedure
6
6
  import expo.modules.updates.procedures.StateMachineSerialExecutorQueue
7
+ import kotlinx.coroutines.CoroutineScope
8
+ import kotlinx.coroutines.Dispatchers
7
9
  import java.util.Date
8
10
 
9
11
  /**
@@ -13,7 +15,8 @@ import java.util.Date
13
15
  class UpdatesStateMachine(
14
16
  private val logger: UpdatesLogger,
15
17
  private val eventManager: IUpdatesEventManager,
16
- private val validUpdatesStateValues: Set<UpdatesStateValue>
18
+ private val validUpdatesStateValues: Set<UpdatesStateValue>,
19
+ scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
17
20
  ) {
18
21
  private val serialExecutorQueue = StateMachineSerialExecutorQueue(
19
22
  logger,
@@ -30,7 +33,8 @@ class UpdatesStateMachine(
30
33
  override fun resetStateAfterRestart() {
31
34
  resetAndIncrementRestartCount()
32
35
  }
33
- }
36
+ },
37
+ scope
34
38
  )
35
39
 
36
40
  /**
@@ -5,18 +5,33 @@ onFlowStart:
5
5
  ---
6
6
  # Use dev client to load a bundle from updates server
7
7
  - evalScript:
8
- script: ${output.api.serveManifest('test-update-basic', MAESTRO_PLATFORM)}
8
+ script: ${output.api.serveManifest('test-update-basic', MAESTRO_PLATFORM)}
9
9
  label: Setup updates server to serve a basic update
10
10
  env:
11
11
  MAESTRO_PLATFORM: ${MAESTRO_PLATFORM}
12
12
  - launchApp
13
- - tapOn:
14
- label: Tap on "Enter URL manually"
15
- text: Enter URL manually
16
- - tapOn:
17
- label: Tap on "Updates URL" text field
18
- text: '.*http:.*'
19
- below: Enter URL manually
13
+ - runFlow:
14
+ when:
15
+ platform: iOS
16
+ commands:
17
+ - tapOn:
18
+ label: Tap on "Enter URL manually"
19
+ text: Enter URL manually
20
+ - tapOn:
21
+ label: Tap on "Updates URL" text field
22
+ text: '.*http:.*'
23
+ below: Enter URL manually
24
+ - runFlow:
25
+ when:
26
+ platform: android
27
+ commands:
28
+ - tapOn:
29
+ label: Tap on "New development server"
30
+ text: New development server
31
+ - tapOn:
32
+ label: Tap on "Updates URL" text field
33
+ text: '.*http:.*'
34
+ below: New development server
20
35
  - inputText:
21
36
  label: Input local update server URL
22
37
  text: "http://localhost:4747/update\n"
@@ -328,11 +328,11 @@ async function preparePackageJson(
328
328
  'adb install android/app/build/outputs/apk/release/app-release.apk',
329
329
  'maestro:android:uninstall': 'adb uninstall dev.expo.updatese2e',
330
330
  'maestro:ios:debug:build':
331
- 'xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e -configuration Debug -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build | npx @expo/xcpretty',
331
+ 'set -o pipefail && xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e -configuration Debug -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build | npx @expo/xcpretty',
332
332
  'maestro:ios:debug:install':
333
333
  'xcrun simctl install booted ios/build/Build/Products/Debug-iphonesimulator/updatese2e.app',
334
334
  'maestro:ios:release:build':
335
- 'xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e -configuration Release -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build | npx @expo/xcpretty',
335
+ 'set -o pipefail && xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e -configuration Release -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build | npx @expo/xcpretty',
336
336
  'maestro:ios:release:install':
337
337
  'xcrun simctl install booted ios/build/Build/Products/Release-iphonesimulator/updatese2e.app',
338
338
  'maestro:ios:uninstall': 'xcrun simctl uninstall booted dev.expo.updatese2e',
@@ -340,7 +340,7 @@ async function preparePackageJson(
340
340
  'eas-build-on-success': './eas-hooks/eas-build-on-success.sh',
341
341
  'check-android-emulator': 'npx ts-node ./scripts/check-android-emulator.ts',
342
342
  'tvos:build':
343
- 'xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e -configuration Debug -sdk appletvsimulator -arch arm64 -derivedDataPath ios/build | npx @expo/xcpretty',
343
+ 'set -o pipefail && xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e -configuration Debug -sdk appletvsimulator -arch arm64 -derivedDataPath ios/build | npx @expo/xcpretty',
344
344
  postinstall: 'patch-package',
345
345
  'start:dev-client':
346
346
  'npx expo start --private-key-path ./keys/private-key.pem > /dev/null 2>&1 &',
@@ -397,8 +397,8 @@ async function preparePackageJson(
397
397
  ...packageJson,
398
398
  dependencies: {
399
399
  ...packageJson.dependencies,
400
- 'react-native': 'npm:react-native-tvos@0.81.0-0',
401
- '@react-native-tvos/config-tv': '^0.1.3',
400
+ 'react-native': 'npm:react-native-tvos@0.81.4-0',
401
+ '@react-native-tvos/config-tv': '^0.1.4',
402
402
  },
403
403
  expo: {
404
404
  install: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-updates",
3
- "version": "29.0.8",
3
+ "version": "29.0.10",
4
4
  "description": "Fetches and manages remotely-hosted assets and updates to your app's JS bundle.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -39,13 +39,13 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@expo/code-signing-certificates": "0.0.5",
42
- "@expo/plist": "^0.4.6",
42
+ "@expo/plist": "^0.4.7",
43
43
  "@expo/spawn-async": "^1.7.2",
44
44
  "arg": "4.1.0",
45
45
  "chalk": "^4.1.2",
46
46
  "debug": "^4.3.4",
47
- "expo-eas-client": "~1.0.6",
48
- "expo-manifests": "~1.0.7",
47
+ "expo-eas-client": "~1.0.7",
48
+ "expo-manifests": "~1.0.8",
49
49
  "expo-structured-headers": "~5.0.0",
50
50
  "expo-updates-interface": "~2.0.0",
51
51
  "getenv": "^2.0.0",
@@ -60,7 +60,7 @@
60
60
  "@types/node-forge": "^1.0.0",
61
61
  "@types/picomatch": "^4.0.0",
62
62
  "@vercel/ncc": "^0.38.3",
63
- "expo-module-scripts": "^5.0.6",
63
+ "expo-module-scripts": "^5.0.7",
64
64
  "express": "^5.1.0",
65
65
  "form-data": "^4.0.4",
66
66
  "js-yaml": "^4.1.0",
@@ -73,5 +73,5 @@
73
73
  "react": "*",
74
74
  "react-native": "*"
75
75
  },
76
- "gitHead": "d635404a12ea7996b987ce0fb7679a238ebcf31b"
76
+ "gitHead": "088e79428be97cf3ee11fc93e0e5a1fc1c8bea1e"
77
77
  }