@bravemobile/react-native-code-push 13.0.0 → 13.1.0-beta.1

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 (109) hide show
  1. package/CodePush.podspec +35 -3
  2. package/README.md +117 -3
  3. package/THIRD-PARTY-NOTICES +96 -0
  4. package/android/app/build.gradle +19 -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 +1 -1
  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 +20 -6
  14. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +168 -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/DownloadProgressCallback.java +7 -0
  17. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressEventDispatcher.java +25 -4
  18. package/android/app/src/main/java/com/microsoft/codepush/react/HDiffPatchNative.java +52 -0
  19. package/android/app/src/test/java/com/microsoft/codepush/react/CodePushBinaryPatchTest.java +393 -0
  20. package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerBinaryPatchTest.java +211 -0
  21. package/android/app/src/test/java/com/microsoft/codepush/react/CodePushUpdateManagerDownloadTest.java +489 -0
  22. package/android/app/src/test/java/com/microsoft/codepush/react/DownloadProgressEventDispatcherTest.java +80 -0
  23. package/android/codepush-export.gradle +115 -0
  24. package/cli/commands/bundleCommand/bundleCodePush.ts +39 -7
  25. package/cli/commands/bundleCommand/index.ts +9 -1
  26. package/cli/commands/releaseCommand/addToReleaseHistory.ts +7 -0
  27. package/cli/commands/releaseCommand/index.test.ts +118 -0
  28. package/cli/commands/releaseCommand/index.ts +20 -2
  29. package/cli/commands/releaseCommand/release.test.ts +582 -0
  30. package/cli/commands/releaseCommand/release.ts +232 -15
  31. package/cli/dist/commands/bundleCommand/bundleCodePush.js +26 -7
  32. package/cli/dist/commands/bundleCommand/index.js +5 -1
  33. package/cli/dist/commands/releaseCommand/addToReleaseHistory.js +6 -1
  34. package/cli/dist/commands/releaseCommand/index.js +8 -1
  35. package/cli/dist/commands/releaseCommand/index.test.js +96 -0
  36. package/cli/dist/commands/releaseCommand/release.js +165 -17
  37. package/cli/dist/commands/releaseCommand/release.test.js +367 -0
  38. package/cli/dist/functions/makeBinaryPatchBundle.js +248 -0
  39. package/cli/dist/functions/makeBinaryPatchBundle.test.js +321 -0
  40. package/cli/dist/functions/makeCodePushBundle.js +4 -2
  41. package/cli/dist/functions/runHermesEmitBinaryCommand.js +48 -1
  42. package/cli/dist/functions/runHermesEmitBinaryCommand.test.js +100 -0
  43. package/cli/dist/jest.globalSetup.js +38 -0
  44. package/cli/dist/utils/binaryPatch.js +86 -0
  45. package/cli/dist/utils/binaryPatch.test.js +190 -0
  46. package/cli/functions/makeBinaryPatchBundle.test.ts +410 -0
  47. package/cli/functions/makeBinaryPatchBundle.ts +367 -0
  48. package/cli/functions/makeCodePushBundle.ts +4 -2
  49. package/cli/functions/runHermesEmitBinaryCommand.test.ts +124 -0
  50. package/cli/functions/runHermesEmitBinaryCommand.ts +57 -1
  51. package/cli/jest.globalSetup.ts +43 -0
  52. package/cli/utils/binaryPatch.test.ts +234 -0
  53. package/cli/utils/binaryPatch.ts +103 -0
  54. package/cpp/binarypatch/binarypatch_zstd_decompressor.c +187 -0
  55. package/cpp/binarypatch/binarypatch_zstd_decompressor.h +47 -0
  56. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/checksum_plugin.h +52 -0
  57. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/hpatch_mt/hpatch_mt.h +147 -0
  58. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.c +3315 -0
  59. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch.h +268 -0
  60. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_private.h +297 -0
  61. package/cpp/binarypatch/vendor/HDiffPatch/libHDiffPatch/HPatch/patch_types.h +434 -0
  62. package/cpp/binarypatch/vendor/zstd/common/allocations.h +55 -0
  63. package/cpp/binarypatch/vendor/zstd/common/bits.h +205 -0
  64. package/cpp/binarypatch/vendor/zstd/common/bitstream.h +454 -0
  65. package/cpp/binarypatch/vendor/zstd/common/compiler.h +464 -0
  66. package/cpp/binarypatch/vendor/zstd/common/cpu.h +249 -0
  67. package/cpp/binarypatch/vendor/zstd/common/debug.c +30 -0
  68. package/cpp/binarypatch/vendor/zstd/common/debug.h +107 -0
  69. package/cpp/binarypatch/vendor/zstd/common/entropy_common.c +340 -0
  70. package/cpp/binarypatch/vendor/zstd/common/error_private.c +64 -0
  71. package/cpp/binarypatch/vendor/zstd/common/error_private.h +158 -0
  72. package/cpp/binarypatch/vendor/zstd/common/fse.h +625 -0
  73. package/cpp/binarypatch/vendor/zstd/common/fse_decompress.c +315 -0
  74. package/cpp/binarypatch/vendor/zstd/common/huf.h +277 -0
  75. package/cpp/binarypatch/vendor/zstd/common/mem.h +422 -0
  76. package/cpp/binarypatch/vendor/zstd/common/pool.c +371 -0
  77. package/cpp/binarypatch/vendor/zstd/common/pool.h +81 -0
  78. package/cpp/binarypatch/vendor/zstd/common/portability_macros.h +171 -0
  79. package/cpp/binarypatch/vendor/zstd/common/threading.c +182 -0
  80. package/cpp/binarypatch/vendor/zstd/common/threading.h +142 -0
  81. package/cpp/binarypatch/vendor/zstd/common/xxhash.c +18 -0
  82. package/cpp/binarypatch/vendor/zstd/common/xxhash.h +7094 -0
  83. package/cpp/binarypatch/vendor/zstd/common/zstd_common.c +48 -0
  84. package/cpp/binarypatch/vendor/zstd/common/zstd_deps.h +123 -0
  85. package/cpp/binarypatch/vendor/zstd/common/zstd_internal.h +324 -0
  86. package/cpp/binarypatch/vendor/zstd/common/zstd_trace.h +156 -0
  87. package/cpp/binarypatch/vendor/zstd/decompress/huf_decompress.c +1944 -0
  88. package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.c +244 -0
  89. package/cpp/binarypatch/vendor/zstd/decompress/zstd_ddict.h +44 -0
  90. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress.c +2410 -0
  91. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.c +2209 -0
  92. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_block.h +73 -0
  93. package/cpp/binarypatch/vendor/zstd/decompress/zstd_decompress_internal.h +240 -0
  94. package/cpp/binarypatch/vendor/zstd/zstd.h +3206 -0
  95. package/cpp/binarypatch/vendor/zstd/zstd_errors.h +107 -0
  96. package/ios/CodePush/CodePush.h +18 -1
  97. package/ios/CodePush/CodePush.mm +24 -1
  98. package/ios/CodePush/CodePushBinaryPatch.h +80 -0
  99. package/ios/CodePush/CodePushBinaryPatch.m +473 -0
  100. package/ios/CodePush/CodePushDownloadHandler.m +6 -0
  101. package/ios/CodePush/CodePushPackage.m +183 -2
  102. package/ios/CodePush/CodePushUpdateUtils.m +49 -1
  103. package/package.json +10 -3
  104. package/scripts/binary-patch/build-hdiffpatch.sh +86 -0
  105. package/scripts/export-embedded-bundle.sh +76 -0
  106. package/src/CodePush.js +46 -3
  107. package/src/CodePush.test.js +382 -0
  108. package/src/package-mixins.js +20 -2
  109. package/typings/react-native-code-push.d.ts +80 -1
