/* eslint-disable no-restricted-syntax, no-await-in-loop */ import { Command } from 'commander'; import execa from 'execa'; import { getOptions, getCommand, getOptionsOrPrompt, createOptions, OptionValues, } from './utils/options'; import type { OptionSpecifier } from './utils/options'; import { filterDataForCurrentCircleCINode } from './utils/concurrency'; import TEMPLATES from '../code/lib/cli/src/repro-templates'; export type Cadence = 'ci' | 'daily' | 'weekly'; export type Template = { name: string; script: string; cadence?: readonly Cadence[]; skipScripts?: string[]; // there are other fields but we don't use them here }; export type TemplateKey = string; export type Templates = Record; export async function parseCommand(commandline: string) { const argv = commandline.split(' '); const [yarn, scriptName] = argv; if (yarn !== 'yarn') throw new Error('only works with scripts at this point'); const { options } = await import(`./${scriptName}`); const command = new Command(scriptName); const values = getOptions(command, options as OptionSpecifier, ['yarn', ...argv]); return { scriptName, command: `yarn ${scriptName}`, options, values, }; } export const options = createOptions({ cadence: { type: 'string', description: 'What cadence are we running on (i.e. which templates should we use)?', values: ['ci', 'daily', 'weekly'] as const, required: true, }, script: { type: 'string', description: 'What command are we running?', }, parallel: { type: 'boolean', description: 'Run commands in parallel?', }, }); export function filterTemplates(templates: Templates, cadence: Cadence, scriptName: string) { const allTemplates = Object.entries(templates); const cadenceTemplates = allTemplates.filter(([, template]) => template.cadence.includes(cadence) ); const jobTemplates = cadenceTemplates.filter(([, t]) => !t.skipScripts?.includes(scriptName)); return Object.fromEntries(filterDataForCurrentCircleCINode(jobTemplates)); } const logger = console; export async function oncePerTemplate({ cadence, script: commandline, parallel, }: OptionValues) { const command = await parseCommand(commandline); const templates = filterTemplates(TEMPLATES, cadence, command.scriptName); const toAwait = []; for (const template of Object.keys(templates)) { const toRun = getCommand(command.command, command.options, { ...command.values, template, }); logger.log(`🏃 Running ${toRun}`); if (parallel) { // Don't pipe stdio as it'll get interleaved toAwait.push( (async () => { await execa.command(toRun); logger.log(`✅ Done with ${toRun}`); })() ); } else { await execa.command(toRun, { stdio: 'inherit' }); } } await Promise.all(toAwait); } async function run() { const optionValues = await getOptionsOrPrompt('yarn once-per-template', options); return oncePerTemplate(optionValues); } if (require.main === module) { run().catch((err) => { logger.error('🚨 An error occurred when executing "once-per-template":'); logger.error(err); process.exit(1); }); }