UNPKG

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
'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); }; }();