1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | return new (P || (P = Promise))(function (resolve, reject) {
|
4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
7 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8 | });
|
9 | };
|
10 | var __generator = (this && this.__generator) || function (thisArg, body) {
|
11 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
|
12 | return { next: verb(0), "throw": verb(1), "return": verb(2) };
|
13 | function verb(n) { return function (v) { return step([n, v]); }; }
|
14 | function step(op) {
|
15 | if (f) throw new TypeError("Generator is already executing.");
|
16 | while (_) try {
|
17 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
|
18 | if (y = 0, t) op = [0, t.value];
|
19 | switch (op[0]) {
|
20 | case 0: case 1: t = op; break;
|
21 | case 4: _.label++; return { value: op[1], done: false };
|
22 | case 5: _.label++; y = op[1]; op = [0]; continue;
|
23 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
24 | default:
|
25 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
26 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
27 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
28 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
29 | if (t[2]) _.ops.pop();
|
30 | _.trys.pop(); continue;
|
31 | }
|
32 | op = body.call(thisArg, _);
|
33 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
34 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
35 | }
|
36 | };
|
37 | var path = require("path");
|
38 | var fs = require("fs");
|
39 | var crypto = require("crypto");
|
40 | var Zip = require("jszip");
|
41 | var JSZip = require("jszip");
|
42 | var Q = require("q");
|
43 | var node_diff3_wrapper_1 = require("@cloudstitch/node-diff3-wrapper");
|
44 | var request_1 = require("./request");
|
45 | var logger_1 = require("../lib/logger");
|
46 | var utils = require("../lib/utils");
|
47 | function _shouldAddFile(file, thisHash, hashes) {
|
48 | logger_1.instance.info("Should add " + file + " ?");
|
49 | if (typeof hashes === "undefined") {
|
50 | logger_1.instance.info("Yes, no hash file");
|
51 | return true;
|
52 | }
|
53 | if (hashes[file]) {
|
54 | logger_1.instance.info("Checking hashes " + hashes[file] + " !== " + thisHash + " : " + (hashes[file] !== thisHash));
|
55 | return hashes[file] !== thisHash;
|
56 | }
|
57 | else {
|
58 | logger_1.instance.info("yes the hash was not in the hash file.");
|
59 | return true;
|
60 | }
|
61 | }
|
62 | function _loadHashFile(folder, fileContent) {
|
63 | return __awaiter(this, void 0, void 0, function () {
|
64 | var hashFile, hashes, hashFilePath, e_1;
|
65 | return __generator(this, function (_a) {
|
66 | switch (_a.label) {
|
67 | case 0:
|
68 | _a.trys.push([0, 3, , 4]);
|
69 | if (!!fileContent) return [3 , 2];
|
70 | hashFilePath = path.join(folder, ".cloudstitch", "cloudstitch.md5");
|
71 | logger_1.instance.info("reading hash file " + hashFilePath);
|
72 | return [4 , Q.nfcall(fs.readFile, hashFilePath)];
|
73 | case 1:
|
74 | hashFile = (_a.sent());
|
75 | hashFile = hashFile.toString("utf-8");
|
76 | logger_1.instance.info("loaded md5 file: " + hashFile);
|
77 | _a.label = 2;
|
78 | case 2: return [3 , 4];
|
79 | case 3:
|
80 | e_1 = _a.sent();
|
81 | return [3 , 4];
|
82 | case 4:
|
83 | if (fileContent) {
|
84 | logger_1.instance.info("reading hashes from string");
|
85 | hashFile = fileContent;
|
86 | }
|
87 | logger_1.instance.info("Parsing hash file: " + hashFile);
|
88 | if (typeof hashFile === "string") {
|
89 | hashes = {};
|
90 | hashFile.split("\n").forEach(function (line) {
|
91 | if (line.length > 1) {
|
92 | var lineParts = line.split(" ");
|
93 | hashes[lineParts[0]] = lineParts[1];
|
94 | }
|
95 | });
|
96 | }
|
97 | logger_1.instance.info("parsed hash file: " + JSON.stringify(hashes));
|
98 | return [2 , hashes];
|
99 | }
|
100 | });
|
101 | });
|
102 | }
|
103 | function _writeNewHashes(file, hashes) {
|
104 | return __awaiter(this, void 0, void 0, function () {
|
105 | var hashFile;
|
106 | return __generator(this, function (_a) {
|
107 | switch (_a.label) {
|
108 | case 0:
|
109 | hashFile = "";
|
110 | Object.keys(hashes).forEach(function (filePath) { return hashFile = "" + hashFile + filePath + " " + hashes[filePath] + "\n"; });
|
111 | return [4 , Q.nfcall(fs.writeFile, file, hashFile)];
|
112 | case 1:
|
113 | _a.sent();
|
114 | return [2 ];
|
115 | }
|
116 | });
|
117 | });
|
118 | }
|
119 | function cleanUpDiff(file) {
|
120 | var splitFile = file.split("\n");
|
121 | var result = [];
|
122 | var skip = false;
|
123 | for (var _i = 0, splitFile_1 = splitFile; _i < splitFile_1.length; _i++) {
|
124 | var line = splitFile_1[_i];
|
125 | if (/\|{7}/.exec(line)) {
|
126 | skip = true;
|
127 | }
|
128 | else if (/={7}/.exec(line)) {
|
129 | skip = false;
|
130 | result.push("=".repeat(8));
|
131 | }
|
132 | else if (/<{7}/.exec(line)) {
|
133 | result.push("<".repeat(8));
|
134 | }
|
135 | else if (/>{7}/.exec(line)) {
|
136 | result.push(">".repeat(8));
|
137 | }
|
138 | else if (!skip) {
|
139 | result.push(line);
|
140 | }
|
141 | }
|
142 | return result.join("\n");
|
143 | }
|
144 | var Project = (function () {
|
145 | function Project() {
|
146 | }
|
147 | Project.verifyIdentifier = function (identifier) {
|
148 | if (!identifier || identifier.indexOf("/") === -1) {
|
149 | return {};
|
150 | }
|
151 | var userAppParts = identifier.split("/");
|
152 | if (userAppParts.length !== 2 || userAppParts[0].length < 1 || userAppParts[1].length < 1) {
|
153 | return {};
|
154 | }
|
155 | return {
|
156 | user: userAppParts[0],
|
157 | app: userAppParts[1]
|
158 | };
|
159 | };
|
160 | Project.pull = function (folder, user, app, force) {
|
161 | return __awaiter(this, void 0, void 0, function () {
|
162 | var _this = this;
|
163 | var hashCodes, result, e_2, e_3, fileContent, zip, e_4, zipMd5FileContent, zipMd5FileHashes;
|
164 | return __generator(this, function (_a) {
|
165 | switch (_a.label) {
|
166 | case 0: return [4 , _loadHashFile(folder)];
|
167 | case 1:
|
168 | hashCodes = _a.sent();
|
169 | _a.label = 2;
|
170 | case 2:
|
171 | _a.trys.push([2, 4, , 5]);
|
172 | return [4 , request_1.default.get("/project/" + user + "/" + app + "/pull")];
|
173 | case 3:
|
174 | result = _a.sent();
|
175 | return [3 , 5];
|
176 | case 4:
|
177 | e_2 = _a.sent();
|
178 | if (e_2.res.statusCode && e_2.res.statusCode === 404) {
|
179 | throw new Error("Project not found");
|
180 | }
|
181 | else if (e_2.res.statusCode && e_2.res.statusCode === 401) {
|
182 | throw new Error("Permission denied");
|
183 | }
|
184 | else {
|
185 | throw e_2;
|
186 | }
|
187 | return [3 , 5];
|
188 | case 5:
|
189 | _a.trys.push([5, 7, , 8]);
|
190 | return [4 , Q.nfcall(fs.mkdir, path.join(folder, ".cloudstitch"))];
|
191 | case 6:
|
192 | _a.sent();
|
193 | return [3 , 8];
|
194 | case 7:
|
195 | e_3 = _a.sent();
|
196 | return [3 , 8];
|
197 | case 8:
|
198 | logger_1.instance.info("Got responce back from server pull: " + result.res.statusCode);
|
199 | fileContent = Buffer.from(result.body, "binary");
|
200 | logger_1.instance.info("Zip responce converted from buffer");
|
201 | _a.label = 9;
|
202 | case 9:
|
203 | _a.trys.push([9, 11, , 12]);
|
204 | return [4 , Zip.loadAsync(fileContent)];
|
205 | case 10:
|
206 | zip = _a.sent();
|
207 | return [3 , 12];
|
208 | case 11:
|
209 | e_4 = _a.sent();
|
210 | logger_1.instance.error(e_4.message);
|
211 | return [3 , 12];
|
212 | case 12:
|
213 | logger_1.instance.info("Zip responce successfully intrpreted");
|
214 | return [4 , zip.file("cloudstitch.md5").async("string")];
|
215 | case 13:
|
216 | zipMd5FileContent = _a.sent();
|
217 | return [4 , _loadHashFile(null, zipMd5FileContent)];
|
218 | case 14:
|
219 | zipMd5FileHashes = _a.sent();
|
220 | zip.forEach(function (filePath, file) { return __awaiter(_this, void 0, void 0, function () {
|
221 | var finalFilePath, writeFile, stat, oldFileContent, fileHash, modifedLocally, modifiedRemotely, originalFilePath, a, b, diff, e_5, fileContent_1, cacheFilePath;
|
222 | return __generator(this, function (_a) {
|
223 | switch (_a.label) {
|
224 | case 0:
|
225 | logger_1.instance.info("Processing: " + filePath);
|
226 | finalFilePath = path.join(folder, filePath);
|
227 | writeFile = false;
|
228 | if (!!hashCodes) return [3 , 1];
|
229 | if (file.dir) {
|
230 | fs.mkdirSync(finalFilePath);
|
231 | }
|
232 | else {
|
233 | writeFile = true;
|
234 | }
|
235 | return [3 , 9];
|
236 | case 1:
|
237 | if (!!file.dir) return [3 , 9];
|
238 | stat = void 0;
|
239 | try {
|
240 | stat = fs.statSync(finalFilePath);
|
241 | }
|
242 | catch (e) { }
|
243 | logger_1.instance.info("Going to check local file. file exists: " + !!stat + ", file has old hash: " + !!hashCodes[filePath] + ", have past file hash: " + (filePath !== "cloudstitch.md5"));
|
244 | if (!(stat && hashCodes[filePath] && filePath !== "cloudstitch.md5")) return [3 , 8];
|
245 | oldFileContent = fs.readFileSync(finalFilePath);
|
246 | fileHash = crypto.createHash("md5").update(oldFileContent).digest("hex");
|
247 | modifedLocally = fileHash !== hashCodes[filePath];
|
248 | modifiedRemotely = hashCodes[filePath] !== zipMd5FileHashes[filePath];
|
249 | logger_1.instance.debug("checking hash " + filePath + " local: " + modifedLocally + ", remote " + modifiedRemotely);
|
250 | if (!(modifedLocally && modifiedRemotely)) return [3 , 7];
|
251 | originalFilePath = path.join(folder, ".cloudstitch", filePath);
|
252 | a = oldFileContent.toString("utf-8");
|
253 | return [4 , file.async("nodebuffer")];
|
254 | case 2:
|
255 | b = (_a.sent()).toString("utf-8");
|
256 | diff = void 0;
|
257 | _a.label = 3;
|
258 | case 3:
|
259 | _a.trys.push([3, 5, , 6]);
|
260 | logger_1.instance.info("Calling: diff3 -m " + finalFilePath + " " + originalFilePath + " - stdin: " + b);
|
261 | return [4 , node_diff3_wrapper_1.Diff3.diffM(finalFilePath, originalFilePath, "-", b)];
|
262 | case 4:
|
263 | diff = _a.sent();
|
264 | return [3 , 6];
|
265 | case 5:
|
266 | e_5 = _a.sent();
|
267 | logger_1.instance.error(e_5);
|
268 | return [3 , 6];
|
269 | case 6:
|
270 | if (diff) {
|
271 | diff = cleanUpDiff(diff);
|
272 | if (diff.indexOf("=".repeat(8)) !== -1) {
|
273 | logger_1.instance.error("Conflict in " + filePath + " please resolve manually");
|
274 | }
|
275 | else {
|
276 | logger_1.instance.warn("Conflict in " + filePath + " resolved automatically");
|
277 | }
|
278 | fs.writeFileSync(finalFilePath, diff);
|
279 | }
|
280 | _a.label = 7;
|
281 | case 7: return [3 , 9];
|
282 | case 8:
|
283 | writeFile = true;
|
284 | _a.label = 9;
|
285 | case 9:
|
286 | if (!writeFile) return [3 , 11];
|
287 | return [4 , file.async("nodebuffer")];
|
288 | case 10:
|
289 | fileContent_1 = _a.sent();
|
290 | if (filePath !== "cloudstitch.md5") {
|
291 | fs.writeFileSync(finalFilePath, fileContent_1);
|
292 | logger_1.instance.info("Wrote content of file: " + finalFilePath);
|
293 | }
|
294 | cacheFilePath = path.join(folder, ".cloudstitch", filePath);
|
295 | fs.writeFileSync(cacheFilePath, fileContent_1);
|
296 | logger_1.instance.info("Wrote content of file: " + cacheFilePath);
|
297 | return [3 , 12];
|
298 | case 11:
|
299 | logger_1.instance.debug("skipping " + filePath);
|
300 | _a.label = 12;
|
301 | case 12: return [2 ];
|
302 | }
|
303 | });
|
304 | }); });
|
305 | return [2 ];
|
306 | }
|
307 | });
|
308 | });
|
309 | };
|
310 | Project.zip = function (folder, baseDir, zip, hashes, newHashes) {
|
311 | return __awaiter(this, void 0, void 0, function () {
|
312 | var files, top, _i, files_1, file, stat, fileContent, dirDifference, thisHash, basePath;
|
313 | return __generator(this, function (_a) {
|
314 | switch (_a.label) {
|
315 | case 0:
|
316 | if (!baseDir) {
|
317 | baseDir = folder;
|
318 | }
|
319 | return [4 , Q.nfcall(fs.readdir, folder)];
|
320 | case 1:
|
321 | files = _a.sent();
|
322 | files = files.filter(function (file) { return file !== "cloudstitch.md5" && file !== ".cloudstitch"; });
|
323 | logger_1.instance.info("Found files: " + JSON.stringify(files));
|
324 | top = false;
|
325 | if (!!zip) return [3 , 3];
|
326 | zip = new JSZip();
|
327 | top = true;
|
328 | return [4 , _loadHashFile(folder)];
|
329 | case 2:
|
330 | hashes = _a.sent();
|
331 | newHashes = {};
|
332 | _a.label = 3;
|
333 | case 3:
|
334 | logger_1.instance.info("starting zip of dir: " + folder);
|
335 | _i = 0, files_1 = files;
|
336 | _a.label = 4;
|
337 | case 4:
|
338 | if (!(_i < files_1.length)) return [3 , 10];
|
339 | file = files_1[_i];
|
340 | return [4 , Q.nfcall(fs.stat, path.join(folder, file))];
|
341 | case 5:
|
342 | stat = _a.sent();
|
343 | if (!stat.isDirectory()) return [3 , 7];
|
344 | return [4 , Project.zip(path.join(folder, file), baseDir, zip, hashes, newHashes)];
|
345 | case 6:
|
346 | zip = (_a.sent());
|
347 | return [3 , 9];
|
348 | case 7:
|
349 | if (!stat.isFile()) return [3 , 9];
|
350 | return [4 , Q.nfcall(fs.readFile, path.join(folder, file))];
|
351 | case 8:
|
352 | fileContent = _a.sent(), dirDifference = folder.replace(baseDir, ""), thisHash = crypto.createHash("md5").update(fileContent).digest("hex");
|
353 | if (fileContent.toString("utf-8").indexOf("<".repeat(8)) !== -1) {
|
354 | throw new Error("It looks like there is a merge conflict in file " + file + ", please manualy resolve this before pushing.");
|
355 | }
|
356 | if (dirDifference && dirDifference.charAt(0) === "/") {
|
357 | dirDifference = dirDifference.slice(1);
|
358 | }
|
359 | if (dirDifference && dirDifference.length > 0) {
|
360 | newHashes[dirDifference + "/" + file] = thisHash;
|
361 | if (_shouldAddFile(dirDifference + "/" + file, thisHash, hashes)) {
|
362 | zip.folder(dirDifference).file(file, fileContent);
|
363 | }
|
364 | }
|
365 | else {
|
366 | basePath = file.replace(baseDir + "/", "");
|
367 | newHashes[basePath] = thisHash;
|
368 | if (_shouldAddFile(file, thisHash, hashes)) {
|
369 | zip.file(file, fileContent);
|
370 | }
|
371 | }
|
372 | _a.label = 9;
|
373 | case 9:
|
374 | _i++;
|
375 | return [3 , 4];
|
376 | case 10:
|
377 | if (!top) return [3 , 14];
|
378 | if (!(Object.keys(zip.files).length === 0)) return [3 , 11];
|
379 | throw new Error("There does not seem to be any changes, nothing to do.");
|
380 | case 11: return [4 , _writeNewHashes(folder + "/.cloudstitch/cloudstitch.md5", newHashes)];
|
381 | case 12:
|
382 | _a.sent();
|
383 | return [2 , zip.generateAsync({
|
384 | type: "nodebuffer",
|
385 | compression: "DEFLATE",
|
386 | })];
|
387 | case 13: return [3 , 15];
|
388 | case 14: return [2 , zip];
|
389 | case 15: return [2 ];
|
390 | }
|
391 | });
|
392 | });
|
393 | };
|
394 | Project.push = function (folder, user, app) {
|
395 | return __awaiter(this, void 0, void 0, function () {
|
396 | var hashes, fileHashes, canPush, zip;
|
397 | return __generator(this, function (_a) {
|
398 | switch (_a.label) {
|
399 | case 0: return [4 , _loadHashFile(folder)];
|
400 | case 1:
|
401 | hashes = _a.sent(), fileHashes = Object.keys(hashes).map(function (key) {
|
402 | return {
|
403 | key: key,
|
404 | hash: hashes[key]
|
405 | };
|
406 | });
|
407 | return [4 , request_1.default.put("project/" + user + "/" + app + "/push-check", { fileHashes: fileHashes })];
|
408 | case 2:
|
409 | canPush = _a.sent();
|
410 | if (!canPush.body.valid) {
|
411 | throw new Error("There were changes made on the server since your last pull. Pull now, then you will be able to push.");
|
412 | }
|
413 | return [4 , Project.zip(folder)];
|
414 | case 3:
|
415 | zip = _a.sent();
|
416 | return [4 , request_1.default.put("/project/" + user + "/" + app + "/push", zip, false, "application/zip")];
|
417 | case 4: return [2 , _a.sent()];
|
418 | }
|
419 | });
|
420 | });
|
421 | };
|
422 | Project.clone = function (name, from, backendStack, frontEnd) {
|
423 | return __awaiter(this, void 0, void 0, function () {
|
424 | var fromParts, user, app, req, res, e_6, cloneRes, appName, appUsername, finished, statusCheck, cloneStatus;
|
425 | return __generator(this, function (_a) {
|
426 | switch (_a.label) {
|
427 | case 0:
|
428 | fromParts = from.split("/"), user = fromParts[0], app = fromParts[1], req = {
|
429 | fromUser: user,
|
430 | fromApp: app,
|
431 | name: name,
|
432 | backendStack: backendStack
|
433 | };
|
434 | if (frontEnd) {
|
435 | req.frontEnd = frontEnd;
|
436 | }
|
437 | _a.label = 1;
|
438 | case 1:
|
439 | _a.trys.push([1, 3, , 4]);
|
440 | return [4 , request_1.default.post("/project/" + user + "/" + app + "/clone", req)];
|
441 | case 2:
|
442 | res = _a.sent();
|
443 | return [3 , 4];
|
444 | case 3:
|
445 | e_6 = _a.sent();
|
446 | throw new Error("Clone Error: " + e_6.message);
|
447 | case 4:
|
448 | cloneRes = res.body, appName = cloneRes.app, appUsername = cloneRes.user;
|
449 | finished = false;
|
450 | _a.label = 5;
|
451 | case 5:
|
452 | if (!!finished) return [3 , 8];
|
453 | return [4 , utils.setTimeoutPromise(500)];
|
454 | case 6:
|
455 | _a.sent();
|
456 | return [4 , request_1.default.get("/project/" + appUsername + "/" + appName + "/clone-status")];
|
457 | case 7:
|
458 | statusCheck = _a.sent();
|
459 | cloneStatus = statusCheck.body;
|
460 | logger_1.instance.info("clone status " + cloneStatus.status);
|
461 | finished = cloneStatus.status === "success";
|
462 | if (cloneStatus.status === "fail" || cloneStatus.status === "error") {
|
463 | throw new Error(cloneStatus.statusMessage);
|
464 | }
|
465 | return [3 , 5];
|
466 | case 8: return [2 , appName];
|
467 | }
|
468 | });
|
469 | });
|
470 | };
|
471 | Project.map = function (frontend) {
|
472 | return "handlebars";
|
473 | };
|
474 | return Project;
|
475 | }());
|
476 | Object.defineProperty(exports, "__esModule", { value: true });
|
477 | exports.default = Project;
|