@appcircle/codepush-cli 0.0.1-alpha.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/.eslintrc.json +17 -0
- package/.github/pre-push +30 -0
- package/.github/prepare-commit--msg +2 -0
- package/CONTRIBUTING.md +71 -0
- package/Dockerfile +9 -0
- package/Jenkinsfile +45 -0
- package/README.md +837 -0
- package/bin/script/acquisition-sdk.js +178 -0
- package/bin/script/cli.js +23 -0
- package/bin/script/command-executor.js +1292 -0
- package/bin/script/command-parser.js +1123 -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 +454 -0
- package/bin/script/react-native-utils.js +249 -0
- package/bin/script/sign.js +69 -0
- package/bin/script/types/cli.js +40 -0
- package/bin/script/types/rest-definitions.js +19 -0
- package/bin/script/types.js +4 -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/package.json +74 -0
- package/prettier.config.js +7 -0
- package/script/acquisition-sdk.ts +273 -0
- package/script/cli.ts +27 -0
- package/script/command-executor.ts +1614 -0
- package/script/command-parser.ts +1340 -0
- package/script/commands/debug.ts +148 -0
- package/script/hash-utils.ts +241 -0
- package/script/index.ts +5 -0
- package/script/management-sdk.ts +627 -0
- package/script/react-native-utils.ts +283 -0
- package/script/sign.ts +80 -0
- package/script/types/cli.ts +234 -0
- package/script/types/rest-definitions.ts +152 -0
- package/script/types.ts +35 -0
- package/script/utils/check-package.mjs +11 -0
- package/script/utils/file-utils.ts +46 -0
- package/test/acquisition-rest-mock.ts +125 -0
- package/test/acquisition-sdk.ts +272 -0
- package/test/cli.ts +1692 -0
- package/test/hash-utils.ts +170 -0
- package/test/management-sdk.ts +438 -0
- package/test/resources/TestApp/android/app/build.gradle +56 -0
- package/test/resources/TestApp/iOS/TestApp/Info.plist +49 -0
- package/test/resources/TestApp/index.android.js +2 -0
- package/test/resources/TestApp/index.ios.js +2 -0
- package/test/resources/TestApp/index.windows.js +2 -0
- package/test/resources/TestApp/package.json +6 -0
- package/test/resources/TestApp/windows/TestApp/Package.appxmanifest +46 -0
- package/test/resources/ignoredMetadata.zip +0 -0
- package/test/resources/test.zip +0 -0
- package/test/superagent-mock-config.js +58 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
const assert = require("assert");
|
|
6
|
+
const crypto = require("crypto");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const hashUtils = require("../script/hash-utils");
|
|
9
|
+
var mkdirp = require("mkdirp");
|
|
10
|
+
const os = require("os");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const q = require("q");
|
|
13
|
+
var yauzl = require("yauzl");
|
|
14
|
+
function randomString() {
|
|
15
|
+
var stringLength = 10;
|
|
16
|
+
return crypto
|
|
17
|
+
.randomBytes(Math.ceil(stringLength / 2))
|
|
18
|
+
.toString("hex") // convert to hexadecimal format
|
|
19
|
+
.slice(0, stringLength); // return required number of characters
|
|
20
|
+
}
|
|
21
|
+
function unzipToDirectory(zipPath, directoryPath) {
|
|
22
|
+
var deferred = q.defer();
|
|
23
|
+
var originalCwd = process.cwd();
|
|
24
|
+
mkdirp(directoryPath, (err) => {
|
|
25
|
+
if (err)
|
|
26
|
+
throw err;
|
|
27
|
+
process.chdir(directoryPath);
|
|
28
|
+
yauzl.open(zipPath, { lazyEntries: true }, function (err, zipfile) {
|
|
29
|
+
if (err)
|
|
30
|
+
throw err;
|
|
31
|
+
zipfile.readEntry();
|
|
32
|
+
zipfile.on("entry", function (entry) {
|
|
33
|
+
if (/\/$/.test(entry.fileName)) {
|
|
34
|
+
// directory file names end with '/'
|
|
35
|
+
mkdirp(entry.fileName, function (err) {
|
|
36
|
+
if (err)
|
|
37
|
+
throw err;
|
|
38
|
+
zipfile.readEntry();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// file entry
|
|
43
|
+
zipfile.openReadStream(entry, function (err, readStream) {
|
|
44
|
+
if (err)
|
|
45
|
+
throw err;
|
|
46
|
+
// ensure parent directory exists
|
|
47
|
+
mkdirp(path.dirname(entry.fileName), function (err) {
|
|
48
|
+
if (err)
|
|
49
|
+
throw err;
|
|
50
|
+
readStream.pipe(fs.createWriteStream(entry.fileName));
|
|
51
|
+
readStream.on("end", function () {
|
|
52
|
+
zipfile.readEntry();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
zipfile.on("end", function (err) {
|
|
59
|
+
if (err)
|
|
60
|
+
deferred.reject(err);
|
|
61
|
+
else
|
|
62
|
+
deferred.resolve(null);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
return deferred.promise.finally(() => {
|
|
67
|
+
process.chdir(originalCwd);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
describe("Hashing utility", () => {
|
|
71
|
+
const TEST_DIRECTORY = path.join(os.tmpdir(), "codepushtests", randomString());
|
|
72
|
+
const TEST_ARCHIVE_FILE_PATH = path.join(__dirname, "resources", "test.zip");
|
|
73
|
+
const TEST_ZIP_HASH = "540fed8df3553079e81d1353c5cc4e3cac7db9aea647a85d550f646e8620c317";
|
|
74
|
+
const TEST_ZIP_MANIFEST_HASH = "9e0499ce7df5c04cb304c9deed684dc137fc603cb484a5b027478143c595d80b";
|
|
75
|
+
const HASH_B = "3e23e8160039594a33894f6564e1b1348bbd7a0088d42c4acb73eeaed59c009d";
|
|
76
|
+
const HASH_C = "2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6";
|
|
77
|
+
const HASH_D = "18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4";
|
|
78
|
+
const IGNORED_METADATA_ARCHIVE_FILE_PATH = path.join(__dirname, "resources", "ignoredMetadata.zip");
|
|
79
|
+
const INDEX_HASH = "b0693dc92f76e08bf1485b3dd9b514a2e31dfd6f39422a6b60edb722671dc98f";
|
|
80
|
+
it("generates a package hash from file", (done) => {
|
|
81
|
+
hashUtils.hashFile(TEST_ARCHIVE_FILE_PATH).done((packageHash) => {
|
|
82
|
+
assert.equal(packageHash, TEST_ZIP_HASH);
|
|
83
|
+
done();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
it("generates a package manifest for an archive", (done) => {
|
|
87
|
+
hashUtils.generatePackageManifestFromZip(TEST_ARCHIVE_FILE_PATH).done((manifest) => {
|
|
88
|
+
var fileHashesMap = manifest.toMap();
|
|
89
|
+
assert.equal(fileHashesMap.size, 3);
|
|
90
|
+
var hash = fileHashesMap.get("b.txt");
|
|
91
|
+
assert.equal(hash, HASH_B);
|
|
92
|
+
hash = fileHashesMap.get("c.txt");
|
|
93
|
+
assert.equal(hash, HASH_C);
|
|
94
|
+
hash = fileHashesMap.get("d.txt");
|
|
95
|
+
assert.equal(hash, HASH_D);
|
|
96
|
+
done();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
it("generates a package manifest for a directory", (done) => {
|
|
100
|
+
var directory = path.join(TEST_DIRECTORY, "testZip");
|
|
101
|
+
unzipToDirectory(TEST_ARCHIVE_FILE_PATH, directory)
|
|
102
|
+
.then(() => {
|
|
103
|
+
return hashUtils.generatePackageManifestFromDirectory(/*directoryPath*/ directory, /*basePath*/ directory);
|
|
104
|
+
})
|
|
105
|
+
.done((manifest) => {
|
|
106
|
+
var fileHashesMap = manifest.toMap();
|
|
107
|
+
assert.equal(fileHashesMap.size, 3);
|
|
108
|
+
var hash = fileHashesMap.get("b.txt");
|
|
109
|
+
assert.equal(hash, HASH_B);
|
|
110
|
+
hash = fileHashesMap.get("c.txt");
|
|
111
|
+
assert.equal(hash, HASH_C);
|
|
112
|
+
hash = fileHashesMap.get("d.txt");
|
|
113
|
+
assert.equal(hash, HASH_D);
|
|
114
|
+
done();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
it("generates a package hash from manifest", (done) => {
|
|
118
|
+
hashUtils
|
|
119
|
+
.generatePackageManifestFromZip(TEST_ARCHIVE_FILE_PATH)
|
|
120
|
+
.then((manifest) => {
|
|
121
|
+
return manifest.computePackageHash();
|
|
122
|
+
})
|
|
123
|
+
.done((packageHash) => {
|
|
124
|
+
assert.equal(packageHash, TEST_ZIP_MANIFEST_HASH);
|
|
125
|
+
done();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
it("generates a package manifest for an archive with ignorable metadata", (done) => {
|
|
129
|
+
hashUtils.generatePackageManifestFromZip(IGNORED_METADATA_ARCHIVE_FILE_PATH).done((manifest) => {
|
|
130
|
+
assert.equal(manifest.toMap().size, 1);
|
|
131
|
+
var hash = manifest.toMap().get("www/index.html");
|
|
132
|
+
assert.equal(hash, INDEX_HASH);
|
|
133
|
+
done();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
it("generates a package manifest for a directory with ignorable metadata", (done) => {
|
|
137
|
+
var directory = path.join(TEST_DIRECTORY, "ignorableMetadata");
|
|
138
|
+
unzipToDirectory(IGNORED_METADATA_ARCHIVE_FILE_PATH, directory)
|
|
139
|
+
.then(() => {
|
|
140
|
+
return hashUtils.generatePackageManifestFromDirectory(/*directoryPath*/ directory, /*basePath*/ directory);
|
|
141
|
+
})
|
|
142
|
+
.done((manifest) => {
|
|
143
|
+
assert.equal(manifest.toMap().size, 1);
|
|
144
|
+
var hash = manifest.toMap().get("www/index.html");
|
|
145
|
+
assert.equal(hash, INDEX_HASH);
|
|
146
|
+
done();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
const assert = require("assert");
|
|
6
|
+
const Q = require("q");
|
|
7
|
+
const AccountManager = require("../script/management-sdk");
|
|
8
|
+
var request = require("superagent");
|
|
9
|
+
var manager;
|
|
10
|
+
describe("Management SDK", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
manager = new AccountManager(/*accessKey=*/ "dummyAccessKey", "dummyPat", /*customHeaders=*/ null, /*serverUrl=*/ "http://localhost");
|
|
13
|
+
});
|
|
14
|
+
after(() => {
|
|
15
|
+
// Prevent an exception that occurs due to how superagent-mock overwrites methods
|
|
16
|
+
request.Request.prototype._callback = function () { };
|
|
17
|
+
});
|
|
18
|
+
it("methods reject the promise with status code info when an error occurs", (done) => {
|
|
19
|
+
mockReturn("Text", 404);
|
|
20
|
+
var methodsWithErrorHandling = [
|
|
21
|
+
manager.addApp.bind(manager, "appName"),
|
|
22
|
+
manager.getApp.bind(manager, "appName"),
|
|
23
|
+
manager.renameApp.bind(manager, "appName", {}),
|
|
24
|
+
manager.removeApp.bind(manager, "appName"),
|
|
25
|
+
manager.transferApp.bind(manager, "appName", "email1"),
|
|
26
|
+
manager.addDeployment.bind(manager, "appName", "deploymentName"),
|
|
27
|
+
manager.getDeployment.bind(manager, "appName", "deploymentName"),
|
|
28
|
+
manager.getDeployments.bind(manager, "appName"),
|
|
29
|
+
manager.renameDeployment.bind(manager, "appName", "deploymentName", {
|
|
30
|
+
name: "newDeploymentName",
|
|
31
|
+
}),
|
|
32
|
+
manager.removeDeployment.bind(manager, "appName", "deploymentName"),
|
|
33
|
+
manager.addCollaborator.bind(manager, "appName", "email1"),
|
|
34
|
+
manager.getCollaborators.bind(manager, "appName"),
|
|
35
|
+
manager.removeCollaborator.bind(manager, "appName", "email1"),
|
|
36
|
+
manager.patchRelease.bind(manager, "appName", "deploymentName", "label", {
|
|
37
|
+
description: "newDescription",
|
|
38
|
+
}),
|
|
39
|
+
manager.promote.bind(manager, "appName", "deploymentName", "newDeploymentName", { description: "newDescription" }),
|
|
40
|
+
manager.rollback.bind(manager, "appName", "deploymentName", "targetReleaseLabel"),
|
|
41
|
+
];
|
|
42
|
+
var result = Q(null);
|
|
43
|
+
methodsWithErrorHandling.forEach(function (f) {
|
|
44
|
+
result = result.then(() => {
|
|
45
|
+
return testErrors(f);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
result.done(() => {
|
|
49
|
+
done();
|
|
50
|
+
});
|
|
51
|
+
// Test that the proper error code and text is passed through on a server error
|
|
52
|
+
function testErrors(method) {
|
|
53
|
+
return Q.Promise((resolve, reject, notify) => {
|
|
54
|
+
method().done(() => {
|
|
55
|
+
assert.fail("Should have thrown an error");
|
|
56
|
+
reject();
|
|
57
|
+
}, (error) => {
|
|
58
|
+
assert.equal(error.message, "Text");
|
|
59
|
+
assert(error.statusCode);
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
it("isAuthenticated handles successful auth", (done) => {
|
|
66
|
+
mockReturn(JSON.stringify({ authenticated: true }), 200, {});
|
|
67
|
+
manager.isAuthenticated().done((authenticated) => {
|
|
68
|
+
assert(authenticated, "Should be authenticated");
|
|
69
|
+
done();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
it("isAuthenticated handles unsuccessful auth", (done) => {
|
|
73
|
+
mockReturn("Unauthorized", 401, {});
|
|
74
|
+
manager.isAuthenticated().done((authenticated) => {
|
|
75
|
+
assert(!authenticated, "Should not be authenticated");
|
|
76
|
+
done();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
it("isAuthenticated handles unsuccessful auth with promise rejection", (done) => {
|
|
80
|
+
mockReturn("Unauthorized", 401, {});
|
|
81
|
+
// use optional parameter to ask for rejection of the promise if not authenticated
|
|
82
|
+
manager.isAuthenticated(true).done((authenticated) => {
|
|
83
|
+
assert.fail("isAuthenticated should have rejected the promise");
|
|
84
|
+
done();
|
|
85
|
+
}, (err) => {
|
|
86
|
+
assert.equal(err.message, "Unauthorized", "Error message should be 'Unauthorized'");
|
|
87
|
+
done();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it("isAuthenticated handles unexpected status codes", (done) => {
|
|
91
|
+
mockReturn("Not Found", 404, {});
|
|
92
|
+
manager.isAuthenticated().done((authenticated) => {
|
|
93
|
+
assert.fail("isAuthenticated should have rejected the promise");
|
|
94
|
+
done();
|
|
95
|
+
}, (err) => {
|
|
96
|
+
assert.equal(err.message, "Not Found", "Error message should be 'Not Found'");
|
|
97
|
+
done();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
it("addApp handles successful response", (done) => {
|
|
101
|
+
mockReturn(JSON.stringify({ success: true }), 201, {
|
|
102
|
+
location: "/appName",
|
|
103
|
+
});
|
|
104
|
+
manager.addApp("appName").done((obj) => {
|
|
105
|
+
assert.ok(obj);
|
|
106
|
+
done();
|
|
107
|
+
}, rejectHandler);
|
|
108
|
+
});
|
|
109
|
+
it("addApp handles error response", (done) => {
|
|
110
|
+
mockReturn(JSON.stringify({ success: false }), 404, {});
|
|
111
|
+
manager.addApp("appName").done((obj) => {
|
|
112
|
+
throw new Error("Call should not complete successfully");
|
|
113
|
+
}, (error) => done());
|
|
114
|
+
});
|
|
115
|
+
it("getApp handles JSON response", (done) => {
|
|
116
|
+
mockReturn(JSON.stringify({ app: {} }), 200, {});
|
|
117
|
+
manager.getApp("appName").done((obj) => {
|
|
118
|
+
assert.ok(obj);
|
|
119
|
+
done();
|
|
120
|
+
}, rejectHandler);
|
|
121
|
+
});
|
|
122
|
+
it("updateApp handles success response", (done) => {
|
|
123
|
+
mockReturn(JSON.stringify({ apps: [] }), 200, {});
|
|
124
|
+
manager.renameApp("appName", "newAppName").done((obj) => {
|
|
125
|
+
assert.ok(!obj);
|
|
126
|
+
done();
|
|
127
|
+
}, rejectHandler);
|
|
128
|
+
});
|
|
129
|
+
it("removeApp handles success response", (done) => {
|
|
130
|
+
mockReturn("", 200, {});
|
|
131
|
+
manager.removeApp("appName").done((obj) => {
|
|
132
|
+
assert.ok(!obj);
|
|
133
|
+
done();
|
|
134
|
+
}, rejectHandler);
|
|
135
|
+
});
|
|
136
|
+
it("transferApp handles successful response", (done) => {
|
|
137
|
+
mockReturn("", 201);
|
|
138
|
+
manager.transferApp("appName", "email1").done((obj) => {
|
|
139
|
+
assert.ok(!obj);
|
|
140
|
+
done();
|
|
141
|
+
}, rejectHandler);
|
|
142
|
+
});
|
|
143
|
+
it("addDeployment handles success response", (done) => {
|
|
144
|
+
mockReturn(JSON.stringify({ deployment: { name: "name", key: "key" } }), 201, { location: "/deploymentName" });
|
|
145
|
+
manager.addDeployment("appName", "deploymentName").done((obj) => {
|
|
146
|
+
assert.ok(obj);
|
|
147
|
+
done();
|
|
148
|
+
}, rejectHandler);
|
|
149
|
+
});
|
|
150
|
+
it("getDeployment handles JSON response", (done) => {
|
|
151
|
+
mockReturn(JSON.stringify({ deployment: {} }), 200, {});
|
|
152
|
+
manager.getDeployment("appName", "deploymentName").done((obj) => {
|
|
153
|
+
assert.ok(obj);
|
|
154
|
+
done();
|
|
155
|
+
}, rejectHandler);
|
|
156
|
+
});
|
|
157
|
+
it("getDeployments handles JSON response", (done) => {
|
|
158
|
+
mockReturn(JSON.stringify({ deployments: [] }), 200, {});
|
|
159
|
+
manager.getDeployments("appName").done((obj) => {
|
|
160
|
+
assert.ok(obj);
|
|
161
|
+
done();
|
|
162
|
+
}, rejectHandler);
|
|
163
|
+
});
|
|
164
|
+
it("renameDeployment handles success response", (done) => {
|
|
165
|
+
mockReturn(JSON.stringify({ apps: [] }), 200, {});
|
|
166
|
+
manager.renameDeployment("appName", "deploymentName", "newDeploymentName").done((obj) => {
|
|
167
|
+
assert.ok(!obj);
|
|
168
|
+
done();
|
|
169
|
+
}, rejectHandler);
|
|
170
|
+
});
|
|
171
|
+
it("removeDeployment handles success response", (done) => {
|
|
172
|
+
mockReturn("", 200, {});
|
|
173
|
+
manager.removeDeployment("appName", "deploymentName").done((obj) => {
|
|
174
|
+
assert.ok(!obj);
|
|
175
|
+
done();
|
|
176
|
+
}, rejectHandler);
|
|
177
|
+
});
|
|
178
|
+
it("getDeploymentHistory handles success response with no packages", (done) => {
|
|
179
|
+
mockReturn(JSON.stringify({ history: [] }), 200);
|
|
180
|
+
manager.getDeploymentHistory("appName", "deploymentName").done((obj) => {
|
|
181
|
+
assert.ok(obj);
|
|
182
|
+
assert.equal(obj.length, 0);
|
|
183
|
+
done();
|
|
184
|
+
}, rejectHandler);
|
|
185
|
+
});
|
|
186
|
+
it("getDeploymentHistory handles success response with two packages", (done) => {
|
|
187
|
+
mockReturn(JSON.stringify({ history: [{ label: "v1" }, { label: "v2" }] }), 200);
|
|
188
|
+
manager.getDeploymentHistory("appName", "deploymentName").done((obj) => {
|
|
189
|
+
assert.ok(obj);
|
|
190
|
+
assert.equal(obj.length, 2);
|
|
191
|
+
assert.equal(obj[0].label, "v1");
|
|
192
|
+
assert.equal(obj[1].label, "v2");
|
|
193
|
+
done();
|
|
194
|
+
}, rejectHandler);
|
|
195
|
+
});
|
|
196
|
+
it("getDeploymentHistory handles error response", (done) => {
|
|
197
|
+
mockReturn("", 404);
|
|
198
|
+
manager.getDeploymentHistory("appName", "deploymentName").done((obj) => {
|
|
199
|
+
throw new Error("Call should not complete successfully");
|
|
200
|
+
}, (error) => done());
|
|
201
|
+
});
|
|
202
|
+
it("clearDeploymentHistory handles success response", (done) => {
|
|
203
|
+
mockReturn("", 204);
|
|
204
|
+
manager.clearDeploymentHistory("appName", "deploymentName").done((obj) => {
|
|
205
|
+
assert.ok(!obj);
|
|
206
|
+
done();
|
|
207
|
+
}, rejectHandler);
|
|
208
|
+
});
|
|
209
|
+
it("clearDeploymentHistory handles error response", (done) => {
|
|
210
|
+
mockReturn("", 404);
|
|
211
|
+
manager.clearDeploymentHistory("appName", "deploymentName").done((obj) => {
|
|
212
|
+
throw new Error("Call should not complete successfully");
|
|
213
|
+
}, (error) => done());
|
|
214
|
+
});
|
|
215
|
+
it("addCollaborator handles successful response", (done) => {
|
|
216
|
+
mockReturn("", 201, { location: "/collaborators" });
|
|
217
|
+
manager.addCollaborator("appName", "email1").done((obj) => {
|
|
218
|
+
assert.ok(!obj);
|
|
219
|
+
done();
|
|
220
|
+
}, rejectHandler);
|
|
221
|
+
});
|
|
222
|
+
it("addCollaborator handles error response", (done) => {
|
|
223
|
+
mockReturn("", 404, {});
|
|
224
|
+
manager.addCollaborator("appName", "email1").done((obj) => {
|
|
225
|
+
throw new Error("Call should not complete successfully");
|
|
226
|
+
}, (error) => done());
|
|
227
|
+
});
|
|
228
|
+
it("getCollaborators handles success response with no collaborators", (done) => {
|
|
229
|
+
mockReturn(JSON.stringify({ collaborators: {} }), 200);
|
|
230
|
+
manager.getCollaborators("appName").done((obj) => {
|
|
231
|
+
assert.ok(obj);
|
|
232
|
+
assert.equal(Object.keys(obj).length, 0);
|
|
233
|
+
done();
|
|
234
|
+
}, rejectHandler);
|
|
235
|
+
});
|
|
236
|
+
it("getCollaborators handles success response with multiple collaborators", (done) => {
|
|
237
|
+
mockReturn(JSON.stringify({
|
|
238
|
+
collaborators: {
|
|
239
|
+
email1: { permission: "Owner", isCurrentAccount: true },
|
|
240
|
+
email2: { permission: "Collaborator", isCurrentAccount: false },
|
|
241
|
+
},
|
|
242
|
+
}), 200);
|
|
243
|
+
manager.getCollaborators("appName").done((obj) => {
|
|
244
|
+
assert.ok(obj);
|
|
245
|
+
assert.equal(obj["email1"].permission, "Owner");
|
|
246
|
+
assert.equal(obj["email2"].permission, "Collaborator");
|
|
247
|
+
done();
|
|
248
|
+
}, rejectHandler);
|
|
249
|
+
});
|
|
250
|
+
it("removeCollaborator handles success response", (done) => {
|
|
251
|
+
mockReturn("", 200, {});
|
|
252
|
+
manager.removeCollaborator("appName", "email1").done((obj) => {
|
|
253
|
+
assert.ok(!obj);
|
|
254
|
+
done();
|
|
255
|
+
}, rejectHandler);
|
|
256
|
+
});
|
|
257
|
+
it("patchRelease handles success response", (done) => {
|
|
258
|
+
mockReturn(JSON.stringify({ package: { description: "newDescription" } }), 200);
|
|
259
|
+
manager
|
|
260
|
+
.patchRelease("appName", "deploymentName", "label", {
|
|
261
|
+
description: "newDescription",
|
|
262
|
+
})
|
|
263
|
+
.done((obj) => {
|
|
264
|
+
assert.ok(!obj);
|
|
265
|
+
done();
|
|
266
|
+
}, rejectHandler);
|
|
267
|
+
});
|
|
268
|
+
it("patchRelease handles error response", (done) => {
|
|
269
|
+
mockReturn("", 400);
|
|
270
|
+
manager.patchRelease("appName", "deploymentName", "label", {}).done((obj) => {
|
|
271
|
+
throw new Error("Call should not complete successfully");
|
|
272
|
+
}, (error) => done());
|
|
273
|
+
});
|
|
274
|
+
it("promote handles success response", (done) => {
|
|
275
|
+
mockReturn(JSON.stringify({ package: { description: "newDescription" } }), 200);
|
|
276
|
+
manager
|
|
277
|
+
.promote("appName", "deploymentName", "newDeploymentName", {
|
|
278
|
+
description: "newDescription",
|
|
279
|
+
})
|
|
280
|
+
.done((obj) => {
|
|
281
|
+
assert.ok(!obj);
|
|
282
|
+
done();
|
|
283
|
+
}, rejectHandler);
|
|
284
|
+
});
|
|
285
|
+
it("promote handles error response", (done) => {
|
|
286
|
+
mockReturn("", 400);
|
|
287
|
+
manager
|
|
288
|
+
.promote("appName", "deploymentName", "newDeploymentName", {
|
|
289
|
+
rollout: 123,
|
|
290
|
+
})
|
|
291
|
+
.done((obj) => {
|
|
292
|
+
throw new Error("Call should not complete successfully");
|
|
293
|
+
}, (error) => done());
|
|
294
|
+
});
|
|
295
|
+
it("rollback handles success response", (done) => {
|
|
296
|
+
mockReturn(JSON.stringify({ package: { label: "v1" } }), 200);
|
|
297
|
+
manager.rollback("appName", "deploymentName", "v1").done((obj) => {
|
|
298
|
+
assert.ok(!obj);
|
|
299
|
+
done();
|
|
300
|
+
}, rejectHandler);
|
|
301
|
+
});
|
|
302
|
+
it("rollback handles error response", (done) => {
|
|
303
|
+
mockReturn("", 400);
|
|
304
|
+
manager.rollback("appName", "deploymentName", "v1").done((obj) => {
|
|
305
|
+
throw new Error("Call should not complete successfully");
|
|
306
|
+
}, (error) => done());
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
// Helper method that is used everywhere that an assert.fail() is needed in a promise handler
|
|
310
|
+
function rejectHandler(val) {
|
|
311
|
+
assert.fail();
|
|
312
|
+
}
|
|
313
|
+
// Wrapper for superagent-mock that abstracts away information not needed for SDK tests
|
|
314
|
+
function mockReturn(bodyText, statusCode, header = {}) {
|
|
315
|
+
require("superagent-mock")(request, [
|
|
316
|
+
{
|
|
317
|
+
pattern: "http://localhost/(\\w+)/?",
|
|
318
|
+
fixtures: function (match, params) {
|
|
319
|
+
var isOk = statusCode >= 200 && statusCode < 300;
|
|
320
|
+
if (!isOk) {
|
|
321
|
+
var err = new Error(bodyText);
|
|
322
|
+
err.status = statusCode;
|
|
323
|
+
throw err;
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
text: bodyText,
|
|
327
|
+
status: statusCode,
|
|
328
|
+
ok: isOk,
|
|
329
|
+
header: header,
|
|
330
|
+
headers: {},
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
callback: function (match, data) {
|
|
334
|
+
return data;
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
]);
|
|
338
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@appcircle/codepush-cli",
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
|
+
"description": "Management CLI for the CodePush service",
|
|
5
|
+
"main": "./script/cli.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node ./bin/script/cli.js",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"prettier": "prettier --write \"./**/*.ts\"",
|
|
10
|
+
"lint": "npx eslint ./script/**/*.ts",
|
|
11
|
+
"lint:fix": "npx eslint ./script/**/*.ts --fix",
|
|
12
|
+
"check:package": "zx ./script/utils/check-package.mjs"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"appcircle-code-push": "./bin/script/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/appcircleio/ac-codepush-cli"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"author": "Appcircle",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"backslash": "^0.2.0",
|
|
28
|
+
"chalk": "^4.1.2",
|
|
29
|
+
"cli-table": "^0.3.11",
|
|
30
|
+
"email-validator": "^2.0.4",
|
|
31
|
+
"gradle-to-js": "2.0.1",
|
|
32
|
+
"jsonwebtoken": "^9.0.2",
|
|
33
|
+
"moment": "^2.29.4",
|
|
34
|
+
"opener": "^1.5.2",
|
|
35
|
+
"parse-duration": "1.1.0",
|
|
36
|
+
"plist": "^3.0.6",
|
|
37
|
+
"progress": "^2.0.3",
|
|
38
|
+
"prompt": "^1.3.0",
|
|
39
|
+
"properties": "^1.2.1",
|
|
40
|
+
"q": "~1.5.1",
|
|
41
|
+
"recursive-fs": "2.1.0",
|
|
42
|
+
"rimraf": "^2.5.1",
|
|
43
|
+
"semver": "^7.5.3",
|
|
44
|
+
"simctl": "^2.0.3",
|
|
45
|
+
"slash": "1.0.0",
|
|
46
|
+
"superagent": "^8.0.9",
|
|
47
|
+
"temp": "^0.9.4",
|
|
48
|
+
"which": "^1.2.7",
|
|
49
|
+
"wordwrap": "1.0.0",
|
|
50
|
+
"xcode": "^3.0.1",
|
|
51
|
+
"xml2js": "^0.6.0",
|
|
52
|
+
"yargs": "^17.7.2",
|
|
53
|
+
"yazl": "^2.5.1"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/express": "^4.17.17",
|
|
57
|
+
"@types/jest": "^29.5.2",
|
|
58
|
+
"@types/mocha": "^10.0.1",
|
|
59
|
+
"@types/node": "^20.3.1",
|
|
60
|
+
"@types/q": "^1.5.5",
|
|
61
|
+
"@types/sinon": "^10.0.15",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
63
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
64
|
+
"eslint": "^8.45.0",
|
|
65
|
+
"express": "^4.19.2",
|
|
66
|
+
"mkdirp": "^3.0.1",
|
|
67
|
+
"prettier": "^2.8.8",
|
|
68
|
+
"sinon": "15.1.2",
|
|
69
|
+
"superagent-mock": "^4.0.0",
|
|
70
|
+
"typescript": "^5.1.3",
|
|
71
|
+
"which": "^3.0.1",
|
|
72
|
+
"zx": "^5.2.3"
|
|
73
|
+
}
|
|
74
|
+
}
|