@revopush/code-push-cli 0.0.7 → 0.0.8-rc.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.
@@ -203,7 +203,13 @@ function deploymentRemove(commandName, yargs) {
203
203
  yargs
204
204
  .usage(USAGE_PREFIX + " deployment " + commandName + " <appName> <deploymentName>")
205
205
  .demand(/*count*/ 2, /*max*/ 2) // Require exactly two non-option arguments
206
- .example("deployment " + commandName + " MyApp MyDeployment", 'Removes deployment "MyDeployment" from app "MyApp"');
206
+ .example("deployment " + commandName + " MyApp MyDeployment", 'Removes deployment "MyDeployment" from app "MyApp"')
207
+ .option("force", {
208
+ default: false,
209
+ demand: false,
210
+ description: "Bypass confirmation when removing deployments",
211
+ type: "boolean",
212
+ });
207
213
  addCommonConfiguration(yargs);
208
214
  }
209
215
  function deploymentHistory(commandName, yargs) {
@@ -616,6 +622,13 @@ yargs
616
622
  default: null,
617
623
  demand: false,
618
624
  description: "Path to the gradle file which specifies the binary version you want to target this release at (android only).",
625
+ })
626
+ .option("initial", {
627
+ alias: "i",
628
+ default: false,
629
+ demand: false,
630
+ description: "Specifies whether release is initial (base) for given targetBinaryVersion.",
631
+ type: "boolean",
619
632
  })
620
633
  .option("mandatory", {
621
634
  alias: "m",
@@ -730,6 +743,207 @@ yargs
730
743
  return checkValidReleaseOptions(argv);
731
744
  });
732
745
  addCommonConfiguration(yargs);
