smooth-release
Version:
Smart CLI tool to safely and automatically do every step to release a new version of a library hosted on GitHub and published on npm
429 lines (325 loc) • 14.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _semver = require('semver');
var _semver2 = _interopRequireDefault(_semver);
var _lodash = require('lodash');
var _utils = require('../utils');
var _config = require('../config');
var _config2 = _interopRequireDefault(_config);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var stdio = [process.stdin, null, process.stderr];
var checkIfVersionShouldBeBreaking = function () {
var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(_ref) {
var lastVersionTag = _ref.lastVersionTag,
dataType = _ref.dataType;
var breakingIssuesUpdatedAfterLastTag, lastVersionTagDateTime, lastVersionTagSha, tagCommit, issuesUpdatedAfterLastTag, unpublishedIssues, unpublishedBreakingIssues, breakingPullRequestsMergedAfterLastTag, _lastVersionTagDateTime, _lastVersionTagSha, _tagCommit, pullRequestsMergedAfterLastTag, unpublishedMergedPullRequests, unpublishedBreakingPullRequests;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!(dataType === 'issues')) {
_context.next = 27;
break;
}
breakingIssuesUpdatedAfterLastTag = undefined;
lastVersionTagDateTime = undefined;
if (!lastVersionTag) {
_context.next = 20;
break;
}
lastVersionTagSha = lastVersionTag.commit.sha;
_context.next = 7;
return _utils.github.commits(lastVersionTagSha).fetch();
case 7:
tagCommit = _context.sent;
lastVersionTagDateTime = tagCommit.commit.author.date;
_context.next = 11;
return _utils.github.issues.fetch({ state: 'closed', since: lastVersionTagDateTime });
case 11:
issuesUpdatedAfterLastTag = _context.sent;
unpublishedIssues = issuesUpdatedAfterLastTag.filter(function (i) {
return i.closedAt >= new Date(lastVersionTagDateTime);
});
if (!(unpublishedIssues.length === 0)) {
_context.next = 15;
break;
}
throw new _utils.SmoothReleaseError('Can\'t find any issue closed after last publish. Are you sure there are new features to publish?');
case 15:
_context.next = 17;
return _utils.github.issues.fetch({ labels: 'breaking', state: 'closed', since: lastVersionTagDateTime });
case 17:
breakingIssuesUpdatedAfterLastTag = _context.sent;
_context.next = 23;
break;
case 20:
_context.next = 22;
return _utils.github.issues.fetch({ labels: 'breaking', state: 'closed' });
case 22:
breakingIssuesUpdatedAfterLastTag = _context.sent;
case 23:
// VERIFY IF RELEASE SHOULD BE BREAKING
unpublishedBreakingIssues = breakingIssuesUpdatedAfterLastTag.filter(function (i) {
var hasIgnoredLabels = _config2.default.github.changelog.ignoredLabels.some(function (il) {
return i.labels.map(function (l) {
return l.name;
}).includes(il);
});
var isUnpublished = !lastVersionTagDateTime || i.closedAt >= new Date(lastVersionTagDateTime);
return isUnpublished && !hasIgnoredLabels;
});
return _context.abrupt('return', unpublishedBreakingIssues.length > 0);
case 27:
if (!(dataType === 'pullRequests')) {
_context.next = 52;
break;
}
breakingPullRequestsMergedAfterLastTag = undefined;
_lastVersionTagDateTime = undefined;
if (!lastVersionTag) {
_context.next = 47;
break;
}
_lastVersionTagSha = lastVersionTag.commit.sha;
_context.next = 34;
return _utils.github.commits(_lastVersionTagSha).fetch();
case 34:
_tagCommit = _context.sent;
_lastVersionTagDateTime = _tagCommit.commit.author.date;
_context.next = 38;
return _utils.github.pulls.fetch({ state: 'closed', since: _lastVersionTagDateTime });
case 38:
pullRequestsMergedAfterLastTag = _context.sent;
unpublishedMergedPullRequests = pullRequestsMergedAfterLastTag.filter(function (i) {
return i.mergedAt >= new Date(_lastVersionTagDateTime);
});
if (!(unpublishedMergedPullRequests.length === 0)) {
_context.next = 42;
break;
}
throw new _utils.SmoothReleaseError('Can\'t find any merged pull request after last publish. Are you sure there are new features to publish?');
case 42:
_context.next = 44;
return _utils.github.pulls.fetch({ labels: 'breaking', state: 'closed', since: _lastVersionTagDateTime });
case 44:
breakingPullRequestsMergedAfterLastTag = _context.sent;
_context.next = 50;
break;
case 47:
_context.next = 49;
return _utils.github.pulls.fetch({ labels: 'breaking', state: 'closed' });
case 49:
breakingPullRequestsMergedAfterLastTag = _context.sent;
case 50:
// VERIFY IF RELEASE SHOULD BE BREAKING
unpublishedBreakingPullRequests = breakingPullRequestsMergedAfterLastTag.filter(function (i) {
return !_lastVersionTagDateTime || i.mergedAt >= new Date(_lastVersionTagDateTime);
});
return _context.abrupt('return', unpublishedBreakingPullRequests.length > 0);
case 52:
case 'end':
return _context.stop();
}
}
}, _callee, undefined);
}));
return function checkIfVersionShouldBeBreaking(_x) {
return _ref2.apply(this, arguments);
};
}();
var computeRelease = function () {
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(_ref3) {
var manualVersion = _ref3.manualVersion,
dataType = _ref3.dataType,
packageJsonVersion = _ref3.packageJsonVersion;
var tags, lastVersionTag, isBreaking, isBeta, level, _version, _version2, _isBeta, _level, _isBreaking;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
(0, _utils.info)('Compute release');
if (manualVersion) {
_context2.next = 19;
break;
}
_utils.status.addSteps(['Get all tags from GitHub', 'Check if version should be "breaking"', 'Compute version']);
// AVOID DUPLICATE PUBLISHED VERSIONS
_context2.next = 5;
return _utils.github.tags.fetch();
case 5:
tags = _context2.sent;
_utils.status.doneStep(true);
// CHECK IF VERSION SHOULD BE BREAKING
lastVersionTag = (0, _lodash.find)(tags, _utils.isVersionTag);
_context2.next = 10;
return checkIfVersionShouldBeBreaking({ lastVersionTag: lastVersionTag, dataType: dataType });
case 10:
isBreaking = _context2.sent;
_utils.status.doneStep(true);
// COMPUTE RELEASE INFO
isBeta = _semver2.default.satisfies(packageJsonVersion, '< 1');
level = isBeta ? isBreaking ? 'minor' : 'patch' : isBreaking ? 'major' : 'patch';
_version = _semver2.default.inc(packageJsonVersion, level);
_utils.status.doneStep(true);
return _context2.abrupt('return', {
isBeta: isBeta,
isBreaking: isBreaking,
level: level,
version: _version
});
case 19:
_utils.status.addSteps(['Compute version']);
_version2 = _semver2.default.valid(manualVersion) || _semver2.default.inc(packageJsonVersion, manualVersion);
if (!_semver2.default.lte(_version2, packageJsonVersion)) {
_context2.next = 23;
break;
}
throw new _utils.SmoothReleaseError('You can\'t pass a version lower than or equal to "' + packageJsonVersion + '" (current version)');
case 23:
_isBeta = _semver2.default.satisfies(packageJsonVersion, '< 1');
_level = _semver2.default.diff(packageJsonVersion, _version2);
_isBreaking = _level === 'major';
_utils.status.doneStep(true);
return _context2.abrupt('return', {
isBeta: _isBeta,
isBreaking: _isBreaking,
level: _level,
version: _version2
});
case 28:
case 'end':
return _context2.stop();
}
}
}, _callee2, undefined);
}));
return function computeRelease(_x2) {
return _ref4.apply(this, arguments);
};
}();
var confirmation = function () {
var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(releaseInfo) {
var keys, longestKey;
return _regenerator2.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
(0, _utils.info)('Release Info');
keys = (0, _keys2.default)(releaseInfo);
longestKey = (0, _lodash.sortBy)(keys, function (key) {
return key.length;
})[keys.length - 1];
keys.forEach(function (key, i) {
var emptySpaces = (0, _lodash.range)(longestKey.length - key.length + 1).map(function () {
return ' ';
}).join('');
var emptyLine = i === keys.length - 1 ? '\n' : '';
(0, _utils.log)(' ' + key + ':' + emptySpaces + releaseInfo[key] + emptyLine);
});
_context3.next = 6;
return _utils.rl.confirmation('If you continue you will update "package.json" and add a tag. Are you sure?');
case 6:
if (_context3.sent) {
_context3.next = 8;
break;
}
throw new _utils.SmoothReleaseError('You refused the computed release. Aborting');
case 8:
(0, _utils.emptyLine)();
case 9:
case 'end':
return _context3.stop();
}
}
}, _callee3, undefined);
}));
return function confirmation(_x3) {
return _ref5.apply(this, arguments);
};
}();
var version = function () {
var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(releaseInfo) {
var tagAlreadyExists;
return _regenerator2.default.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
(0, _utils.info)('Increase version');
_utils.status.addSteps(['Ensure tag "v' + releaseInfo.version + '" doesn\'t already exist', 'Run "npm version --no-git-tag-version"']);
_context4.next = 4;
return (0, _utils.exec)('git rev-parse -q --verify v' + releaseInfo.version).then(function () {
return true;
}).catch(function () {
return false;
});
case 4:
tagAlreadyExists = _context4.sent;
if (!tagAlreadyExists) {
_context4.next = 9;
break;
}
throw new _utils.SmoothReleaseError('tag "v' + releaseInfo.version + '" already exists.');
case 9:
_utils.status.doneStep(true);
case 10:
_context4.next = 12;
return (0, _utils.exec)('npm version v' + releaseInfo.version + ' --no-git-tag-version', { stdio: stdio });
case 12:
_utils.status.doneStep(true);
case 13:
case 'end':
return _context4.stop();
}
}
}, _callee4, undefined);
}));
return function version(_x4) {
return _ref6.apply(this, arguments);
};
}();
exports.default = function () {
var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(_ref7) {
var manualVersion = _ref7.manualVersion,
dataType = _ref7.dataType;
var releaseInfo;
return _regenerator2.default.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
(0, _utils.title)('Increase version in "package.json"');
_context5.next = 3;
return computeRelease({
manualVersion: manualVersion,
dataType: dataType,
packageJsonVersion: (0, _utils.getPackageJsonVersion)()
});
case 3:
releaseInfo = _context5.sent;
if (!_config2.default.publish.npmVersionConfirmation) {
_context5.next = 7;
break;
}
_context5.next = 7;
return confirmation(releaseInfo);
case 7:
_context5.next = 9;
return version(releaseInfo);
case 9:
case 'end':
return _context5.stop();
}
}
}, _callee5, undefined);
}));
return function (_x5) {
return _ref8.apply(this, arguments);
};
}();