@bravemobile/react-native-code-push 8.2.4 → 8.3.0

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.
@@ -0,0 +1,94 @@
1
+ trigger:
2
+ - master
3
+
4
+ pr:
5
+ - master
6
+
7
+ variables:
8
+ - name: api-level
9
+ value: '27'
10
+
11
+ pool:
12
+ vmImage: 'macOS-12'
13
+
14
+ stages:
15
+ - stage: RunTests
16
+ displayName: 'Run Android & IOS tests'
17
+ jobs:
18
+ - job: TestAndroid
19
+ timeoutInMinutes: 120
20
+ displayName: 'Test android'
21
+ steps:
22
+
23
+ - script: |
24
+ adb devices
25
+ displayName: 'Start adb server'
26
+
27
+ - script: |
28
+ $ANDROID_HOME/tools/bin/sdkmanager "system-images;android-$(api-level);google_apis;x86"
29
+ displayName: 'Download system image'
30
+
31
+ - script: |
32
+ $ANDROID_HOME/tools/bin/avdmanager create avd --force --name TestEmulator --abi google_apis/x86 --package 'system-images;android-$(api-level);google_apis;x86' --device "Nexus 6P"
33
+ displayName: 'Creating Android emulator'
34
+
35
+ - script: |
36
+ $ANDROID_HOME/emulator/emulator -avd TestEmulator -noaudio -no-window -no-snapshot-save -no-boot-anim -memory 6144 &
37
+ displayName: 'Start Android emulator'
38
+
39
+ - script: |
40
+ $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done'
41
+ displayName: 'Wait for emulator to boot'
42
+
43
+ - script: |
44
+ adb shell settings put global window_animation_scale 0.0
45
+ displayName: 'Disable animations and transitions'
46
+
47
+ - script: |
48
+ adb shell settings put global transition_animation_scale 0.0
49
+ displayName: 'Disable animations and transitions'
50
+
51
+ - script: |
52
+ adb shell settings put global animator_duration_scale 0.0
53
+ displayName: 'Disable animations and transitions'
54
+
55
+
56
+ - task: JavaToolInstaller@0
57
+ inputs:
58
+ versionSpec: '11'
59
+ jdkArchitectureOption: 'x64'
60
+ jdkSourceOption: 'PreInstalled'
61
+ displayName: 'Change Java version'
62
+
63
+ - script: |
64
+ npm install
65
+ displayName: 'Package Installation'
66
+
67
+ - script: |
68
+ npm run build:tests && npm run test:setup:android
69
+ displayName: 'Setup Android tests'
70
+
71
+ - script: |
72
+ npm run test:fast:android
73
+ displayName: 'Run Android test'
74
+
75
+ - job: TestIOS
76
+ timeoutInMinutes: 120
77
+ displayName: 'Test IOS'
78
+ steps:
79
+
80
+ - script: |
81
+ npm install
82
+ displayName: 'Install dependencies'
83
+
84
+ - script: |
85
+ npm run build:tests && npm run test:setup:ios
86
+ displayName: 'Setup iOS tests'
87
+
88
+ - script: |
89
+ npm run test:fast:ios
90
+ displayName: 'Run tests'
91
+
92
+
93
+
94
+
package/CodePush.js CHANGED
@@ -90,14 +90,19 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC
90
90
  log(`An error has occurred at update checker : ${error.stack}`);
91
91
  if (sharedCodePushOptions.fallbackToAppCenter) {
92
92
  return await sdk.queryUpdateWithCurrentPackage(queryPackage);
93
+ } else {
94
+ // update will not happen
95
+ return undefined;
93
96
  }
94
97
  }
95
98
  })()
96
99
  : await sdk.queryUpdateWithCurrentPackage(queryPackage);
97
100
 