746
+ })
747
+ .command("release-expo", "Release an Expo / React Native update to an app deployment", (yargs) => {
748
+ isValidCommand = true;
749
+ yargs
750
+ .usage(USAGE_PREFIX + " release-expo <appName> <platform> [options]")
751
+ .demand(/*count*/ 2, /*max*/ 2) // Require exactly two non-option arguments
752
+ .example("release-expo MyApp ios", 'Releases the Expo-managed iOS project in the current working directory to the "MyApp" app\'s "Staging" deployment')
753
+ .example("release-expo MyApp android -d Production", 'Releases the Expo-managed Android project in the current working directory to the "MyApp" app\'s "Production" deployment')
754
+ .option("bundleName", {
755
+ alias: "b",
756
+ default: null,
757
+ demand: false,
758
+ description: 'Name of the generated JS bundle file. If unspecified, the standard bundle name will be used, depending on the specified platform: "main.jsbundle" (iOS), "index.android.bundle" (Android) or "index.windows.bundle" (Windows)',
759
+ type: "string",
760
+ })
761
+ .option("deploymentName", {
762
+ alias: "d",
763
+ default: "Staging",
764
+ demand: false,
765
+ description: "Deployment to release the update to",
766
+ type: "string",
767
+ })
768
+ .option("description", {
769
+ alias: "des",
770
+ default: null,
771
+ demand: false,
772
+ description: "Description of the changes made to the app with this release",
773
+ type: "string",
774
+ })
775
+ .option("development", {
776
+ alias: "dev",
777
+ default: false,
778
+ demand: false,
779
+ description: "Specifies whether to generate a dev or release build",
780
+ type: "boolean",
781
+ })
782
+ .option("disabled", {
783
+ alias: "x",
784
+ default: false,
785
+ demand: false,
786
+ description: "Specifies whether this release should be immediately downloadable",
787
+ type: "boolean",
788
+ })
789
+ .option("entryFile", {
790
+ alias: "e",
791
+ default: null,
792
+ demand: false,
793
+ description: 'Path to the app\'s entry Javascript file. If omitted, "index.<platform>.js" and then "index.js" will be used (if they exist)',
794
+ type: "string",
795
+ })
796
+ .option("gradleFile", {
797
+ alias: "g",
798
+ default: null,
799
+ demand: false,
800
+ description: "Path to the gradle file which specifies the binary version you want to target this release at (android only).",
801
+ })
802
+ .option("initial", {
803
+ alias: "i",
804
+ default: false,
805
+ demand: false,
806
+ description: "Specifies whether release is initial (base) for given targetBinaryVersion.",
807
+ type: "boolean",
808
+ })
809
+ .option("mandatory", {
810
+ alias: "m",
811
+ default: false,
812
+ demand: false,
813
+ description: "Specifies whether this release should be considered mandatory",
814
+ type: "boolean",
815
+ })
816
+ .option("noDuplicateReleaseError", {
817
+ default: false,
818
+ demand: false,
819
+ description: "When this flag is set, releasing a package that is identical to the latest release will produce a warning instead of an error",
820
+ type: "boolean",
821
+ })
822
+ .option("plistFile", {
823
+ alias: "p",
824
+ default: null,
825
+ demand: false,
826
+ description: "Path to the plist file which specifies the binary version you want to target this release at (iOS only).",
827
+ })
828
+ .option("plistFilePrefix", {
829
+ alias: "pre",
830
+ default: null,
831
+ demand: false,
832
+ description: "Prefix to append to the file name when attempting to find your app's Info.plist file (iOS only).",
833
+ })
834
+ .option("rollout", {
835
+ alias: "r",
836
+ default: "100%",
837
+ demand: false,
838
+ description: "Percentage of users this release should be immediately available to",
839
+ type: "string",
840
+ })
841
+ .option("sourcemapOutput", {
842
+ alias: "s",
843
+ default: null,
844
+ demand: false,
845
+ description: "Path to where the sourcemap for the resulting bundle should be written. If omitted, a sourcemap will not be generated.",
846
+ type: "string",
847
+ })
848
+ .option("targetBinaryVersion", {
849
+ alias: "t",
850
+ default: null,
851
+ demand: false,
852
+ description: 'Semver expression that specifies the binary app version(s) this release is targeting (e.g. 1.1.0, ~1.2.3). If omitted, the release will target the exact version specified in the "Info.plist" (iOS), "build.gradle" (Android) or "Package.appxmanifest" (Windows) files.',
853
+ type: "string",
854
+ })
855
+ .option("outputDir", {
856
+ alias: "o",
857
+ default: null,
858
+ demand: false,
859
+ description: "Path to where the bundle and sourcemap should be written. If omitted, a bundle and sourcemap will not be written.",
860
+ type: "string",
861
+ })
862
+ .option("useHermes", {
863
+ alias: "h",
864
+ default: false,
865
+ demand: false,
866
+ description: "Enable hermes and bypass automatic checks",
867
+ type: "boolean",
868
+ })
869
+ .option("podFile", {
870
+ alias: "pod",
871
+ default: null,
872
+ demand: false,
873
+ description: "Path to the cocopods config file (iOS only).",
874
+ type: "string",
875
+ })
876
+ .option("extraHermesFlags", {
877
+ alias: "hf",
878
+ default: [],
879
+ demand: false,
880
+ description: "Flags that get passed to Hermes, JavaScript to bytecode compiler. Can be specified multiple times.",
881
+ type: "array",
882
+ })
883
+ .option("privateKeyPath", {
884
+ alias: "k",
885
+ default: null,
886
+ demand: false,
887
+ description: "Path to private key used for code signing.",
888
+ type: "string",
889
+ })
890
+ .option("xcodeProjectFile", {
891
+ alias: "xp",
892
+ default: null,
893
+ demand: false,
894
+ description: "Path to the Xcode project or project.pbxproj file",
895
+ type: "string",
896
+ })
897
+ .option("xcodeTargetName", {
898
+ alias: "xt",
899
+ default: undefined,
900
+ demand: false,
901
+ description: "Name of target (PBXNativeTarget) which specifies the binary version you want to target this release at (iOS only)",
902
+ type: "string",
903
+ })
904
+ .option("buildConfigurationName", {
905
+ alias: "c",
906
+ default: undefined,
907
+ demand: false,
908
+ description: "Name of build configuration which specifies the binary version you want to target this release at. For example, 'Debug' or 'Release' (iOS only)",
909
+ type: "string",
910
+ })
911
+ .option("extraBundlerOption", {
912
+ alias: "eo",
913
+ default: [],
914
+ demand: false,
915
+ description: "Option that gets passed to react-native bundler. Can be specified multiple times.",
916
+ type: "array",
917
+ })
918
+ .check((argv) => {
919
+ return checkValidReleaseOptions(argv);
920
+ });
921
+ addCommonConfiguration(yargs);
922
+ })
923
+ .command("release-native", "Release a binary update to an app deployment", (yargs) => {
924
+ yargs
925
+ .usage(USAGE_PREFIX + " release-native <appName> <platform> <targetBinary> [options]")
926
+ .demand(/*count*/ 3, /*max*/ 3) // Require exactly three non-option arguments
927
+ .example("release-native MyApp ios ./app.ipa", 'Releases the React Native iOS project from "./app.ipa" to the "MyApp" app\'s "Staging" deployment')
928
+ .example("release-native MyApp android ./app.apk -d Production", 'Releases the React Native Android project from "./app.apk" to the "MyApp" app\'s "Production" deployment')
929
+ .option("deploymentName", {
930
+ alias: "d",
931
+ default: "Staging",
932
+ demand: false,
933
+ description: "Deployment to release the update to",
934
+ type: "string",
935
+ })
936
+ .option("targetBinaryVersion", {
937
+ alias: "t",
938
+ default: null,
939
+ demand: false,
940
+ description: "Semver expression that specifies the binary app version(s) this release is targeting (e.g. 1.1.0, ~1.2.3).",
941
+ type: "string",
942
+ })
943
+ .check((argv, aliases) => {
944
+ return checkValidReleaseOptions(argv);
945
+ });
946
+ addCommonConfiguration(yargs);
733
947
  })