@@ -4,29 +4,49 @@ import { prepareToBundleJS } from "../../functions/prepareToBundleJS.js";
4
4
  import { runReactNativeBundleCommand } from "../../functions/runReactNativeBundleCommand.js";
5
5
  import { runExpoBundleCommand } from "../../functions/runExpoBundleCommand.js";
6
6
  import { getReactTempDir } from "../../functions/getReactTempDir.js";
7
- import { runHermesEmitBinaryCommand } from "../../functions/runHermesEmitBinaryCommand.js";
7
+ import { resolveBaseBytecodeHermesFlags, runHermesEmitBinaryCommand } from "../../functions/runHermesEmitBinaryCommand.js";
8
8
  import { makeCodePushBundle } from "../../functions/makeCodePushBundle.js";
9
+ import { hashBundleFile, writeBinaryPatchBaseRecord } from "../../functions/makeBinaryPatchBundle.js";
9
10
  import { ROOT_OUTPUT_DIR, ENTRY_FILE } from "../../constant.js";
10
11
 
12
+ export type CodePushBundleResult = {
13
+ /** CodePush bundle file name (equals to packageHash) */
14
+ bundleFileName: string;
15
+ /** Directory holding the files that were packed into the CodePush bundle file */
16
+ contentsPath: string;
17
+ /** JS bundle file name inside the contents directory */
18
+ jsBundleName: string;
19
+ };
20
+
11
21
  /**
12
- * @return {Promise<string>} CodePush bundle file name (equals to packageHash)
22
+ * JS bundle file name react-native writes, which is also the name the app looks for
23
+ * inside an update, so it has to be decided the same way everywhere.
24
+ */
25
+ export function resolveJsBundleName(platform: 'ios' | 'android', jsBundleName?: string): string {
26
+ const DEFAULT_JS_BUNDLE_NAME = platform === 'ios' ? 'main.jsbundle' : 'index.android.bundle';
27
+ return jsBundleName || DEFAULT_JS_BUNDLE_NAME;
28
+ }
29
+
30
+ /**
31
+ * @param baseBundlePath {string} JS bundle from the target binary. When given, the compilation is aligned with it and the base is recorded for a later `release`.
32
+ * @return {Promise<CodePushBundleResult>} CodePush bundle file name (equals to packageHash) and the contents it was made of
13
33
  */
