UNPKG

35.8 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14 return new (P || (P = Promise))(function (resolve, reject) {
15 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
18 step((generator = generator.apply(thisArg, _arguments || [])).next());
19 });
20};
21var __generator = (this && this.__generator) || function (thisArg, body) {
22 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24 function verb(n) { return function (v) { return step([n, v]); }; }
25 function step(op) {
26 if (f) throw new TypeError("Generator is already executing.");
27 while (_) try {
28 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29 if (y = 0, t) op = [op[0] & 2, t.value];
30 switch (op[0]) {
31 case 0: case 1: t = op; break;
32 case 4: _.label++; return { value: op[1], done: false };
33 case 5: _.label++; y = op[1]; op = [0]; continue;
34 case 7: op = _.ops.pop(); _.trys.pop(); continue;
35 default:
36 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40 if (t[2]) _.ops.pop();
41 _.trys.pop(); continue;
42 }
43 op = body.call(thisArg, _);
44 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46 }
47};
48Object.defineProperty(exports, "__esModule", { value: true });
49var Fs = require("fs-extra");
50var Path = require("path");
51var Tmp = require("tmp");
52var git_1 = require("./git");
53var local_storage_1 = require("./local-storage");
54var paths_1 = require("./paths");
55var prompt_1 = require("./prompt");
56var step_1 = require("./step");
57var submodule_1 = require("./submodule");
58var utils_1 = require("./utils");
59/**
60 The 'release' module contains different utilities and methods which are responsible
61 for release management. Before invoking any method, be sure to fetch **all** the step
62 tags from the git-host, since most calculations are based on them.
63 */
64// TODO: Create a dedicated registers/temp dirs module with no memory leaks
65var tmp1Dir = Tmp.dirSync({ unsafeCleanup: true });
66var tmp2Dir = Tmp.dirSync({ unsafeCleanup: true });
67var tmp3Dir = Tmp.dirSync({ unsafeCleanup: true });
68function promptForGitRevision(submoduleName, submodulePath) {
69 return __awaiter(this, void 0, void 0, function () {
70 var mostRecentCommit, answer, commitId;
71 var _a;
72 return __generator(this, function (_b) {
73 switch (_b.label) {
74 case 0:
75 mostRecentCommit = git_1.Git.recentCommit(null, '--format=oneline', null, submodulePath);
76 return [4 /*yield*/, prompt_1.prompt([
77 {
78 type: 'list',
79 name: 'update-module',
80 message: "Submodule '" + submoduleName + "' is pointing to the following commit: " + mostRecentCommit + ", is that correct?",
81 choices: [
82 { name: "Yes, it's the correct commit!", value: 'yes' },
83 { name: "No - I need to make sure some things before releasing", value: 'exit' },
84 ],
85 default: 'yes',
86 },
87 ])];
88 case 1:
89 answer = _b.sent();
90 if (answer === 'yes') {
91 commitId = git_1.Git.recentCommit(null, '--format="%H"', null, submodulePath);
92 return [2 /*return*/, (_a = {},
93 _a[submodulePath] = { gitRevision: commitId },
94 _a)];
95 }
96 else if (answer === 'exit') {
97 return [2 /*return*/, null];
98 }
99 return [2 /*return*/];
100 }
101 });
102 });
103}
104function promptAndHandleSubmodules(listSubmodules) {
105 return __awaiter(this, void 0, void 0, function () {
106 var modulesToVersionsMap, _i, listSubmodules_1, submodulePath, fullPath, submoduleName, isTortillaProject, allReleases, answer, result;
107 var _a;
108 return __generator(this, function (_b) {
109 switch (_b.label) {
110 case 0:
111 console.log("\u2139\uFE0F Found total of " + listSubmodules.length + " submodules.");
112 console.log("\u2757 Note that you need to make sure your submodules are pointing the correct versions:");
113 console.log("\t- If your submodule is a Tortilla project, make sure to release a new version there.");
114 console.log("\t- If your submodule is NOT a Tortilla project, make sure it's updated and pointing to the correct Git revision.\n");
115 modulesToVersionsMap = {};
116 _i = 0, listSubmodules_1 = listSubmodules;
117 _b.label = 1;
118 case 1:
119 if (!(_i < listSubmodules_1.length)) return [3 /*break*/, 8];
120 submodulePath = listSubmodules_1[_i];
121 fullPath = Path.resolve(utils_1.Utils.cwd(), submodulePath);
122 submoduleName = Path.basename(submodulePath);
123 submodule_1.Submodule.update(submoduleName);
124 // If hash doesn't exist
125 if (git_1.Git(['diff', '--name-only']).split('\n').filter(Boolean).includes(submoduleName)) {
126 // Fetch so we can have all release tags available to us
127 submodule_1.Submodule.fetch(submoduleName);
128 }
129 isTortillaProject = utils_1.Utils.exists(paths_1.resolveProject(fullPath).tortillaDir);
130 if (!isTortillaProject) return [3 /*break*/, 5];
131 allReleases = getAllReleasesOfAllBranches(fullPath);
132 if (!(allReleases.length === 0)) return [3 /*break*/, 2];
133 console.log("\uD83D\uDED1 Found a Tortilla project submodule: '" + submoduleName + "', but there are no Tortilla releases!");
134 console.log("Please make sure to release a version with Tortilla, and then try again");
135 return [2 /*return*/, null];
136 case 2: return [4 /*yield*/, prompt_1.prompt([
137 {
138 type: 'list',
139 name: submodulePath,
140 message: "Submodule '" + submoduleName + "' is a valid Tortilla project. Please pick a release from the list:",
141 choices: allReleases.map(function (releaseInfo) { return releaseInfo.tagName; }),
142 },
143 ])];
144 case 3:
145 answer = _b.sent();
146 modulesToVersionsMap = __assign({}, (modulesToVersionsMap || {}), (_a = {}, _a[fullPath] = { tortillaVersion: answer }, _a));
147 _b.label = 4;
148 case 4: return [3 /*break*/, 7];
149 case 5: return [4 /*yield*/, promptForGitRevision(submoduleName, fullPath)];
150 case 6:
151 result = _b.sent();
152 if (result === null) {
153 return [2 /*return*/, null];
154 }
155 modulesToVersionsMap = __assign({}, modulesToVersionsMap, result);
156 _b.label = 7;
157 case 7:
158 _i++;
159 return [3 /*break*/, 1];
160 case 8: return [2 /*return*/, modulesToVersionsMap];
161 }
162 });
163 });
164}
165// Creates a bumped release tag of the provided type
166// e.g. if the current release is @1.0.0 and we provide this function with a release type
167// of 'patch', the new release would be @1.0.1
168function bumpRelease(releaseType, options) {
169 return __awaiter(this, void 0, void 0, function () {
170 var currentRelease, listSubmodules, submodulesRevisions, onInitialCheckout, hasSubmodules, e_1, branch, formattedRelease, rootHash, rootTag, tag;
171 var _this = this;
172 return __generator(this, function (_a) {
173 switch (_a.label) {
174 case 0:
175 options = options || {};
176 if (releaseType === 'next') {
177 currentRelease = {
178 major: 0,
179 minor: 0,
180 patch: 0,
181 next: true,
182 };
183 }
184 else {
185 currentRelease = getCurrentRelease(true);
186 // Increase release type
187 switch (releaseType) {
188 case 'major':
189 currentRelease.major++;
190 currentRelease.minor = 0;
191 currentRelease.patch = 0;
192 break;
193 case 'minor':
194 currentRelease.minor++;
195 currentRelease.patch = 0;
196 break;
197 case 'patch':
198 currentRelease.patch++;
199 break;
200 default:
201 throw Error('Provided release type must be one of "major", "minor", "patch" or "next"');
202 }
203 }
204 listSubmodules = submodule_1.Submodule.list();
205 submodulesRevisions = {};
206 onInitialCheckout = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
207 return [2 /*return*/, undefined];
208 }); }); };
209 hasSubmodules = listSubmodules.length > 0;
210 if (hasSubmodules) {
211 // This will run at the root commit, just before we render all the manuals
212 onInitialCheckout = function () { return __awaiter(_this, void 0, void 0, function () {
213 var _i, _a, _b, submodulePath, revisionChoice;
214 return __generator(this, function (_c) {
215 switch (_c.label) {
216 case 0: return [4 /*yield*/, promptAndHandleSubmodules(listSubmodules)];
217 case 1:
218 submodulesRevisions = _c.sent();
219 if (!submodulesRevisions || Object.keys(submodulesRevisions).length !== listSubmodules.length) {
220 throw new Error("Unexpected submodules versions results!");
221 }
222 else {
223 for (_i = 0, _a = Object.entries(submodulesRevisions); _i < _a.length; _i++) {
224 _b = _a[_i], submodulePath = _b[0], revisionChoice = _b[1];
225 if (revisionChoice && revisionChoice.tortillaVersion) {
226 console.log("\u25B6\uFE0F Checking out \"" + revisionChoice.tortillaVersion + "\" in Tortilla submodule \"" + Path.basename(submodulePath) + "\"...");
227 git_1.Git(['checkout', revisionChoice.tortillaVersion], { cwd: submodulePath });
228 }
229 else if (revisionChoice && revisionChoice.gitRevision) {
230 console.log("\u25B6\uFE0F Checking out \"" + revisionChoice.gitRevision + "\" in submodule \"" + Path.basename(submodulePath) + "\"...");
231 git_1.Git(['checkout', revisionChoice.gitRevision], { cwd: submodulePath });
232 }
233 }
234 }
235 return [2 /*return*/];
236 }
237 });
238 }); };
239 }
240 // Since 'next' release is weakly held, it should be overridden by the given release
241 deleteNextReleaseTags();
242 _a.label = 1;
243 case 1:
244 _a.trys.push([1, , 6, 7]);
245 // Store potential release so it can be used during rendering
246 local_storage_1.localStorage.setItem('POTENTIAL_RELEASE', JSON.stringify(currentRelease));
247 step_1.Step.edit('root');
248 // Render once we continue
249 git_1.Git.print(['rebase', '--edit-todo'], {
250 env: {
251 GIT_SEQUENCE_EDITOR: "node " + paths_1.Paths.tortilla.editor + " render",
252 },
253 });
254 _a.label = 2;
255 case 2:
256 _a.trys.push([2, 4, , 5]);
257 return [4 /*yield*/, onInitialCheckout()];
258 case 3:
259 _a.sent();
260 return [3 /*break*/, 5];
261 case 4:
262 e_1 = _a.sent();
263 // Abort before throw if error occurred
264 git_1.Git.print(['rebase', '--abort']);
265 throw e_1;
266 case 5:
267 git_1.Git.print(['rebase', '--continue']);
268 return [3 /*break*/, 7];
269 case 6:
270 local_storage_1.localStorage.removeItem('POTENTIAL_RELEASE');
271 return [7 /*endfinally*/];
272 case 7:
273 branch = git_1.Git.activeBranchName();
274 formattedRelease = formatRelease(currentRelease);
275 rootHash = git_1.Git.rootHash();
276 rootTag = [branch, 'root', formattedRelease].join('@');
277 // Create root tag
278 // e.g. master@root@1.0.1
279 createReleaseTag(rootTag, rootHash);
280 // Create a release tag for each super step
281 git_1.Git([
282 // Log commits
283 'log',
284 // Specifically for steps
285 '--grep', '^Step [0-9]\\+:',
286 // Formatted with their subject followed by their hash
287 '--format=%s %H',
288 ]).split('\n')
289 .filter(Boolean)
290 .forEach(function (line) {
291 // Extract data
292 var words = line.split(' ');
293 var hash = words.pop();
294 var subject = words.join(' ');
295 var descriptor = step_1.Step.descriptor(subject);
296 var currentTag = [branch, "step" + descriptor.number, formattedRelease].join('@');
297 // Create tag
298 // e.g. master@step1@1.0.1
299 createReleaseTag(currentTag, hash);
300 });
301 tag = branch + "@" + formattedRelease;
302 // Create a tag with the provided message which will reference to HEAD
303 // e.g. 'master@1.0.1'
304 if (options.message) {
305 createReleaseTag(tag, 'HEAD', options.message);
306 // If no message provided, open the editor
307 }
308 else {
309 createReleaseTag(tag, 'HEAD', true);
310 }
311 createDiffReleasesBranch();
312 printCurrentRelease();
313 return [2 /*return*/];
314 }
315 });
316 });
317}
318// Removes all the references of the most recent release so the previous one would be the latest
319function revertRelease() {
320 var branch = git_1.Git.activeBranchName();
321 // Getting all branch release tags. Most recent would be first
322 var branchTags = git_1.Git(['tag', '-l'])
323 .split('\n')
324 .map(function (tag) {
325 if (!tag) {
326 return null;
327 }
328 if (new RegExp("^" + branch + "@(\\d+\\.\\d+\\.\\d+|next)$").test(tag)) {
329 return tag;
330 }
331 if (new RegExp("^" + branch + "@root@(\\d+\\.\\d+\\.\\d+|next)$").test(tag)) {
332 return tag;
333 }
334 if (new RegExp("^" + branch + "@step\\d+@(\\d+\\.\\d+\\.\\d+|next)$").test(tag)) {
335 return tag;
336 }
337 })
338 .filter(Boolean)
339 .map(function (tag) {
340 var splitted = tag.split('@');
341 return {
342 tag: tag,
343 deformatted: deformatRelease(splitted[1]),
344 };
345 })
346 .sort(function (a, b) { return (b.deformatted.next ? 1 : a.deformatted.next ? -1 :
347 (b.deformatted.major - a.deformatted.major) ||
348 (b.deformatted.minor - a.deformatted.minor) ||
349 (b.deformatted.patch - a.deformatted.patch)); })
350 .map(function (_a) {
351 var tag = _a.tag;
352 return tag;
353 });
354 if (!branchTags.length) {
355 throw Error("No release found for branch " + branch);
356 }
357 var recentRelease = branchTags[0].split('@').pop();
358 var recentReleaseTags = branchTags.filter(function (t) { return t.split('@').pop() === recentRelease; });
359 git_1.Git.print(['tag', '--delete'].concat(recentReleaseTags));
360 // Move history branch pointer one commit backward
361 try {
362 git_1.Git.print(['branch', '-f', branch + "-history", branch + "-history~1"]);
363 }
364 // was probably root, in which case we will delete the history branch
365 catch (e) {
366 git_1.Git.print(['branch', '--delete', branch + "-history"]);
367 }
368 console.log(branch + "@" + recentRelease + " has been successfuly reverted");
369}
370// Creates a branch that represents a list of our releases, this way we can view any
371// diff combination in the git-host
372function createDiffReleasesBranch() {
373 var destinationDir = createDiffReleasesRepo();
374 var sourceDir = destinationDir === tmp1Dir.name ? tmp2Dir.name : tmp1Dir.name;
375 // e.g. master
376 var currBranch = git_1.Git.activeBranchName();
377 // e.g. master-history
378 var historyBranch = currBranch + "-history";
379 // Make sure source is empty
380 Fs.emptyDirSync(sourceDir);
381 // Create dummy repo in source
382 git_1.Git(['init', sourceDir, '--bare']);
383 git_1.Git(['checkout', '-b', historyBranch], { cwd: destinationDir });
384 git_1.Git(['push', sourceDir, historyBranch], { cwd: destinationDir });
385 // Pull the newly created project to the branch name above
386 if (git_1.Git.tagExists(historyBranch)) {
387 git_1.Git(['branch', '-D', historyBranch]);
388 }
389 git_1.Git(['fetch', sourceDir, historyBranch]);
390 git_1.Git(['branch', historyBranch, 'FETCH_HEAD']);
391 // Clear registers
392 tmp1Dir.removeCallback();
393 tmp2Dir.removeCallback();
394}
395// Invokes 'git diff' with the given releases. An additional arguments vector which will
396// be invoked as is may be provided
397function diffRelease(sourceRelease, destinationRelease, argv, options) {
398 if (options === void 0) { options = {}; }
399 // Will work even if null
400 argv = argv || [];
401 // Will assume that we would like to run diff with the most recent release
402 if (!destinationRelease) {
403 var releases = getAllReleases().map(formatRelease);
404 var destinationIndex = releases.indexOf(sourceRelease) + 1;
405 destinationRelease = releases[destinationIndex];
406 }
407 var branch = options.branch || git_1.Git.activeBranchName();
408 // Compose tags
409 // If release ain't exist we will print the entire changes
410 var sourceReleaseTag = sourceRelease && branch + "@" + sourceRelease;
411 var destinationReleaseTag = branch + "@" + destinationRelease;
412 // Create repo
413 var sourceDir = createDiffReleasesRepo(sourceReleaseTag, destinationReleaseTag);
414 var gitOptions = {
415 cwd: sourceDir,
416 stdio: options.pipe ? 'pipe' : 'inherit'
417 };
418 // Exclude manual view files because we already have templates
419 argv.push('--', '.', "':!.tortilla/manuals/views'", "':!README.md'");
420 // Exclude submodules Tortilla files completely
421 submodule_1.Submodule.getFSNodes({ cwd: sourceDir }).forEach(function (_a) {
422 var file = _a.file;
423 argv.push("':!" + file + "/.tortilla'", "':!" + file + "/README.md'");
424 });
425 var result;
426 if (sourceReleaseTag) {
427 // Run 'diff' between the newly created commits
428 result = git_1.Git.print(['diff', 'HEAD^', 'HEAD'].concat(argv), gitOptions);
429 }
430 else {
431 // Run so called 'diff' between HEAD and --root. A normal diff won't work here
432 result = git_1.Git.print(['show', '--format='].concat(argv), gitOptions);
433 }
434 // Clear registers
435 tmp1Dir.removeCallback();
436 tmp2Dir.removeCallback();
437 // If the right arguments were specified we could receive the diff as a string
438 // Remove trailing white space so patch can be applied
439 return result.output && result.output.join('').replace(/ +\n/g, '\n');
440}
441// Creates the releases diff repo in a temporary dir. The result will be a path for the
442// newly created repo
443function createDiffReleasesRepo() {
444 var tags = [];
445 for (var _i = 0; _i < arguments.length; _i++) {
446 tags[_i] = arguments[_i];
447 }
448 if (tags.length === 0) {
449 var branch_1 = git_1.Git.activeBranchName();
450 // Fetch all releases in reversed order, since the commits are going to be stacked
451 // in the opposite order
452 tags = getAllReleases()
453 .map(formatRelease)
454 .reverse()
455 .map(function (releaseString) { return branch_1 + "@" + releaseString; });
456 }
457 else {
458 // Sometimes an empty argument might be provided e.g. diffRelease() method
459 tags = tags.filter(Boolean);
460 }
461 var submodules = submodule_1.Submodule.list();
462 // Resolve relative git module paths into absolute ones so they can be initialized
463 // later on
464 var submodulesUrls = submodules.reduce(function (result, submodule) {
465 var urlField = "submodule." + submodule + ".url";
466 var url = git_1.Git(['config', '--file', '.gitmodules', urlField]);
467 // Resolve relative paths
468 if (url.substr(0, 1) === '.') {
469 url = Path.resolve(utils_1.Utils.cwd(), url);
470 }
471 result[submodule] = url;
472 return result;
473 }, {});
474 // We're gonna clone the projects once, and copy paste them whenever a re-clone is needed
475 var submodulesProjectsDir = tmp3Dir.name;
476 Fs.ensureDirSync(submodulesProjectsDir);
477 var existingSubmodules = Fs.readdirSync(submodulesProjectsDir);
478 var submodulesProjects = submodules.reduce(function (result, submodule) {
479 var url = submodulesUrls[submodule];
480 result[submodule] = submodulesProjectsDir + '/' + submodule;
481 // Clone only if haven't cloned before
482 if (!existingSubmodules.includes(submodule)) {
483 var authorizedUrl = addHttpCredentials(url);
484 git_1.Git.print(['clone', authorizedUrl, submodule], { cwd: submodulesProjectsDir });
485 }
486 return result;
487 }, {});
488 // The 'registers' are directories which will be used for temporary FS calculations
489 var destinationDir = tmp1Dir.name;
490 var sourceDir = tmp2Dir.name;
491 // Make sure register2 is empty
492 Fs.emptyDirSync(sourceDir);
493 // Initialize an empty git repo in register2
494 git_1.Git(['init'], { cwd: sourceDir });
495 // Start building the diff-branch by stacking releases on top of each-other
496 return tags.reduce(function (registers, tag, index) {
497 sourceDir = registers[0];
498 destinationDir = registers[1];
499 var sourcePaths = paths_1.Paths.resolveProject(sourceDir);
500 var destinationPaths = paths_1.Paths.resolveProject(destinationDir);
501 // Make sure destination is empty
502 Fs.emptyDirSync(destinationDir);
503 // Copy current git dir to destination
504 Fs.copySync(paths_1.Paths.git.resolve(), destinationPaths.git.resolve(), {
505 filter: function (filePath) {
506 // Exclude .git/.tortilla
507 return !/\.git\/\.tortilla/.test(filePath);
508 },
509 });
510 // Checkout release
511 git_1.Git(['checkout', tag], { cwd: destinationDir });
512 git_1.Git(['checkout', '.'], { cwd: destinationDir });
513 // Dir will be initialized with git at master by default
514 submodule_1.Submodule.getFSNodes({
515 whitelist: submodules,
516 revision: tag,
517 }).forEach(function (_a) {
518 var hash = _a.hash, file = _a.file;
519 var args = [];
520 for (var _i = 1; _i < arguments.length; _i++) {
521 args[_i - 1] = arguments[_i];
522 }
523 var url = submodulesUrls[file];
524 var subDir = destinationDir + "/" + file;
525 var subPaths = paths_1.Paths.resolveProject(subDir);
526 Fs.copySync(submodulesProjects[file], subDir);
527 try {
528 git_1.Git(['checkout', hash], { cwd: subDir });
529 }
530 catch (e) {
531 console.warn();
532 console.warn("Object " + hash + " is missing for submodule " + file + " at release " + tag + ".");
533 console.warn("I don't think release for submodule exists anymore...");
534 console.warn();
535 }
536 Fs.removeSync(subPaths.readme);
537 Fs.removeSync(subPaths.tortillaDir);
538 Fs.removeSync(subPaths.git.resolve());
539 });
540 // Removing views which are irrelevant to diff. It's much more comfortable to view
541 // the templates instead
542 Fs.removeSync(destinationPaths.readme);
543 Fs.removeSync(destinationPaths.manuals.views);
544 Fs.removeSync(destinationPaths.gitModules);
545 Fs.removeSync(destinationPaths.git.resolve());
546 // Copy destination to source, but without the git dir so there won't be any
547 // conflicts with the commits
548 Fs.copySync(sourcePaths.git.resolve(), destinationPaths.git.resolve());
549 // Add commit for release
550 git_1.Git(['add', '.'], { cwd: destinationDir });
551 git_1.Git(['add', '-u'], { cwd: destinationDir });
552 // Extracting tag message
553 var tagLine = git_1.Git(['tag', '-l', tag, '-n99']);
554 var tagMessage = tagLine.replace(/([^\s]+)\s+((?:.|\n)+)/, '$1: $2');
555 // Creating a new commit with the tag's message
556 git_1.Git(['commit', '-m', tagMessage, '--allow-empty'], {
557 cwd: destinationDir,
558 });
559 return registers.reverse();
560 }, [
561 sourceDir, destinationDir,
562 ]).shift();
563}
564function printCurrentRelease() {
565 var currentRelease = getCurrentRelease();
566 var formattedRelease = formatRelease(currentRelease);
567 var branch = git_1.Git.activeBranchName();
568 console.log();
569 console.log("\uD83C\uDF1F Release: " + formattedRelease);
570 console.log("\uD83C\uDF1F Branch: " + branch);
571 console.log();
572}
573// Will transform SSH url into HTTP and will add credentials to HTTP. Originally created
574// because of tortilla.academy github app.
575function addHttpCredentials(url) {
576 if (!process.env.TORTILLA_USERNAME || !process.env.TORTILLA_PASSWORD) {
577 return url;
578 }
579 // e.g. git@github.com:Urigo/WhatsApp.git
580 var sshMatch = url.match(/^\w+@(\w+\.\w+):([\w-]+\/[\w-]+)\.git$/);
581 if (sshMatch) {
582 url = "https://" + sshMatch[1] + "/" + sshMatch[2];
583 }
584 return url.replace(/^http(s)?:\/\/(.+)$/, "http$1://" + process.env.TORTILLA_USERNAME + ":" + process.env.TORTILLA_PASSWORD + "@$2");
585}
586// Gets the current release based on the latest release tag
587// e.g. if we have the tags 'master@0.0.1', 'master@0.0.2' and 'master@0.1.0' this method
588// will return { major: 0, minor: 1, patch: 0, next: false }
589function getCurrentRelease(skipNext) {
590 if (skipNext === void 0) { skipNext = false; }
591 // Return potential release, if defined
592 var potentialRelease = local_storage_1.localStorage.getItem('POTENTIAL_RELEASE');
593 if (potentialRelease) {
594 return JSON.parse(potentialRelease);
595 }
596 var allReleases = getAllReleases();
597 var currentRelease = allReleases.shift();
598 // If version was yet to be released, assume this is a null version
599 if (!currentRelease) {
600 return {
601 major: 0,
602 minor: 0,
603 patch: 0,
604 next: false,
605 };
606 }
607 // Skip next if we asked to and if necessary
608 if (skipNext && currentRelease.next) {
609 currentRelease = allReleases.shift();
610 }
611 // No version before next
612 if (!currentRelease) {
613 return {
614 major: 0,
615 minor: 0,
616 patch: 0,
617 next: false,
618 };
619 }
620 return currentRelease;
621}
622function getAllReleasesOfAllBranches(path) {
623 if (path === void 0) { path = null; }
624 return git_1.Git(['tag'], path ? { cwd: path } : null)
625 // Put tags into an array
626 .split('\n')
627 // If no tags found, filter the empty string
628 .filter(Boolean)
629 // Filter all the release tags which are proceeded by their release
630 .filter(function (tagName) {
631 var pattern1 = /^[^@]+@\d+\.\d+\.\d+$/;
632 var pattern2 = /^[^@]+@next$/;
633 return (tagName.match(pattern1) ||
634 tagName.match(pattern2));
635 })
636 // Map all the release strings
637 .map(function (tagName) {
638 var splitted = tagName.split('@');
639 return {
640 tagName: tagName,
641 deformatted: deformatRelease(splitted[1]),
642 };
643 })
644 // Put the latest release first
645 .sort(function (a, b) { return (b.deformatted.next ? 1 : a.deformatted.next ? -1 :
646 (b.deformatted.major - a.deformatted.major) ||
647 (b.deformatted.minor - a.deformatted.minor) ||
648 (b.deformatted.patch - a.deformatted.patch)); });
649}
650// Gets a list of all the releases represented as JSONs e.g.
651// [{ major: 0, minor: 1, patch: 0 }]
652function getAllReleases(path, branch) {
653 if (path === void 0) { path = null; }
654 if (branch === void 0) { branch = git_1.Git.activeBranchName(path); }
655 return git_1.Git(['tag'], path ? { cwd: path } : null)
656 // Put tags into an array
657 .split('\n')
658 // If no tags found, filter the empty string
659 .filter(Boolean)
660 // Filter all the release tags which are proceeded by their release
661 .filter(function (tagName) {
662 var pattern1 = new RegExp("^" + branch + "@\\d+\\.\\d+\\.\\d+");
663 var pattern2 = new RegExp(branch + "@next$");
664 return (tagName.match(pattern1) ||
665 tagName.match(pattern2));
666 })
667 // Map all the release strings
668 .map(function (tagName) { return tagName.split('@').pop(); })
669 // Deformat all the releases into a json so it would be more comfortable to work with
670 .map(function (releaseString) { return deformatRelease(releaseString); })
671 // Put the latest release first
672 .sort(function (a, b) { return (b.next ? 1 : a.next ? -1 :
673 (b.major - a.major) ||
674 (b.minor - a.minor) ||
675 (b.patch - a.patch)); });
676}
677// Takes a release json and puts it into a pretty string
678// e.g. { major: 1, minor: 1, patch: 1, next: false } -> '1.1.1'
679function formatRelease(releaseJson) {
680 if (releaseJson.next) {
681 return 'next';
682 }
683 return [
684 releaseJson.major,
685 releaseJson.minor,
686 releaseJson.patch,
687 ].join('.');
688}
689// Takes a release string and puts it into a pretty json object
690// e.g. '1.1.1' -> { major: 1, minor: 1, patch: 1, next: false }
691function deformatRelease(releaseString) {
692 if (releaseString === 'next') {
693 return {
694 major: 0,
695 minor: 0,
696 patch: 0,
697 next: true,
698 };
699 }
700 var releaseSlices = releaseString.split('.').map(Number);
701 return {
702 major: releaseSlices[0],
703 minor: releaseSlices[1],
704 patch: releaseSlices[2],
705 next: false,
706 };
707}
708function createReleaseTag(tag, dstHash, message) {
709 var srcHash = git_1.Git.activeBranchName();
710 if (srcHash === 'HEAD') {
711 srcHash = git_1.Git(['rev-parse', 'HEAD']);
712 }
713 git_1.Git(['checkout', dstHash]);
714 // Remove files which shouldn't be included in releases
715 // TODO: Remove files based on a user defined blacklist
716 Fs.removeSync(paths_1.Paths.travis);
717 Fs.removeSync(paths_1.Paths.renovate);
718 // Releasing a version
719 git_1.Git.print(['commit', '--amend'], { env: { GIT_EDITOR: true } });
720 // Provide a quick message
721 if (typeof message === 'string') {
722 git_1.Git.print(['tag', tag, '-m', message]);
723 // Open editor
724 }
725 else if (message === true) {
726 git_1.Git.print(['tag', tag, '-a']);
727 // No message
728 }
729 else {
730 git_1.Git(['tag', tag]);
731 }
732 // Returning to the original hash
733 git_1.Git(['checkout', srcHash]);
734 // Restore renovate.json and .travis.yml
735 git_1.Git(['checkout', '.']);
736}
737// Delete all @next tag releases of the current branch.
738// e.g. master@next, master@root@next, master@step1@next
739function deleteNextReleaseTags() {
740 var branch = git_1.Git.activeBranchName();
741 git_1.Git(['tag', '-l']).split('\n').filter(Boolean).forEach(function (tagName) {
742 if (new RegExp("^" + branch + "@").test(tagName) &&
743 new RegExp("@next$").test(tagName)) {
744 git_1.Git(['tag', '--delete', tagName]);
745 }
746 });
747}
748exports.Release = {
749 bump: bumpRelease,
750 revert: revertRelease,
751 createDiffBranch: createDiffReleasesBranch,
752 printCurrent: printCurrentRelease,
753 current: getCurrentRelease,
754 all: getAllReleases,
755 diff: diffRelease,
756 format: formatRelease,
757 deformat: deformatRelease,
758};
759//# sourceMappingURL=release.js.map
\No newline at end of file