734
948
  .command("rollback", "Rollback the latest release for an app deployment", (yargs) => {
735
949
  yargs
@@ -933,6 +1147,7 @@ function createCommand() {
933
1147
  const deploymentRemoveCommand = cmd;
934
1148
  deploymentRemoveCommand.appName = arg2;
935
1149
  deploymentRemoveCommand.deploymentName = arg3;
1150
+ deploymentRemoveCommand.isForce = argv["force"];
936
1151
  }
937
1152
  break;
938
1153
  case "rename":
@@ -1039,6 +1254,7 @@ function createCommand() {
1039
1254
  releaseReactCommand.entryFile = argv["entryFile"];
1040
1255
  releaseReactCommand.gradleFile = argv["gradleFile"];
1041
1256
  releaseReactCommand.mandatory = argv["mandatory"];
1257
+ releaseReactCommand.initial = argv["initial"];
1042
1258
  releaseReactCommand.noDuplicateReleaseError = argv["noDuplicateReleaseError"];
1043
1259
  releaseReactCommand.plistFile = argv["plistFile"];
1044
1260
  releaseReactCommand.plistFilePrefix = argv["plistFilePrefix"];
@@ -1055,6 +1271,54 @@ function createCommand() {
1055
1271
  releaseReactCommand.extraBundlerOptions = argv["extraBundlerOption"];
1056
1272
  }
1057
1273
  break;
1274
+ case "release-expo":
1275
+ if (arg1 && arg2) {
1276
+ cmd = { type: cli.CommandType.releaseExpo };
1277
+ const releaseExpoCommand = cmd;
1278
+ releaseExpoCommand.appName = arg1;
1279
+ releaseExpoCommand.platform = arg2;
1280
+ releaseExpoCommand.appStoreVersion = argv["targetBinaryVersion"];
1281
+ releaseExpoCommand.bundleName = argv["bundleName"];
1282
+ releaseExpoCommand.deploymentName = argv["deploymentName"];
1283
+ releaseExpoCommand.disabled = argv["disabled"];
1284
+ releaseExpoCommand.description = argv["description"] ? backslash(argv["description"]) : "";
1285
+ releaseExpoCommand.development = argv["development"];
1286
+ releaseExpoCommand.entryFile = argv["entryFile"];
1287
+ releaseExpoCommand.gradleFile = argv["gradleFile"];
1288
+ releaseExpoCommand.mandatory = argv["mandatory"];
1289
+ releaseExpoCommand.initial = argv["initial"];
1290
+ releaseExpoCommand.noDuplicateReleaseError = argv["noDuplicateReleaseError"];
1291
+ releaseExpoCommand.plistFile = argv["plistFile"];
1292
+ releaseExpoCommand.plistFilePrefix = argv["plistFilePrefix"];
1293
+ releaseExpoCommand.rollout = getRolloutValue(argv["rollout"]);
1294
+ releaseExpoCommand.sourcemapOutput = argv["sourcemapOutput"];
1295
+ releaseExpoCommand.outputDir = argv["outputDir"];
1296
+ releaseExpoCommand.useHermes = argv["useHermes"];
1297
+ releaseExpoCommand.extraHermesFlags = argv["extraHermesFlags"];
1298
+ releaseExpoCommand.podFile = argv["podFile"];
1299
+ releaseExpoCommand.privateKeyPath = argv["privateKeyPath"];
1300
+ releaseExpoCommand.xcodeProjectFile = argv["xcodeProjectFile"];
1301
+ releaseExpoCommand.xcodeTargetName = argv["xcodeTargetName"];
1302
+ releaseExpoCommand.buildConfigurationName = argv["buildConfigurationName"];
1303
+ releaseExpoCommand.extraBundlerOptions = argv["extraBundlerOption"];
1304
+ }
1305
+ break;
1306
+ case "release-native":
1307
+ if (arg1 && arg2 && arg3) {
1308
+ cmd = { type: cli.CommandType.releaseNative };
1309
+ const releaseBinaryCommand = cmd;
1310
+ releaseBinaryCommand.appName = arg1;
1311
+ releaseBinaryCommand.platform = arg2;
1312
+ releaseBinaryCommand.targetBinary = arg3;
1313
+ releaseBinaryCommand.deploymentName = argv["deploymentName"];
1314
+ releaseBinaryCommand.appStoreVersion = argv["targetBinaryVersion"];
1315
+ releaseBinaryCommand.initial = true;
1316
+ releaseBinaryCommand.disabled = true;
1317
+ releaseBinaryCommand.mandatory = false;
1318
+ // TODO add support for releaseBinaryCommand.bundleName
1319
+ // TODO add support for releaseBinaryCommand.outputDir
1320
+ }
1321
+ break;
1058
1322
  case "rollback":
1059
1323
  if (arg1 && arg2) {
1060
1324
  cmd = { type: cli.CommandType.rollback };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getExpoCliPath = void 0;
4
+ const childProcess = require("child_process");
5
+ function getExpoCliPath() {
6
+ const result = childProcess.spawnSync("node", ["--print", "require.resolve('@expo/cli')"]);
7
+ const cliPath = result.stdout.toString().trim();
8
+ if (result.status === 0 && cliPath) {
9
+ return cliPath;
10
+ }
11
+ throw new Error('Unable to resolve "@expo/cli". Please make sure it is installed in your project (e.g. "npm install --save-dev @expo/cli").');
12
+ }
13
+ exports.getExpoCliPath = getExpoCliPath;
@@ -195,6 +195,9 @@ class AccountManager {
195
195
  getDeployment(appName, deploymentName) {
196
196
  return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then((res) => res.body.deployment);
197
197
  }
198
+ getBaseRelease(appName, deploymentName, appVerison) {
199
+ return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/basebundle?appVersion=${appVerison}`])).then((res) => res.body.basebundle);
200
+ }
198
201
  renameDeployment(appName, oldDeploymentName, newDeploymentName) {
199
202
  return this.patch(urlEncode([`/apps/${appName}/deployments/${oldDeploymentName}`]), JSON.stringify({ name: newDeploymentName })).then(() => null);
200
203
  }
@@ -207,9 +210,8 @@ class AccountManager {
207
210
  getDeploymentHistory(appName, deploymentName) {
208
211
  return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then((res) => res.body.history);
209
212
  }
210
- release(appName, deploymentName, filePath, targetBinaryVersion, updateMetadata, uploadProgressCallback) {
213
+ release(appName, deploymentName, filePath, updateMetadata, uploadProgressCallback) {
211
214
  return Promise((resolve, reject, notify) => {
212
- updateMetadata.appVersion = targetBinaryVersion;
213
215
  const request = superagent.post(this._serverUrl + urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`]));
214
216
  this.attachCredentials(request);
215
217
  const getPackageFilePromise = Q.Promise((resolve, reject) => {
@@ -266,6 +268,51 @@ class AccountManager {
266
268
  });
267
269
  });
268
270
  }
271
+ releaseNative(appName, deploymentName, filePath, updateMetadata, uploadProgressCallback) {
272
+ return Promise((resolve, reject, notify) => {
273
+ const request = superagent.post(this._serverUrl + urlEncode([`/apps/${appName}/deployments/${deploymentName}/nativerelease`]));
274
+ this.attachCredentials(request);
275
+ const file = fs.createReadStream(filePath);
276
+ request
277
+ .attach("package", file)
278
+ .field("packageInfo", JSON.stringify(updateMetadata))
279
+ .on("progress", (event) => {
280
+ if (uploadProgressCallback && event && event.total > 0) {
281
+ const currentProgress = (event.loaded / event.total) * 100;
282
+ uploadProgressCallback(currentProgress);
283
+ }
284
+ })
285
+ .end((err, res) => {
286
+ fs.unlinkSync(filePath);
287
+ if (err) {
288
+ reject(this.getCodePushError(err, res));
289
+ return;
290
+ }
291
+ if (res.ok) {
292
+ resolve(null);
293
+ }
294
+ else {
295
+ let body;
296
+ try {
297
+ body = JSON.parse(res.text);
298
+ }
299
+ catch (err) { }
300
+ if (body) {
301
+ reject({
302
+ message: body.message,
303
+ statusCode: res && res.status,
304
+ });
305
+ }
306
+ else {
307
+ reject({
308
+ message: res.text,
309
+ statusCode: res && res.status,
310
+ });
311
+ }
312
+ }
313
+ });
314
+ });
315
+ }
269
316
  patchRelease(appName, deploymentName, label, updateMetadata) {
270
317
  updateMetadata.label = label;
271
318
  const requestBody = JSON.stringify({ packageInfo: updateMetadata });
@@ -414,7 +461,7 @@ class AccountManager {
414
461
  }
415
462
  request.set("Accept", `application/vnd.code-push.v${AccountManager.API_VERSION}+json`);
416
463
  request.set("Authorization", `Bearer ${this._accessKey}`);
417
- request.set("X-CodePush-SDK-Version", packageJson.version);
464
+ request.set("X-CodePush-SDK-Version", packageJson.version); // TODO get version differently without require("../../package.json");
418
465
  }
419
466
  }
