// // Copyright 2021 DXOS.org // import chalk from 'chalk'; import columnify from 'columnify'; import { Argv, CommandModule } from 'yargs'; import { asyncExec, getRepos, deleteFile, writeFile, parseOrigin, getRepo } from '../helpers'; const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); /** * Repo commands. */ export const repoModule: CommandModule = { command: 'repo', aliases: ['r'], describe: 'Github repo utils.', handler: async (argv: any) => { const dir = await getRepo(); if (dir) { console.log(chalk.green(dir)); } }, builder: (yargs: Argv) => yargs .command({ command: 'list', describe: 'List repos.', builder: (yargs: Argv) => yargs .option('branch', { type: 'boolean', default: false }) .option('exclude-users', { type: 'string', default: 'dependabot' }) .option('pr', { type: 'boolean', default: false }), handler: async ({ projects, json, branch, pr, excludeUsers, verbose }: any) => { const repos = await getRepos(projects); if (!repos.length) { return; } if (verbose) { console.log(repos); } else if (json || branch) { // // Branches // // https://www.npmjs.com/package/columnify const flag = (modified = false) => modified ? chalk.red('✗') : chalk.green('✔'); const rows = columnify(repos.map(({ origin, branches }) => ({ name: chalk.green(parseOrigin(origin)), branches: branches?.map(({ name, current, modified, status }) => { return chalk.blue(name) + (current ? ` ${flag(modified)} ${status ?? ''}` : ''); }).join('\n') })), { preserveNewLines: true, columns: ['name', 'branches'], }); console.log(rows); } else if (pr) { // // PRs // // Process in parallel. Promise.all(repos.map(async ({ dir, origin }) => { const repo = parseOrigin(origin); process.chdir(dir); try { const output = await asyncExec('gh pr list --json="number,author,title,createdAt,headRefName"'); const data = JSON.parse(output) as [{ number: number title: string createdAt: string headRefName: string author: { login: string } }]; // TODO(burdon): Truncate long strings. // TODO(burdon): Whitelist (e.g., dependabot). console.log(chalk.green(repo)); const exclude = excludeUsers.split(','); const rows = columnify(data.map(({ number, title, createdAt, headRefName, author: { login } }) => { const now = new Date().setHours(0, 0, 0, 0); const then = new Date(Date.parse(createdAt)).setHours(0, 0, 0, 0); const days = Math.round((then - now) / 86400000); const ageColor = (days < -5) ? 'red' : 'green'; // TODO(burdon): Config. if (exclude.indexOf(login) === -1) { return { number, branch: chalk.blue(headRefName), user: chalk.white(login), date: chalk[ageColor](rtf.format(days, 'day')), title } } }).filter(Boolean), { preserveNewLines: true, truncate: true, columns: ['number', 'branch', 'user', 'date', 'title'], config: { number: { align: 'right' }, branch: { minWidth: 32, maxWidth: 32 }, user: { minWidth: 16, maxWidth: 16 }, date: { minWidth: 16, maxWidth: 16 } } }); console.log(rows); } catch (err) { console.log(chalk.red(err)); } console.log(); })); } else { // // Normal // repos.map(({ origin }) => console.log(chalk.green(parseOrigin(origin)))); } } }) .command({ command: 'cd [name]', describe: 'Change to the directory of the first repo matching the name.', builder: (yargs: Argv) => yargs, handler: async ({ shellCommand, projects, name }: any) => { deleteFile(shellCommand); const repos = await getRepos(projects); if (!repos.length) { return; } const match = repos.filter(({ origin }) => !name || origin.indexOf(name) !== -1); if (!match.length) { console.log('Repo not found.'); repos.forEach(repo => console.log(chalk.green(repo.name))); return; } // Write to file that is sourced by the external alias. writeFile(shellCommand, `cd ${match[0].dir}`); } }) };