autoforce
Version:
Developer Automation tool for Github / Gitlab and Salesforce projects.
240 lines (239 loc) • 8.52 kB
JavaScript
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;
}