UNPKG

@hunterparks/betterpr

Version:

A better way to look at PRs in Bitbucket!

363 lines (362 loc) 15.4 kB
#!/usr/bin/env node "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bitbucket_1 = require("bitbucket"); const fs_1 = __importDefault(require("fs")); const kleur_1 = __importDefault(require("kleur")); const loading_cli_1 = __importDefault(require("loading-cli")); const fetch = require('node-fetch'); const path_1 = __importDefault(require("path")); const prompts_1 = __importDefault(require("prompts")); const utils_1 = require("./lib/utils"); const crypto_1 = require("./lib/crypto"); const terminal_1 = require("./lib/terminal"); const baseClientOptions = { baseUrl: 'https://api.bitbucket.org/2.0', notice: false, }; const cacheFileName = 'betterpr_cache.json'; const cacheFilePath = path_1.default.join(__dirname, cacheFileName); const NPM_REGISTRY_URL = 'https://registry.npmjs.com/'; const main = () => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; // Version and Header const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json'); const { name: packageName, version: localVersion, author: { name }, } = require(packageJsonPath); // Get State Configuration if (!fs_1.default.existsSync(cacheFilePath)) { fs_1.default.writeFileSync(cacheFilePath, JSON.stringify({ version: localVersion, })); } let betterPrCache = JSON.parse(fs_1.default.readFileSync(cacheFilePath, { encoding: 'utf8' })); const response = yield fetch(`${NPM_REGISTRY_URL}${packageName}`); const data = (yield response.json()); const npmVersion = data['dist-tags'].latest; const newerVersionAvailable = (0, utils_1.compareVersions)(npmVersion, localVersion) >= 1; (0, terminal_1.printHeader)(localVersion, name); if (newerVersionAvailable) { (0, terminal_1.printNewVersionNotice)(npmVersion); } if (!betterPrCache.version || (0, utils_1.compareVersions)(localVersion, betterPrCache.version) >= 1) { betterPrCache.version = localVersion; betterPrCache.workspace = undefined; betterPrCache.repositories = undefined; saveCache(betterPrCache); } // Check Username and Password console.log(''); let useStoredCreds = false; if (betterPrCache.username && betterPrCache.password) { const { useCreds } = yield (0, prompts_1.default)([ { type: 'toggle', name: 'useCreds', message: 'Use stored credentials?', initial: true, active: 'Yes', inactive: 'No', }, ]); useStoredCreds = useCreds; } let username = ''; let password = ''; if (useStoredCreds === true && betterPrCache.password) { username = betterPrCache.username || ''; password = (0, crypto_1.passwordDecrypt)(betterPrCache.password); console.log(`${terminal_1.icons.greenCheck()}${kleur_1.default .white() .bold(' Using Bitbucket username:')} ${username}`); console.log(`${terminal_1.icons.greenCheck()}${kleur_1.default .white() .bold(' Using stored password! 🎉')}`); } else if (useStoredCreds === false) { // Get Username and App Password const { enteredUsername, enteredAppPassword } = yield (0, prompts_1.default)([ { type: 'text', name: 'enteredUsername', message: 'Enter your Bitbucket username>', validate: (value) => !value ? 'Must provide a username' : true, }, { type: 'invisible', name: 'enteredAppPassword', message: 'Enter your Bitbucket APP password>', validate: (value) => !value ? 'Must provide an app password' : true, }, ]); if (!enteredUsername || !enteredAppPassword) { (0, terminal_1.sayGoodbye)(); return; } // Save Username and App Password betterPrCache.username = enteredUsername; username = enteredUsername; betterPrCache.password = (0, crypto_1.passwordEncrypt)(enteredAppPassword); password = enteredAppPassword; betterPrCache.workspace = undefined; betterPrCache.repositories = undefined; saveCache(betterPrCache); } else { (0, terminal_1.sayGoodbye)(); return; } try { // Setup Bitbucket Client const bitbucket = new bitbucket_1.Bitbucket(Object.assign(Object.assign({}, baseClientOptions), { auth: { username, password, } })); // Get User const { data: user } = yield bitbucket.user.get({}); // Check Workspace console.log(''); let useStoredWorkspace = false; if (betterPrCache.workspace) { const { useWorkspace } = yield (0, prompts_1.default)([ { type: 'toggle', name: 'useWorkspace', message: 'Use stored workspace?', initial: true, active: 'Yes', inactive: 'No', }, ]); useStoredWorkspace = useWorkspace; } let workspace; if (useStoredWorkspace === true) { workspace = betterPrCache.workspace; console.log(`${terminal_1.icons.greenCheck()}${kleur_1.default .white() .bold(' Using stored workspace:')} ${workspace.name || ''}`); } else if (useStoredWorkspace === false) { // Workspaces const availableWorkspaces = []; const { data: workspaceData } = yield bitbucket.workspaces.getWorkspaces({}); (_a = workspaceData.values) === null || _a === void 0 ? void 0 : _a.forEach((workspaceDatum) => { if (workspaceDatum) { availableWorkspaces.push(workspaceDatum); } }); const { selectedWorkspace } = yield (0, prompts_1.default)([ { type: 'select', name: 'selectedWorkspace', message: 'Pick a workspace>', choices: availableWorkspaces.map((availableWorkspace) => { return { title: availableWorkspace.name || '', value: availableWorkspace.uuid || '', }; }), }, ]); if (!selectedWorkspace) { (0, terminal_1.sayGoodbye)(); return; } workspace = availableWorkspaces.find((availableWorkspace) => availableWorkspace.uuid === selectedWorkspace); betterPrCache.workspace = { uuid: workspace.uuid || '', name: workspace.name || '', }; betterPrCache.repositories = undefined; saveCache(betterPrCache); } else { (0, terminal_1.sayGoodbye)(); return; } // Check Repos console.log(''); let useStoredRepos = false; if (betterPrCache.repositories) { const { useRepos } = yield (0, prompts_1.default)([ { type: 'toggle', name: 'useRepos', message: 'Use stored repositories?', initial: true, active: 'Yes', inactive: 'No', }, ]); useStoredRepos = useRepos; } let repos = []; if (useStoredRepos === true) { repos = betterPrCache.repositories; console.log(`${terminal_1.icons.greenCheck()}${kleur_1.default .white() .bold(' Using stored repositories:')} ${repos .map((repo) => repo.name || '') .join(', ')}`); } else if (useStoredRepos === false) { // Repositories const availableRepos = []; const { data: repoData } = yield bitbucket.repositories.list({ workspace: workspace.uuid, pagelen: 50, }); (_b = repoData.values) === null || _b === void 0 ? void 0 : _b.forEach((repoDatum) => { if (repoDatum) { availableRepos.push(repoDatum); } }); const { selectedRepos } = yield (0, prompts_1.default)([ { type: 'multiselect', name: 'selectedRepos', message: 'Pick repositories>', choices: availableRepos.map((availableRepo) => { return { title: availableRepo.name || '', value: availableRepo.uuid || '', }; }), }, ]); if (!selectedRepos) { (0, terminal_1.sayGoodbye)(); return; } repos = availableRepos.filter((availableRepo) => selectedRepos.includes(availableRepo.uuid)); betterPrCache.repositories = repos.map((repo) => { return { uuid: repo.uuid || '', name: repo.name || '', }; }); saveCache(betterPrCache); } else { (0, terminal_1.sayGoodbye)(); } let counts = { reviewerUnapproved: 0, reviewerApproved: 0, notReviewer: 0, author: 0, wip: 0, }; for (let repo of repos) { (0, terminal_1.printRepoHeader)(repo.name); const { data: rawPrs } = yield bitbucket.repositories.listPullRequests({ repo_slug: repo.uuid || '', state: 'OPEN', workspace: workspace.uuid || '', pagelen: 50, }); let openPrs = rawPrs.values; if (openPrs.length <= 0) { console.log(kleur_1.default.italic('No open PRs! 🎉')); continue; } const load = (0, loading_cli_1.default)({ text: kleur_1.default.italic('Loading PRs...'), color: 'magenta', }).start(); const formattedPrs = (yield Promise.all(openPrs.map((openPr) => __awaiter(void 0, void 0, void 0, function* () { var _c; const { data: rawPr } = yield bitbucket.repositories.getPullRequest({ pull_request_id: openPr.id || 0, repo_slug: repo.uuid || '', workspace: workspace.uuid || '', }); // Order: // 10 - Reviewer Unapproved // 20 - Reviewer Approved // 30 - Not Reviewer // 40 - Author // 50 - WIP if (rawPr.title && rawPr.title.toLowerCase().indexOf('[wip]') !== -1) { counts.wip += 1; return { order: 50, display: (0, terminal_1.printRepoLine)(50, rawPr), }; } else if (rawPr.author && rawPr.author.uuid === user.uuid) { counts.author += 1; return { order: 40, display: (0, terminal_1.printRepoLine)(40, rawPr), }; } else { const participation = (_c = rawPr.participants) === null || _c === void 0 ? void 0 : _c.find((participant) => { var _a; return ((_a = participant.user) === null || _a === void 0 ? void 0 : _a.uuid) === user.uuid; }); if (!participation) { counts.notReviewer += 1; return { order: 30, display: (0, terminal_1.printRepoLine)(30, rawPr), }; } else { if (participation.approved) { counts.reviewerApproved += 1; return { order: 20, display: (0, terminal_1.printRepoLine)(20, rawPr), }; } else { counts.reviewerUnapproved += 1; return { order: 10, display: (0, terminal_1.printRepoLine)(10, rawPr), }; } } } })))).sort((a, b) => a.order - b.order); load.stop(); console.log(formattedPrs.map((pr) => pr.display).join('\n')); } const needPadding = Object.values(counts).some((count) => count > 9); console.log(''); (0, terminal_1.printTotalLine)(kleur_1.default.red, terminal_1.icons.redEx(), counts.reviewerUnapproved, needPadding, '- Reviewer, Not Approved'); (0, terminal_1.printTotalLine)(kleur_1.default.green, terminal_1.icons.greenCheck(), counts.reviewerApproved, needPadding, '- Reviewer, Approved'); (0, terminal_1.printTotalLine)(kleur_1.default.magenta, terminal_1.icons.magentaQuestion(), counts.notReviewer, needPadding, '- Not Reviewer'); (0, terminal_1.printTotalLine)(kleur_1.default.blue, terminal_1.icons.blueCircle(), counts.author, needPadding, '- Author'); (0, terminal_1.printTotalLine)(kleur_1.default.white, ' ', counts.wip, needPadding, '- Work In Progress'); console.log(kleur_1.default.yellow(`${terminal_1.icons.yellowTri()} ${needPadding ? ' ' : ' '} - Requested Changes`)); } catch (error) { (0, terminal_1.errorMessage)(error.message); betterPrCache = { version: localVersion, }; saveCache(betterPrCache); } finally { (0, terminal_1.sayGoodbye)(); return; } }); const saveCache = (cache) => fs_1.default.writeFileSync(cacheFilePath, JSON.stringify(cache)); main();