github-pewpew
Version:
Clean up and remove those unnecessary GitHub repositories (and more). Pew pew!
359 lines (283 loc) • 9.22 kB
JavaScript
import style from 'ansi-colors';
import ora from 'ora';
import clipboard from 'clipboardy';
import Enquirer from 'enquirer';
const { prompt } = Enquirer;
import Utils from './utils.js';
import Github from './github.js';
const PACKAGE = Utils.getPackageDetails().package;
const PACKAGE_COMMAND = Object.keys(Utils.getPackageDetails().package.bin)[0];
function printWelcome() {
const name = PACKAGE.name;
const description = PACKAGE.description;
const version = PACKAGE.version;
if (name && description && version) {
console.log(`${style.bold(`${name} v${version}`)}`);
console.log(description);
console.log();
}
}
function printHelp() {
printHelpHeader('Usage');
printHelpUsage();
console.log();
printHelpHeader('Commands');
printHelpCommand('codespaces', 'Delete codespaces');
printHelpCommand('repos [--archive]', 'Delete or optionally archive repositories');
printHelpCommand('help', 'Show help');
console.log();
}
function printHelpUsage() {
const command = PACKAGE_COMMAND;
const spacing = Utils.uiHelpGetSpacing();
console.log(`${spacing}${command} <command>`);
}
function printHelpHeader(text) {
const header = text.toUpperCase();
console.log(style.bold(header));
}
function printHelpCommand(command, description) {
const spacing = Utils.uiHelpGetSpacing();
const PADDING = 25;
const paddedCommand = `${command}:`.padEnd(PADDING, ' ');
console.log(`${spacing}${paddedCommand}${description}`);
}
async function promptAuth() {
const strSignIn = `Sign in to GitHub:`;
const spinner = ora();
console.log(style.dim(strSignIn));
const token = await Github.auth((verification) => {
requestToken(verification);
spinner.start();
clipboard.writeSync(verification.user_code);
});
spinner.stop();
console.log();
return token;
}
function requestToken(verification) {
const strOpen = `Open:`;
const strURL = verification.verification_uri;
const strCode = `Code:`;
const strCodeValue = verification.user_code;
const strClipboard = `Copied to clipboard!`;
console.log(`${style.bold(strOpen)} ${style.cyan.underline(strURL)}`);
console.log(`${style.bold(strCode)} ${strCodeValue} ${style.dim(strClipboard)}`);
}
async function promptSelectRepositories(repositories) {
try {
if (repositories.length === 0) throw error;
return await prompt({
type: 'autocomplete',
name: 'repos',
message: 'Select repositories you want to process:',
limit: 12,
multiple: true,
footer: '—————————————————————————————————————————————————',
format: (value) => style.green(value),
choices: repositories.map((repo) => {
return {
name: repo.full_name,
message: repo.archived
? `${repo.full_name} ${style.dim('(archived)')}`
: repo.full_name,
};
}),
});
} catch (error) {
return { repos: [] };
}
}
async function promptSelectCodespaces(codespaces) {
try {
if (codespaces.length === 0) throw error;
return await prompt({
type: 'autocomplete',
name: 'codespaces',
message: `Select codespaces you want to delete:`,
limit: 12,
multiple: true,
footer: '—————————————————————————————————————————————————',
format: (value) => style.green(value),
choices: codespaces.map(({ name }) => name),
});
} catch (error) {
return { codespaces: [] };
}
}
async function getRepositories() {
const strMessage = `Fetching repositories…`;
const spinner = ora(strMessage).start();
try {
const repositories = await Github.getRepositories();
const count = repositories.length;
const strSucceed = printReposFound(count);
spinner.succeed(style.dim(strSucceed));
return repositories;
} catch (error) {
spinner.stop();
if (error instanceof Github.AuthError || error instanceof Github.ScopesError) {
throw error;
}
}
}
async function getCodespaces() {
const strMessage = `Fetching codespaces…`;
const spinner = ora(strMessage).start();
try {
const codespaces = await Github.getCodespaces();
const count = codespaces.length;
const strSucceed = printCodespacesFound(count);
spinner.succeed(style.dim(strSucceed));
return codespaces;
} catch (error) {
spinner.stop();
if (error instanceof Github.AuthError || error instanceof Github.ScopesError) {
throw error;
}
}
}
function printReposFound(count) {
const strMessage = `${count} ${count > 1 ? 'repositories' : 'repository'} found.`;
return strMessage;
}
function printCodespacesFound(count) {
const strMessage = `${count} ${count > 1 ? 'codespaces' : 'codespace'} found.`;
return strMessage;
}
async function deleteRepositories(repositories) {
const deletedRepos = [];
for (const repo of repositories) {
const spinner = ora().start();
try {
await Github.deleteRepository(repo);
deletedRepos.push(repo);
spinner.stopAndPersist({ symbol: '', text: style.strikethrough.dim(repo) });
} catch (error) {
const message = error.response?.data?.message;
spinner.fail(style.dim(`${repo} (Oops! ${message})`));
}
}
if (deletedRepos.length > 0) {
printConfirmation(deletedRepos, 'repos', 'delete');
} else {
printNoReposDeleted();
}
}
async function archiveRepositories(repositories) {
const archivedRepos = [];
for (const repo of repositories) {
const spinner = ora().start();
try {
await Github.archiveRepository(repo);
archivedRepos.push(repo);
spinner.stopAndPersist({ symbol: '', text: style.dim(repo) });
} catch (error) {
const message = error.response?.data?.message;
spinner.fail(style.dim(`${repo} (Oops! ${message})`));
}
}
if (archivedRepos.length > 0) {
printConfirmation(archivedRepos, 'repos', 'archive');
} else {
printNoReposArchived();
}
}
async function deleteCodespaces(codespaces) {
const deletedCodespaces = [];
for (const codespace of codespaces) {
const spinner = ora().start();
try {
await Github.deleteCodespace(codespace);
deletedCodespaces.push(codespace);
spinner.stopAndPersist({ symbol: '', text: style.strikethrough.dim(codespace) });
} catch (error) {
const message = error.response?.data?.message;
spinner.fail(style.dim(`${codespace} (Oops! ${message})`));
}
}
if (deletedCodespaces.length > 0) {
printConfirmation(deletedCodespaces, 'codespaces', 'delete');
} else {
printNoCodespacesDeleted();
}
}
async function promptConfirm(count, type, action) {
const capitalizedAction = action.charAt(0).toUpperCase() + action.slice(1);
return await prompt({
type: 'select',
name: 'confirm',
message: 'Are you sure?',
format: (value) => value,
choices: [
{
name: 'Yes',
message: `${style.redBright(
`Yes, ${action} ${Utils.uiGetLabel(type, count)} (${count})`
)}`,
value: 'Yes',
},
{
name: 'Cancel',
message: 'Cancel',
value: 'No',
},
],
});
}
function printConfirmation(processedItems, type, action) {
const count = processedItems.length;
const strProcessedItems = count > 1 ? processedItems.join(', ') : processedItems[0];
const strItems = Utils.uiGetLabel(type, count);
const pastTenseAction = action === 'delete' ? 'deleted' : 'archived';
const strConfirm = `🔫 pew pew! ${count} ${strItems} ${pastTenseAction} successfully: ${strProcessedItems}`;
const strRecover = `Recover repositories from github.com/settings/repositories`;
console.log(strConfirm);
if (type === 'repos' && action === 'delete') {
console.log(style.dim(strRecover));
}
return true;
}
function printNoReposArchived() {
const strMessage = `Rest assured, no repositories were archived.`;
return console.log(style.dim(strMessage));
}
function printNoReposDeleted() {
const strMessage = `Rest assured, no repositories were deleted.`;
return console.log(style.dim(strMessage));
}
function printNoCodespacesDeleted() {
const strMessage = `Rest assured, no codespaces were deleted.`;
return console.log(style.dim(strMessage));
}
function printNoReposSelected() {
const strMessage = `No repositories selected. (Press 'space' to select)`;
return console.log(style.dim(strMessage));
}
function printNoCodespaceSelected() {
const strMessage = `No codespaces selected. (Press 'space' to select)`;
return console.log(style.dim(strMessage));
}
function printError(strError) {
console.log();
return console.log(style.redBright(strError));
}
export default {
printWelcome,
printHelp,
promptAuth,
getRepositories,
getCodespaces,
promptSelectRepositories,
promptSelectCodespaces,
deleteRepositories,
archiveRepositories,
deleteCodespaces,
promptConfirm,
printNoReposDeleted,
printNoReposArchived,
printNoReposSelected,
printNoCodespacesDeleted,
printNoCodespaceSelected,
printError,
};