98
- const fileName = update && typeof update.downloadUrl === 'string' ? update.downloadUrl.split('/').pop() : null;
99
- if (sharedCodePushOptions.bundleHost && fileName) {
100
- update.downloadUrl = sharedCodePushOptions.bundleHost + fileName;
101
+ if (sharedCodePushOptions.bundleHost && update) {
102
+ const fileName = typeof update.downloadUrl === 'string' ? update.downloadUrl.split('/').pop() : null;
103
+ if (fileName) {
104
+ update.downloadUrl = sharedCodePushOptions.bundleHost + fileName;
105
+ }
101
106
  }
102
107
 
103
108
  /*
@@ -620,42 +625,41 @@ function codePushify(options = {}) {
620
625
  sharedCodePushOptions.setUpdateChecker(options.updateChecker);
621
626
  sharedCodePushOptions.setFallbackToAppCenter(options.fallbackToAppCenter);
622
627
 
623
- var decorator = (RootComponent) => {
624
- const extended = class CodePushComponent extends React.Component {
628
+ const decorator = (RootComponent) => {
629
+ class CodePushComponent extends React.Component {
630
+ constructor(props) {
631
+ super(props);
632
+ this.rootComponentRef = React.createRef();
633
+ }
634
+
625
635
  componentDidMount() {
626
636
  if (options.checkFrequency === CodePush.CheckFrequency.MANUAL) {
627
637
  CodePush.notifyAppReady();
628
638
  } else {
629
- let rootComponentInstance = this.refs.rootComponent;
639
+ const rootComponentInstance = this.rootComponentRef.current;
630
640
 
631
641
  let syncStatusCallback;
632
642
  if (rootComponentInstance && rootComponentInstance.codePushStatusDidChange) {
633
- syncStatusCallback = rootComponentInstance.codePushStatusDidChange;
634
- if (rootComponentInstance instanceof React.Component) {
635
- syncStatusCallback = syncStatusCallback.bind(rootComponentInstance);
636
- }
643
+ syncStatusCallback = rootComponentInstance.codePushStatusDidChange.bind(rootComponentInstance);
637
644
  }
638
645
 
639
646
  let downloadProgressCallback;
640
647
  if (rootComponentInstance && rootComponentInstance.codePushDownloadDidProgress) {
641
- downloadProgressCallback = rootComponentInstance.codePushDownloadDidProgress;
642
- if (rootComponentInstance instanceof React.Component) {
643
- downloadProgressCallback = downloadProgressCallback.bind(rootComponentInstance);
644
- }
648
+ downloadProgressCallback = rootComponentInstance.codePushDownloadDidProgress.bind(rootComponentInstance);
645
649
  }
646
650
 
647
651
  let handleBinaryVersionMismatchCallback;
648
652
  if (rootComponentInstance && rootComponentInstance.codePushOnBinaryVersionMismatch) {
649
- handleBinaryVersionMismatchCallback = rootComponentInstance.codePushOnBinaryVersionMismatch;
650
- if (rootComponentInstance instanceof React.Component) {
651
- handleBinaryVersionMismatchCallback = handleBinaryVersionMismatchCallback.bind(rootComponentInstance);
652
- }
653
+ handleBinaryVersionMismatchCallback = rootComponentInstance.codePushOnBinaryVersionMismatch.bind(rootComponentInstance);
653
654
  }
654
655
 
655
656
  CodePush.sync(options, syncStatusCallback, downloadProgressCallback, handleBinaryVersionMismatchCallback);
657
+
656
658
  if (options.checkFrequency === CodePush.CheckFrequency.ON_APP_RESUME) {
657
659
  ReactNative.AppState.addEventListener("change", (newState) => {
658
- newState === "active" && CodePush.sync(options, syncStatusCallback, downloadProgressCallback);
660
+ if (newState === "active") {
661
+ CodePush.sync(options, syncStatusCallback, downloadProgressCallback);
662
+ }
659
663
  });
660
664
  }
661
665
  }
@@ -664,17 +668,17 @@ function codePushify(options = {}) {
664
668
  render() {
665
669
  const props = {...this.props};
666
670
 
667
- // we can set ref property on class components only (not stateless)
668
- // check it by render method
669
- if (RootComponent.prototype.render) {
670
- props.ref = "rootComponent";
671
+ // We can set ref property on class components only (not stateless)
672
+ // Check it by render method
673
+ if (RootComponent.prototype && RootComponent.prototype.render) {
674
+ props.ref = this.rootComponentRef;
671
675
  }
672
676
 
673
677
  return <RootComponent {...props} />
674
678
  }
675
679
  }
676
680
 
677
- return hoistStatics(extended, RootComponent);
681
+ return hoistStatics(CodePushComponent, RootComponent);
678
682
  }
679
683
 
680
684
  if (typeof options === "function") {
package/README.md CHANGED
@@ -1,3 +1,57 @@
1
+ ## @bravemobile/react-native-code-push
2
+
3
+ Fork of `code-push-react-native`
4
+
5
+ ```bash
6
+ npm install @bravemobile/react-native-code-push
7
+ ```
8
+
9
+ You'll have more flexibility and freedom in your deployment strategy.
10
+ You can still use CodePush, but become independent of AppCenter's cloud infrastructure.
11
+
12
+ ### Self-host the update bundle file
13
+
14
+ Specify the host and path using the `bundleHost` option.
15
+
16
+ Upload the code-push bundle file to your server. The file name should be exactly same as the file on AppCenter.
17
+
18
+ ```javascript
19
+ const codePushOptions = {
20
+ bundlehost: 'https://cdn.yours.com/codepush/bundle/',
21
+ };
22
+
23
+ export default codePush(codePushOptions)(MyApp);
24
+ ```
25
+
26
+ ### Customize the update check behavior
27
+
28
+ Specify a function to perform the update check using the `updateChecker` option.
29
+
30
+ (The `bundleHost` option can be used in combination.)
31
+
32
+ `fallbackToAppCenter` : If an error occurs during the execution of the updateChecker function, the original update check behavior is performed as a fallback. (default: true)
33
+
34
+ ```javascript
35
+ const codePushOptions = {
36
+ updateChecker: async (updateCheckRequest) => {
37
+ // It's up to you to decide what to do.
38
+ // However, you need to have a good understanding of Code Push's interface to configure your response.
39
+ const { data: response } = await axios.get('https://your.api.com/update_check', {
40
+ params: { app_version: updateCheckRequest.app_version }
41
+ });
42
+ return response;
43
+ },
44
+ fallbackToAppCenter: true,
45
+ };
46
+
47
+ export default codePush(codePushOptions)(MyApp);
48
+ ```
49
+
50
+
51
+ ## Original README.md is below.
52
+
53
+ ---
54
+
1
55
  [![appcenterbanner](https://user-images.githubusercontent.com/31293287/32969262-3cc5d48a-cb99-11e7-91bf-fa57c67a371c.png)](http://microsoft.github.io/code-push/)
2
56
 
3
57
  #### [Sign up With App Center](https://appcenter.ms/signup?utm_source=CodePush&utm_medium=Azure) to use CodePush
@@ -246,44 +300,6 @@ If you would like to display an update confirmation dialog (an "active install")
246
300
 
247
301
  *NOTE: If you are using [Redux](http://redux.js.org) and [Redux Saga](https://redux-saga.js.org/), you can alternatively use the [react-native-code-push-saga](http://github.com/lostintangent/react-native-code-push-saga) module, which allows you to customize when `sync` is called in a perhaps simpler/more idiomatic way.*
248
302
 
249
- You can self-host the update's file. Upload the file to your server exactly as you uploaded it to AppCenter. And specify the host and path using the `bundleHost` option.
250
-
251
- ```javascript
252
- let codePushOptions = {
253
- checkFrequency: codePush.CheckFrequency.MANUAL,
254
- bundlehost: 'https://cdn.yours.com/codepush/bundle/',
255
- };
256
-
257
- let MyApp: () => React$Node = () => {
258
- }
259
-
260
- MyApp = codePush(codePushOptions)(MyApp);
261
- ```
262
-
263
- You can customize the behavior by specifying a function to perform the update check instead. (The 'bundleHost' option can be used with it.)
264
-
265
- ```javascript
266
- let codePushOptions = {
267
- checkFrequency: codePush.CheckFrequency.MANUAL,
268
- bundlehost: 'https://cdn.yours.com/codepush/bundle/',
269
- updateChecker: async (updateCheckRequest) => {
270
- // It's up to you to decide what to do.
271
- const { data: response } = await axios.get('https://your.api.com/update_check', {
272
- params: { app_version: updateCheckRequest.app_version }
273
- });
274
- return response;
275
- },
276
- // If an error occurs during the execution of the updateChecker function, the original update check behavior is performed as a fallback. (default: true)
277
- fallbackToAppCenter: true,
278
- };
279
-
280
- let MyApp: () => React$Node = () => {
281
- }
282
-
283
- MyApp = codePush(codePushOptions)(MyApp);
284
- ```
285
-
286
-
287
303
  ### Store Guideline Compliance
288
304
 
289
305
  Android Google Play and iOS App Store have corresponding guidelines that have rules you should be aware of before integrating the CodePush solution within your application.
@@ -0,0 +1,10 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.microsoft.codepush.react">
3
+
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+
6
+ <application>
7
+ <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
8
+ </application>
9
+
10
+ </manifest>
@@ -3,8 +3,4 @@
3
3
 
4
4
  <uses-permission android:name="android.permission.INTERNET" />
5
5
 
6
- <application>
7
- <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
8
- </application>
9
-
10
- </manifest>
6
+ </manifest>
@@ -98,7 +98,10 @@ public class CodePushUpdateUtils {
98
98
  }
99
99
 
100
100
  public static void copyNecessaryFilesFromCurrentPackage(String diffManifestFilePath, String currentPackageFolderPath, String newPackageFolderPath) throws IOException {
101
- FileUtils.copyDirectoryContents(currentPackageFolderPath, newPackageFolderPath);
101
+ if (currentPackageFolderPath == null || !new File(currentPackageFolderPath).exists()) {
102
+ CodePushUtils.log("Unable to copy files from current package during diff update, because currentPackageFolderPath is invalid.");
103
+ return;
104
+ }
102
105
  JSONObject diffManifest = CodePushUtils.getJsonObjectFromFile(diffManifestFilePath);
103
106
  try {
104
107
  JSONArray deletedFiles = diffManifest.getJSONArray("deletedFiles");
@@ -67,7 +67,9 @@ public class CodePushUtils {
67
67
  String key = it.next();
68
68
  Object obj = null;
69
69
  try {
70
- obj = jsonObj.get(key);
70
+ if (!jsonObj.isNull(key)) {
71
+ obj = jsonObj.get(key);
72
+ }
71
73
  } catch (JSONException jsonException) {
72
74
  // Should not happen.
73
75
  throw new CodePushUnknownException("Key " + key + " should exist in " + jsonObj.toString() + ".", jsonException);
@@ -2,8 +2,8 @@
2
2
 
3
3
  import java.nio.file.Paths;
4
4
 
5
- def config = project.extensions.findByName("react") ?: []
6
- def bundleAssetName = config.bundleAssetName.get() ?: "index.android.bundle"
5
+ def config = project.extensions.findByName("react") ?: [:]
6
+ def bundleAssetName = config.bundleAssetName ? config.bundleAssetName.get() : "index.android.bundle"
7
7
 
8
8
  // because elvis operator
9
9
  def elvisFile(thing) {
@@ -17,6 +17,41 @@ void runBefore(String dependentTaskName, Task task) {
17
17
  }
18
18
  }
19
19
 
20
+ /**
21
+ * Finds the path of the installed npm package with the given name using Node's
22
+ * module resolution algorithm, which searches "node_modules" directories up to
23
+ * the file system root. This handles various cases, including:
24
+ *
25
+ * - Working in the open-source RN repo:
26
+ * Gradle: /path/to/react-native/ReactAndroid
27
+ * Node module: /path/to/react-native/node_modules/[package]
28
+ *
29
+ * - Installing RN as a dependency of an app and searching for hoisted
30
+ * dependencies:
31
+ * Gradle: /path/to/app/node_modules/react-native/ReactAndroid
32
+ * Node module: /path/to/app/node_modules/[package]
33
+ *
34
+ * - Working in a larger repo (e.g., Facebook) that contains RN:
35
+ * Gradle: /path/to/repo/path/to/react-native/ReactAndroid
36
+ * Node module: /path/to/repo/node_modules/[package]
37
+ *
38
+ * The search begins at the given base directory (a File object). The returned
39
+ * path is a string.
40
+ */
41
+ static def findNodeModulePath(baseDir, packageName) {
42
+ def basePath = baseDir.toPath().normalize()
43
+ // Node's module resolution algorithm searches up to the root directory,
44
+ // after which the base path will be null
45
+ while (basePath) {
46
+ def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName)
47
+ if (candidatePath.toFile().exists()) {
48
+ return candidatePath.toString()
49
+ }
50
+ basePath = basePath.getParent()
51
+ }
52
+ return null
53
+ }
54
+
20
55
  android.buildTypes.each { buildType ->
21
56
  // to prevent incorrect long value restoration from strings.xml we need to wrap it with double quotes
22
57
  // https://github.com/microsoft/cordova-plugin-code-push/issues/264
@@ -24,7 +59,7 @@ android.buildTypes.each { buildType ->
24
59
  }
25
60
 
26
61
  gradle.projectsEvaluated {
27
- def debuggableVariants = config.debuggableVariants.get() ?: ['debug']
62
+ def debuggableVariants = config.debuggableVariants ? config.debuggableVariants.get() : ['debug']
28
63
 
29
64
  android.applicationVariants.all { variant ->
30
65
  // No code push for debuggable variants
@@ -34,11 +69,9 @@ gradle.projectsEvaluated {
34
69
 
35
70
  def nodeModulesPath;
36
71
  if (project.hasProperty('nodeModulesPath')) {
37
- nodeModulesPath = project.nodeModulesPath
38
- } else if (config.root) {
39
- nodeModulesPath = Paths.get(config.root.asFile.get().absolutePath, "/node_modules");
72
+ nodeModulesPath = "${project.nodeModulesPath}/react-native-code-push"
40
73
  } else {
41
- nodeModulesPath = "../../node_modules";
74
+ nodeModulesPath = findNodeModulePath(projectDir, "react-native-code-push")
42
75
  }
43
76
 
44
77
  def targetName = variant.name.capitalize()
@@ -49,8 +82,8 @@ gradle.projectsEvaluated {
49
82
  def jsBundleFile;
50
83
 
51
84
  // Additional node commandline arguments
52
- def nodeExecutableAndArgs = config.nodeExecutableAndArgs.get() ?: ["node"]
53
- def extraPackagerArgs = config.extraPackagerArgs.get() ?: []
85
+ def nodeExecutableAndArgs = config.nodeExecutableAndArgs ? config.nodeExecutableAndArgs.get(): ["node"]
86
+ def extraPackagerArgs = config.extraPackagerArgs ? config.extraPackagerArgs.get() : []
54
87
 
55
88
  // Make this task run right after the bundle task
56
89
  def generateBundledResourcesHash;
@@ -59,6 +92,9 @@ gradle.projectsEvaluated {
59
92
  if (reactBundleTask) {
60
93
  jsBundleDir = reactBundleTask.property('jsBundleDir').asFile.get()
61
94
  resourcesDir = reactBundleTask.property('resourcesDir').asFile.get()
95
+
96
+ // mitigates Resource and asset merger: Duplicate resources error
97
+ project.delete(files("${jsBundleDir}"))
62
98
 
63
99
  jsBundleDir.mkdirs()
64
100
  resourcesDir.mkdirs()
@@ -68,7 +104,7 @@ gradle.projectsEvaluated {
68
104
  generateBundledResourcesHash = tasks.create(
69
105
  name: "generateBundledResourcesHash${targetName}",
70
106
  type: Exec) {
71
- commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/react-native-code-push/scripts/generateBundledResourcesHash.js", resourcesDir, jsBundleFile, jsBundleDir)
107
+ commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/scripts/generateBundledResourcesHash.js", resourcesDir, jsBundleFile, jsBundleDir)
72
108
 
73
109
  enabled !debuggableVariants.contains(variant.name) ?: targetName.toLowerCase().contains("release")
74
110
  }
@@ -77,11 +113,11 @@ gradle.projectsEvaluated {
77
113
  runBefore("merge${targetName}Assets", generateBundledResourcesHash)
78
114
  } else {
79
115
  def jsBundleDirConfigName = "jsBundleDir${targetName}"
80
- jsBundleDir = elvisFile(config."$jsBundleDirConfigName").get() ?:
116
+ jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ? elvisFile(config."$jsBundleDirConfigName").get():
81
117
  file("$buildDir/intermediates/assets/${targetPath}")
82
118
 
83
119
  def resourcesDirConfigName = "resourcesDir${targetName}"
84
- resourcesDir = elvisFile(config."${resourcesDirConfigName}").get() ?:
120
+ resourcesDir = elvisFile(config."${resourcesDirConfigName}") ? elvisFile(config."${resourcesDirConfigName}").get():
85
121
  file("$buildDir/intermediates/res/merged/${targetPath}")
86
122
 
87
123
  // In case version of 'Android Plugin for Gradle'' is lower than 1.3.0
@@ -98,14 +134,14 @@ gradle.projectsEvaluated {
98
134
  generateBundledResourcesHash = tasks.create(
99
135
  name: "generateBundledResourcesHash${targetName}",
100
136
  type: Exec) {
101
- commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/react-native-code-push/scripts/generateBundledResourcesHash.js", resourcesDir, jsBundleFile, jsBundleDir, resourcesMapTempFileName)
137
+ commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/scripts/generateBundledResourcesHash.js", resourcesDir, jsBundleFile, jsBundleDir, resourcesMapTempFileName)
102
138
  }
103
139
 
104
140
  // Make this task run right before the bundle task
105
141
  def recordFilesBeforeBundleCommand = tasks.create(
106
142
  name: "recordFilesBeforeBundleCommand${targetName}",
107
143
  type: Exec) {
108
- commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/react-native-code-push/scripts/recordFilesBeforeBundleCommand.js", resourcesDir, resourcesMapTempFileName)
144
+ commandLine (*nodeExecutableAndArgs, "${nodeModulesPath}/scripts/recordFilesBeforeBundleCommand.js", resourcesDir, resourcesMapTempFileName)
109
145
  }
110
146
 
111
147
  recordFilesBeforeBundleCommand.dependsOn("merge${targetName}Resources")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bravemobile/react-native-code-push",
3
- "version": "8.2.4",
3
+ "version": "8.3.0",
4
4
  "description": "React Native plugin for the CodePush service",
5
5
  "main": "CodePush.js",
6
6
  "typings": "typings/react-native-code-push.d.ts",
@@ -36,7 +36,7 @@
36
36
  "url": "https://github.com/Soomgo-Mobile/react-native-code-push"
37
37
  },
38
38
  "dependencies": {
39
- "code-push": "^4.2.1",
39
+ "code-push": "^4.2.2",
40
40
  "glob": "^7.1.7",
41
41
  "hoist-non-react-statics": "^3.3.2",
42
42
  "inquirer": "^8.1.5",