@bravemobile/react-native-code-push 12.4.1 → 13.1.0-beta.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.
Files changed (111) hide show
  1. package/CodePush.podspec +47 -2
  2. package/README.md +118 -3
  3. package/THIRD-PARTY-NOTICES +96 -0
  4. package/android/app/build.gradle +26 -0
  5. package/android/app/proguard-rules.pro +5 -0
  6. package/android/app/src/main/cpp/CMakeLists.txt +38 -0
  7. package/android/app/src/main/cpp/binarypatch_jni.c +181 -0
  8. package/android/app/src/main/java/com/microsoft/codepush/react/BinaryPatchAttempt.java +92 -0
  9. package/android/app/src/main/java/com/microsoft/codepush/react/BinaryPatchResult.java +64 -0
  10. package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +46 -11
  11. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBinaryPatch.java +293 -0
  12. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +25 -0
  13. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +79 -88
  14. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +159 -4
  15. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +13 -0
  16. package/android/app/src/main/java/com/microsoft/codepush/react/HDiffPatchNative.java +52 -0
  17. package/android/app/src/test/java/com/microsoft/codepush/react/CodePushBinaryPatchTest.java +393 -0
  18. package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerBinaryPatchTest.java +207 -0
  19. package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerDownloadTest.java +449 -0
  20. package/android/codepush-export.gradle +115 -0
  21. package/cli/commands/bundleCommand/bundleCodePush.ts +39 -7
  22. package/cli/commands/bundleCommand/index.ts +9 -1
  23. package/cli/commands/releaseCommand/addToReleaseHistory.ts +7 -0
  24. package/cli/commands/releaseCommand/index.test.ts +118 -0
  25. package/cli/commands/releaseCommand/index.ts +20 -2
  26. package/cli/commands/releaseCommand/release.test.ts +582 -0
  27. package/cli/commands/releaseCommand/release.ts +232 -15
  28. package/cli/dist/commands/bundleCommand/bundleCodePush.js +26 -7
  29. package/cli/dist/commands/bundleCommand/index.js +5 -1
  30. package/cli/dist/commands/releaseCommand/addToReleaseHistory.js +6 -1
  31. package/cli/dist/commands/releaseCommand/index.js +8 -1
  32. package/cli/dist/commands/releaseCommand/index.test.js +96 -0
  33. package/cli/dist/commands/releaseCommand/release.js +165 -17
  34. package/cli/dist/commands/releaseCommand/release.test.js +367 -0
  35. package/cli/dist/functions/makeBinaryPatchBundle.js +248 -0
  36. package/cli/dist/functions/makeBinaryPatchBundle.test.js +321 -0
  37. package/cli/dist/functions/makeCodePushBundle.js +4 -2
  38. package/cli/dist/functions/runHermesEmitBinaryCommand.js +98 -27
  39. package/cli/dist/functions/runHermesEmitBinaryCommand.test.js +100 -0
  40. package/cli/dist/jest.globalSetup.js +38 -0
  41. package/cli/dist/utils/binaryPatch.js +86 -0
  42. package/cli/dist/utils/binaryPatch.test.js +190 -0
  43. package/cli/functions/makeBinaryPatchBundle.test.ts +410 -0
  44. package/cli/functions/makeBinaryPatchBundle.ts +367 -0
  45. package/cli/functions/makeCodePushBundle.ts +4 -2
  46. package/cli/functions/runHermesEmitBinaryCommand.test.ts +124 -0
  47. package/cli/functions/runHermesEmitBinaryCommand.ts +112 -26
  48. package/cli/jest.globalSetup.ts +43 -0
  49. package/cli/utils/binaryPatch.test.ts +234 -0
  50. package/cli/utils/binaryPatch.ts +103 -0
  51. package/cpp/binarypatch/binarypatch_zstd_decompressor.c +187 -0
  52. package/cpp/binarypatch/binarypatch_zstd_decompressor.h +47 -0
  53. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/checksum_plugin.h +52 -0
  54. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/hpatch_mt/hpatch_mt.h +147 -0
  55. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c +3315 -0
  56. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.h +268 -0
  57. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_private.h +297 -0
  58. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_types.h +434 -0
  59. package/cpp/binarypatch/vendor/zstd/common/allocations.h +55 -0
  60. package/cpp/binarypatch/vendor/zstd/common/bits.h +205 -0
  61. package/cpp/binarypatch/vendor/zstd/common/bitstream.h +454 -0
  62. package/cpp/binarypatch/vendor/zstd/common/compiler.h +464 -0
  63. package/cpp/binarypatch/vendor/zstd/common/cpu.h +249 -0
  64. package/cpp/binarypatch/vendor/zstd/common/debug.c +30 -0
  65. package/cpp/binarypatch/vendor/zstd/common/debug.h +107 -0
  66. package/cpp/binarypatch/vendor/zstd/common/entropy_common.c +340 -0
  67. package/cpp/binarypatch/vendor/zstd/common/error_private.c +64 -0
  68. package/cpp/binarypatch/vendor/zstd/common/error_private.h +158 -0
  69. package/cpp/binarypatch/vendor/zstd/common/fse.h +625 -0
  70. package/cpp/binarypatch/vendor/zstd/common/fse_decompress.c +315 -0
  71. package/cpp/binarypatch/vendor/zstd/common/huf.h +277 -0
  72. package/cpp/binarypatch/vendor/zstd/common/mem.h +422 -0
  73. package/cpp/binarypatch/vendor/zstd/common/pool.c +371 -0
  74. package/cpp/binarypatch/vendor/zstd/common/pool.h +81 -0
  75. package/cpp/binarypatch/vendor/zstd/common/portability_macros.h +171 -0
  76. package/cpp/binarypatch/vendor/zstd/common/threading.c +182 -0
  77. package/cpp/binarypatch/vendor/zstd/common/threading.h +142 -0
  78. package/cpp/binarypatch/vendor/zstd/common/xxhash.c +18 -0
  79. package/cpp/binarypatch/vendor/zstd/common/xxhash.h +7094 -0
  80. package/cpp/binarypatch/vendor/zstd/common/zstd_common.c +48 -0
  81. package/cpp/binarypatch/vendor/zstd/common/zstd_deps.h +123 -0
  82. package/cpp/binarypatch/vendor/zstd/common/zstd_internal.h +324 -0
  83. package/cpp/binarypatch/vendor/zstd/common/zstd_trace.h +156 -0
  84. package/cpp/binarypatch/vendor/zstd/decompress/huf_decompress.c +1944 -0
  85. package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.c +244 -0
  86. package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.h +44 -0
  87. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress.c +2410 -0
  88. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.c +2209 -0
  89. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.h +73 -0
  90. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_internal.h +240 -0
  91. package/cpp/binarypatch/vendor/zstd/zstd.h +3206 -0
  92. package/cpp/binarypatch/vendor/zstd/zstd_errors.h +107 -0
  93. package/ios/CodePush/CodePush.h +58 -9
  94. package/ios/CodePush/{CodePush.m → CodePush.mm} +77 -63
  95. package/ios/CodePush/CodePushBinaryPatch.h +80 -0
  96. package/ios/CodePush/CodePushBinaryPatch.m +473 -0
  97. package/ios/CodePush/CodePushPackage.m +183 -2
  98. package/ios/CodePush/CodePushUpdateUtils.m +49 -1
  99. package/ios/CodePush.xcodeproj/project.pbxproj +6 -6
  100. package/package.json +28 -4
  101. package/react-native.config.js +3 -1
  102. package/scripts/binary-patch/build-hdiffpatch.sh +86 -0
  103. package/scripts/export-embedded-bundle.sh +76 -0
  104. package/src/CodePush.js +190 -96
  105. package/src/CodePush.test.js +382 -0
  106. package/src/native/NativeCodePush.ts +162 -0
  107. package/src/package-mixins.js +24 -11
  108. package/src/specs/NativeCodePush.ts +43 -0
  109. package/typings/react-native-code-push.d.ts +77 -0
  110. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +0 -102
  111. package/src/AlertAdapter.js +0 -24