420
467
  module.exports = AccountManager;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getReactNativeVersion = exports.directoryExistsSync = exports.getReactNativePackagePath = exports.isHermesEnabled = exports.getMinifyParams = exports.getXcodeDotEnvValue = exports.runHermesEmitBinaryCommand = exports.getBundleSourceMapOutput = exports.isValidVersion = void 0;
3
+ exports.getReactNativeVersion = exports.directoryExistsSync = exports.getReactNativePackagePath = exports.isHermesEnabled = exports.getMinifyParams = exports.getXcodeDotEnvValue = exports.runHermesEmitBinaryCommand = exports.takeHermesBaseBytecode = exports.getBundleSourceMapOutput = exports.isValidVersion = void 0;
4
4
  const fs = require("fs");
5
5
  const chalk = require("chalk");
6
6
  const path = require("path");
@@ -8,6 +8,7 @@ const childProcess = require("child_process");
8
8
  const semver_1 = require("semver");
9
9
  const file_utils_1 = require("./utils/file-utils");
10
10
  const dotenv = require("dotenv");
11
+ const command_executor_1 = require("./command-executor");
11
12
  const g2js = require("gradle-to-js/lib/parser");
12
13
  function isValidVersion(version) {
13
14
  return !!(0, semver_1.valid)(version) || /^\d+\.\d+$/.test(version);
@@ -44,7 +45,22 @@ async function getBundleSourceMapOutput(command, bundleName, sourcemapOutputFold
44
45
  return bundleSourceMapOutput;
45
46
  }
46
47
  exports.getBundleSourceMapOutput = getBundleSourceMapOutput;
47
- async function runHermesEmitBinaryCommand(command, bundleName, outputFolder, sourcemapOutputFolder, extraHermesFlags, gradleFile) {
48
+ async function takeHermesBaseBytecode(command, baseReleaseTmpFolder, outputFolder, bundleName) {
49
+ const { bundleBlobUrl } = await command_executor_1.sdk.getBaseRelease(command.appName, command.deploymentName, command.appStoreVersion);
50
+ if (!bundleBlobUrl) {
51
+ return null;
52
+ }
53
+ const baseReleaseArchive = await (0, file_utils_1.downloadBlob)(bundleBlobUrl, baseReleaseTmpFolder);
54
+ await (0, file_utils_1.extractIPA)(baseReleaseArchive, baseReleaseTmpFolder);
55
+ const baseReleaseBundle = path.join(baseReleaseTmpFolder, path.basename(outputFolder), bundleName);
56
+ if (!fs.existsSync(baseReleaseBundle)) {
57
+ (0, command_executor_1.log)(chalk.cyan("\nNo base release available...\n"));
58
+ return null;
59
+ }
60
+ return baseReleaseBundle;
61
+ }
62
+ exports.takeHermesBaseBytecode = takeHermesBaseBytecode;
63
+ async function runHermesEmitBinaryCommand(command, bundleName, outputFolder, sourcemapOutputFolder, extraHermesFlags, gradleFile, baseBytecode) {
48
64
  const hermesArgs = [];
49
65
  const envNodeArgs = process.env.CODE_PUSH_NODE_ARGS;
50
66
  if (typeof envNodeArgs !== "undefined") {
@@ -56,11 +72,16 @@ async function runHermesEmitBinaryCommand(command, bundleName, outputFolder, sou
56
72
  "-out",
57
73
  path.join(outputFolder, bundleName + ".hbc"),
58
74
  path.join(outputFolder, bundleName),
75
+ "-w",
76
+ "-max-diagnostic-width=80",
59
77
  ...extraHermesFlags,
60
78
  ]);
61
79
  if (sourcemapOutputFolder) {
62
80
  hermesArgs.push("-output-source-map");
63
81
  }
82
+ if (baseBytecode) {
83
+ hermesArgs.push("-base-bytecode", baseBytecode);
84
+ }
64
85
  console.log(chalk.cyan("Converting JS bundle to byte code via Hermes, running command:\n"));
65
86
  const hermesCommand = await getHermesCommand(gradleFile);
66
87
  const hermesProcess = childProcess.spawn(hermesCommand, hermesArgs);
@@ -37,4 +37,6 @@ var CommandType;
37
37
  CommandType[CommandType["sessionList"] = 29] = "sessionList";
38
38
  CommandType[CommandType["sessionRemove"] = 30] = "sessionRemove";
39
39
  CommandType[CommandType["whoami"] = 31] = "whoami";
40
+ CommandType[CommandType["releaseExpo"] = 32] = "releaseExpo";
41
+ CommandType[CommandType["releaseNative"] = 33] = "releaseNative";
40
42
  })(CommandType || (exports.CommandType = CommandType = {}));
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizePath = exports.fileDoesNotExistOrIsDirectory = exports.copyFileToTmpDir = exports.fileExists = exports.isDirectory = exports.isBinaryOrZip = void 0;
3
+ exports.extractAPK = exports.extractIPA = exports.downloadBlob = exports.normalizePath = exports.fileDoesNotExistOrIsDirectory = exports.copyFileToTmpDir = exports.fileExists = exports.isDirectory = exports.isBinaryOrZip = void 0;
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const rimraf = require("rimraf");
7
7
  const temp = require("temp");
8
+ const unzipper = require("unzipper");
9
+ const AdmZip = require("adm-zip");
10
+ const superagent = require("superagent");
8
11
  function isBinaryOrZip(path) {
9
12
  return path.search(/\.zip$/i) !== -1 || path.search(/\.apk$/i) !== -1 || path.search(/\.ipa$/i) !== -1;
10
13
  }
@@ -22,7 +25,6 @@ function fileExists(file) {
22
25
  }
23
26
  }
