@revopush/code-push-cli 0.0.5 → 0.0.8-rc.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.
- package/bin/script/command-executor.js +116 -74
- package/bin/script/command-parser.js +16 -1
- package/bin/script/management-sdk.js +5 -3
- package/bin/script/react-native-utils.js +207 -16
- package/bin/script/utils/file-utils.js +27 -2
- package/bin/test/management-sdk.js +7 -0
- package/package.json +5 -2
- package/script/command-executor.ts +163 -104
- package/script/command-parser.ts +17 -3
- package/script/management-sdk.ts +8 -3
- package/script/react-native-utils.ts +236 -21
- package/script/types/cli.ts +3 -0
- package/script/types/rest-definitions.ts +12 -0
- package/script/types.ts +1 -0
- package/script/utils/file-utils.ts +30 -1
- package/test/management-sdk.ts +9 -0
- package/test.json +5963 -0
- package/test2.json +5844 -0
|
@@ -1,18 +1,66 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getReactNativeVersion = exports.directoryExistsSync = exports.
|
|
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");
|
|
7
7
|
const childProcess = require("child_process");
|
|
8
8
|
const semver_1 = require("semver");
|
|
9
9
|
const file_utils_1 = require("./utils/file-utils");
|
|
10
|
+
const dotenv = require("dotenv");
|
|
11
|
+
const command_executor_1 = require("./command-executor");
|
|
10
12
|
const g2js = require("gradle-to-js/lib/parser");
|
|
11
13
|
function isValidVersion(version) {
|
|
12
14
|
return !!(0, semver_1.valid)(version) || /^\d+\.\d+$/.test(version);
|
|
13
15
|
}
|
|
14
16
|
exports.isValidVersion = isValidVersion;
|
|
15
|
-
async function
|
|
17
|
+
async function getBundleSourceMapOutput(command, bundleName, sourcemapOutputFolder) {
|
|
18
|
+
let bundleSourceMapOutput;
|
|
19
|
+
switch (command.platform) {
|
|
20
|
+
case "android": {
|
|
21
|
+
// see BundleHermesCTask -> resolvePackagerSourceMapFile
|
|
22
|
+
// for Hermes targeted bundles there are 2 source maps: "packager" (metro) and "compiler" (Hermes)
|
|
23
|
+
// Metro bundles use <bundleAssetName>.packager.map notation
|
|
24
|
+
const isHermes = await isHermesEnabled(command, command.platform);
|
|
25
|
+
if (isHermes) {
|
|
26
|
+
bundleSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".packager.map");
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
bundleSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".map");
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "ios": {
|
|
34
|
+
// see react-native-xcode.sh
|
|
35
|
+
// to match js bundle generated by Xcode and by Revopush cli we must respect SOURCEMAP_FILE value
|
|
36
|
+
// because it appears as //# sourceMappingURL value in a js bundle
|
|
37
|
+
const xcodeDotEnvValue = getXcodeDotEnvValue("SOURCEMAP_FILE");
|
|
38
|
+
const sourceMapFilename = xcodeDotEnvValue ? path.basename(xcodeDotEnvValue) : bundleName + ".map";
|
|
39
|
+
bundleSourceMapOutput = path.join(sourcemapOutputFolder, sourceMapFilename);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
default:
|
|
43
|
+
throw new Error('Platform must be either "android", "ios" or "windows".');
|
|
44
|
+
}
|
|
45
|
+
return bundleSourceMapOutput;
|
|
46
|
+
}
|
|
47
|
+
exports.getBundleSourceMapOutput = getBundleSourceMapOutput;
|
|
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.extract)(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) {
|
|
16
64
|
const hermesArgs = [];
|
|
17
65
|
const envNodeArgs = process.env.CODE_PUSH_NODE_ARGS;
|
|
18
66
|
if (typeof envNodeArgs !== "undefined") {
|
|
@@ -20,14 +68,18 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
|
|
|
20
68
|
}
|
|
21
69
|
Array.prototype.push.apply(hermesArgs, [
|
|
22
70
|
"-emit-binary",
|
|
71
|
+
"-O",
|
|
23
72
|
"-out",
|
|
24
73
|
path.join(outputFolder, bundleName + ".hbc"),
|
|
25
74
|
path.join(outputFolder, bundleName),
|
|
26
75
|
...extraHermesFlags,
|
|
27
76
|
]);
|
|
28
|
-
if (
|
|
77
|
+
if (sourcemapOutputFolder) {
|
|
29
78
|
hermesArgs.push("-output-source-map");
|
|
30
79
|
}
|
|
80
|
+
if (baseBytecode) {
|
|
81
|
+
hermesArgs.push("-base-bytecode", baseBytecode);
|
|
82
|
+
}
|
|
31
83
|
console.log(chalk.cyan("Converting JS bundle to byte code via Hermes, running command:\n"));
|
|
32
84
|
const hermesCommand = await getHermesCommand(gradleFile);
|
|
33
85
|
const hermesProcess = childProcess.spawn(hermesCommand, hermesArgs);
|
|
@@ -60,8 +112,8 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
|
|
|
60
112
|
});
|
|
61
113
|
});
|
|
62
114
|
});
|
|
63
|
-
}).then(() => {
|
|
64
|
-
if (!
|
|
115
|
+
}).then(async () => {
|
|
116
|
+
if (!sourcemapOutputFolder) {
|
|
65
117
|
// skip source map compose if source map is not enabled
|
|
66
118
|
return;
|
|
67
119
|
}
|
|
@@ -73,8 +125,32 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
|
|
|
73
125
|
if (!fs.existsSync(jsCompilerSourceMapFile)) {
|
|
74
126
|
throw new Error(`sourcemap file ${jsCompilerSourceMapFile} is not found`);
|
|
75
127
|
}
|
|
128
|
+
const platformSourceMapOutput = await getBundleSourceMapOutput(command, bundleName, sourcemapOutputFolder);
|
|
76
129
|
return new Promise((resolve, reject) => {
|
|
77
|
-
|
|
130
|
+
let bundleSourceMapOutput = sourcemapOutputFolder;
|
|
131
|
+
let combinedSourceMapOutput = sourcemapOutputFolder;
|
|
132
|
+
if (!sourcemapOutputFolder.endsWith(".map")) {
|
|
133
|
+
bundleSourceMapOutput = platformSourceMapOutput;
|
|
134
|
+
switch (command.platform) {
|
|
135
|
+
case "android": {
|
|
136
|
+
combinedSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".map");
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "ios": {
|
|
140
|
+
combinedSourceMapOutput = bundleSourceMapOutput;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
default:
|
|
144
|
+
throw new Error('Platform must be either "android", "ios" or "windows".');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const composeSourceMapsArgs = [
|
|
148
|
+
composeSourceMapsPath,
|
|
149
|
+
bundleSourceMapOutput,
|
|
150
|
+
jsCompilerSourceMapFile,
|
|
151
|
+
"-o",
|
|
152
|
+
combinedSourceMapOutput,
|
|
153
|
+
];
|
|
78
154
|
// https://github.com/facebook/react-native/blob/master/react.gradle#L211
|
|
79
155
|
// https://github.com/facebook/react-native/blob/master/scripts/react-native-xcode.sh#L178
|
|
80
156
|
// packager.sourcemap.map + hbc.sourcemap.map = sourcemap.map
|
|
@@ -103,6 +179,38 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
|
|
|
103
179
|
});
|
|
104
180
|
}
|
|
105
181
|
exports.runHermesEmitBinaryCommand = runHermesEmitBinaryCommand;
|
|
182
|
+
function getXcodeDotEnvValue(key) {
|
|
183
|
+
const xcodeEnvs = loadEnvAsMap([path.join("ios", ".xcode.env.local"), path.join("ios", ".xcode.env.local")]);
|
|
184
|
+
return xcodeEnvs.get(key);
|
|
185
|
+
}
|
|
186
|
+
exports.getXcodeDotEnvValue = getXcodeDotEnvValue;
|
|
187
|
+
async function getMinifyParams(command) {
|
|
188
|
+
const isHermes = await isHermesEnabled(command);
|
|
189
|
+
switch (command.platform) {
|
|
190
|
+
case "android": {
|
|
191
|
+
// android always explicitly pass --minify true/false
|
|
192
|
+
// TaskConfiguration it.minifyEnabled.set(!isHermesEnabledInThisVariant)
|
|
193
|
+
return ["--minify", !isHermes];
|
|
194
|
+
}
|
|
195
|
+
case "ios": {
|
|
196
|
+
//if [[ $USE_HERMES != false && $DEV == false ]]; then
|
|
197
|
+
// EXTRA_ARGS+=("--minify" "false")
|
|
198
|
+
// fi
|
|
199
|
+
// ios does pass --minify false only if Hermes enables and does pass anything otherwise
|
|
200
|
+
return isHermes ? ["--minify", false] : [];
|
|
201
|
+
}
|
|
202
|
+
default:
|
|
203
|
+
throw new Error('Platform must be either "android", "ios" or "windows".');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.getMinifyParams = getMinifyParams;
|
|
207
|
+
async function isHermesEnabled(command, platform = command.platform.toLowerCase()) {
|
|
208
|
+
// Check if we have to run hermes to compile JS to Byte Code if Hermes is enabled in Podfile and we're releasing an iOS build
|
|
209
|
+
const isAndroidHermesEnabled = await getAndroidHermesEnabled(command.gradleFile);
|
|
210
|
+
const isIOSHermesEnabled = getiOSHermesEnabled(command.podFile);
|
|
211
|
+
return command.useHermes || (platform === "android" && isAndroidHermesEnabled) || (platform === "ios" && isIOSHermesEnabled);
|
|
212
|
+
}
|
|
213
|
+
exports.isHermesEnabled = isHermesEnabled;
|
|
106
214
|
function parseBuildGradleFile(gradleFile) {
|
|
107
215
|
let buildGradlePath = path.join("android", "app");
|
|
108
216
|
if (gradleFile) {
|
|
@@ -118,6 +226,45 @@ function parseBuildGradleFile(gradleFile) {
|
|
|
118
226
|
throw new Error(`Unable to parse the "${buildGradlePath}" file. Please ensure it is a well-formed Gradle file.`);
|
|
119
227
|
});
|
|
120
228
|
}
|
|
229
|
+
function parseGradlePropertiesFile(gradleFile) {
|
|
230
|
+
let gradlePropsPath = path.join("android", "gradle.properties");
|
|
231
|
+
try {
|
|
232
|
+
if (gradleFile) {
|
|
233
|
+
const base = gradleFile;
|
|
234
|
+
const stat = fs.lstatSync(base);
|
|
235
|
+
if (stat.isDirectory()) {
|
|
236
|
+
if (path.basename(base) === "app") {
|
|
237
|
+
gradlePropsPath = path.join(base, "..", "gradle.properties");
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
gradlePropsPath = path.join(base, "gradle.properties");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
gradlePropsPath = path.join(path.dirname(base), "..", "gradle.properties");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch { }
|
|
249
|
+
gradlePropsPath = path.normalize(gradlePropsPath);
|
|
250
|
+
if ((0, file_utils_1.fileDoesNotExistOrIsDirectory)(gradlePropsPath)) {
|
|
251
|
+
throw new Error(`Unable to find gradle.properties file "${gradlePropsPath}".`);
|
|
252
|
+
}
|
|
253
|
+
const text = fs.readFileSync(gradlePropsPath, "utf8");
|
|
254
|
+
const props = {};
|
|
255
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
256
|
+
const line = rawLine.trim();
|
|
257
|
+
if (!line || line.startsWith("#"))
|
|
258
|
+
continue;
|
|
259
|
+
const m = line.match(/^([^=\s]+)\s*=\s*(.*)$/);
|
|
260
|
+
if (m) {
|
|
261
|
+
const key = m[1].trim();
|
|
262
|
+
const val = m[2].trim();
|
|
263
|
+
props[key] = val;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return props;
|
|
267
|
+
}
|
|
121
268
|
async function getHermesCommandFromGradle(gradleFile) {
|
|
122
269
|
const buildGradle = await parseBuildGradleFile(gradleFile);
|
|
123
270
|
const hermesCommandProperty = Array.from(buildGradle["project.ext.react"] || []).find((prop) => prop.trim().startsWith("hermesCommand:"));
|
|
@@ -128,12 +275,30 @@ async function getHermesCommandFromGradle(gradleFile) {
|
|
|
128
275
|
return "";
|
|
129
276
|
}
|
|
130
277
|
}
|
|
131
|
-
function getAndroidHermesEnabled(gradleFile) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
278
|
+
async function getAndroidHermesEnabled(gradleFile) {
|
|
279
|
+
try {
|
|
280
|
+
const props = parseGradlePropertiesFile(gradleFile);
|
|
281
|
+
if (typeof props.hermesEnabled !== "undefined") {
|
|
282
|
+
const v = String(props.hermesEnabled).trim().toLowerCase();
|
|
283
|
+
if (v === "true")
|
|
284
|
+
return true;
|
|
285
|
+
if (v === "false")
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch { }
|
|
290
|
+
try {
|
|
291
|
+
const buildGradle = await parseBuildGradleFile(gradleFile);
|
|
292
|
+
const lines = Array.from(buildGradle["project.ext.react"] || []);
|
|
293
|
+
if (lines.some((l) => /\benableHermes\s*:\s*true\b/.test(l)))
|
|
294
|
+
return true;
|
|
295
|
+
if (lines.some((l) => /\benableHermes\s*:\s*false\b/.test(l)))
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
catch { }
|
|
299
|
+
const rnVersion = (0, semver_1.coerce)(getReactNativeVersion())?.version;
|
|
300
|
+
return rnVersion && (0, semver_1.compare)(rnVersion, "0.70.0") >= 0;
|
|
135
301
|
}
|
|
136
|
-
exports.getAndroidHermesEnabled = getAndroidHermesEnabled;
|
|
137
302
|
function getiOSHermesEnabled(podFile) {
|
|
138
303
|
let podPath = path.join("ios", "Podfile");
|
|
139
304
|
if (podFile) {
|
|
@@ -144,13 +309,29 @@ function getiOSHermesEnabled(podFile) {
|
|
|
144
309
|
}
|
|
145
310
|
try {
|
|
146
311
|
const podFileContents = fs.readFileSync(podPath).toString();
|
|
147
|
-
|
|
312
|
+
const hasTrue = /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?true)/.test(podFileContents);
|
|
313
|
+
if (hasTrue)
|
|
314
|
+
return true;
|
|
315
|
+
const hasFalse = /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?false)/.test(podFileContents);
|
|
316
|
+
if (hasFalse)
|
|
317
|
+
return false;
|
|
318
|
+
const rnVersion = (0, semver_1.coerce)(getReactNativeVersion())?.version;
|
|
319
|
+
return rnVersion && (0, semver_1.compare)(rnVersion, "0.70.0") >= 0;
|
|
148
320
|
}
|
|
149
321
|
catch (error) {
|
|
150
322
|
throw error;
|
|
151
323
|
}
|
|
152
324
|
}
|
|
153
|
-
|
|
325
|
+
function loadEnvAsMap(envPaths = []) {
|
|
326
|
+
const merged = {};
|
|
327
|
+
for (const envPath of envPaths) {
|
|
328
|
+
if (fs.existsSync(envPath)) {
|
|
329
|
+
Object.assign(merged, dotenv.parse(fs.readFileSync(envPath))); // later files override earlier ones
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// fallback to process.env for anything missing
|
|
333
|
+
return new Map([...Object.entries(process.env), ...Object.entries(merged)]);
|
|
334
|
+
}
|
|
154
335
|
function getHermesOSBin() {
|
|
155
336
|
switch (process.platform) {
|
|
156
337
|
case "win32":
|
|
@@ -184,7 +365,8 @@ async function getHermesCommand(gradleFile) {
|
|
|
184
365
|
}
|
|
185
366
|
};
|
|
186
367
|
// Hermes is bundled with react-native since 0.69
|
|
187
|
-
const
|
|
368
|
+
const reactNativePath = getReactNativePackagePath();
|
|
369
|
+
const bundledHermesEngine = path.join(reactNativePath, "sdks", "hermesc", getHermesOSBin(), getHermesOSExe());
|
|
188
370
|
if (fileExists(bundledHermesEngine)) {
|
|
189
371
|
return bundledHermesEngine;
|
|
190
372
|
}
|
|
@@ -193,12 +375,13 @@ async function getHermesCommand(gradleFile) {
|
|
|
193
375
|
return path.join("android", "app", gradleHermesCommand.replace("%OS-BIN%", getHermesOSBin()));
|
|
194
376
|
}
|
|
195
377
|
else {
|
|
378
|
+
const nodeModulesPath = getNodeModulesPath(reactNativePath);
|
|
196
379
|
// assume if hermes-engine exists it should be used instead of hermesvm
|
|
197
|
-
const hermesEngine = path.join(
|
|
380
|
+
const hermesEngine = path.join(nodeModulesPath, "hermes-engine", getHermesOSBin(), getHermesOSExe());
|
|
198
381
|
if (fileExists(hermesEngine)) {
|
|
199
382
|
return hermesEngine;
|
|
200
383
|
}
|
|
201
|
-
return path.join(
|
|
384
|
+
return path.join(nodeModulesPath, "hermesvm", getHermesOSBin(), "hermes");
|
|
202
385
|
}
|
|
203
386
|
}
|
|
204
387
|
function getComposeSourceMapsPath() {
|
|
@@ -209,6 +392,13 @@ function getComposeSourceMapsPath() {
|
|
|
209
392
|
}
|
|
210
393
|
return null;
|
|
211
394
|
}
|
|
395
|
+
function getNodeModulesPath(reactNativePath) {
|
|
396
|
+
const nodeModulesPath = path.dirname(reactNativePath);
|
|
397
|
+
if (directoryExistsSync(nodeModulesPath)) {
|
|
398
|
+
return nodeModulesPath;
|
|
399
|
+
}
|
|
400
|
+
return path.join("node_modules");
|
|
401
|
+
}
|
|
212
402
|
function getReactNativePackagePath() {
|
|
213
403
|
const result = childProcess.spawnSync("node", ["--print", "require.resolve('react-native/package.json')"]);
|
|
214
404
|
const packagePath = path.dirname(result.stdout.toString());
|
|
@@ -217,6 +407,7 @@ function getReactNativePackagePath() {
|
|
|
217
407
|
}
|
|
218
408
|
return path.join("node_modules", "react-native");
|
|
219
409
|
}
|
|
410
|
+
exports.getReactNativePackagePath = getReactNativePackagePath;
|
|
220
411
|
function directoryExistsSync(dirname) {
|
|
221
412
|
try {
|
|
222
413
|
return fs.statSync(dirname).isDirectory();
|
|
@@ -1,10 +1,12 @@
|
|
|
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.extract = 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 superagent = require("superagent");
|
|
8
10
|
function isBinaryOrZip(path) {
|
|
9
11
|
return path.search(/\.zip$/i) !== -1 || path.search(/\.apk$/i) !== -1 || path.search(/\.ipa$/i) !== -1;
|
|
10
12
|
}
|
|
@@ -22,7 +24,6 @@ function fileExists(file) {
|
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
exports.fileExists = fileExists;
|
|
25
|
-
;
|
|
26
27
|
function copyFileToTmpDir(filePath) {
|
|
27
28
|
if (!isDirectory(filePath)) {
|
|
28
29
|
const outputFolderPath = temp.mkdirSync("code-push");
|
|
@@ -48,3 +49,27 @@ function normalizePath(filePath) {
|
|
|
48
49
|
return filePath.replace(/\\/g, "/");
|
|
49
50
|
}
|
|
50
51
|
exports.normalizePath = normalizePath;
|
|
52
|
+
async function downloadBlob(url, folder, filename = "blob.zip") {
|
|
53
|
+
const destination = path.join(folder, filename);
|
|
54
|
+
const writeStream = fs.createWriteStream(destination);
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
writeStream.on("finish", () => resolve(destination));
|
|
57
|
+
writeStream.on("error", reject);
|
|
58
|
+
superagent
|
|
59
|
+
.get(url)
|
|
60
|
+
.ok((res) => res.status < 400)
|
|
61
|
+
.on("error", (err) => {
|
|
62
|
+
writeStream.destroy();
|
|
63
|
+
reject(err);
|
|
64
|
+
})
|
|
65
|
+
.pipe(writeStream);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
exports.downloadBlob = downloadBlob;
|
|
69
|
+
async function extract(zipPath, extractTo) {
|
|
70
|
+
const extractStream = unzipper.Extract({ path: extractTo });
|
|
71
|
+
await new Promise((resolve, reject) => {
|
|
72
|
+
fs.createReadStream(zipPath).pipe(extractStream).on("close", resolve).on("error", reject);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
exports.extract = extract;
|
|
@@ -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.
|
|
3
|
+
"version": "0.0.8-rc.0",
|
|
4
4
|
"description": "Management CLI for the CodePush service",
|
|
5
5
|
"main": "./script/cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"backslash": "^0.2.0",
|
|
27
27
|
"chalk": "^4.1.2",
|
|
28
28
|
"cli-table": "^0.3.11",
|
|
29
|
+
"dotenv": "^17.2.1",
|
|
29
30
|
"email-validator": "^2.0.4",
|
|
30
31
|
"gradle-to-js": "2.0.1",
|
|
31
32
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"slash": "1.0.0",
|
|
45
46
|
"superagent": "^8.0.9",
|
|
46
47
|
"temp": "^0.9.4",
|
|
48
|
+
"unzipper": "^0.12.3",
|
|
47
49
|
"which": "^1.2.7",
|
|
48
50
|
"wordwrap": "1.0.0",
|
|
49
51
|
"xcode": "^3.0.1",
|
|
@@ -58,6 +60,7 @@
|
|
|
58
60
|
"@types/node": "^20.3.1",
|
|
59
61
|
"@types/q": "^1.5.8",
|
|
60
62
|
"@types/sinon": "^10.0.15",
|
|
63
|
+
"@types/unzipper": "^0.10.11",
|
|
61
64
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
62
65
|
"@typescript-eslint/parser": "^6.0.0",
|
|
63
66
|
"eslint": "^8.45.0",
|
|
@@ -67,8 +70,8 @@
|
|
|
67
70
|
"prettier": "^2.8.8",
|
|
68
71
|
"sinon": "15.1.2",
|
|
69
72
|
"superagent-mock": "^4.0.0",
|
|
70
|
-
"typescript": "^5.1.3",
|
|
71
73
|
"ts-node": "^10.9.2",
|
|
74
|
+
"typescript": "^5.1.3",
|
|
72
75
|
"which": "^3.0.1"
|
|
73
76
|
}
|
|
74
77
|
}
|