@@ -1,4 +1,5 @@
1
1
  #import "CodePush.h"
2
+ #import "CodePushBinaryPatch.h"
2
3
  #if __has_include(<SSZipArchive/SSZipArchive.h>)
3
4
  #import <SSZipArchive/SSZipArchive.h>
4
5
  #else
@@ -9,8 +10,18 @@
9
10
 
10
11
  #pragma mark - Private constants
11
12
 
13
+ static NSString *const BinaryPatchDownloadUrlKey = @"binaryPatchDownloadUrl";
14
+ static NSString *const BinaryPatchFolderName = @"binary-patch";
15
+ // The fields of what a download that tried a patch reports back with. They are read by the
16
+ // app that asked for the download and by nothing else, so they never reach a file.
17
+ static NSString *const BinaryPatchResultApplyDurationMsKey = @"applyDurationMs";
18
+ static NSString *const BinaryPatchResultFallbackReasonKey = @"fallbackReason";
19
+ static NSString *const BinaryPatchResultStatusKey = @"status";
20
+ static NSString *const BinaryPatchResultStatusApplied = @"applied";
21
+ static NSString *const BinaryPatchResultStatusFallback = @"fallback";
12
22
  static NSString *const DiffManifestFileName = @"hotcodepush.json";
13
23
  static NSString *const DownloadFileName = @"download.zip";
24
+ static NSString *const DownloadUrlKey = @"downloadUrl";
14
25
  static NSString *const RelativeBundlePathKey = @"bundlePath";
15
26
  static NSString *const StatusFile = @"codepush.json";
16
27
  static NSString *const UpdateBundleFileName = @"app.jsbundle";
@@ -47,8 +58,136 @@ static NSString *const UnzippedFolderName = @"unzipped";
47
58
  expectedBundleFileName:(NSString *)expectedBundleFileName
48
59
  operationQueue:(dispatch_queue_t)operationQueue
49
60
  progressCallback:(void (^)(long long, long long))progressCallback
50
- doneCallback:(void (^)())doneCallback
61
+ doneCallback:(void (^)(NSDictionary *binaryPatchResult))doneCallback
51
62
  failCallback:(void (^)(NSError *err))failCallback
