// // Copyright 2021 DXOS.org // import { Table } from 'console-table-printer'; // @ts-ignore import { prompts } from 'enquirer'; import { Argv, CommandModule } from 'yargs'; import { getJobs, getLogger, getRepo, rt, viewJob, watchJob } from '../helpers'; const getJob = (jobs: any[], id?: number, filter?: (job: any) => boolean) => { if (!id) { const filtered = jobs.filter(filter || Boolean); return filtered.length ? filtered[0] : undefined; } if (id <= jobs.length) { return jobs[id - 1]; } else { return jobs.find(job => job.databaseId === id); } }; const truncate = (str: string, maxLength: number) => { return str.length < maxLength ? str : str.substring(0, maxLength - 1) + '…'; }; const renderTable = (jobs: any[] = []): string => { // Fits 120 characters. // https://github.com/ayonious/console-table-printer const table = new Table({ columns: [ { name: 'id', title: 'ID' }, { name: 'databaseId' }, { name: 'conclusion' }, { name: 'status' }, { name: 'createdAt', minLen: 12 }, { name: 'headBranch', title: 'branch', maxLen: 24 }, { name: 'name', alignment: 'left', maxLen: 30 } ] }); const now = Date.now(); jobs.forEach(({ conclusion, createdAt, databaseId, headBranch, status, name }, i) => { const then = Date.parse(createdAt); table.addRow({ id: i + 1, conclusion, databaseId, headBranch: truncate(headBranch, 24), createdAt: truncate(rt(now, then), 12), status, name: truncate(name, 30) }, { color: conclusion === 'failure' ? 'red' : status === 'in_progress' ? 'green' : status === 'queued' ? 'blue' : 'grey' }); }); return table.render(); } /** * GH actions commands. */ // TODO(burdon): Correlated with ~/.github/workflows. // TODO(burdon): Normalize names to be machine readable (e.g., check-install-build). export const workflowModule: CommandModule = { command: 'workflow', aliases: ['w'], describe: 'Github workflow actions.', handler: (argv: any) => {}, builder: (yargs: Argv) => yargs // // List // .command({ command: 'list', describe: 'List job.', builder: (yargs: Argv) => yargs .option('watch', { type: 'boolean', default: false }) .option('interval', { type: 'number', default: 5000 }), handler: async ({ watch, interval }: any) => { const repo = await getRepo(); if (!repo) { return; } const logger = await getLogger(); const update = async () => { const jobs = await getJobs(); const table = renderTable(jobs); const message = watch ? `Updating every ${Math.round(interval/1000)} seconds [${Date.now()}]. Press Ctrl+C to quit.\n` : ''; await logger(`${message}${table}`); return jobs; }; let jobs = await update(); if (watch) { const polling = () => { let i: ReturnType; let running = false; return { start: () => { running = true; clearInterval(i); i = setInterval(async () => { if (running) { jobs = await update(); prompt.render(); } }, interval); }, stop: () => { running = false; clearInterval(i); } } } // https://www.npmjs.com/package/enquirer const prompt = new prompts.Text({ type: 'input', message: 'ID' }); const { start, stop } = polling(); start(); const line = await prompt.run(); stop(); if (line) { const job = getJob(jobs, parseInt(line)); const { databaseId, status } = job; const showResult = async (job: any) => { console.log(); console.log(job); const log = await viewJob(job.databaseId, job.conclusion !== 'success' ? 'FAILURE' : undefined); console.log('\n' + log); } if (status === 'completed') { await showResult(job); } else { await watchJob(databaseId, console.log); const jobs = await getJobs(); await showResult(getJob(jobs, job.databaseId)); } } } } }) // // Watch // .command({ command: 'watch [id]', describe: 'Watch running job.', handler: async ({ id }: any) => { const repo = await getRepo(); if (!repo) { return; } const jobs = await getJobs(); const job = getJob(jobs, id, job => job.status === 'in_progress'); if (!job) { console.log(renderTable(jobs)); return; } console.log(job); await watchJob(job.databaseId, console.log); } }) // // View // .command({ command: 'view [id]', describe: 'View job.', builder: (yargs: Argv) => yargs .option('error', { type: 'boolean', default: false }), handler: async ({ id, error }: any) => { const repo = await getRepo(); if (!repo) { return; } const jobs = await getJobs(); const job = getJob(jobs, id, error ? job => job.conclusion === 'failure' : undefined); if (!job) { console.log(renderTable(jobs)); return; } console.log(job); const log = await viewJob(job.databaseId, job.conclusion === 'failure' ? 'FAILURE' : undefined); console.log(log); } }) };