24
27
  exports.fileExists = fileExists;
25
- ;
26
28
  function copyFileToTmpDir(filePath) {
27
29
  if (!isDirectory(filePath)) {
28
30
  const outputFolderPath = temp.mkdirSync("code-push");
@@ -48,3 +50,32 @@ function normalizePath(filePath) {
48
50
  return filePath.replace(/\\/g, "/");
49
51
  }
50
52
  exports.normalizePath = normalizePath;
53
+ async function downloadBlob(url, folder, filename = "blob.zip") {
54
+ const destination = path.join(folder, filename);
55
+ const writeStream = fs.createWriteStream(destination);
56
+ return new Promise((resolve, reject) => {
57
+ writeStream.on("finish", () => resolve(destination));
58
+ writeStream.on("error", reject);
59
+ superagent
60
+ .get(url)
61
+ .ok((res) => res.status < 400)
62
+ .on("error", (err) => {
63
+ writeStream.destroy();
64
+ reject(err);
65
+ })
66
+ .pipe(writeStream);
67
+ });
68
+ }
69
+ exports.downloadBlob = downloadBlob;
70
+ async function extractIPA(zipPath, extractTo) {
71
+ const extractStream = unzipper.Extract({ path: extractTo });
72
+ await new Promise((resolve, reject) => {
73
+ fs.createReadStream(zipPath).pipe(extractStream).on("close", resolve).on("error", reject);
74
+ });
75
+ }
76
+ exports.extractIPA = extractIPA;
77
+ async function extractAPK(zipPath, extractTo) {
78
+ const zip = new AdmZip(zipPath);
79
+ zip.extractAllTo(extractTo, true);
80
+ }
81
+ exports.extractAPK = extractAPK;
@@ -154,6 +154,13 @@ describe("Management SDK", () => {
154
154
  done();
155
155
  }, rejectHandler);
156
156
  });