63
+ {
64
+ void (^downloadFullPackage)(NSDictionary *) = ^(NSDictionary *binaryPatchResult) {
65
+ [self downloadAndInstallPackage:updatePackage
66
+ expectedBundleFileName:expectedBundleFileName
67
+ operationQueue:operationQueue
68
+ downloadUrl:updatePackage[DownloadUrlKey]
69
+ isBinaryPatchUpdate:NO
70
+ progressCallback:progressCallback
71
+ doneCallback:^{
72
+ doneCallback(binaryPatchResult);
73
+ }
74
+ failCallback:failCallback
75
+ patchAppliedCallback:nil
76
+ patchFallbackCallback:nil];
77
+ };
78
+
79
+ // A release that was published with a binary patch offers two archives of the same
80
+ // update. The patch is worth trying because it is a fraction of the size, and the
81
+ // full archive is always there when it does not work out.
82
+ NSString *binaryPatchDownloadUrl = updatePackage[BinaryPatchDownloadUrlKey];
83
+ if (![binaryPatchDownloadUrl isKindOfClass:[NSString class]] || [binaryPatchDownloadUrl length] == 0) {
84
+ // A download that never had a patch to try has nothing to report about one.
85
+ return downloadFullPackage(nil);
86
+ }
87
+
88
+ // Every way the patch can fail ends the same way, with the update being downloaded in
89
+ // full instead, so none of them reaches the caller as an error. The fallback happens
90
+ // exactly once without anything having to count it: the full archive is downloaded by
91
+ // a call that is not allowed to take the patch path, so it has no failure of its own
92
+ // to fall back from.
93
+ NSDate *patchAttemptStartTime = [NSDate date];
94
+ // Set once the applier has restored the bundle, which is also what tells a failure
95
+ // that follows apart from one that came before.
96
+ __block NSNumber *applyDurationMs = nil;
97
+ [self downloadAndInstallPackage:updatePackage
98
+ expectedBundleFileName:expectedBundleFileName
99
+ operationQueue:operationQueue
100
+ downloadUrl:binaryPatchDownloadUrl
101
+ isBinaryPatchUpdate:YES
102
+ progressCallback:progressCallback
103
+ doneCallback:^{
104
+ [self deleteBinaryPatchFolder];
105
+ doneCallback([self appliedBinaryPatchResult:applyDurationMs]);
106
+ }
107
+ failCallback:^(NSError *err) {
108
+ [self deleteBinaryPatchFolder];
109
+ CPLog(@"The binary patch update could not be completed (%@). Downloading the full update instead.", err.localizedDescription);
110
+ // An error raised after the bundle was restored is the restored
111
+ // update failing the checks every update passes before it is
112
+ // installed. Before that point the appliers have no word for what
113
+ // happened - the patch archive not being downloadable, say - and
114
+ // inventing one here would put a value on the wire that no
115
+ // platform reports, so the fallback is reported without a reason.
116
+ NSString *failureReason = applyDurationMs == nil
117
+ ? nil
118
+ : CodePushBinaryPatchReasonPackageVerificationFailed;
119
+ downloadFullPackage([self binaryPatchFallbackResult:failureReason
120
+ attemptStartTime:patchAttemptStartTime]);
121
+ }
122
+ patchAppliedCallback:^(double durationMs) {
123
+ applyDurationMs = @(durationMs);
124
+ }
125
+ patchFallbackCallback:^(NSString *failureReason) {
126
+ [self deleteBinaryPatchFolder];
127
+ CPLog(@"Binary patch update failed (%@). Downloading the full update instead.", failureReason);
128
+ downloadFullPackage([self binaryPatchFallbackResult:failureReason
129
+ attemptStartTime:patchAttemptStartTime]);
130
+ }];
131
+ }
132
+
133
+ /*
134
+ * The result of a patch attempt the update was installed from, timed over the apply itself.
135
+ */
136
+ + (NSDictionary *)appliedBinaryPatchResult:(NSNumber *)applyDurationMs
137
+ {
138
+ return @{
139
+ BinaryPatchResultStatusKey: BinaryPatchResultStatusApplied,
140
+ BinaryPatchResultApplyDurationMsKey: applyDurationMs ?: @0,
141
+ };
142
+ }
143
+
144
+ /*
145
+ * The result of a patch attempt the update had to be downloaded in full after.
146
+ *
147
+ * There is no completed apply to time here, so the attempt itself is what is timed: from the
148
+ * patch archive being asked for to the moment the attempt was given up on. The reason is left
149
+ * out when the attempt ended in an error none of the appliers has a word for.
150
+ */
151
+ + (NSDictionary *)binaryPatchFallbackResult:(NSString *)failureReason
152
+ attemptStartTime:(NSDate *)attemptStartTime
153
+ {
154
+ NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:@{
155
+ BinaryPatchResultStatusKey: BinaryPatchResultStatusFallback,
156
+ BinaryPatchResultApplyDurationMsKey: @(round([[NSDate date] timeIntervalSinceDate:attemptStartTime] * 1000)),
157
+ }];
158
+ if (failureReason) {
159
+ result[BinaryPatchResultFallbackReasonKey] = failureReason;
160
+ }
161
+
162
+ return result;
163
+ }
164
+
165
+ /*
166
+ * Downloads an update from one of its archives and installs it.
167
+ *
168
+ * isBinaryPatchUpdate says whether the archive holds a binary patch of the JS bundle,
169
+ * which has to be applied before the contents are the update. Only an archive downloaded
170
+ * from the binary patch URL is treated that way, so an update being downloaded in full
171
+ * can never end up on the patch path.
172
+ *
173
+ * patchAppliedCallback is called with how long the apply took the moment the bundle has
174
+ * been restored, which is before the update is known to install. It is nil for a full
175
+ * download, which has no patch to apply.
176
+ *
177
+ * patchFallbackCallback is called instead of doneCallback when the patch cannot be
178
+ * applied: the update was not installed, and the caller has to download the full archive.
179
+ * It is nil for a full download, which has nothing to fall back to.
180
+ */
181
+ + (void)downloadAndInstallPackage:(NSDictionary *)updatePackage
182
+ expectedBundleFileName:(NSString *)expectedBundleFileName
183
+ operationQueue:(dispatch_queue_t)operationQueue
184
+ downloadUrl:(NSString *)downloadUrl
185
+ isBinaryPatchUpdate:(BOOL)isBinaryPatchUpdate
186
+ progressCallback:(void (^)(long long, long long))progressCallback
187
+ doneCallback:(void (^)(void))doneCallback
188
+ failCallback:(void (^)(NSError *err))failCallback
189
+ patchAppliedCallback:(void (^)(double applyDurationMs))patchAppliedCallback
190
+ patchFallbackCallback:(void (^)(NSString *failureReason))patchFallbackCallback
52
191
  {
53
192
  NSString *newUpdateHash = updatePackage[@"packageHash"];
54
193
  NSString *newUpdateFolderPath = [self getPackageFolderPath:newUpdateHash];
@@ -109,6 +248,24 @@ static NSString *const UnzippedFolderName = @"unzipped";
109
248
  nonFailingError = nil;
110
249
  }
