code-push-itspar 1.0.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/LICENSE.txt +22 -0
- package/README.md +174 -0
- package/bin/script/acquisition-sdk.js +178 -0
- package/bin/script/cli.js +23 -0
- package/bin/script/command-executor.js +1392 -0
- package/bin/script/command-parser.js +1225 -0
- package/bin/script/commands/debug.js +125 -0
- package/bin/script/hash-utils.js +203 -0
- package/bin/script/index.js +5 -0
- package/bin/script/management-sdk.js +531 -0
- package/bin/script/patch-scripts/apply-patch.sh +111 -0
- package/bin/script/patch-scripts/create-patch.sh +53 -0
- package/bin/script/react-native-utils.js +249 -0
- package/bin/script/sign.js +69 -0
- package/bin/script/types/cli.js +50 -0
- package/bin/script/types/rest-definitions.js +19 -0
- package/bin/script/types.js +4 -0
- package/bin/script/utils/config.constants.js +13 -0
- package/bin/script/utils/file-utils.js +50 -0
- package/bin/test/acquisition-rest-mock.js +108 -0
- package/bin/test/acquisition-sdk.js +188 -0
- package/bin/test/cli.js +1342 -0
- package/bin/test/hash-utils.js +149 -0
- package/bin/test/management-sdk.js +338 -0
- package/bsdiff/README.md +114 -0
- package/bsdiff/bsdiff43 +0 -0
- package/package.json +107 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const Q = require("q");
|
|
8
|
+
const superagent = require("superagent");
|
|
9
|
+
const recursiveFs = require("recursive-fs");
|
|
10
|
+
const yazl = require("yazl");
|
|
11
|
+
const slash = require("slash");
|
|
12
|
+
const zlib = require("zlib");
|
|
13
|
+
const ORG_FILE_PATH = path.resolve(__dirname, 'organisations.json');
|
|
14
|
+
var Promise = Q.Promise;
|
|
15
|
+
const config_constants_1 = require("./utils/config.constants");
|
|
16
|
+
const packageJson = require("../../package.json");
|
|
17
|
+
// A template string tag function that URL encodes the substituted values
|
|
18
|
+
function urlEncode(strings, ...values) {
|
|
19
|
+
let result = "";
|
|
20
|
+
for (let i = 0; i < strings.length; i++) {
|
|
21
|
+
result += strings[i];
|
|
22
|
+
if (i < values.length) {
|
|
23
|
+
result += encodeURIComponent(values[i]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function saveOrganizationsSync(orgs, forceSave = false) {
|
|
29
|
+
try {
|
|
30
|
+
// Check if file exists and is non-empty
|
|
31
|
+
const fileExists = fs.existsSync(ORG_FILE_PATH);
|
|
32
|
+
const isFileEmpty = fileExists && fs.readFileSync(ORG_FILE_PATH, 'utf-8').trim() === '';
|
|
33
|
+
if (forceSave || !fileExists || isFileEmpty) {
|
|
34
|
+
fs.writeFileSync(ORG_FILE_PATH, JSON.stringify(orgs, null, 2), 'utf-8');
|
|
35
|
+
//console.log(`Organizations saved to ${ORG_FILE_PATH}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
//console.log("Organizations already exist, skipping save.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(`Error saving organizations: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Load organizations from the file (synchronous)
|
|
46
|
+
function loadOrganizationsSync() {
|
|
47
|
+
try {
|
|
48
|
+
if (fs.existsSync(ORG_FILE_PATH)) {
|
|
49
|
+
const data = fs.readFileSync(ORG_FILE_PATH, 'utf-8');
|
|
50
|
+
// console.log("data ::", data);
|
|
51
|
+
return JSON.parse(data);
|
|
52
|
+
}
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(`Error loading organizations: ${error.message}`);
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
class AccountManager {
|
|
61
|
+
static AppPermission = {
|
|
62
|
+
OWNER: "Owner",
|
|
63
|
+
COLLABORATOR: "Collaborator",
|
|
64
|
+
};
|
|
65
|
+
static SERVER_URL = "http://localhost:3000";
|
|
66
|
+
static API_VERSION = 2;
|
|
67
|
+
static ERROR_GATEWAY_TIMEOUT = 504; // Used if there is a network error
|
|
68
|
+
static ERROR_INTERNAL_SERVER = 500;
|
|
69
|
+
static ERROR_NOT_FOUND = 404;
|
|
70
|
+
static ERROR_CONFLICT = 409; // Used if the resource already exists
|
|
71
|
+
static ERROR_UNAUTHORIZED = 401;
|
|
72
|
+
organisations = [];
|
|
73
|
+
organisationsFetched = false;
|
|
74
|
+
_accessKey;
|
|
75
|
+
_serverUrl;
|
|
76
|
+
_customHeaders;
|
|
77
|
+
passedOrgName;
|
|
78
|
+
constructor(accessKey, customHeaders, serverUrl) {
|
|
79
|
+
if (!accessKey)
|
|
80
|
+
throw new Error("An access key must be specified.");
|
|
81
|
+
this._accessKey = accessKey;
|
|
82
|
+
this._customHeaders = customHeaders;
|
|
83
|
+
this._serverUrl = serverUrl || AccountManager.SERVER_URL;
|
|
84
|
+
this.organisations = loadOrganizationsSync();
|
|
85
|
+
}
|
|
86
|
+
get accessKey() {
|
|
87
|
+
return this._accessKey;
|
|
88
|
+
}
|
|
89
|
+
isAuthenticated(throwIfUnauthorized) {
|
|
90
|
+
return Promise((resolve, reject, _notify) => {
|
|
91
|
+
const request = superagent.get(`${this._serverUrl}${urlEncode(["/authenticated"])}`);
|
|
92
|
+
this.attachCredentials(request);
|
|
93
|
+
request.end((err, res) => {
|
|
94
|
+
const status = this.getErrorStatus(err, res);
|
|
95
|
+
if (err && status !== AccountManager.ERROR_UNAUTHORIZED) {
|
|
96
|
+
reject(this.getCodePushError(err, res));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const authenticated = status === 200;
|
|
100
|
+
if (!authenticated && throwIfUnauthorized) {
|
|
101
|
+
reject(this.getCodePushError(err, res));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
resolve(authenticated);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//Tenants
|
|
109
|
+
getTenants() {
|
|
110
|
+
return this.get(urlEncode(["/tenants"])).then((res) => {
|
|
111
|
+
this.organisations = res.body.organisations;
|
|
112
|
+
saveOrganizationsSync(res.body.organisations, true);
|
|
113
|
+
return res.body.organisations;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
getOrganisations() {
|
|
117
|
+
return this.organisations;
|
|
118
|
+
}
|
|
119
|
+
getTenantId(tenantName) {
|
|
120
|
+
if (!this.organisations || this.organisations.length === 0) {
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
let tenantId = "";
|
|
124
|
+
this.organisations.forEach((org) => {
|
|
125
|
+
if (org.displayName === tenantName) {
|
|
126
|
+
tenantId = org.id;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return tenantId;
|
|
130
|
+
}
|
|
131
|
+
addAccessKey(friendlyName, ttl) {
|
|
132
|
+
if (!friendlyName) {
|
|
133
|
+
throw new Error("A name must be specified when adding an access key.");
|
|
134
|
+
}
|
|
135
|
+
const accessKeyRequest = {
|
|
136
|
+
createdBy: os.hostname(),
|
|
137
|
+
friendlyName,
|
|
138
|
+
ttl,
|
|
139
|
+
};
|
|
140
|
+
return this.post(urlEncode(["/accessKeys/"]), JSON.stringify(accessKeyRequest), /*expectResponseBody=*/ true).then((response) => {
|
|
141
|
+
return {
|
|
142
|
+
createdTime: response.body.accessKey.createdTime,
|
|
143
|
+
expires: response.body.accessKey.expires,
|
|
144
|
+
key: response.body.accessKey.name,
|
|
145
|
+
name: response.body.accessKey.friendlyName,
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
getAccessKey(accessKeyName) {
|
|
150
|
+
return this.get(urlEncode([`/accessKeys/${accessKeyName}`])).then((res) => {
|
|
151
|
+
return {
|
|
152
|
+
createdTime: res.body.accessKey.createdTime,
|
|
153
|
+
expires: res.body.accessKey.expires,
|
|
154
|
+
name: res.body.accessKey.friendlyName,
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
getAccessKeys() {
|
|
159
|
+
return this.get(urlEncode(["/accessKeys"])).then((res) => {
|
|
160
|
+
const accessKeys = [];
|
|
161
|
+
res.body.accessKeys.forEach((serverAccessKey) => {
|
|
162
|
+
!serverAccessKey.isSession &&
|
|
163
|
+
accessKeys.push({
|
|
164
|
+
createdTime: serverAccessKey.createdTime,
|
|
165
|
+
expires: serverAccessKey.expires,
|
|
166
|
+
name: serverAccessKey.friendlyName,
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
return accessKeys;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
getSessions() {
|
|
173
|
+
return this.get(urlEncode(["/accessKeys"])).then((res) => {
|
|
174
|
+
// A machine name might be associated with multiple session keys,
|
|
175
|
+
// but we should only return one per machine name.
|
|
176
|
+
const sessionMap = {};
|
|
177
|
+
const now = new Date().getTime();
|
|
178
|
+
res.body.accessKeys.forEach((serverAccessKey) => {
|
|
179
|
+
if (serverAccessKey.isSession && serverAccessKey.expires > now) {
|
|
180
|
+
sessionMap[serverAccessKey.createdBy] = {
|
|
181
|
+
loggedInTime: serverAccessKey.createdTime,
|
|
182
|
+
machineName: serverAccessKey.createdBy,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const sessions = Object.keys(sessionMap).map((machineName) => sessionMap[machineName]);
|
|
187
|
+
return sessions;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
patchAccessKey(oldName, newName, ttl) {
|
|
191
|
+
const accessKeyRequest = {
|
|
192
|
+
friendlyName: newName,
|
|
193
|
+
ttl,
|
|
194
|
+
};
|
|
195
|
+
return this.patch(urlEncode([`/accessKeys/${oldName}`]), JSON.stringify(accessKeyRequest)).then((res) => {
|
|
196
|
+
return {
|
|
197
|
+
createdTime: res.body.accessKey.createdTime,
|
|
198
|
+
expires: res.body.accessKey.expires,
|
|
199
|
+
name: res.body.accessKey.friendlyName,
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
removeAccessKey(name) {
|
|
204
|
+
return this.del(urlEncode([`/accessKeys/${name}`])).then(() => null);
|
|
205
|
+
}
|
|
206
|
+
removeSession(machineName) {
|
|
207
|
+
return this.del(urlEncode([`/sessions/${machineName}`])).then(() => null);
|
|
208
|
+
}
|
|
209
|
+
// Account
|
|
210
|
+
getAccountInfo() {
|
|
211
|
+
return this.get(urlEncode(["/account"])).then((res) => res.body.account);
|
|
212
|
+
}
|
|
213
|
+
// Apps
|
|
214
|
+
getApps() {
|
|
215
|
+
//add tenant here
|
|
216
|
+
return this.get(urlEncode(["/apps"])).then((res) => res.body.apps);
|
|
217
|
+
}
|
|
218
|
+
getApp(appName) {
|
|
219
|
+
//add tenant here
|
|
220
|
+
return this.get(urlEncode([`/apps/${appName}`])).then((res) => res.body.app);
|
|
221
|
+
}
|
|
222
|
+
addApp(appName) {
|
|
223
|
+
//add tenant here
|
|
224
|
+
const app = { name: appName };
|
|
225
|
+
const tenantId = this.getTenantId(this.passedOrgName);
|
|
226
|
+
if (tenantId && tenantId.length > 0) {
|
|
227
|
+
app.organisation = {};
|
|
228
|
+
app.organisation.orgId = tenantId;
|
|
229
|
+
}
|
|
230
|
+
else if (this.passedOrgName && this.passedOrgName.length > 0) {
|
|
231
|
+
app.organisation = {};
|
|
232
|
+
app.organisation.orgName = this.passedOrgName;
|
|
233
|
+
}
|
|
234
|
+
return this.post(urlEncode(["/apps/"]), JSON.stringify(app), /*expectResponseBody=*/ false).then(() => app);
|
|
235
|
+
}
|
|
236
|
+
removeApp(appName) {
|
|
237
|
+
//add tenant here
|
|
238
|
+
return this.del(urlEncode([`/apps/${appName}`])).then(() => null);
|
|
239
|
+
}
|
|
240
|
+
renameApp(oldAppName, newAppName) {
|
|
241
|
+
//add tenant here
|
|
242
|
+
return this.patch(urlEncode([`/apps/${oldAppName}`]), JSON.stringify({ name: newAppName })).then(() => null);
|
|
243
|
+
}
|
|
244
|
+
transferApp(appName, email) {
|
|
245
|
+
return this.post(urlEncode([`/apps/${appName}/transfer/${email}`]), /*requestBody=*/ null, /*expectResponseBody=*/ false).then(() => null);
|
|
246
|
+
}
|
|
247
|
+
// Collaborators
|
|
248
|
+
getCollaborators(appName) {
|
|
249
|
+
return this.get(urlEncode([`/apps/${appName}/collaborators`])).then((res) => res.body.collaborators);
|
|
250
|
+
}
|
|
251
|
+
addCollaborator(appName, email) {
|
|
252
|
+
return this.post(urlEncode([`/apps/${appName}/collaborators/${email}`]),
|
|
253
|
+
/*requestBody=*/ null,
|
|
254
|
+
/*expectResponseBody=*/ false).then(() => null);
|
|
255
|
+
}
|
|
256
|
+
removeCollaborator(appName, email) {
|
|
257
|
+
return this.del(urlEncode([`/apps/${appName}/collaborators/${email}`])).then(() => null);
|
|
258
|
+
}
|
|
259
|
+
// Deployments
|
|
260
|
+
addDeployment(appName, deploymentName, deploymentKey) {
|
|
261
|
+
const deployment = { name: deploymentName, key: deploymentKey };
|
|
262
|
+
return this.post(urlEncode([`/apps/${appName}/deployments/`]), JSON.stringify(deployment), /*expectResponseBody=*/ true).then((res) => res.body.deployment);
|
|
263
|
+
}
|
|
264
|
+
clearDeploymentHistory(appName, deploymentName) {
|
|
265
|
+
return this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then(() => null);
|
|
266
|
+
}
|
|
267
|
+
getDeployments(appName) {
|
|
268
|
+
return this.get(urlEncode([`/apps/${appName}/deployments/`])).then((res) => res.body.deployments);
|
|
269
|
+
}
|
|
270
|
+
getDeployment(appName, deploymentName) {
|
|
271
|
+
return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then((res) => res.body.deployment);
|
|
272
|
+
}
|
|
273
|
+
renameDeployment(appName, oldDeploymentName, newDeploymentName) {
|
|
274
|
+
return this.patch(urlEncode([`/apps/${appName}/deployments/${oldDeploymentName}`]), JSON.stringify({ name: newDeploymentName })).then(() => null);
|
|
275
|
+
}
|
|
276
|
+
removeDeployment(appName, deploymentName) {
|
|
277
|
+
return this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then(() => null);
|
|
278
|
+
}
|
|
279
|
+
getDeploymentMetrics(appName, deploymentName) {
|
|
280
|
+
return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/metrics`])).then((res) => res.body.metrics);
|
|
281
|
+
}
|
|
282
|
+
getDeploymentHistory(appName, deploymentName) {
|
|
283
|
+
return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then((res) => res.body.history);
|
|
284
|
+
}
|
|
285
|
+
release(appName, deploymentName, filePath, targetBinaryVersion, updateMetadata, uploadProgressCallback, compression = config_constants_1.CompressionType.DEFLATE) {
|
|
286
|
+
return Promise((resolve, reject) => {
|
|
287
|
+
updateMetadata.appVersion = targetBinaryVersion;
|
|
288
|
+
const request = superagent.post(this._serverUrl + urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`]));
|
|
289
|
+
this.attachCredentials(request);
|
|
290
|
+
const getPackageFilePromise = Q.Promise((resolve, reject) => {
|
|
291
|
+
this.packageFileFromPath(filePath, compression)
|
|
292
|
+
.then((result) => {
|
|
293
|
+
resolve(result);
|
|
294
|
+
})
|
|
295
|
+
.catch((error) => {
|
|
296
|
+
reject(error);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
getPackageFilePromise.then((packageFile) => {
|
|
300
|
+
const file = fs.createReadStream(packageFile.path);
|
|
301
|
+
console.log('\nUploading Zip File of size ::', fs.statSync(packageFile.path).size);
|
|
302
|
+
request
|
|
303
|
+
.attach("package", file)
|
|
304
|
+
.field("packageInfo", JSON.stringify(updateMetadata))
|
|
305
|
+
.on("progress", (event) => {
|
|
306
|
+
if (uploadProgressCallback && event && event.total > 0) {
|
|
307
|
+
const currentProgress = (event.loaded / event.total) * 100;
|
|
308
|
+
uploadProgressCallback(currentProgress);
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
.end((err, res) => {
|
|
312
|
+
if (packageFile.isTemporary) {
|
|
313
|
+
fs.unlinkSync(packageFile.path);
|
|
314
|
+
}
|
|
315
|
+
if (err) {
|
|
316
|
+
reject(this.getCodePushError(err, res));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (res.ok) {
|
|
320
|
+
resolve(null);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
let body;
|
|
324
|
+
try {
|
|
325
|
+
body = JSON.parse(res.text);
|
|
326
|
+
}
|
|
327
|
+
catch (err) { }
|
|
328
|
+
if (body) {
|
|
329
|
+
reject({
|
|
330
|
+
message: body.message,
|
|
331
|
+
statusCode: res && res.status,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
reject({
|
|
336
|
+
message: res.text,
|
|
337
|
+
statusCode: res && res.status,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
patchRelease(appName, deploymentName, label, updateMetadata) {
|
|
346
|
+
updateMetadata.label = label;
|
|
347
|
+
const requestBody = JSON.stringify({ packageInfo: updateMetadata });
|
|
348
|
+
return this.patch(urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`]), requestBody,
|
|
349
|
+
/*expectResponseBody=*/ false).then(() => null);
|
|
350
|
+
}
|
|
351
|
+
promote(appName, sourceDeploymentName, destinationDeploymentName, updateMetadata) {
|
|
352
|
+
const requestBody = JSON.stringify({ packageInfo: updateMetadata });
|
|
353
|
+
return this.post(urlEncode([`/apps/${appName}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`]), requestBody,
|
|
354
|
+
/*expectResponseBody=*/ false).then(() => null);
|
|
355
|
+
}
|
|
356
|
+
rollback(appName, deploymentName, targetRelease) {
|
|
357
|
+
return this.post(urlEncode([`/apps/${appName}/deployments/${deploymentName}/rollback/${targetRelease || ``}`]),
|
|
358
|
+
/*requestBody=*/ null,
|
|
359
|
+
/*expectResponseBody=*/ false).then(() => null);
|
|
360
|
+
}
|
|
361
|
+
packageFileFromPath(filePath, compression) {
|
|
362
|
+
let getPackageFilePromise;
|
|
363
|
+
if (fs.lstatSync(filePath).isDirectory()) {
|
|
364
|
+
getPackageFilePromise = Promise((resolve, reject) => {
|
|
365
|
+
const directoryPath = filePath;
|
|
366
|
+
recursiveFs.readdirr(directoryPath, (error, directories, files) => {
|
|
367
|
+
if (error) {
|
|
368
|
+
reject(error);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const baseDirectoryPath = path.dirname(directoryPath);
|
|
372
|
+
const fileName = this.generateRandomFilename(15) + ".zip";
|
|
373
|
+
const zipFile = new yazl.ZipFile();
|
|
374
|
+
const writeStream = fs.createWriteStream(fileName);
|
|
375
|
+
zipFile.outputStream
|
|
376
|
+
.pipe(writeStream)
|
|
377
|
+
.on("error", (error) => {
|
|
378
|
+
reject(error);
|
|
379
|
+
})
|
|
380
|
+
.on("close", () => {
|
|
381
|
+
filePath = path.join(process.cwd(), fileName);
|
|
382
|
+
resolve({ isTemporary: true, path: filePath });
|
|
383
|
+
});
|
|
384
|
+
try {
|
|
385
|
+
if (compression === config_constants_1.CompressionType.BROTLI) {
|
|
386
|
+
console.log(`\nCompressing ${files.length} files...`);
|
|
387
|
+
// For Brotli, compress each file individually
|
|
388
|
+
for (let i = 0; i < files.length; ++i) {
|
|
389
|
+
const file = files[i];
|
|
390
|
+
const relativePath = slash(path.relative(baseDirectoryPath, file));
|
|
391
|
+
const fileContent = fs.readFileSync(file);
|
|
392
|
+
// Create Brotli compressed content
|
|
393
|
+
const brotliStream = zlib.createBrotliCompress({
|
|
394
|
+
params: {
|
|
395
|
+
[zlib.constants.BROTLI_PARAM_QUALITY]: 11 // Maximum compression
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
// Add compressed content to zip
|
|
399
|
+
zipFile.addReadStream(brotliStream, `${relativePath}.br`);
|
|
400
|
+
// Write content to stream
|
|
401
|
+
brotliStream.end(fileContent);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
for (let i = 0; i < files.length; ++i) {
|
|
406
|
+
const file = files[i];
|
|
407
|
+
// yazl does not like backslash (\) in the metadata path.
|
|
408
|
+
const relativePath = slash(path.relative(baseDirectoryPath, file));
|
|
409
|
+
zipFile.addFile(file, relativePath);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
reject(err);
|
|
415
|
+
}
|
|
416
|
+
zipFile.end();
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
console.log('Provided file path is a file. Ignoring compression.');
|
|
422
|
+
getPackageFilePromise = Q({ isTemporary: false, path: filePath });
|
|
423
|
+
}
|
|
424
|
+
return getPackageFilePromise;
|
|
425
|
+
}
|
|
426
|
+
generateRandomFilename(length) {
|
|
427
|
+
let filename = "";
|
|
428
|
+
const validChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
429
|
+
for (let i = 0; i < length; i++) {
|
|
430
|
+
filename += validChar.charAt(Math.floor(Math.random() * validChar.length));
|
|
431
|
+
}
|
|
432
|
+
return filename;
|
|
433
|
+
}
|
|
434
|
+
get(endpoint, expectResponseBody = true) {
|
|
435
|
+
return this.makeApiRequest("get", endpoint, /*requestBody=*/ null, expectResponseBody, /*contentType=*/ null);
|
|
436
|
+
}
|
|
437
|
+
post(endpoint, requestBody, expectResponseBody, contentType = "application/json;charset=UTF-8") {
|
|
438
|
+
return this.makeApiRequest("post", endpoint, requestBody, expectResponseBody, contentType);
|
|
439
|
+
}
|
|
440
|
+
patch(endpoint, requestBody, expectResponseBody = false, contentType = "application/json;charset=UTF-8") {
|
|
441
|
+
return this.makeApiRequest("patch", endpoint, requestBody, expectResponseBody, contentType);
|
|
442
|
+
}
|
|
443
|
+
del(endpoint, expectResponseBody = false) {
|
|
444
|
+
return this.makeApiRequest("del", endpoint, /*requestBody=*/ null, expectResponseBody, /*contentType=*/ null);
|
|
445
|
+
}
|
|
446
|
+
makeApiRequest(method, endpoint, requestBody, expectResponseBody, contentType) {
|
|
447
|
+
return Promise((resolve, reject, _notify) => {
|
|
448
|
+
let request = superagent[method](this._serverUrl + endpoint);
|
|
449
|
+
this.attachCredentials(request);
|
|
450
|
+
if (requestBody) {
|
|
451
|
+
if (contentType) {
|
|
452
|
+
request = request.set("Content-Type", contentType);
|
|
453
|
+
}
|
|
454
|
+
request = request.send(requestBody);
|
|
455
|
+
}
|
|
456
|
+
request.end((err, res) => {
|
|
457
|
+
if (err) {
|
|
458
|
+
reject(this.getCodePushError(err, res));
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
let body;
|
|
462
|
+
try {
|
|
463
|
+
body = JSON.parse(res.text);
|
|
464
|
+
}
|
|
465
|
+
catch (err) { }
|
|
466
|
+
if (res.ok) {
|
|
467
|
+
if (expectResponseBody && !body) {
|
|
468
|
+
reject({
|
|
469
|
+
message: `Could not parse response: ${res.text}`,
|
|
470
|
+
statusCode: AccountManager.ERROR_INTERNAL_SERVER,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
resolve({
|
|
475
|
+
headers: res.header,
|
|
476
|
+
body: body,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
if (body) {
|
|
482
|
+
reject({
|
|
483
|
+
message: body.message,
|
|
484
|
+
statusCode: this.getErrorStatus(err, res),
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
reject({
|
|
489
|
+
message: res.text,
|
|
490
|
+
statusCode: this.getErrorStatus(err, res),
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
getCodePushError(error, response) {
|
|
498
|
+
if (error.syscall === "getaddrinfo") {
|
|
499
|
+
error.message = `Unable to connect to the CodePush server. Are you offline, or behind a firewall or proxy?\n(${error.message})`;
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
message: this.getErrorMessage(error, response),
|
|
503
|
+
statusCode: this.getErrorStatus(error, response),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
getErrorStatus(error, response) {
|
|
507
|
+
return (error && error.status) || (response && response.status) || AccountManager.ERROR_GATEWAY_TIMEOUT;
|
|
508
|
+
}
|
|
509
|
+
getErrorMessage(error, response) {
|
|
510
|
+
return response && response.text ? response.text : error.message;
|
|
511
|
+
}
|
|
512
|
+
attachCredentials(request) {
|
|
513
|
+
if (this._customHeaders) {
|
|
514
|
+
for (const headerName in this._customHeaders) {
|
|
515
|
+
request.set(headerName, this._customHeaders[headerName]);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// console.log("this.organisations ::", this.organisations);
|
|
519
|
+
// console.log("this.passedOrgName ::", this.passedOrgName);
|
|
520
|
+
if (this.passedOrgName && this.passedOrgName.length > 0) {
|
|
521
|
+
// eslint-disable-next-line prefer-const
|
|
522
|
+
let tenantId = this.getTenantId(this.passedOrgName);
|
|
523
|
+
request.set("tenant", tenantId);
|
|
524
|
+
}
|
|
525
|
+
const bearerToken = "cli-" + this._accessKey;
|
|
526
|
+
request.set("Accept", `application/vnd.code-push.v${AccountManager.API_VERSION}+json`);
|
|
527
|
+
request.set("Authorization", `Bearer ${bearerToken}`);
|
|
528
|
+
request.set("X-CodePush-SDK-Version", packageJson.version);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
module.exports = AccountManager;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Get the directory where the script is located
|
|
4
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
5
|
+
|
|
6
|
+
# Print detailed usage
|
|
7
|
+
print_usage() {
|
|
8
|
+
echo "Usage: $0 <old_bundle> <patch_file> <output_bundle> <is_patch_compressed>"
|
|
9
|
+
echo "Example: $0 originalBundle/index.android.bundle patch/bundle.patch patchedBundle/index.android.bundle false"
|
|
10
|
+
echo ""
|
|
11
|
+
echo "Arguments:"
|
|
12
|
+
echo " old_bundle - Path to the original bundle file"
|
|
13
|
+
echo " patch_file - Path to the patch file"
|
|
14
|
+
echo " output_bundle - Path where the new bundle will be created"
|
|
15
|
+
echo ""
|
|
16
|
+
echo "Current directory: $(pwd)"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Check number of arguments
|
|
20
|
+
if [ "$#" -ne 4 ]; then
|
|
21
|
+
echo "Error: Incorrect number of arguments"
|
|
22
|
+
print_usage
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Convert to absolute paths, handling non-existent directories
|
|
27
|
+
OLD_BUNDLE="$1"
|
|
28
|
+
PATCH_FILE="$2"
|
|
29
|
+
OUTPUT_BUNDLE="$3"
|
|
30
|
+
IS_PATCH_COMPRESSED="$4"
|
|
31
|
+
|
|
32
|
+
# If paths are relative, make them absolute from current directory
|
|
33
|
+
if [[ ! "$OLD_BUNDLE" = /* ]]; then
|
|
34
|
+
OLD_BUNDLE="$(pwd)/$OLD_BUNDLE"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [[ ! "$PATCH_FILE" = /* ]]; then
|
|
38
|
+
PATCH_FILE="$(pwd)/$PATCH_FILE"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [[ ! "$OUTPUT_BUNDLE" = /* ]]; then
|
|
42
|
+
OUTPUT_BUNDLE="$(pwd)/$OUTPUT_BUNDLE"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "Using paths:"
|
|
46
|
+
echo "Old bundle: $OLD_BUNDLE"
|
|
47
|
+
echo "Patch file: $PATCH_FILE"
|
|
48
|
+
echo "Output bundle: $OUTPUT_BUNDLE"
|
|
49
|
+
echo ""
|
|
50
|
+
|
|
51
|
+
# Check if input files exist with detailed error messages
|
|
52
|
+
if [ ! -e "$OLD_BUNDLE" ]; then
|
|
53
|
+
echo "Error: Old bundle file not found"
|
|
54
|
+
echo "Path: $OLD_BUNDLE"
|
|
55
|
+
echo "Directory contents of $(dirname "$OLD_BUNDLE"):"
|
|
56
|
+
ls -la "$(dirname "$OLD_BUNDLE")" 2>/dev/null || echo "Directory does not exist"
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [ ! -f "$OLD_BUNDLE" ]; then
|
|
61
|
+
echo "Error: Old bundle exists but is not a regular file"
|
|
62
|
+
echo "Path: $OLD_BUNDLE"
|
|
63
|
+
ls -la "$OLD_BUNDLE"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if [ ! -e "$PATCH_FILE" ]; then
|
|
68
|
+
echo "Error: Patch file not found"
|
|
69
|
+
echo "Path: $PATCH_FILE"
|
|
70
|
+
echo "Directory contents of $(dirname "$PATCH_FILE"):"
|
|
71
|
+
ls -la "$(dirname "$PATCH_FILE")" 2>/dev/null || echo "Directory does not exist"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ ! -f "$PATCH_FILE" ]; then
|
|
76
|
+
echo "Error: Patch file exists but is not a regular file"
|
|
77
|
+
echo "Path: $PATCH_FILE"
|
|
78
|
+
ls -la "$PATCH_FILE"
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Create output directory if it doesn't exist
|
|
83
|
+
OUTPUT_DIR="$(dirname "$OUTPUT_BUNDLE")"
|
|
84
|
+
if ! mkdir -p "$OUTPUT_DIR"; then
|
|
85
|
+
echo "Error: Failed to create output directory"
|
|
86
|
+
echo "Path: $OUTPUT_DIR"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Verify patch format
|
|
91
|
+
if ! head -c 16 "$PATCH_FILE" 2>/dev/null | grep -q "ENDSLEY/BSDIFF43"; then
|
|
92
|
+
echo "Warning: Patch is not in ENDSLEY/BSDIFF43 format"
|
|
93
|
+
echo "First 16 bytes of patch file:"
|
|
94
|
+
head -c 16 "$PATCH_FILE" | xxd
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Apply the patch using bsdiff43
|
|
98
|
+
echo "Applying patch..."
|
|
99
|
+
"$SCRIPT_DIR/../../../bsdiff/bsdiff43" patch "$OLD_BUNDLE" "$OUTPUT_BUNDLE" "$PATCH_FILE" "$IS_PATCH_COMPRESSED"
|
|
100
|
+
|
|
101
|
+
if [ $? -eq 0 ]; then
|
|
102
|
+
echo "Successfully applied patch:"
|
|
103
|
+
echo "Old bundle: $(wc -c < "$OLD_BUNDLE") bytes"
|
|
104
|
+
echo "Patch size: $(wc -c < "$PATCH_FILE") bytes"
|
|
105
|
+
echo "New bundle: $(wc -c < "$OUTPUT_BUNDLE") bytes"
|
|
106
|
+
echo "Output file: $OUTPUT_BUNDLE"
|
|
107
|
+
else
|
|
108
|
+
echo "Failed to apply patch"
|
|
109
|
+
echo "Command: $SCRIPT_DIR/../../bsdiff/bsdiff43 patch $OLD_BUNDLE $OUTPUT_BUNDLE $PATCH_FILE"
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Get the directory where the script is located
|
|
4
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
5
|
+
|
|
6
|
+
if [ "$#" -ne 4 ]; then
|
|
7
|
+
echo "Usage: $0 <old_bundle> <new_bundle> <patch_file> <compression>"
|
|
8
|
+
echo "Example: $0 path/to/old.bundle path/to/new.bundle directory/to/bundle.patch false"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Convert to absolute paths
|
|
13
|
+
OLD_BUNDLE="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
|
14
|
+
NEW_BUNDLE="$(cd "$(dirname "$2")" && pwd)/$(basename "$2")"
|
|
15
|
+
COMPRESSION="$4"
|
|
16
|
+
|
|
17
|
+
# Check if third argument is a file path
|
|
18
|
+
if [[ -f "$3" ]]; then
|
|
19
|
+
echo "Error: Third argument must be a directory path, not a file path"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
PATCH_DIR="$3"
|
|
24
|
+
PATCH_FILE="$PATCH_DIR/bundle.patch"
|
|
25
|
+
|
|
26
|
+
echo "==Using Paths=="
|
|
27
|
+
echo "Old bundle: $OLD_BUNDLE"
|
|
28
|
+
echo "New bundle: $NEW_BUNDLE"
|
|
29
|
+
echo "Patch file: $PATCH_FILE"
|
|
30
|
+
|
|
31
|
+
# Create the patch using bsdiff43
|
|
32
|
+
echo "Creating patch using bsdiff43..."
|
|
33
|
+
"$SCRIPT_DIR/../../../bsdiff/bsdiff43" diff "$OLD_BUNDLE" "$NEW_BUNDLE" "$PATCH_FILE" "$COMPRESSION"
|
|
34
|
+
|
|
35
|
+
if [ $? -eq 0 ]; then
|
|
36
|
+
echo "===Successfully created patch==="
|
|
37
|
+
echo "Old bundle Size: $(wc -c < "$OLD_BUNDLE") bytes"
|
|
38
|
+
echo "New bundle Size: $(wc -c < "$NEW_BUNDLE") bytes"
|
|
39
|
+
echo "Patch Size: $(wc -c < "$PATCH_FILE") bytes"
|
|
40
|
+
echo "Patch file created at: $PATCH_FILE"
|
|
41
|
+
|
|
42
|
+
# Verify patch format
|
|
43
|
+
echo "==Verifying patch format==="
|
|
44
|
+
if head -c 16 "$PATCH_FILE" | grep -q "ENDSLEY/BSDIFF43"; then
|
|
45
|
+
echo "Verified: Patch is in ENDSLEY/BSDIFF43 format"
|
|
46
|
+
else
|
|
47
|
+
echo "Patch is not in ENDSLEY/BSDIFF43 format"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
else
|
|
51
|
+
echo "Failed to create patch"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|