UNPKG

autoforce

Version:

Developer Automation tool for Github / Gitlab and Salesforce projects.

240 lines (239 loc) 8.52 kB
import context, { initializeContext } from "./context.js"; import { logError, logStep } from "./color.js"; import { getModelFolders, getFiles, filterJson, readJsonSync } from "./util.js"; import { validateCommand, validateFunction, executeFunction, executeCommand, taskFunctions } from "./taskFunctions.js"; import prompts from "prompts"; export function getTaskFolders(command = 'task') { const folders = { 'task': getModelFolders('tasks'), 'subtask': getModelFolders('subtasks'), 'tasks': getModelFolders('tasks'), 'subtasks': getModelFolders('subtasks'), 'new': getModelFolders('new') }; return folders[command.toLowerCase()] || folders.tasks; } function getTaskLists(folder) { const files = getFiles(folder, filterJson); return files.map(filename => filename.split(".")[0]); } export function getTasks(command = 'tasks') { const folders = getTaskFolders(command); const tasks = {}; for (const folder of folders) { for (const taskName of getTaskLists(folder)) { const filename = folder + "/" + taskName + ".json"; const task = readJsonSync(filename); tasks[taskName] = tasks[taskName] ? mergeTask(tasks[taskName], task) : task; } } return tasks; } function mergeTask(newTask, oldTask) { const newSteps = newTask.steps ? [...newTask.steps, ...oldTask.steps] : oldTask.steps; const newArguments = newTask.arguments ? { ...newTask.arguments, ...oldTask.arguments } : oldTask.arguments; const newGuards = newTask.guards ? [...newTask.guards, ...oldTask.guards] : oldTask.guards; const newVerbose = newTask.verbose || oldTask.verbose; const newDescription = newTask.description ? newTask.description + ' ' + oldTask.description : oldTask.description; const mergedTask = { name: newTask.name, verbose: newVerbose, description: newDescription, arguments: newArguments, guards: newGuards, steps: newSteps }; return mergedTask; } function getTask(taskName, subfolder) { const tasks = getTasks(subfolder); if (!tasks[taskName]) { throw new Error(`Verifique que el ${taskName} exista en alguno las subcarpetas de ${subfolder}`); } return tasks[taskName]; } function isCriteriaMet(criteria) { if (!criteria) { return true; } const { field, value } = criteria; // si no viene valor solo verifica que tenga no este vacio if (typeof value == 'undefined') { return context.get(field); ; } const result = context[field] == value; return result; } export async function helpTask(task) { console.log('Nombre:', task.name); console.log('Descripcion:', task.description); console.log('Guards:', task.guards); console.log('Argumentos:', task.arguments); return true; } export function validateTask(task) { initializeContext(); if (task.guards) { // Valida que sea } // Pide datos de entrada y los deja en context if (task.arguments) { // Valida que sea } for (const step of task.steps) { if (step.criteria) { // Valida que sea } let validateStep = false; if (typeof step.command === 'string') { validateStep = validateCommand(step); } else if (typeof step.function === 'string') { validateStep = validateFunction(step); } else if (typeof step.subtask === 'string') { const subtask = getTask(step.subtask, 'subtasks'); validateStep = validateTask(subtask); } else if (typeof step.task === 'string') { const subtask = getTask(step.task, 'tasks'); validateStep = validateTask(subtask); } else { console.log('Step no tiene command ni function ni subtask'); } if (!validateStep) { return false; } } return true; } export async function runTask(task, taskContext, tabs = '') { initializeContext(); // Valida que este ya esten las variables de enotorno y configuracion if (task.guards) { await context.validate(task.guards); } // Pide datos de entrada y los deja en context if (task.arguments) { if (taskContext) { context.setObject(taskContext); } await context.askForArguments(task.arguments); } if (task.verbose) { logStep(`[INICIO] ${task.name}`, tabs); } for (const step of task.steps) { if (isCriteriaMet(step.criteria)) { if (!await executeStep(step, tabs + '\t', task.verbose)) { return false; } } } if (task.verbose) { logStep(`[FIN] ${task.name}`, tabs); } return true; } export async function previewTask(task, taskContext, tabs = '') { initializeContext(); logStep(`${task.name}: ${task.description}`, tabs); for (const step of task.steps) { previewStep(step, taskContext, tabs); } return true; } function previewStep(step, taskContext, tabs = '') { if (step.criteria) { logStep(`Si ${step.criteria.field} ${step.criteria.operator || '=='} ${step.criteria.value}`, tabs); tabs += '\t'; } if (step.subtask) { tabs += '\t'; const subtask = getTask(step.subtask, 'subtasks'); previewTask(subtask, taskContext, tabs); } else { logStep(`${step.name}`, tabs); } } export function createObject(fields, values) { const fieldArray = Array.isArray(fields) ? fields : Object.keys(fields); const argsObject = {}; for (const value of values) { const field = fieldArray.shift(); if (field) { argsObject[field] = value; } } return argsObject; } async function runStep(step, tabs) { if (typeof step.command === 'string') { return executeCommand(step); } else if (typeof step.function === 'string') { return await executeFunction(step); } else if (typeof step.subtask === 'string' || typeof step.task === 'string') { const subtask = typeof step.subtask === 'string' ? getTask(step.subtask, 'subtask') : getTask(step.task, 'tasks'); let stepContext = step.arguments ? context.mergeArgs(step.arguments) : {}; if (Array.isArray(stepContext)) { stepContext = createObject(subtask.arguments, stepContext); } return await runTask(subtask, stepContext, tabs); } throw new Error(`No se pudo ejecutar el step ${step.name} porque no tiene command, function o subtasks`); } async function askForContinueOrRetry() { if (!context.isVerbose) { return 'quit'; } const answer = await prompts([ { type: "select", name: "continue", message: "No se pudo ejecutar el step, ¿que quiere hacer? ", choices: [{ title: 'Salir', value: 'quit' }, { title: 'Continuar', value: 'continue' }, { title: 'Reintentar', value: 'retry' }], } ]); return answer.continue; } function getStepError(step, stepName) { return step.errorMessage ? context.merge(step.errorMessage) : stepName ? `Fallo el step ${stepName}` : ''; } async function executeStep(step, tabs, verbose = false) { const stepName = step.name ? context.merge(step.name) : undefined; if (verbose && stepName) { logStep(`[INICIO] ${stepName}`, tabs); } let retry = false; let success = false; do { try { success = await runStep(step, tabs); if (!success) { logError(getStepError(step, stepName), tabs); // Si tiene un custom handler cuando hay un error if (step.onError) { const errorHandler = taskFunctions[step.onError]; if (typeof errorHandler === 'function') { success = await errorHandler(); } } } } catch (error) { if (error instanceof Error) { logError(error.message, tabs); } } if (!success) { const result = await askForContinueOrRetry(); retry = result == 'retry'; success = result == 'continue'; } } while (!success && retry); if (verbose && stepName) { logStep(`[FIN] ${stepName}`, tabs); } if (!success) { process.exit(!context.isVerbose ? -1 : 0); } return success; }