111
250
 
251
+ // Rebuild the JS bundle the archive only carries a patch of, which leaves the
252
+ // contents identical to the ones the full archive would have delivered.
253
+ if (isBinaryPatchUpdate) {
254
+ NSDate *patchStartTime = [NSDate date];
255
+ NSString *patchFailureReason = nil;
256
+ if (![CodePushBinaryPatch restoreBundleInUnzippedFolder:unzippedFolderPath
257
+ workingFolder:[self getBinaryPatchFolderPath]
258
+ baseBundleURL:[CodePush binaryBundleURL]
259
+ failureReason:&patchFailureReason]) {
260
+ patchFallbackCallback(patchFailureReason);
261
+ return;
262
+ }
263
+
264
+ double applyDurationMs = round([[NSDate date] timeIntervalSinceDate:patchStartTime] * 1000);
265
+ CPLog(@"Restored the update from its binary patch in %.0f ms.", applyDurationMs);
266
+ patchAppliedCallback(applyDurationMs);
267
+ }
268
+
112
269
  NSString *diffManifestFilePath = [unzippedFolderPath stringByAppendingPathComponent:DiffManifestFileName];
113
270
  BOOL isDiffUpdate = [[NSFileManager defaultManager] fileExistsAtPath:diffManifestFilePath];
114
271
 
@@ -252,6 +409,15 @@ static NSString *const UnzippedFolderName = @"unzipped";
252
409
  }
253
410
 
254
411
  } else {
412
+ if (isBinaryPatchUpdate) {
413
+ // Whatever the patch URL served, it is not a patch archive - an error page
414
+ // answered with a 200 looks like this too. Moving it into place would
415
+ // install bytes no hash has ever been checked against, so the full archive
416
+ // is downloaded instead.
417
+ patchFallbackCallback(CodePushBinaryPatchReasonInvalidManifest);
418
+ return;
419
+ }
420
+
255
421
  [[NSFileManager defaultManager] createDirectoryAtPath:newUpdateFolderPath
256
422
  withIntermediateDirectories:YES
257
423
  attributes:nil
@@ -284,7 +450,22 @@ static NSString *const UnzippedFolderName = @"unzipped";
284
450
 
285
451
  failCallback:failCallback];
286
452
 
287
- [downloadHandler download:updatePackage[@"downloadUrl"]];
453
+ [downloadHandler download:downloadUrl];
454
+ }
455
+
456
+ /*
457
+ * Removes what a patch attempt works in, whichever way the attempt ended - and whatever
458
+ * an attempt that was killed halfway through left there.
459
+ */
460
+ + (void)deleteBinaryPatchFolder
461
+ {
462
+ [[NSFileManager defaultManager] removeItemAtPath:[self getBinaryPatchFolderPath]
463
+ error:nil];
464
+ }
465
+
466
+ + (NSString *)getBinaryPatchFolderPath
467
+ {
468
+ return [[self getCodePushPath] stringByAppendingPathComponent:BinaryPatchFolderName];
288
469
  }
289
470
 
290
471
  + (NSString *)getCodePushPath
@@ -7,6 +7,9 @@ NSString * const AssetsFolderName = @"assets";
7
7
  NSString * const BinaryHashKey = @"CodePushBinaryHash";
8
8
  NSString * const ManifestFolderPrefix = @"CodePush";
9
9
 
10
+ /* How much of a file is read at a time while hashing it. */
11
+ static const NSUInteger HashChunkSize = 1024 * 8;
12
+
10
13
  /*
11
14
  Ignore list for hashing
12
15
  */