14
34
  export async function bundleCodePush(
15
35
  framework: 'expo' | undefined,
16
36
  platform: 'ios' | 'android' = 'ios',
17
37
  outputRootPath: string = ROOT_OUTPUT_DIR,
18
38
  entryFile: string = ENTRY_FILE,
19
- jsBundleName: string, // JS bundle file name (not CodePush bundle file)
39
+ jsBundleName: string | undefined, // JS bundle file name (not CodePush bundle file)
20
40
  bundleDirectory: string, // CodePush bundle output directory
21
41
  outputMetroDir?: string,
22
- ): Promise<string> {
42
+ baseBundlePath?: string,
43
+ ): Promise<CodePushBundleResult> {
23
44
  if (fs.existsSync(outputRootPath)) {
24
45
  fs.rmSync(outputRootPath, { recursive: true });
25
46
  }
26
47
 
27
48
  const OUTPUT_CONTENT_PATH = `${outputRootPath}/CodePush`;
28
- const DEFAULT_JS_BUNDLE_NAME = platform === 'ios' ? 'main.jsbundle' : 'index.android.bundle';
29
- const _jsBundleName = jsBundleName || DEFAULT_JS_BUNDLE_NAME; // react-native JS bundle output name
49
+ const _jsBundleName = resolveJsBundleName(platform, jsBundleName); // react-native JS bundle output name
30
50
  const SOURCEMAP_OUTPUT = `${outputRootPath}/${_jsBundleName}.map`;
31
51
 
32
52
  prepareToBundleJS({ deleteDirs: [outputRootPath, getReactTempDir()], makeDir: OUTPUT_CONTENT_PATH });
@@ -57,13 +77,25 @@ export async function bundleCodePush(
57
77
  _jsBundleName,
58
78
  OUTPUT_CONTENT_PATH,
59
79
  SOURCEMAP_OUTPUT,
80
+ baseBundlePath ? resolveBaseBytecodeHermesFlags(baseBundlePath) : [],
60
81
  );
61
82
  console.log('log: Hermes compilation complete');
62
83
 
63
84
  const { bundleFileName: codePushBundleFileName } = await makeCodePushBundle(OUTPUT_CONTENT_PATH, bundleDirectory);
64
85
  console.log(`log: CodePush bundle created (file path: ./${bundleDirectory}/${codePushBundleFileName})`);
65
86
 
66
- return codePushBundleFileName;
87
+ if (baseBundlePath) {
88
+ // Written after the bundle file, and to the output root instead of the update
89
+ // contents, so recording the base cannot change what was just packed or its hash.
90
+ const recordPath = writeBinaryPatchBaseRecord(outputRootPath, hashBundleFile(baseBundlePath));
91
+ console.log(`log: Binary patch base recorded (file path: ${recordPath})`);
92
+ }
93
+
94
+ return {
95
+ bundleFileName: codePushBundleFileName,
96
+ contentsPath: OUTPUT_CONTENT_PATH,
97
+ jsBundleName: _jsBundleName,
98
+ };
67
99
  }
