UNPKG

@buildo/hophop

Version:

A minimal tool to accelerate the GitHub workflow from the command line.

652 lines (514 loc) 20.2 kB
'use strict'; var _regeneratorRuntime = require('babel-runtime/regenerator')['default']; var _Object$values = require('babel-runtime/core-js/object/values')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; exports.__esModule = true; var _lodash = require('lodash'); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _ramda = require('ramda'); var _ramda2 = _interopRequireDefault(_ramda); var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _es6Promisify = require('es6-promisify'); var _es6Promisify2 = _interopRequireDefault(_es6Promisify); var _inquirer = require('inquirer'); var _inquirer2 = _interopRequireDefault(_inquirer); var _utils = require('../utils'); var _toggl = require('../toggl'); function gh_create_issue() { var _ref, repo, title, createIssue, created; return _regeneratorRuntime.async(function gh_create_issue$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_utils.gh_helper()); case 2: _ref = context$1$0.sent; repo = _ref.repo; context$1$0.next = 6; return _regeneratorRuntime.awrap(_utils.rl.question({ message: 'Title:' })); case 6: title = context$1$0.sent; createIssue = _es6Promisify2['default'](repo.issue.bind(repo)); context$1$0.next = 10; return _regeneratorRuntime.awrap(createIssue({ title: title })); case 10: created = context$1$0.sent; _utils.log(created.html_url); context$1$0.next = 14; return _regeneratorRuntime.awrap(_utils.exec('open "' + created.html_url + '"')); case 14: return context$1$0.abrupt('return', created); case 15: case 'end': return context$1$0.stop(); } }, null, this); } function gh_fetch_issue(issueNumber) { var _ref2, repo_name, client, issueRef, issue; return _regeneratorRuntime.async(function gh_fetch_issue$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_utils.gh_helper()); case 2: _ref2 = context$1$0.sent; repo_name = _ref2.repo_name; client = _ref2.client; context$1$0.next = 7; return _regeneratorRuntime.awrap(client.issue(repo_name, issueNumber)); case 7: issueRef = context$1$0.sent; context$1$0.prev = 8; context$1$0.next = 11; return _regeneratorRuntime.awrap(_es6Promisify2['default'](issueRef.info.bind(issueRef))()); case 11: issue = context$1$0.sent; return context$1$0.abrupt('return', issue); case 15: context$1$0.prev = 15; context$1$0.t0 = context$1$0['catch'](8); if (!(context$1$0.t0.statusCode === 404)) { context$1$0.next = 19; break; } return context$1$0.abrupt('return', null); case 19: case 'end': return context$1$0.stop(); } }, null, this, [[8, 15]]); } // Retrieve the folder name of the project function getProjectFolderName(currentPath) { var findGitFolderRecursively = function findGitFolderRecursively(_x3, _x4) { var _again = true; _function: while (_again) { var projectPath = _x3, previousFolder = _x4; _again = false; var dirs = _fs2['default'].readdirSync(projectPath).filter(function (file) { return _fs2['default'].statSync(_path2['default'].join(projectPath, file)).isDirectory(); }); var gitMatch = _lodash.find(dirs, function (d) { return d === '.git'; }); if (gitMatch) { var gitFolderPath = _path2['default'].join(projectPath, '.git'); if (_fs2['default'].lstatSync(gitFolderPath).isDirectory()) { return previousFolder; } } var parentFolder = _path2['default'].join(projectPath, '../'); if (parentFolder === '/') { process.exit(0); } _x3 = _path2['default'].join(projectPath, '../'); _x4 = _lodash.last(_lodash.compact(projectPath.split('/'))); _again = true; dirs = gitMatch = gitFolderPath = parentFolder = undefined; continue _function; } }; return findGitFolderRecursively(currentPath, _lodash.last(currentPath.split('/'))).replace(/[ -]/g, '_'); } function gh_fetch_issues() { var issuesOptions = arguments.length <= 0 || arguments[0] === undefined ? { page: 1 } : arguments[0]; var allIssues = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; var _ref3, repo, client, issues, filteredIssues, issueQuestion, issuesChoices, _allIssues, issueno, issue; return _regeneratorRuntime.async(function gh_fetch_issues$(context$1$0) { var _this = this; while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_utils.gh_helper()); case 2: _ref3 = context$1$0.sent; repo = _ref3.repo; client = _ref3.client; if (issuesOptions.getFilter) { context$1$0.next = 15; break; } context$1$0.next = 8; return _regeneratorRuntime.awrap(_utils.rl.yesOrNoQuestion('Show only issues assigned to you?', 'y')); case 8: context$1$0.t0 = context$1$0.sent; if (!(context$1$0.t0 !== 'n')) { context$1$0.next = 14; break; } context$1$0.next = 12; return _regeneratorRuntime.awrap((function callee$1$0() { var me; return _regeneratorRuntime.async(function callee$1$0$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: context$2$0.next = 2; return _regeneratorRuntime.awrap(_utils.gh_me(client)); case 2: me = context$2$0.sent; issuesOptions.getFilter = function (i) { return i.assignee && i.assignee.id === me.id; }; case 4: case 'end': return context$2$0.stop(); } }, null, _this); })()); case 12: context$1$0.next = 15; break; case 14: issuesOptions.getFilter = function () { return function () { return true; }; }; case 15: context$1$0.next = 17; return _regeneratorRuntime.awrap(_es6Promisify2['default'](repo.issues.bind(repo))(issuesOptions)); case 17: issues = context$1$0.sent; filteredIssues = issues.filter(function (i) { return !i.pull_request; }).filter(issuesOptions.getFilter); issueQuestion = undefined; if (filteredIssues && filteredIssues.length > 0) { issuesChoices = _utils.issuesLabelMapper(filteredIssues).concat(new _inquirer2['default'].Separator(), { name: 'Create new issue.', value: 'y' }, { name: 'Show older issue.', value: 'm' }); issueQuestion = { message: 'Which issue?', type: 'list', choices: issuesChoices }; } else { issueQuestion = { message: 'No issues found. Create a new one? (y/n)', 'default': 'y' }; } _allIssues = allIssues.concat(filteredIssues); context$1$0.next = 24; return _regeneratorRuntime.awrap(_utils.rl.question(issueQuestion)); case 24: issueno = context$1$0.sent; context$1$0.t1 = issueno; context$1$0.next = context$1$0.t1 === '' ? 28 : context$1$0.t1 === 'y' ? 30 : context$1$0.t1 === 'm' ? 31 : 35; break; case 28: process.exit(1); return context$1$0.abrupt('break', 38); case 30: return context$1$0.abrupt('return', gh_create_issue()); case 31: issuesOptions.page = issuesOptions.page + 1; context$1$0.next = 34; return _regeneratorRuntime.awrap(gh_fetch_issues(issuesOptions, _allIssues)); case 34: return context$1$0.abrupt('return', context$1$0.sent); case 35: issue = _ramda2['default'].find(function (i) { return i.number === issueno; })(_allIssues); if (!issue) { _utils.log('error: issue #' + issueno + ' does not exist'); process.exit(1); } return context$1$0.abrupt('return', issue); case 38: case 'end': return context$1$0.stop(); } }, null, this); } function gh_feature(_ref4) { var issueNumber = _ref4.issueNumber; var issue, inferredBranchPrefix, branchPrefixAnswer, branch_prefix, slug, branchSuffixAnswer, branch_suffix, branch_name; return _regeneratorRuntime.async(function gh_feature$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_utils.currBranch()); case 2: context$1$0.t0 = context$1$0.sent; if (!(context$1$0.t0 !== 'master')) { context$1$0.next = 9; break; } context$1$0.next = 6; return _regeneratorRuntime.awrap(_utils.rl.yesOrNoQuestion('You\'re not on master, are you sure you want to continue?', 'n')); case 6: context$1$0.t1 = context$1$0.sent; if (!(context$1$0.t1 !== 'y')) { context$1$0.next = 9; break; } process.exit(0); case 9: issue = undefined; if (!_lodash.includes(['c', 'create'], issueNumber)) { context$1$0.next = 16; break; } context$1$0.next = 13; return _regeneratorRuntime.awrap(gh_create_issue()); case 13: issue = context$1$0.sent; context$1$0.next = 19; break; case 16: context$1$0.next = 18; return _regeneratorRuntime.awrap(issueNumber ? gh_fetch_issue(issueNumber) : gh_fetch_issues()); case 18: issue = context$1$0.sent; case 19: if (issueNumber && !issue) { _utils.log('😱 Issue #' + issueNumber + ' does not exist'); process.exit(0); } inferredBranchPrefix = getProjectFolderName(process.cwd()); context$1$0.next = 23; return _regeneratorRuntime.awrap(_utils.rl.question({ message: 'Branch prefix? (\'n\' to leave empty)' }, inferredBranchPrefix)); case 23: branchPrefixAnswer = context$1$0.sent; branch_prefix = branchPrefixAnswer !== 'n' ? (branchPrefixAnswer || inferredBranchPrefix) + '-' : ''; slug = _utils.slugify(issue.title); context$1$0.next = 28; return _regeneratorRuntime.awrap(_utils.rl.question({ message: 'Branch suffix? (\'n\' to leave empty)' }, slug)); case 28: branchSuffixAnswer = context$1$0.sent; branch_suffix = branchSuffixAnswer !== 'n' ? '-' + (branchSuffixAnswer || slug) : ''; branch_name = '' + branch_prefix + issue.number + branch_suffix; context$1$0.next = 33; return _regeneratorRuntime.awrap(_utils.exec('git checkout -b ' + branch_name)); case 33: _utils.log('👍 Created and switched to branch \'' + branch_name + '\''); context$1$0.next = 36; return _regeneratorRuntime.awrap(_utils.rl.yesOrNoQuestion('Start tracking time for issue ' + issue.number + '?', 'y')); case 36: context$1$0.t2 = context$1$0.sent; if (!(context$1$0.t2 !== 'n')) { context$1$0.next = 40; break; } context$1$0.next = 40; return _regeneratorRuntime.awrap(_toggl.toggl_start()); case 40: case 'end': return context$1$0.stop(); } }, null, this); } function gh_pr() { var _ref5, repo_name, client, repo, branch, issueno, issueRef, issue, pullRequests, pullRequest, pushResult, isWIP, prefix, json, created, customErr; return _regeneratorRuntime.async(function gh_pr$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_utils.gh_helper()); case 2: _ref5 = context$1$0.sent; repo_name = _ref5.repo_name; client = _ref5.client; repo = _ref5.repo; context$1$0.next = 8; return _regeneratorRuntime.awrap(_utils.currBranch()); case 8: branch = context$1$0.sent; if (branch === 'master') { _utils.log('Cannot create a PR to merge from master to master, switch branch first'); process.exit(0); } issueno = _ramda2['default'].take(2, branch.split('-').map(function (i) { return parseInt(i, 10); })).filter(function (x) { return !!x; })[0]; if (issueno) { context$1$0.next = 19; break; } _utils.log('Cannot infer the issue number from the branch name'); _utils.log('Open issues:'); // const issues = await promisify(repo.issues.bind(repo))(); // const issuesChoices = issuesLabelMapper(issues.filter((i) => !i.pull_request)); context$1$0.next = 16; return _regeneratorRuntime.awrap(_utils.rl.question({ message: 'Which issue?' })); case 16: issueno = context$1$0.sent; context$1$0.next = 20; break; case 19: _utils.log('Inferred issue number ' + issueno + ' from branch name \'' + branch + '\''); case 20: issueRef = client.issue(repo_name, issueno); context$1$0.next = 23; return _regeneratorRuntime.awrap(_es6Promisify2['default'](issueRef.info.bind(issueRef))()); case 23: issue = context$1$0.sent; if (!issue) { _utils.log('Invalid issue'); } _utils.log('Issue ' + _utils.issuec('#' + issueno + ': ' + issue.title)); context$1$0.next = 28; return _regeneratorRuntime.awrap(_es6Promisify2['default'](repo.prs.bind(repo))()); case 28: pullRequests = context$1$0.sent; pullRequest = _ramda2['default'].find(function (pr) { return pr.title.replace(/^\[WIP\]/, '').trim().split(' ')[0] === '#' + issueno + ':'; })(pullRequests); if (!!pullRequest) { _utils.log('Existing pull request ' + pullRequest.number + ': ' + pullRequest.title + ' ( ' + pullRequest.html_url + ' )'); } else { _utils.log('No pull request found for this issue'); } context$1$0.next = 33; return _regeneratorRuntime.awrap(_utils.exec('git push -u origin ' + branch)); case 33: pushResult = context$1$0.sent; _utils.log(_utils.cmdoutc(pushResult.stdout + '\n' + pushResult.stderr)); if (pullRequest) { context$1$0.next = 57; break; } context$1$0.next = 38; return _regeneratorRuntime.awrap(_utils.rl.yesOrNoQuestion('Is this a WIP pull request?', 'n')); case 38: context$1$0.t0 = context$1$0.sent; isWIP = context$1$0.t0 === 'y'; prefix = isWIP ? '[WIP] ' : ''; json = { title: prefix + '#' + issueno + ': ' + issue.title + ' (closes #' + issueno + ')', body: 'Issue #' + issueno, head: repo_name.split('/')[0] + ':' + branch, base: 'master' }; context$1$0.prev = 42; context$1$0.next = 45; return _regeneratorRuntime.awrap(_es6Promisify2['default'](repo.pr.bind(repo))(json)); case 45: created = context$1$0.sent; _utils.log('👍 ' + created.html_url); context$1$0.next = 49; return _regeneratorRuntime.awrap(_utils.exec('open "' + created.html_url + '"')); case 49: context$1$0.next = 55; break; case 51: context$1$0.prev = 51; context$1$0.t1 = context$1$0['catch'](42); customErr = context$1$0.t1.body.errors.filter(function (err) { return err.code === 'custom'; })[0]; if (!!customErr) { _utils.log(customErr.message); } else { _utils.log('Unexpected error'); } case 55: context$1$0.next = 58; break; case 57: _utils.log('Updated existing pull request'); case 58: case 'end': return context$1$0.stop(); } }, null, this, [[42, 51]]); } function gh_commit() { var _ref6, repo_name, client, repo, issues, issuesChoices, issueno, issueRef, issue, message, commitResult; return _regeneratorRuntime.async(function gh_commit$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_utils.gh_helper()); case 2: _ref6 = context$1$0.sent; repo_name = _ref6.repo_name; client = _ref6.client; repo = _ref6.repo; context$1$0.next = 8; return _regeneratorRuntime.awrap(_es6Promisify2['default'](repo.issues.bind(repo))()); case 8: issues = context$1$0.sent; issuesChoices = _utils.issuesLabelMapper(issues.filter(function (i) { return !i.pull_request; })); context$1$0.next = 12; return _regeneratorRuntime.awrap(_utils.rl.question({ type: 'list', message: 'Which issue? (number)', choices: issuesChoices })); case 12: issueno = context$1$0.sent; issueRef = client.issue(repo_name, issueno); context$1$0.next = 16; return _regeneratorRuntime.awrap(_es6Promisify2['default'](issueRef.info.bind(issueRef))()); case 16: issue = context$1$0.sent; if (!issue) { _utils.log('Invalid issue'); } message = 'closes #' + issueno + ': ' + issue.title.replace(/^\s*/g, '').replace(/\s*$/g, '').replace('\n', ' ').replace(/["@]/g, '').replace(/`/g, '\\`'); context$1$0.next = 21; return _regeneratorRuntime.awrap(_utils.rl.yesOrNoQuestion('Commit with the following message:\n' + _utils.issuec(message) + '?', 'y')); case 21: context$1$0.t0 = context$1$0.sent; if (!(context$1$0.t0 === 'y')) { context$1$0.next = 27; break; } context$1$0.next = 25; return _regeneratorRuntime.awrap(_utils.execF('git commit -m "' + message + '"')); case 25: commitResult = context$1$0.sent; _utils.log(_utils.cmdoutc(commitResult.stdout + '\n' + commitResult.stderr)); case 27: case 'end': return context$1$0.stop(); } }, null, this); } function gh_setup(_ref7) { var gheAccountName = _ref7.gheAccountName; var _ref8, githubURL, url, token, fileName; return _regeneratorRuntime.async(function gh_setup$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: _ref8 = _Object$values(_utils.enterpriseRepos).filter(function (_ref9) { var accountName = _ref9.accountName; return gheAccountName === accountName; })[0] || {}; githubURL = _ref8.githubURL; url = githubURL || 'github.com'; context$1$0.next = 5; return _regeneratorRuntime.awrap(_utils.rl.question({ message: 'Personal access token: (from https://' + url + '/settings/tokens):' })); case 5: token = context$1$0.sent; if (!token) { _utils.error('No token inserted.'); process.exit(0); } fileName = _utils.gh_token_file_name(gheAccountName); _fs2['default'].writeFileSync(process.env.HOME + '/' + fileName, token); case 9: case 'end': return context$1$0.stop(); } }, null, this); } exports['default'] = { gh_commit: gh_commit, gh_feature: gh_feature, gh_pr: gh_pr, gh_setup: gh_setup }; module.exports = exports['default']; // FIXME: don't display `n` hint if there aren't anymore issues