@@ -95,11 +98,56 @@ NSString * const IgnoreDSStore = @".DS_Store";
95
98
  {
96
99
  uint8_t digest[CC_SHA256_DIGEST_LENGTH];
97
100
  CC_SHA256(inputData.bytes, (CC_LONG)inputData.length, digest);
101
+ return [self hashStringForDigest:digest];
102
+ }
103
+
104
+ /*
105
+ A file is hashed a chunk at a time, so that hashing one costs no more memory than the
106
+ chunk however large the file is.
107
+ */
108
+ + (NSString *)computeHashForFileAtPath:(NSString *)filePath
109
+ {
110
+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
111
+ if (!fileHandle) {
112
+ CPLog(@"Unable to open %@ to hash it.", filePath);
113
+ return nil;
114
+ }
115
+
116
+ CC_SHA256_CTX context;
117
+ CC_SHA256_Init(&context);
118
+
119
+ while (YES) {
120
+ @autoreleasepool {
121
+ NSError *error = nil;
122
+ NSData *chunk = [fileHandle readDataUpToLength:HashChunkSize error:&error];
123
+ if (!chunk) {
124
+ CPLog(@"Unable to read %@ to hash it: %@", filePath, error);
125
+ [fileHandle closeAndReturnError:nil];
126
+ return nil;
127
+ }
128
+
129
+ if (chunk.length == 0) {
130
+ break;
131
+ }
132
+
133
+ CC_SHA256_Update(&context, chunk.bytes, (CC_LONG)chunk.length);
134
+ }
135
+ }
136
+
137
+ [fileHandle closeAndReturnError:nil];
138
+
139
+ uint8_t digest[CC_SHA256_DIGEST_LENGTH];
140
+ CC_SHA256_Final(digest, &context);
141
+ return [self hashStringForDigest:digest];
142
+ }
143
+
144
+ + (NSString *)hashStringForDigest:(const uint8_t *)digest
145
+ {
98
146
  NSMutableString* inputHash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
99
147
  for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
100
148
  [inputHash appendFormat:@"%02x", digest[i]];
101
149
  }
102
-
150
+
103
151
  return inputHash;
104
152
  }
105
153
 
@@ -7,7 +7,7 @@
7
7
  objects = {
8
8
 
9
9
  /* Begin PBXBuildFile section */
10
- 13BE3DEE1AC21097009241FE /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; };
10
+ 13BE3DEE1AC21097009241FE /* CodePush.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.mm */; };
11
11
  1B23B9141BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */; };
12
12
  1B762E901C9A5E9A006EF800 /* CodePushErrorUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */; };
13
13
  1BCC09A71CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCC09A61CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m */; };
@@ -82,7 +82,7 @@
82
82
  5498D8F61D21F14100B5EB43 /* CodePushUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5498D8F51D21F14100B5EB43 /* CodePushUtils.m */; };
83
83
  54FFEDE01BF550630061DD23 /* CodePushDownloadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */; };
84
84
  6463C82D1EBA0CFB0095B8CD /* CodePushUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5498D8F51D21F14100B5EB43 /* CodePushUtils.m */; };
85
- 6463C82E1EBA0CFB0095B8CD /* CodePush.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.m */; };
85
+ 6463C82E1EBA0CFB0095B8CD /* CodePush.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* CodePush.mm */; };
86
86
  6463C82F1EBA0CFB0095B8CD /* CodePushConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D51F391B6181C2000DA084 /* CodePushConfig.m */; };
87
87
  6463C8301EBA0CFB0095B8CD /* CodePushDownloadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */; };
88
88
  6463C8311EBA0CFB0095B8CD /* CodePushErrorUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */; };
@@ -125,7 +125,7 @@
125
125
  /* Begin PBXFileReference section */
126
126
  134814201AA4EA6300B7C361 /* libCodePush.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCodePush.a; sourceTree = BUILT_PRODUCTS_DIR; };
127
127
  13BE3DEC1AC21097009241FE /* CodePush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CodePush.h; path = CodePush/CodePush.h; sourceTree = "<group>"; };
128
- 13BE3DED1AC21097009241FE /* CodePush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePush.m; path = CodePush/CodePush.m; sourceTree = "<group>"; };
128
+ 13BE3DED1AC21097009241FE /* CodePush.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CodePush.mm; path = CodePush/CodePush.mm; sourceTree = "<group>"; };
129
129
  1B23B9131BF9267B000BB2F0 /* RCTConvert+CodePushInstallMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCTConvert+CodePushInstallMode.m"; path = "CodePush/RCTConvert+CodePushInstallMode.m"; sourceTree = "<group>"; };
130
130
  1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CodePushErrorUtils.m; path = CodePush/CodePushErrorUtils.m; sourceTree = "<group>"; };
131
131
  1BCC09A61CC19EB700DDC0DD /* RCTConvert+CodePushUpdateState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCTConvert+CodePushUpdateState.m"; path = "CodePush/RCTConvert+CodePushUpdateState.m"; sourceTree = "<group>"; };
@@ -277,7 +277,7 @@
277
277
  FF90DEF92C5A808600CA8692 /* PrivacyInfo.xcprivacy */,
278
278
  5498D8F51D21F14100B5EB43 /* CodePushUtils.m */,
279
279
  13BE3DEC1AC21097009241FE /* CodePush.h */,
280
- 13BE3DED1AC21097009241FE /* CodePush.m */,
280
+ 13BE3DED1AC21097009241FE /* CodePush.mm */,
281
281
  81D51F391B6181C2000DA084 /* CodePushConfig.m */,
282
282
  54FFEDDF1BF550630061DD23 /* CodePushDownloadHandler.m */,
283
283
  1B762E8F1C9A5E9A006EF800 /* CodePushErrorUtils.m */,