68
100
 
69
101
  function copyMetroOutputsIfNeeded(
@@ -1,5 +1,6 @@
1
1
  import { program, Option } from "commander";
2
2
  import { bundleCodePush } from "./bundleCodePush.js";
3
+ import { resolveBinaryBundlePathOption } from "../../functions/makeBinaryPatchBundle.js";
3
4
  import { OUTPUT_BUNDLE_DIR, ROOT_OUTPUT_DIR, ENTRY_FILE } from "../../constant.js";
4
5
 
5
6
  type Options = {
@@ -7,9 +8,11 @@ type Options = {
7
8
  platform: 'ios' | 'android';
8
9
  outputPath: string;
9
10
  entryFile: string;
10
- bundleName: string;
11
+ // Commander derives this from the "-b, --bundle-name" flag.
12
+ bundleName?: string;
11
13
  outputBundleDir: string;
12
14
  outputMetroDir?: string;
15
+ binaryBundlePath?: string;
13
16
  }
14
17
 
15
18
  program.command('bundle')
@@ -21,7 +24,11 @@ program.command('bundle')
21
24
  .option('-b, --bundle-name <string>', 'bundle file name (default-ios: "main.jsbundle" / default-android: "index.android.bundle")')
22
25
  .option('--output-metro-dir <string>', 'name of directory to copy the Metro JS bundle and sourcemap before Hermes compilation')
23
26
  .option('--output-bundle-dir <string>', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR)
27
+ .option('--binary-bundle-path <string>', 'path to the JS bundle of the target binary. Aligns the Hermes compilation with it and records it as the binary patch base.')
24
28
  .action((options: Options) => {
29
+ // Resolved before bundling so a wrong path fails now rather than after a build.
30
+ const baseBundlePath = resolveBinaryBundlePathOption(options.binaryBundlePath);
31
+
25
32
  bundleCodePush(
26
33
  options.framework,
27
34
  options.platform,
@@ -30,5 +37,6 @@ program.command('bundle')
30
37
  options.bundleName,
31
38
  `${options.outputPath}/${options.outputBundleDir}`,
32
39
  options.outputMetroDir,
40
+ baseBundlePath,
33
41
  )
34
42
  });
@@ -6,6 +6,7 @@ export async function addToReleaseHistory(
6
6
  appVersion: string,
7
7
  binaryVersion: string,
8
8
  bundleDownloadUrl: string,
9
+ binaryPatchDownloadUrl: string | undefined,
9
10
  packageHash: string,
10
11
  getReleaseHistory: CliConfigInterface['getReleaseHistory'],
11
12
  setReleaseHistory: CliConfigInterface['setReleaseHistory'],
@@ -32,6 +33,12 @@ export async function addToReleaseHistory(
32
33
  packageHash: packageHash,
33
34
  };
34
35
 
36
+ // A release without a binary patch says nothing about one, so that a client reading
37
+ // this history downloads the full bundle exactly as it did before patches existed.
38
+ if (binaryPatchDownloadUrl) {
39
+ newReleaseHistory[appVersion].binaryPatchDownloadUrl = binaryPatchDownloadUrl;
40
+ }
41
+
35
42
  if (typeof rollout === 'number') {
36
43
  newReleaseHistory[appVersion].rollout = rollout;
37
44
  }
@@ -0,0 +1,118 @@
1
+ import path from "path";
2
+ import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals";
3
+
4
+ /**
5
+ * Checks the command definition against the arguments it forwards, which is where an
6
+ * option name can silently drift: commander derives the name it stores from the flag,
7
+ * so reading a differently named property yields `undefined` for every release rather
8
+ * than an error, and every downstream default looks as if it had been chosen.
9
+ */
10
+
11
+ jest.mock("./release.js");
12
+ jest.mock("../../utils/fsUtils.js", () => ({
13
+ findAndReadConfigFile: () => ({
14
+ bundleUploader: async () => ({ downloadUrl: 'https://cdn.example.com/bundle' }),
15
+ getReleaseHistory: async () => ({}),
16
+ setReleaseHistory: async () => undefined,
17
+ }),
18
+ }));
19
+
20
+ /**
21
+ * `release()` takes positional arguments; these are the positions this suite asserts on.
22
+ */
23
+ const ARG_INDEX = {
24
+ platform: 6,
25
+ outputPath: 8,
26
+ entryFile: 9,
27
+ jsBundleName: 10,
28
+ skipBundle: 14,
29
+ bundleDirectory: 16,
30
+ baseBundlePath: 19,
31
+ onOversizedPatch: 20,
32
+ } as const;
33
+
34
+ /**
35
+ * Parses a `release` invocation against the real command definition. Commander is asked
36
+ * to throw instead of exiting, and to keep its diagnostics to itself, so a rejected
37
+ * option can be asserted on without ending the worker or the output.
38
+ */
39
+ async function parseReleaseCommand(args: string[]): Promise<void> {
40
+ const { program } = await import("commander");
41
+ await import("./index.js");
42
+
43
+ const releaseCommand = program.commands.find((command) => command.name() === 'release');
44
+ releaseCommand?.exitOverride();
45
+ releaseCommand?.configureOutput({ writeErr: () => {} });
46
+
47
+ await program.parseAsync(['release', ...args], { from: 'user' });
48
+ }
49
+
50
+ async function runReleaseCommand(args: string[]): Promise<unknown[]> {
51
+ const { release } = await import("./release.js");
52
+
53
+ await parseReleaseCommand(args);
54
+
55
+ const releaseMock = jest.mocked(release);
56
+ expect(releaseMock).toHaveBeenCalledTimes(1);
57
+ return releaseMock.mock.calls[0];
58
+ }
59
+
60
+ beforeEach(() => {
61
+ jest.resetModules();
62
+ jest.spyOn(console, 'log').mockImplementation(() => {});
63
+ });
64
+
65
+ afterEach(() => {
66
+ jest.restoreAllMocks();
67
+ });
68
+
69
+ /** Relative to the CLI workspace root, which is where the test runner starts. */
70
+ const BASE_BUNDLE_FIXTURE = 'fixtures/binary-patch/base.bundle';
71
+
72
+ describe("release command options", () => {
73
+ it("passes the JS bundle name from -j through to the release", async () => {
74
+ const args = await runReleaseCommand([
75
+ '-b', '1.0.0',
76
+ '-v', '1.0.1',
77
+ '-p', 'android',
78
+ '-j', 'custom.jsbundle',
79
+ '--skip-bundle', 'true',
80
+ '--binary-bundle-path', BASE_BUNDLE_FIXTURE,
81
+ ]);
82
+
83
+ expect(args[ARG_INDEX.jsBundleName]).toBe('custom.jsbundle');
84
+ expect(args[ARG_INDEX.platform]).toBe('android');
85
+ expect(args[ARG_INDEX.skipBundle]).toBe(true);
86
+ expect(args[ARG_INDEX.baseBundlePath]).toBe(path.resolve(BASE_BUNDLE_FIXTURE));
87
+ });
88
+
89
+ it("leaves the JS bundle name unset when -j is not given, so the platform default applies", async () => {
90
+ const args = await runReleaseCommand(['-b', '1.0.0', '-v', '1.0.1']);
91
+
92
+ expect(args[ARG_INDEX.jsBundleName]).toBeUndefined();
93
+ expect(args[ARG_INDEX.baseBundlePath]).toBeUndefined();
94
+ expect(args[ARG_INDEX.outputPath]).toBe('build');
95
+ expect(args[ARG_INDEX.entryFile]).toBe('index.ts');
96
+ expect(args[ARG_INDEX.bundleDirectory]).toBe('build/bundleOutput');
97
+ });
98
+
99
+ it("defaults the oversized patch policy to skipping the patch", async () => {
100
+ const args = await runReleaseCommand(['-b', '1.0.0', '-v', '1.0.1']);
101
+
102
+ expect(args[ARG_INDEX.onOversizedPatch]).toBe('skip');
103
+ });
104
+
105
+ it("passes the chosen oversized patch policy through to the release", async () => {
106
+ const args = await runReleaseCommand(['-b', '1.0.0', '-v', '1.0.1', '--on-oversized-patch', 'fail']);
107
+
108
+ expect(args[ARG_INDEX.onOversizedPatch]).toBe('fail');
109
+ });
110
+
111
+ it("rejects an oversized patch policy it does not know", async () => {
112
+ await expect(parseReleaseCommand(['-b', '1.0.0', '-v', '1.0.1', '--on-oversized-patch', 'ask']))
113
+ .rejects.toThrow(/--on-oversized-patch.*'ask'.*skip, fail/s);
114
+
115
+ const { release } = await import("./release.js");
116
+ expect(jest.mocked(release)).not.toHaveBeenCalled();
117
+ });
118
+ });
@@ -1,6 +1,12 @@
1
1
  import { program, Option } from "commander";
2
2
  import { findAndReadConfigFile } from "../../utils/fsUtils.js";
3
3
  import { release } from "./release.js";
4
+ import {
5
+ DEFAULT_OVERSIZED_PATCH_POLICY,
6
+ OVERSIZED_PATCH_POLICIES,
7
+ resolveBinaryBundlePathOption,
8
+ type OversizedPatchPolicy,
9
+ } from "../../functions/makeBinaryPatchBundle.js";
4
10
  import { OUTPUT_BUNDLE_DIR, CONFIG_FILE_NAME, ROOT_OUTPUT_DIR, ENTRY_FILE } from "../../constant.js";
5
11
 
6
12
  type Options = {
@@ -12,7 +18,8 @@ type Options = {
12
18
  config: string;
13
19
  outputPath: string;
14
20
  entryFile: string;
15
- bundleName: string;
21
+ // Commander derives this from the "-j, --js-bundle-name" flag.
22
+ jsBundleName?: string;
16
23
  mandatory: boolean;
17
24
  enable: boolean;
18
25
  rollout?: number;
@@ -21,6 +28,8 @@ type Options = {
21
28
  outputBundleDir: string;
22
29
  outputMetroDir?: string;
23
30
  hashCalc?: boolean;
31
+ binaryBundlePath?: string;
32
+ onOversizedPatch: OversizedPatchPolicy;
24
33
  }
25
34
 
26
35
  program.command('release')
@@ -42,6 +51,10 @@ program.command('release')
42
51
  .option('--skip-cleanup <bool>', 'skip cleanup process', parseBoolean, false)
43
52
  .option('--output-metro-dir <string>', 'name of directory to copy the Metro JS bundle and sourcemap before Hermes compilation')
44
53
  .option('--output-bundle-dir <string>', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR)
54
+ .option('--binary-bundle-path <string>', 'path to the JS bundle of the target binary. Releases an additional binary patch bundle against it, and aligns the Hermes compilation with it.')
55
+ .addOption(new Option('--on-oversized-patch <policy>', 'what to do when the binary patch bundle is not smaller than the full bundle: "skip" releases the full bundle only, "fail" stops the release before any upload')
56
+ .choices(OVERSIZED_PATCH_POLICIES)
57
+ .default(DEFAULT_OVERSIZED_PATCH_POLICY))
45
58
  .action(async (options: Options) => {
46
59
  const config = findAndReadConfigFile(process.cwd(), options.config);
47
60
 
@@ -55,6 +68,9 @@ program.command('release')
55
68
  process.exit(1);
56
69
  }
57
70
 
71
+ // Resolved before bundling so a wrong path fails now rather than after a build.
72
+ const baseBundlePath = resolveBinaryBundlePathOption(options.binaryBundlePath);
73
+
58
74
  await release(
59
75
  config.bundleUploader,
60
76
  config.getReleaseHistory,
@@ -66,7 +82,7 @@ program.command('release')
66
82
  options.identifier,
67
83
  options.outputPath,
68
84
  options.entryFile,
69
- options.bundleName,
85
+ options.jsBundleName,
70
86
  options.mandatory,
71
87
  options.enable,
72
88
  options.rollout,
@@ -75,6 +91,8 @@ program.command('release')
75
91
  `${options.outputPath}/${options.outputBundleDir}`,
76
92
  options.outputMetroDir,
77
93
  options.hashCalc,
94
+ baseBundlePath,
95
+ options.onOversizedPatch,
78
96
  )
79
97
 
80
98
  console.log('🚀 Release completed.')