expo-updates 0.24.6 → 0.24.7

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,25 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.24.7 — 2024-01-10
14
+
15
+ ### 🎉 New features
16
+
17
+ - Added `AppController.overrideConfiguration()` to override the `UpdatesConfig` on iOS. ([#26093](https://github.com/expo/expo/pull/26093) by [@kudo](https://github.com/kudo))
18
+
19
+ ### 🛠 Breaking changes
20
+
21
+ - Deprecated `UpdatesController.initialize(Context, Map)` and replaced with `UpdatesController.overrideConfiguration()` method to prevent ANR when overriding the `UpdatesConfiguration` on Android. [#26093](https://github.com/expo/expo/pull/26093) by [@kudo](https://github.com/kudo))
22
+
23
+ ### 🐛 Bug fixes
24
+
25
+ - Fix metro asset call in embedded manifest creation step. ([#26307](https://github.com/expo/expo/pull/26307) by [@wschurman](https://github.com/wschurman))
26
+ - [expo-updates] dev launcher updates controller should not read embedded manifest. ([#26336](https://github.com/expo/expo/pull/26336) by [@douglowder](https://github.com/douglowder))
27
+
28
+ ### 💡 Others
29
+
30
+ - Replace deprecated `com.facebook.react:react-native:+` Android dependency with `com.facebook.react:react-android`. ([#26237](https://github.com/expo/expo/pull/26237) by [@kudo](https://github.com/kudo))
31
+
13
32
  ## 0.24.6 — 2024-01-05
14
33
 
15
34
  ### 🎉 New features
package/DEVELOPMENT.md CHANGED
@@ -33,7 +33,7 @@ To test the new EAS update manifest format, set `EXUpdatesUsesLegacyManifest` an
33
33
 
34
34
  ### Additional Headers
35
35
 
36
- If you want any additional headers to be sent in manifest requests, you can add these to a map under the key `EXUpdatesRequestHeaders` on iOS, or `requestHeaders` on Android (currently, this can't be configured in AndroidManifest.xml and you need to use the `UpdatesController.initialize(Context context, Map<String, Object> configuration)` method in MainApplication.java).
36
+ If you want any additional headers to be sent in manifest requests, you can add these to a map under the key `EXUpdatesRequestHeaders` on iOS, or `requestHeaders` on Android (currently, this can't be configured in AndroidManifest.xml and you need to use the `UpdatesController.overrideConfiguration(Context context, Map<String, Object> configuration)` method in MainApplication.java).
37
37
 
38
38
  ## Making a Build
39
39
 
@@ -4,7 +4,7 @@ apply plugin: 'kotlin-kapt'
4
4
  apply plugin: 'maven-publish'
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '0.24.6'
7
+ version = '0.24.7'
8
8
 
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
10
  if (expoModulesCorePlugin.exists()) {
@@ -122,7 +122,7 @@ android {
122
122
  namespace "expo.modules.updates"
123
123
  defaultConfig {
124
124
  versionCode 31
125
- versionName '0.24.6'
125
+ versionName '0.24.7'
126
126
  consumerProguardFiles("proguard-rules.pro")
127
127
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
128
128
 
@@ -159,8 +159,7 @@ dependencies {
159
159
  implementation project(':expo-manifests')
160
160
  implementation project(':expo-json-utils')
161
161
  implementation project(':expo-eas-client')
162
- //noinspection GradleDynamicVersion
163
- implementation "com.facebook.react:react-native:+"
162
+ implementation "com.facebook.react:react-android"
164
163
 
165
164
  def room_version = "2.4.2"
166
165
 
@@ -23,13 +23,14 @@ import expo.modules.updatesinterface.UpdatesInterfaceCallbacks
23
23
  class UpdatesController {
24
24
  companion object {
25
25
  private var singletonInstance: IUpdatesController? = null
26
+ private var overrideConfiguration: UpdatesConfiguration? = null
26
27
 
27
28
  @JvmStatic val instance: IUpdatesController
28
29
  get() {
29
30
  return checkNotNull(singletonInstance) { "UpdatesController.instance was called before the module was initialized" }
30
31
  }
31
32
 
32
- @JvmStatic fun initializeWithoutStarting(context: Context, configuration: Map<String, Any>? = null) {
33
+ @JvmStatic fun initializeWithoutStarting(context: Context) {
33
34
  if (singletonInstance == null) {
34
35
  var updatesDirectoryException: Exception? = null
35
36
  val updatesDirectory = try {
@@ -39,19 +40,11 @@ class UpdatesController {
39
40
  null
40
41
  }
41
42
 
42
- val updatesConfigurationValidationResult = UpdatesConfiguration.getUpdatesConfigurationValidationResult(context, configuration)
43
- singletonInstance = if (updatesConfigurationValidationResult == UpdatesConfigurationValidationResult.VALID && updatesDirectory != null) {
44
- val updatesConfiguration = UpdatesConfiguration(context, null)
45
- EnabledUpdatesController(context, updatesConfiguration, updatesDirectory)
46
- } else {
43
+ val updatesConfiguration: UpdatesConfiguration? = overrideConfiguration ?: run {
47
44
  val logger = UpdatesLogger(context)
48
- when (updatesConfigurationValidationResult) {
45
+ when (UpdatesConfiguration.getUpdatesConfigurationValidationResult(context, null)) {
49
46
  UpdatesConfigurationValidationResult.VALID -> {
50
- // this means there was a storage error
51
- logger.error(
52
- "The expo-updates system is disabled due to a storage access error: ${updatesDirectoryException?.message ?: "Unknown Error"}",
53
- UpdatesErrorCode.InitializationError
54
- )
47
+ return@run UpdatesConfiguration(context, null)
55
48
  }
56
49
  UpdatesConfigurationValidationResult.INVALID_NOT_ENABLED -> logger.warn(
57
50
  "The expo-updates system is explicitly disabled. To enable it, set the enabled setting to true.",
@@ -66,8 +59,21 @@ class UpdatesController {
66
59
  UpdatesErrorCode.InitializationError
67
60
  )
68
61
  }
62
+ return@run null
63
+ }
69
64
 
70
- DisabledUpdatesController(context, updatesDirectoryException, UpdatesConfiguration.isMissingRuntimeVersion(context, configuration))
65
+ singletonInstance = if (updatesConfiguration != null && updatesDirectory != null) {
66
+ EnabledUpdatesController(context, updatesConfiguration, updatesDirectory)
67
+ } else {
68
+ val logger = UpdatesLogger(context)
69
+ if (updatesDirectory == null) {
70
+ // this means there was a storage error
71
+ logger.error(
72
+ "The expo-updates system is disabled due to a storage access error: ${updatesDirectoryException?.message ?: "Unknown Error"}",
73
+ UpdatesErrorCode.InitializationError
74
+ )
75
+ }
76
+ DisabledUpdatesController(context, updatesDirectoryException, UpdatesConfiguration.isMissingRuntimeVersion(context, null))
71
77
  }
72
78
  }
73
79
  }
@@ -83,11 +89,14 @@ class UpdatesController {
83
89
  null
84
90
  }
85
91
 
86
- val initialUpdatesConfiguration = if (UpdatesConfiguration.getUpdatesConfigurationValidationResult(context, null) == UpdatesConfigurationValidationResult.VALID) {
87
- UpdatesConfiguration(context, null)
88
- } else {
89
- null
92
+ val initialUpdatesConfiguration = overrideConfiguration ?: run {
93
+ if (UpdatesConfiguration.getUpdatesConfigurationValidationResult(context, null) == UpdatesConfigurationValidationResult.VALID) {
94
+ UpdatesConfiguration(context, null)
95
+ } else {
96
+ null
97
+ }
90
98
  }
99
+
91
100
  val instance = UpdatesDevLauncherController(
92
101
  context,
93
102
  initialUpdatesConfiguration,
@@ -107,11 +116,34 @@ class UpdatesController {
107
116
  * @param context the base context of the application, ideally a [ReactApplication]
108
117
  * @param configuration map of configuration pairs to override those from AndroidManifest.xml
109
118
  */
110
- @JvmStatic fun initialize(context: Context, configuration: Map<String, Any>? = null) {
119
+ @JvmStatic fun initialize(context: Context) {
111
120
  if (singletonInstance == null) {
112
- initializeWithoutStarting(context, configuration)
121
+ initializeWithoutStarting(context)
113
122
  singletonInstance!!.start()
114
123
  }
115
124
  }
125
+
126
+ /**
127
+ * Overrides the [UpdatesConfiguration] that will be used inside [UpdatesController]
128
+ * This should be called as early as possible in the application's lifecycle.
129
+ * Can pass additional configuration to this method to set or override
130
+ * configuration values at runtime rather than just AndroidManifest.xml.
131
+ *
132
+ * @param context the base context of the application, ideally a [ReactApplication]
133
+ * @param configuration map of configuration pairs to override those from AndroidManifest.xml
134
+ */
135
+ @JvmStatic
136
+ fun overrideConfiguration(context: Context, configuration: Map<String, Any>) {
137
+ if (singletonInstance != null) {
138
+ throw AssertionError("The method should be called before UpdatesController.initialize()")
139
+ }
140
+ val updatesConfigurationValidationResult = UpdatesConfiguration.getUpdatesConfigurationValidationResult(context, configuration)
141
+ if (updatesConfigurationValidationResult == UpdatesConfigurationValidationResult.VALID) {
142
+ overrideConfiguration = UpdatesConfiguration(context, configuration)
143
+ } else {
144
+ val logger = UpdatesLogger(context)
145
+ logger.warn("Failed to overrideConfiguration: invalid configuration: ${updatesConfigurationValidationResult.name}")
146
+ }
147
+ }
116
148
  }
117
149
  }
@@ -17,7 +17,6 @@ import expo.modules.updates.loader.Loader
17
17
  import expo.modules.updates.loader.RemoteLoader
18
18
  import expo.modules.updates.loader.UpdateDirective
19
19
  import expo.modules.updates.loader.UpdateResponse
20
- import expo.modules.updates.manifest.EmbeddedManifest
21
20
  import expo.modules.updates.selectionpolicy.LauncherSelectionPolicySingleUpdate
22
21
  import expo.modules.updates.selectionpolicy.ReaperSelectionPolicyDevelopmentClient
23
22
  import expo.modules.updates.selectionpolicy.SelectionPolicy
@@ -280,7 +279,7 @@ class UpdatesDevLauncherController(
280
279
  override fun getConstantsForModule(): IUpdatesController.UpdatesModuleConstants {
281
280
  return IUpdatesController.UpdatesModuleConstants(
282
281
  launchedUpdate = launchedUpdate,
283
- embeddedUpdate = updatesConfiguration?.let { EmbeddedManifest.get(context, it) }?.updateEntity,
282
+ embeddedUpdate = null, // no embedded update in debug builds
284
283
  isEmergencyLaunch = isEmergencyLaunch,
285
284
  isEnabled = true,
286
285
  releaseChannel = updatesConfiguration?.releaseChannel ?: "default",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-updates",
3
- "platforms": ["ios", "android"],
3
+ "platforms": ["ios", "android", "tvos"],
4
4
  "ios": {
5
5
  "modules": ["UpdatesModule"],
6
6
  "appDelegateSubscribers": ["ExpoUpdatesAppDelegateSubscriber"],
@@ -151,45 +151,29 @@ public class AppController: NSObject {
151
151
  assert(_sharedInstance != nil, "AppController.sharedInstace was called before the module was initialized")
152
152
  return _sharedInstance!
153
153
  }
154
+ private static var _overrideConfig: UpdatesConfig?
154
155
 
155
- public static func initializeWithoutStarting(configuration: [String: Any]?) {
156
+ public static func initializeWithoutStarting() {
156
157
  if _sharedInstance != nil {
157
158
  return
158
159
  }
159
160
 
160
161
  let logger = UpdatesLogger()
161
162
 
162
- let updatesConfigurationValidationResult = UpdatesConfig.getUpdatesConfigurationValidationResult(mergingOtherDictionary: configuration)
163
- if updatesConfigurationValidationResult == UpdatesConfigurationValidationResult.Valid {
164
- var config: UpdatesConfig?
165
- do {
166
- config = try UpdatesConfig.configWithExpoPlist(mergingOtherDictionary: configuration)
167
- } catch {
168
- NSException(
169
- name: .internalInconsistencyException,
170
- reason: "Cannot load configuration from Expo.plist. Please ensure you've followed the setup and installation instructions for expo-updates to create Expo.plist and add it to your Xcode project."
171
- )
172
- .raise()
173
- }
174
-
175
- let updatesDatabase = UpdatesDatabase()
176
- do {
177
- let directory = try initializeUpdatesDirectory()
178
- try initializeUpdatesDatabase(updatesDatabase: updatesDatabase, inUpdatesDirectory: directory)
179
- _sharedInstance = EnabledAppController(config: config!, database: updatesDatabase, updatesDirectory: directory)
180
- } catch {
181
- logger.error(
182
- message: "The expo-updates system is disabled due to an error during initialization: \(error.localizedDescription)",
183
- code: .initializationError
184
- )
185
- _sharedInstance = DisabledAppController(error: error, isMissingRuntimeVersion: UpdatesConfig.isMissingRuntimeVersion(mergingOtherDictionary: configuration))
186
- return
187
- }
188
- } else {
163
+ // swiftlint:disable closure_body_length
164
+ let config = _overrideConfig != nil ? _overrideConfig : {
165
+ let updatesConfigurationValidationResult = UpdatesConfig.getUpdatesConfigurationValidationResult(mergingOtherDictionary: nil)
189
166
  switch updatesConfigurationValidationResult {
190
167
  case .Valid:
191
- assertionFailure("Valid case should be handled above")
192
- break
168
+ guard let config = try? UpdatesConfig.configWithExpoPlist(mergingOtherDictionary: nil) else {
169
+ NSException(
170
+ name: .internalInconsistencyException,
171
+ reason: "Cannot load configuration from Expo.plist. Please ensure you've followed the setup and installation instructions for expo-updates to create Expo.plist and add it to your Xcode project."
172
+ )
173
+ .raise()
174
+ return nil
175
+ }
176
+ return config
193
177
  case .InvalidPlistError:
194
178
  logger.warn(
195
179
  message: "The expo-updates system is disabled due to an invalid configuration. Ensure a valid Expo.plist is present in the application bundle.",
@@ -211,8 +195,42 @@ public class AppController: NSObject {
211
195
  code: .initializationError
212
196
  )
213
197
  }
198
+ return nil
199
+ }()
200
+ // swiftlint:enable closure_body_length
201
+
202
+ if let config = config {
203
+ let updatesDatabase = UpdatesDatabase()
204
+ do {
205
+ let directory = try initializeUpdatesDirectory()
206
+ try initializeUpdatesDatabase(updatesDatabase: updatesDatabase, inUpdatesDirectory: directory)
207
+ _sharedInstance = EnabledAppController(config: config, database: updatesDatabase, updatesDirectory: directory)
208
+ } catch {
209
+ logger.error(
210
+ message: "The expo-updates system is disabled due to an error during initialization: \(error.localizedDescription)",
211
+ code: .initializationError
212
+ )
213
+ _sharedInstance = DisabledAppController(error: error, isMissingRuntimeVersion: UpdatesConfig.isMissingRuntimeVersion(mergingOtherDictionary: nil))
214
+ return
215
+ }
216
+ } else {
217
+ _sharedInstance = DisabledAppController(error: nil, isMissingRuntimeVersion: UpdatesConfig.isMissingRuntimeVersion(mergingOtherDictionary: nil))
218
+ }
219
+ }
214
220
 
215
- _sharedInstance = DisabledAppController(error: nil, isMissingRuntimeVersion: UpdatesConfig.isMissingRuntimeVersion(mergingOtherDictionary: configuration))
221
+ public static func overrideConfiguration(configuration: [String: Any]?) {
222
+ if _sharedInstance != nil {
223
+ fatalError("The method should be called before AppController.initializeWithoutStarting()")
224
+ }
225
+ let updatesConfigurationValidationResult = UpdatesConfig.getUpdatesConfigurationValidationResult(mergingOtherDictionary: configuration)
226
+ if updatesConfigurationValidationResult == UpdatesConfigurationValidationResult.Valid {
227
+ do {
228
+ _overrideConfig = try UpdatesConfig.configWithExpoPlist(mergingOtherDictionary: configuration)
229
+ } catch {
230
+ UpdatesLogger().warn(message: "Failed to overrideConfiguration: invalid configuration: Cannot load configuration from Expo.plist")
231
+ }
232
+ } else {
233
+ UpdatesLogger().warn(message: "Failed to overrideConfiguration: \(updatesConfigurationValidationResult)")
216
234
  }
217
235
  }
218
236
 
@@ -300,16 +300,9 @@ public final class DevLauncherAppController: NSObject, InternalAppControllerInte
300
300
  }
301
301
 
302
302
  public func getConstantsForModule() -> UpdatesModuleConstants {
303
- let embeddedUpdate: Update?
304
- if isStarted {
305
- embeddedUpdate = self.config.let { it in EmbeddedAppLoader.embeddedManifest(withConfig: it, database: self.database) }
306
- } else {
307
- embeddedUpdate = nil
308
- }
309
-
310
303
  return UpdatesModuleConstants(
311
304
  launchedUpdate: launcher?.launchedUpdate,
312
- embeddedUpdate: embeddedUpdate,
305
+ embeddedUpdate: nil, // no embedded update in debug builds
313
306
  isEmergencyLaunch: isEmergencyLaunch,
314
307
  isEnabled: true,
315
308
  releaseChannel: self.config?.releaseChannel ?? "default",
@@ -49,7 +49,7 @@ public final class ExpoUpdatesReactDelegateHandler: ExpoReactDelegateHandler, Ap
49
49
  }
50
50
 
51
51
  self.reactDelegate = reactDelegate
52
- AppController.initializeWithoutStarting(configuration: nil)
52
+ AppController.initializeWithoutStarting()
53
53
  let controller = AppController.sharedInstance
54
54
  controller.delegate = self
55
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-updates",
3
- "version": "0.24.6",
3
+ "version": "0.24.7",
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",
@@ -64,5 +64,5 @@
64
64
  "peerDependencies": {
65
65
  "expo": "*"
66
66
  },
67
- "gitHead": "b5a3db3c8993f3b22e8cba1e2c6005fee57988c2"
67
+ "gitHead": "ca014bf2516c7644ef303f4c21fdd68de4d99f76"
68
68
  }
@@ -1,8 +1,10 @@
1
- const { loadMetroConfigAsync } = require('@expo/cli/build/src/start/server/metro/instantiateMetro');
1
+ const {
2
+ createMetroServerAndBundleRequestAsync,
3
+ exportEmbedAssetsAsync,
4
+ } = require('@expo/cli/build/src/export/embed/exportEmbedAsync');
2
5
  const { resolveEntryPoint } = require('@expo/config/paths');
3
6
  const crypto = require('crypto');
4
7
  const fs = require('fs');
5
- const Server = require('metro/src/Server');
6
8
  const path = require('path');
7
9
 
8
10
  const filterPlatformAssetScales = require('./filterPlatformAssetScales');
@@ -50,38 +52,28 @@ function getRelativeEntryPoint(projectRoot, platform) {
50
52
 
51
53
  process.chdir(projectRoot);
52
54
 
53
- let metroConfig;
54
- try {
55
- // Load the metro config the same way it would be loaded in Expo CLI.
56
- // This ensures dynamic features like tsconfig paths can be used.
57
- metroConfig = (
58
- await loadMetroConfigAsync(
59
- projectRoot,
60
- {
61
- // No config options can be passed to this point.
62
- },
63
- {
64
- isExporting: true,
65
- }
66
- )
67
- ).config;
68
- } catch (e) {
69
- let message = `Error loading Metro config and Expo app config: ${e.message}\n\nMake sure your project is configured properly and your app.json / app.config.js is valid.`;
70
- if (process.env.EAS_BUILD) {
71
- message +=
72
- '\nIf you are using environment variables in app.config.js, verify that you have set them in your EAS Build profile configuration or secrets.';
73
- }
74
- throw new Error(message);
75
- }
55
+ const options = {
56
+ platform,
57
+ entryFile,
58
+ minify: false,
59
+ dev: false,
60
+ };
61
+
62
+ const { server, bundleRequest } = await createMetroServerAndBundleRequestAsync(
63
+ projectRoot,
64
+ options
65
+ );
76
66
 
77
67
  let assets;
78
68
  try {
79
- assets = await fetchAssetManifestAsync(platform, projectRoot, entryFile, metroConfig);
69
+ assets = await exportEmbedAssetsAsync(server, bundleRequest, projectRoot, options);
80
70
  } catch (e) {
81
71
  throw new Error(
82
72
  "Error loading assets JSON from Metro. Ensure you've followed all expo-updates installation steps correctly. " +
83
73
  e.message
84
74
  );
75
+ } finally {
76
+ server.end();
85
77
  }
86
78
 
87
79
  const manifest = {
@@ -155,38 +147,3 @@ function getBasePath(asset) {
155
147
  }
156
148
  return basePath;
157
149
  }
158
-
159
- // Spawn a Metro server to get the asset manifest
160
- async function fetchAssetManifestAsync(platform, projectRoot, entryFile, metroConfig) {
161
- // Project-level babel config does not load unless we change to the
162
- // projectRoot before instantiating the server
163
- process.chdir(projectRoot);
164
-
165
- const server = new Server(metroConfig);
166
-
167
- const requestOpts = {
168
- entryFile,
169
- dev: false,
170
- minify: false,
171
- platform,
172
- };
173
-
174
- let assetManifest;
175
- let error;
176
- try {
177
- assetManifest = await server.getAssets({
178
- ...Server.DEFAULT_BUNDLE_OPTIONS,
179
- ...requestOpts,
180
- });
181
- } catch (e) {
182
- error = e;
183
- } finally {
184
- server.end();
185
- }
186
-
187
- if (error) {
188
- throw error;
189
- }
190
-
191
- return assetManifest;
192
- }