@hunterparks/betterpr
Version:
A better way to look at PRs in Bitbucket!
363 lines (362 loc) • 15.4 kB
JavaScript
;
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();