157
+ it("getBaseBundle handles JSON response", (done) => {
158
+ mockReturn(JSON.stringify({ basebundle: { bundleBlobUrl: "https://test.test/release.zip" } }), 200, {});
159
+ manager.getBaseRelease("appName", "deploymentName", "1.2.3").done((obj) => {
160
+ assert.ok(obj);
161
+ done();
162
+ }, rejectHandler);
163
+ });
157
164
  it("getDeployments handles JSON response", (done) => {
158
165
  mockReturn(JSON.stringify({ deployments: [] }), 200, {});
159
166
  manager.getDeployments("appName").done((obj) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revopush/code-push-cli",
3
- "version": "0.0.7",
3
+ "version": "0.0.8-rc.1",
4
4
  "description": "Management CLI for the CodePush service",
5
5
  "main": "./script/cli.js",
6
6
  "scripts": {
@@ -23,7 +23,10 @@
23
23
  "Revopush"
24
24
  ],
25
25
  "dependencies": {
26
+ "@devicefarmer/adbkit-apkreader": "^3.2.4",
27
+ "adm-zip": "^0.5.16",
26
28
  "backslash": "^0.2.0",
29
+ "bplist-parser": "^0.3.2",
27
30
  "chalk": "^4.1.2",
28
31
  "cli-table": "^0.3.11",
29
32
  "dotenv": "^17.2.1",
@@ -33,7 +36,7 @@
33
36
  "moment": "^2.29.4",
34
37
  "opener": "^1.5.2",
35
38
  "parse-duration": "1.1.0",
36
- "plist": "^3.0.6",
39
+ "plist": "^3.1.0",
37
40
  "progress": "^2.0.3",
38
41
  "prompt": "^1.3.0",
39
42
  "properties": "^1.2.1",
@@ -45,6 +48,7 @@
45
48
  "slash": "1.0.0",
46
49
  "superagent": "^8.0.9",
47
50
  "temp": "^0.9.4",
51
+ "unzipper": "^0.12.3",
48
52
  "which": "^1.2.7",
49
53
  "wordwrap": "1.0.0",
50
54
  "xcode": "^3.0.1",
@@ -59,6 +63,7 @@
59
63
  "@types/node": "^20.3.1",
60
64
  "@types/q": "^1.5.8",
61
65
  "@types/sinon": "^10.0.15",
66
+ "@types/unzipper": "^0.10.11",
62
67
  "@typescript-eslint/eslint-plugin": "^6.0.0",
63
68
  "@typescript-eslint/parser": "^6.0.0",
64
69
  "eslint": "^8.45.0",
@@ -68,8 +73,8 @@
68
73
  "prettier": "^2.8.8",
69
74
  "sinon": "15.1.2",
70
75
  "superagent-mock": "^4.0.0",
71
- "typescript": "^5.1.3",
72
76
  "ts-node": "^10.9.2",
77
+ "typescript": "^5.1.3",
73
78
  "which": "^3.0.1"
74
79
  }
75
80
  }