@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.
- package/bin/script/binary-utils.js +176 -0
- package/bin/script/command-executor.js +377 -44
- package/bin/script/command-parser.js +265 -1
- package/bin/script/expo-utils.js +13 -0
- package/bin/script/management-sdk.js +50 -3
- package/bin/script/react-native-utils.js +23 -2
- package/bin/script/types/cli.js +2 -0
- package/bin/script/utils/file-utils.js +33 -2
- package/bin/test/management-sdk.js +7 -0
- package/package.json +8 -3
- package/script/binary-utils.ts +209 -0
- package/script/command-executor.ts +458 -51
- package/script/command-parser.ts +295 -2
- package/script/expo-utils.ts +14 -0
- package/script/management-sdk.ts +65 -3
- package/script/react-native-utils.ts +34 -3
- package/script/types/cli.ts +13 -0
- package/script/types/rest-definitions.ts +12 -0
- package/script/types.ts +1 -0
- package/script/utils/file-utils.ts +36 -1
- package/test/management-sdk.ts +9 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getIosVersion = exports.extractMetadataFromIOS = exports.extractMetadataFromAndroid = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const chalk = require("chalk");
|
|
7
|
+
const command_executor_1 = require("./command-executor");
|
|
8
|
+
const hash_utils_1 = require("./hash-utils");
|
|
9
|
+
const os = require("os");
|
|
10
|
+
const Q = require("q");
|
|
11
|
+
const yazl = require("yazl");
|
|
12
|
+
const promises_1 = require("node:fs/promises");
|
|
13
|
+
const plist_1 = require("plist");
|
|
14
|
+
const bplist = require("bplist-parser");
|
|
15
|
+
async function extractMetadataFromAndroid(extractFolder, outputFolder) {
|
|
16
|
+
const assetsFolder = path.join(extractFolder, "assets");
|
|
17
|
+
if (!fs.existsSync(assetsFolder)) {
|
|
18
|
+
throw new Error("Invalid APK structure: assets folder not found.");
|
|
19
|
+
}
|
|
20
|
+
const codepushMetadata = path.join(assetsFolder, "CodePushMetadata");
|
|
21
|
+
let fileHashes = {};
|
|
22
|
+
if (fs.existsSync(codepushMetadata)) {
|
|
23
|
+
fileHashes = await takeHashesFromMetadata(codepushMetadata);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
(0, command_executor_1.log)(chalk.yellow(`\nWarning: CodepushMetadata file not found in APK. Check used version of SDK\n`));
|
|
27
|
+
}
|
|
28
|
+
// Get index.android.bundle from root of app folder
|
|
29
|
+
const mainJsBundlePath = path.join(assetsFolder, "index.android.bundle");
|
|
30
|
+
if (fs.existsSync(mainJsBundlePath)) {
|
|
31
|
+
// Copy bundle to output folder
|
|
32
|
+
const outputCodePushFolder = path.join(outputFolder, "CodePush");
|
|
33
|
+
fs.mkdirSync(outputCodePushFolder, { recursive: true });
|
|
34
|
+
const outputBundlePath = path.join(outputCodePushFolder, "index.android.bundle");
|
|
35
|
+
fs.copyFileSync(mainJsBundlePath, outputBundlePath);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
throw new Error("index.android.bundle not found in APK root folder.");
|
|
39
|
+
}
|
|
40
|
+
// Save packageManifest.json
|
|
41
|
+
const manifestPath = path.join(outputFolder, "packageManifest.json");
|
|
42
|
+
fs.writeFileSync(manifestPath, JSON.stringify(fileHashes, null, 2));
|
|
43
|
+
(0, command_executor_1.log)(chalk.cyan(`\nSaved packageManifest.json with ${Object.keys(fileHashes).length} entries.\n`));
|
|
44
|
+
// Create zip archive with packageManifest.json and bundle file
|
|
45
|
+
const zipPath = path.join(os.tmpdir(), `CodePushBinary-${Date.now()}.zip`);
|
|
46
|
+
await createZipArchive(outputFolder, zipPath, ["packageManifest.json", "CodePush/index.android.bundle"]);
|
|
47
|
+
return zipPath;
|
|
48
|
+
}
|
|
49
|
+
exports.extractMetadataFromAndroid = extractMetadataFromAndroid;
|
|
50
|
+
async function extractMetadataFromIOS(extractFolder, outputFolder) {
|
|
51
|
+
const payloadFolder = path.join(extractFolder, "Payload");
|
|
52
|
+
if (!fs.existsSync(payloadFolder)) {
|
|
53
|
+
throw new Error("Invalid IPA structure: Payload folder not found.");
|
|
54
|
+
}
|
|
55
|
+
const appFolders = fs.readdirSync(payloadFolder).filter((item) => {
|
|
56
|
+
const itemPath = path.join(payloadFolder, item);
|
|
57
|
+
return fs.statSync(itemPath).isDirectory() && item.endsWith(".app");
|
|
58
|
+
});
|
|
59
|
+
if (appFolders.length === 0) {
|
|
60
|
+
throw new Error("Invalid IPA structure: No .app folder found in Payload.");
|
|
61
|
+
}
|
|
62
|
+
const appFolder = path.join(payloadFolder, appFolders[0]);
|
|
63
|
+
const codePushFolder = path.join(appFolder, "assets");
|
|
64
|
+
const fileHashes = {};
|
|
65
|
+
if (fs.existsSync(codePushFolder)) {
|
|
66
|
+
await calculateHashesForDirectory(codePushFolder, appFolder, fileHashes);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
(0, command_executor_1.log)(chalk.yellow(`\nWarning: CodePush folder not found in IPA.\n`));
|
|
70
|
+
}
|
|
71
|
+
const mainJsBundlePath = path.join(appFolder, "main.jsbundle");
|
|
72
|
+
if (fs.existsSync(mainJsBundlePath)) {
|
|
73
|
+
(0, command_executor_1.log)(chalk.cyan(`\nFound main.jsbundle, calculating hash:\n`));
|
|
74
|
+
const bundleHash = await (0, hash_utils_1.hashFile)(mainJsBundlePath);
|
|
75
|
+
fileHashes["CodePush/main.jsbundle"] = bundleHash;
|
|
76
|
+
// Copy bundle to output folder
|
|
77
|
+
const outputCodePushFolder = path.join(outputFolder, "CodePush");
|
|
78
|
+
fs.mkdirSync(outputCodePushFolder, { recursive: true });
|
|
79
|
+
const outputBundlePath = path.join(outputCodePushFolder, "main.jsbundle");
|
|
80
|
+
fs.copyFileSync(mainJsBundlePath, outputBundlePath);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error("main.jsbundle not found in IPA root folder.");
|
|
84
|
+
}
|
|
85
|
+
// Save packageManifest.json
|
|
86
|
+
const manifestPath = path.join(outputFolder, "packageManifest.json");
|
|
87
|
+
fs.writeFileSync(manifestPath, JSON.stringify(fileHashes, null, 2));
|
|
88
|
+
(0, command_executor_1.log)(chalk.cyan(`\nSaved packageManifest.json with ${Object.keys(fileHashes).length} entries.\n`));
|
|
89
|
+
// Create zip archive with packageManifest.json and bundle file
|
|
90
|
+
const zipPath = path.join(os.tmpdir(), `CodePushBinary-${Date.now()}.zip`);
|
|
91
|
+
await createZipArchive(outputFolder, zipPath, ["packageManifest.json", "CodePush/main.jsbundle"]);
|
|
92
|
+
return zipPath;
|
|
93
|
+
}
|
|
94
|
+
exports.extractMetadataFromIOS = extractMetadataFromIOS;
|
|
95
|
+
async function calculateHashesForDirectory(directoryPath, basePath, fileHashes) {
|
|
96
|
+
const items = fs.readdirSync(directoryPath);
|
|
97
|
+
for (const item of items) {
|
|
98
|
+
const itemPath = path.join(directoryPath, item);
|
|
99
|
+
const stat = fs.statSync(itemPath);
|
|
100
|
+
if (stat.isDirectory()) {
|
|
101
|
+
await calculateHashesForDirectory(itemPath, basePath, fileHashes);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Calculate relative path from basePath (app folder) to the file
|
|
105
|
+
const relativePath = path.relative(basePath, itemPath).replace(/\\/g, "/");
|
|
106
|
+
const hash = await (0, hash_utils_1.hashFile)(itemPath);
|
|
107
|
+
const hashKey = `CodePush/${relativePath}`;
|
|
108
|
+
fileHashes[hashKey] = hash;
|
|
109
|
+
(0, command_executor_1.log)(chalk.gray(` ${relativePath}:${hash.substring(0, 8)}...\n`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function takeHashesFromMetadata(metadataPath) {
|
|
114
|
+
const content = await (0, promises_1.readFile)(metadataPath, "utf-8");
|
|
115
|
+
const metadata = JSON.parse(content);
|
|
116
|
+
if (!metadata || !metadata.manifest) {
|
|
117
|
+
throw new Error("Failed to take manifest from metadata file of APK");
|
|
118
|
+
}
|
|
119
|
+
return Object.fromEntries(metadata.manifest.map((item) => item.split(":")));
|
|
120
|
+
}
|
|
121
|
+
function createZipArchive(sourceFolder, zipPath, filesToInclude) {
|
|
122
|
+
return Q.Promise((resolve, reject) => {
|
|
123
|
+
const zipFile = new yazl.ZipFile();
|
|
124
|
+
const writeStream = fs.createWriteStream(zipPath);
|
|
125
|
+
zipFile.outputStream
|
|
126
|
+
.pipe(writeStream)
|
|
127
|
+
.on("error", (error) => {
|
|
128
|
+
reject(error);
|
|
129
|
+
})
|
|
130
|
+
.on("close", () => {
|
|
131
|
+
resolve();
|
|
132
|
+
});
|
|
133
|
+
for (const file of filesToInclude) {
|
|
134
|
+
const filePath = path.join(sourceFolder, file);
|
|
135
|
+
if (fs.existsSync(filePath)) {
|
|
136
|
+
zipFile.addFile(filePath, file);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
zipFile.end();
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function parseAnyPlistFile(plistPath) {
|
|
143
|
+
const buf = fs.readFileSync(plistPath);
|
|
144
|
+
if (buf.slice(0, 6).toString("ascii") === "bplist") {
|
|
145
|
+
const arr = bplist.parseBuffer(buf);
|
|
146
|
+
if (!arr?.length)
|
|
147
|
+
throw new Error("Empty binary plist");
|
|
148
|
+
return arr[0];
|
|
149
|
+
}
|
|
150
|
+
const xml = buf.toString("utf8");
|
|
151
|
+
return plist_1.default.parse(xml);
|
|
152
|
+
}
|
|
153
|
+
async function getIosVersion(extractFolder) {
|
|
154
|
+
const payloadFolder = path.join(extractFolder, "Payload");
|
|
155
|
+
if (!fs.existsSync(payloadFolder)) {
|
|
156
|
+
throw new Error("Invalid IPA structure: Payload folder not found.");
|
|
157
|
+
}
|
|
158
|
+
const appFolders = fs.readdirSync(payloadFolder).filter((item) => {
|
|
159
|
+
const itemPath = path.join(payloadFolder, item);
|
|
160
|
+
return fs.statSync(itemPath).isDirectory() && item.endsWith(".app");
|
|
161
|
+
});
|
|
162
|
+
if (appFolders.length === 0) {
|
|
163
|
+
throw new Error("Invalid IPA structure: No .app folder found in Payload.");
|
|
164
|
+
}
|
|
165
|
+
const appFolder = path.join(payloadFolder, appFolders[0]);
|
|
166
|
+
const plistPath = path.join(appFolder, "Info.plist");
|
|
167
|
+
const data = parseAnyPlistFile(plistPath);
|
|
168
|
+
console.log('App Version (Short):', data.CFBundleShortVersionString);
|
|
169
|
+
console.log('Build Number:', data.CFBundleVersion);
|
|
170
|
+
console.log('Bundle ID:', data.CFBundleIdentifier);
|
|
171
|
+
return {
|
|
172
|
+
version: data.CFBundleShortVersionString,
|
|
173
|
+
build: data.CFBundleVersion
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
exports.getIosVersion = getIosVersion;
|