UNPKG

25.2 kBJavaScriptView Raw
1"use strict";
2var __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};
10var __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};
37var path = require("path");
38var fs = require("fs");
39var crypto = require("crypto");
40var Zip = require("jszip");
41var JSZip = require("jszip");
42var Q = require("q");
43var node_diff3_wrapper_1 = require("@cloudstitch/node-diff3-wrapper");
44var request_1 = require("./request");
45var logger_1 = require("../lib/logger");
46var utils = require("../lib/utils");
47function _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}
62function _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 /*break*/, 2];
70 hashFilePath = path.join(folder, ".cloudstitch", "cloudstitch.md5");
71 logger_1.instance.info("reading hash file " + hashFilePath);
72 return [4 /*yield*/, 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 /*break*/, 4];
79 case 3:
80 e_1 = _a.sent();
81 return [3 /*break*/, 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 /*return*/, hashes];
99 }
100 });
101 });
102}
103function _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 /*yield*/, Q.nfcall(fs.writeFile, file, hashFile)];
112 case 1:
113 _a.sent();
114 return [2 /*return*/];
115 }
116 });
117 });
118}
119function 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}
144var 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 /*yield*/, _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 /*yield*/, request_1.default.get("/project/" + user + "/" + app + "/pull")];
173 case 3:
174 result = _a.sent();
175 return [3 /*break*/, 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 /*break*/, 5];
188 case 5:
189 _a.trys.push([5, 7, , 8]);
190 return [4 /*yield*/, Q.nfcall(fs.mkdir, path.join(folder, ".cloudstitch"))];
191 case 6:
192 _a.sent();
193 return [3 /*break*/, 8];
194 case 7:
195 e_3 = _a.sent();
196 return [3 /*break*/, 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 /*yield*/, Zip.loadAsync(fileContent)];
205 case 10:
206 zip = _a.sent();
207 return [3 /*break*/, 12];
208 case 11:
209 e_4 = _a.sent();
210 logger_1.instance.error(e_4.message);
211 return [3 /*break*/, 12];
212 case 12:
213 logger_1.instance.info("Zip responce successfully intrpreted");
214 return [4 /*yield*/, zip.file("cloudstitch.md5").async("string")];
215 case 13:
216 zipMd5FileContent = _a.sent();
217 return [4 /*yield*/, _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 /*break*/, 1];
229 if (file.dir) {
230 fs.mkdirSync(finalFilePath);
231 }
232 else {
233 writeFile = true;
234 }
235 return [3 /*break*/, 9];
236 case 1:
237 if (!!file.dir) return [3 /*break*/, 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 /*break*/, 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 /*break*/, 7];
251 originalFilePath = path.join(folder, ".cloudstitch", filePath);
252 a = oldFileContent.toString("utf-8");
253 return [4 /*yield*/, 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 /*yield*/, node_diff3_wrapper_1.Diff3.diffM(finalFilePath, originalFilePath, "-", b)];
262 case 4:
263 diff = _a.sent();
264 return [3 /*break*/, 6];
265 case 5:
266 e_5 = _a.sent();
267 logger_1.instance.error(e_5);
268 return [3 /*break*/, 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 /*break*/, 9];
282 case 8:
283 writeFile = true;
284 _a.label = 9;
285 case 9:
286 if (!writeFile) return [3 /*break*/, 11];
287 return [4 /*yield*/, 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 /*break*/, 12];
298 case 11:
299 logger_1.instance.debug("skipping " + filePath);
300 _a.label = 12;
301 case 12: return [2 /*return*/];
302 }
303 });
304 }); });
305 return [2 /*return*/];
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 /*yield*/, 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 /*break*/, 3];
326 zip = new JSZip();
327 top = true;
328 return [4 /*yield*/, _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 /*break*/, 10];
339 file = files_1[_i];
340 return [4 /*yield*/, Q.nfcall(fs.stat, path.join(folder, file))];
341 case 5:
342 stat = _a.sent();
343 if (!stat.isDirectory()) return [3 /*break*/, 7];
344 return [4 /*yield*/, Project.zip(path.join(folder, file), baseDir, zip, hashes, newHashes)];
345 case 6:
346 zip = (_a.sent());
347 return [3 /*break*/, 9];
348 case 7:
349 if (!stat.isFile()) return [3 /*break*/, 9];
350 return [4 /*yield*/, 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 /*break*/, 4];
376 case 10:
377 if (!top) return [3 /*break*/, 14];
378 if (!(Object.keys(zip.files).length === 0)) return [3 /*break*/, 11];
379 throw new Error("There does not seem to be any changes, nothing to do.");
380 case 11: return [4 /*yield*/, _writeNewHashes(folder + "/.cloudstitch/cloudstitch.md5", newHashes)];
381 case 12:
382 _a.sent();
383 return [2 /*return*/, zip.generateAsync({
384 type: "nodebuffer",
385 compression: "DEFLATE",
386 })];
387 case 13: return [3 /*break*/, 15];
388 case 14: return [2 /*return*/, zip];
389 case 15: return [2 /*return*/];
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 /*yield*/, _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 /*yield*/, 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 /*yield*/, Project.zip(folder)];
414 case 3:
415 zip = _a.sent();
416 return [4 /*yield*/, request_1.default.put("/project/" + user + "/" + app + "/push", zip, false, "application/zip")];
417 case 4: return [2 /*return*/, _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 /*yield*/, request_1.default.post("/project/" + user + "/" + app + "/clone", req)];
441 case 2:
442 res = _a.sent();
443 return [3 /*break*/, 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 /*break*/, 8];
453 return [4 /*yield*/, utils.setTimeoutPromise(500)];
454 case 6:
455 _a.sent();
456 return [4 /*yield*/, 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 /*break*/, 5];
466 case 8: return [2 /*return*/, appName];
467 }
468 });
469 });
470 };
471 Project.map = function (frontend) {
472 return "handlebars";
473 };
474 return Project;
475}());
476Object.defineProperty(exports, "__esModule", { value: true });
477exports.default = Project;