@@ -451,7 +451,7 @@
451
451
  3221E4912C8ABE1400268379 /* SSZipArchive.m in Sources */,
452
452
  5421FE311C58AD5A00986A55 /* CodePushTelemetryManager.m in Sources */,
453
453
  3221E46F2C8ABE1300268379 /* mz_strm_wzaes.c in Sources */,
454
- 13BE3DEE1AC21097009241FE /* CodePush.m in Sources */,
454
+ 13BE3DEE1AC21097009241FE /* CodePush.mm in Sources */,
455
455
  3221E45D2C8ABE1300268379 /* mz_zip.c in Sources */,
456
456
  1B762E901C9A5E9A006EF800 /* CodePushErrorUtils.m in Sources */,
457
457
  3221E4832C8ABE1400268379 /* mz_strm_split.c in Sources */,
@@ -473,7 +473,7 @@
473
473
  3221E4822C8ABE1400268379 /* mz_strm_buf.c in Sources */,
474
474
  3221E45E2C8ABE1300268379 /* mz_zip.c in Sources */,
475
475
  6463C82D1EBA0CFB0095B8CD /* CodePushUtils.m in Sources */,
476
- 6463C82E1EBA0CFB0095B8CD /* CodePush.m in Sources */,
476
+ 6463C82E1EBA0CFB0095B8CD /* CodePush.mm in Sources */,
477
477
  6463C82F1EBA0CFB0095B8CD /* CodePushConfig.m in Sources */,
478
478
  6463C8301EBA0CFB0095B8CD /* CodePushDownloadHandler.m in Sources */,
479
479
  6463C8311EBA0CFB0095B8CD /* CodePushErrorUtils.m in Sources */,
package/package.json CHANGED
@@ -1,11 +1,24 @@
1
1
  {
2
2
  "name": "@bravemobile/react-native-code-push",
3
- "version": "12.4.1",
3
+ "version": "13.1.0-beta.0",
4
4
  "description": "React Native plugin for the CodePush service",
5
5
  "main": "src/CodePush.js",
6
6
  "react-native": "src/CodePush.js",
7
7
  "source": "src/CodePush.js",
8
8
  "types": "typings/react-native-code-push.d.ts",
9
+ "codegenConfig": {
10
+ "name": "RNCodePushSpec",
11
+ "type": "modules",
12
+ "jsSrcsDir": "src/specs",
13
+ "android": {
14
+ "javaPackageName": "com.microsoft.codepush.react"
15
+ },
16
+ "ios": {
17
+ "modulesProvider": {
18
+ "CodePush": "CodePush"
19
+ }
20
+ }
21
+ },
9
22
  "homepage": "https://microsoft.github.io/code-push",
10
23
  "keywords": [
11
24
  "react-native",
@@ -29,15 +42,19 @@
29
42
  "bin/code-push.js",
30
43
  "android",
31
44
  "ios",
45
+ "cpp",
32
46
  "cli/dist",
33
47
  "cli/**/*.ts",
34
48
  "!cli/node_modules",
49
+ "scripts/export-embedded-bundle.sh",
50
+ "scripts/binary-patch/build-hdiffpatch.sh",
35
51
  "expo",
36
52
  "typings",
37
53
  "*.podspec",
38
54
  "react-native.config.js",
39
55
  "app.plugin.js",
40
56
  "package.json",
57
+ "THIRD-PARTY-NOTICES",
41
58
  "!ios/build",
42
59
  "!android/build",
43
60
  "!android/app/build",
@@ -45,6 +62,7 @@
45
62
  "!android/gradlew",
46
63
  "!android/gradlew.bat",
47
64
  "!android/local.properties",
65
+ "!cpp/binarypatch/host",
48
66
  "!**/__tests__",
49
67
  "!**/__fixtures__",
50
68
  "!**/__mocks__",
@@ -54,17 +72,20 @@
54
72
  "setup": "npm install --quiet --no-progress",
55
73
  "setup-example-app": "ts-node --project scripts/setupExampleApp/tsconfig.json scripts/setupExampleApp/runSetupExampleApp.ts",
56
74
  "setup-expo-example-app": "ts-node --project scripts/setupExampleApp/tsconfig.json scripts/setupExpoExampleApp/runSetupExpoExampleApp.ts",
75
+ "type:src": "tsc --project tsconfig.json --noEmit",
57
76
  "type:cli": "npm run --workspace cli typecheck",
58
77
  "type:scripts": "tsc --project scripts/tsconfig.json --noEmit",
59
78
  "type:e2e": "tsc --project e2e/tsconfig.json --noEmit",
60
- "typecheck": "npm run type:cli && npm run type:scripts && npm run type:e2e",
79
+ "typecheck": "npm run type:src && npm run type:cli && npm run type:scripts && npm run type:e2e",
61
80
  "build:cli": "npm run --workspace cli clean && npm run --workspace cli build",
62
81
  "prepack": "npm run build:cli",
63
- "publish": "npm publish --access=public",
64
82
  "eslint": "eslint --quiet .",
65
- "jest": "jest src/versioning/* expo/* && npm run --workspace cli test",
83
+ "jest": "jest src/CodePush.test.js src/versioning/* expo/* && npm run --workspace cli test",
66
84
  "e2e": "ts-node --project e2e/tsconfig.json e2e/run.ts"
67
85
  },
86
+ "publishConfig": {
87
+ "access": "public"
88
+ },
68
89
  "repository": {
69
90
  "type": "git",
70
91
  "url": "git+https://github.com/Soomgo-Mobile/react-native-code-push.git"
@@ -94,6 +115,7 @@
94
115
  "@eslint/js": "^9.13.0",
95
116
  "@types/express": "^5.0.6",
96
117
  "@types/node": "^18.19.129",
118
+ "@types/react": "19.2.14",
97
119
  "@types/semver": "^7.5.8",
98
120
  "@types/shelljs": "^0.8.15",
99
121
  "@types/yauzl": "^2.10.3",
@@ -103,6 +125,8 @@
103
125
  "express": "latest",
104
126
  "globals": "^15.11.0",
105
127
  "jest": "^29.7.0",
128
+ "react": "19.2.3",
129
+ "react-native": "0.84.1",
106
130
  "ts-node": "^10.9.2",
107
131
  "typescript": "5.0.4",
108
132
  "typescript-eslint": "^8.11.0"
@@ -5,7 +5,9 @@ module.exports = {
5
5
  packageImportPath: "import com.microsoft.codepush.react.CodePush;",
6
6
  packageInstance:
7
7
  "CodePush.getInstance(getApplicationContext(), BuildConfig.DEBUG)",
8
- sourceDir: './android/app'
8
+ sourceDir: './android/app',
9
+ libraryName: 'RNCodePushSpec',
10
+ cmakeListsPath: 'build/generated/source/codegen/jni/CMakeLists.txt',
9
11
  }
10
12
  }
11
13
  }
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Builds the `hdiffz` / `hpatchz` command line tools that the CodePush CLI uses to
4
+ # generate and verify binary patches, and installs them into `.hdiffpatch-tools/`
5
+ # (git-ignored) at the repository root.
6
+ #
7
+ # The tools are built from source instead of being vendored as prebuilt binaries so
8
+ # that the exact upstream revision is reproducible on every platform and no opaque
9
+ # executable is committed to the repository.
10
+ #
11
+ # The `make` flags below disable every codec except zstd. That keeps the build to a
12
+ # single extra dependency (sisong/zstd, which must sit next to the HDiffPatch clone)
13
+ # and matches the patch format the appliers support: `hdiffz -f -m-6 -c-zstd-21-24`.
14
+ #
15
+ # Usage:
16
+ # scripts/binary-patch/build-hdiffpatch.sh [--force]
17
+ #
18
+ # Environment:
19
+ # HDIFFPATCH_TOOLS_DIR install directory (default: <repo>/.hdiffpatch-tools)
20
+ #
21
+ set -euo pipefail
22
+
23
+ HDIFFPATCH_REPO="https://github.com/sisong/HDiffPatch.git"
24
+ HDIFFPATCH_TAG="v5.1.3"
25
+ # sisong/zstd is a fork of facebook/zstd that HDiffPatch's makefile expects as a
26
+ # sibling directory. It publishes no release tags, so it is pinned by commit: an
27
+ # unpinned default branch would silently change the generator on any upstream push,
28
+ # and this SHA is the snapshot the vendored decompressor sources came from.
29
+ ZSTD_REPO="https://github.com/sisong/zstd.git"
30
+ ZSTD_COMMIT="68c88c7c7ad22b5e6882a5296ef96d27dc8750c4"
31
+
32
+ force=0
33
+ for arg in "$@"; do
34
+ case "$arg" in
35
+ --force) force=1 ;;
36
+ *)
37
+ echo "error: unknown argument: $arg" >&2
38
+ echo "usage: $0 [--force]" >&2
39
+ exit 2
40
+ ;;
41
+ esac
42
+ done
43
+
44
+ script_dir=$(cd "$(dirname "$0")" && pwd)
45
+ repo_root=$(cd "$script_dir/../.." && pwd)
46
+ tools_dir="${HDIFFPATCH_TOOLS_DIR:-$repo_root/.hdiffpatch-tools}"
47
+
48
+ if [ "$force" -eq 0 ] && [ -x "$tools_dir/hdiffz" ] && [ -x "$tools_dir/hpatchz" ]; then
49
+ echo "hdiffz/hpatchz already present in $tools_dir (pass --force to rebuild)"
50
+ exit 0
51
+ fi
52
+
53
+ for tool in git make cc c++; do
54
+ if ! command -v "$tool" >/dev/null 2>&1; then
55
+ echo "error: '$tool' is required to build hdiffz/hpatchz" >&2
56
+ exit 1
57
+ fi
58
+ done
59
+
60
+ work_dir=$(mktemp -d)
61
+ cleanup() { rm -rf "$work_dir"; }
62
+ trap cleanup EXIT
63
+
64
+ echo "cloning $HDIFFPATCH_REPO @ $HDIFFPATCH_TAG"
65
+ git clone --quiet --depth 1 --branch "$HDIFFPATCH_TAG" "$HDIFFPATCH_REPO" "$work_dir/HDiffPatch"
66
+ echo "cloning $ZSTD_REPO @ $ZSTD_COMMIT"
67
+ # `git clone --branch` does not take a commit, so the pinned commit is fetched into an
68
+ # empty repository instead - still a single-commit download.
69
+ git init --quiet "$work_dir/zstd"
70
+ git -C "$work_dir/zstd" remote add origin "$ZSTD_REPO"
71
+ git -C "$work_dir/zstd" fetch --quiet --depth 1 origin "$ZSTD_COMMIT"
72
+ git -C "$work_dir/zstd" checkout --quiet FETCH_HEAD
73
+
74
+ jobs=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)
75
+ echo "building hdiffz/hpatchz (zstd only, $jobs jobs)"
76
+ make -C "$work_dir/HDiffPatch" \
77
+ BSD=0 LDEF=0 LZMA=0 MD5=0 VCD=0 BZIP2=0 ZLIB=0 ZSTD=1 DIR_DIFF=0 XXH=0 \
78
+ -j"$jobs"
79
+
80
+ mkdir -p "$tools_dir"
81
+ cp "$work_dir/HDiffPatch/hdiffz" "$work_dir/HDiffPatch/hpatchz" "$tools_dir/"
82
+ chmod +x "$tools_dir/hdiffz" "$tools_dir/hpatchz"
83
+
84
+ echo "installed:"
85
+ echo " $tools_dir/hdiffz"
86
+ echo " $tools_dir/hpatchz"
@@ -0,0 +1,76 @@
1
+ #!/bin/bash
2
+ #
3
+ # Exports the JS bundle an Xcode build embeds in the app, so a later CodePush release can
4
+ # compute a binary patch against exactly the bytes the store binary ships.
5
+ #
6
+ # Call it at the end of the "Bundle React Native code and images" build phase:
7
+ #
8
+ # "$SRCROOT/../node_modules/@bravemobile/react-native-code-push/scripts/export-embedded-bundle.sh"
9
+ #
10
+ # The bundle and a `binary-patch-base.json` record describing it land in
11
+ # `$BUILD_DIR/codepush/embedded-bundle/$CONFIGURATION-$PLATFORM_NAME/`. Set
12
+ # CODEPUSH_EXPORT_DIR to export somewhere else; the `$CONFIGURATION-$PLATFORM_NAME`
13
+ # directory is appended to it either way, so builds of different configurations never
14
+ # overwrite each other.
15
+ #
16
+ # Builds that embed no bundle - Debug for the simulator, or any build run with
17
+ # SKIP_BUNDLING - have nothing to export, and the script exits without doing anything.
18
+
19
+ set -euo pipefail
20
+
21
+ if [[ -z "${CONFIGURATION_BUILD_DIR:-}" || -z "${UNLOCALIZED_RESOURCES_FOLDER_PATH:-}" || -z "${BUILD_DIR:-}" || -z "${CONFIGURATION:-}" || -z "${PLATFORM_NAME:-}" ]]; then
22
+ echo "error: export-embedded-bundle.sh must run as an Xcode build phase; the build settings it needs are not set." >&2
23
+ exit 1
24
+ fi
25
+
26
+ # The name React Native gives the bundle it writes into the app, which BUNDLE_NAME renames.
27
+ embedded_bundle="$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/${BUNDLE_NAME:-main}.jsbundle"
28
+
29
+ if [[ ! -f "$embedded_bundle" ]]; then
30
+ exit 0
31
+ fi
32
+
33
+ export_dir="${CODEPUSH_EXPORT_DIR:-$BUILD_DIR/codepush/embedded-bundle}/$CONFIGURATION-$PLATFORM_NAME"
34
+ mkdir -p "$export_dir"
35
+ cp "$embedded_bundle" "$export_dir/"
36
+
37
+ exported_bundle="$export_dir/$(basename "$embedded_bundle")"
38
+ bundle_hash="$(shasum -a 256 "$exported_bundle" | awk '{ print $1 }')"
39
+
40
+ # Read from the built product rather than the source Info.plist, where the versions are
41
+ # still unexpanded build settings.
42
+ binary_version=""
43
+ build_number=""
44
+ info_plist="$CONFIGURATION_BUILD_DIR/${INFOPLIST_PATH:-}"
45
+ if [[ -n "${INFOPLIST_PATH:-}" && -f "$info_plist" ]]; then
46
+ binary_version="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$info_plist" 2>/dev/null || true)"
47
+ build_number="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$info_plist" 2>/dev/null || true)"
48
+ fi
49
+
50
+ # A build outside a git checkout still exports a usable record, just without the commit.
51
+ git_sha="$(git -C "${PROJECT_DIR:-$PWD}" rev-parse HEAD 2>/dev/null || true)"
52
+
53
+ # Record name and field names are a contract shared with the CodePush CLI
54
+ # (cli/functions/makeBinaryPatchBundle.ts) and the Android export script
55
+ # (android/codepush-export.gradle). Keep the three in step.
56
+ record_json=""
57
+
58
+ append_field() {
59
+ local name="$1" value="$2"
60
+ if [[ -z "$value" ]]; then
61
+ return 0
62
+ fi
63
+ if [[ -n "$record_json" ]]; then
64
+ record_json+=","$'\n'
65
+ fi
66
+ record_json+=" \"$name\": \"$value\""
67
+ }
68
+
69
+ append_field baseBundleHash "$bundle_hash"
70
+ append_field binaryVersion "$binary_version"
71
+ append_field buildNumber "$build_number"
72
+ append_field gitSha "$git_sha"
73
+
74
+ printf '{\n%s\n}\n' "$record_json" > "$export_dir/binary-patch-base.json"
75
+
76
+ echo "CodePush: exported the embedded bundle to $exported_bundle"