@deinossrl/dgp-agent 1.4.39 → 1.4.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.mjs +64 -9
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -352,7 +352,7 @@ function getPlinkPath() {
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
// Versión del agente
|
|
355
|
-
const AGENT_VERSION = '1.4.
|
|
355
|
+
const AGENT_VERSION = '1.4.40';
|
|
356
356
|
let AGENT_MODE = 'smart'; // Siempre inteligente
|
|
357
357
|
|
|
358
358
|
// Configuración (prioridad: env vars > archivo config > platform config > defaults)
|
|
@@ -887,6 +887,61 @@ function shell(command, options = {}) {
|
|
|
887
887
|
// Alias para compatibilidad
|
|
888
888
|
const shellSync = shell;
|
|
889
889
|
|
|
890
|
+
/**
|
|
891
|
+
* Verifica si un error es recuperable (timeout, conexión, etc)
|
|
892
|
+
*/
|
|
893
|
+
function isRecoverableError(error) {
|
|
894
|
+
const msg = error.message.toLowerCase();
|
|
895
|
+
return (
|
|
896
|
+
msg.includes('timeout') ||
|
|
897
|
+
msg.includes('timed out') ||
|
|
898
|
+
msg.includes('connection refused') ||
|
|
899
|
+
msg.includes('network') ||
|
|
900
|
+
msg.includes('temporarily unavailable') ||
|
|
901
|
+
(error.code === 255 && msg.includes('ssh'))
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Ejecuta un comando shell de forma asíncrona con reintentos automáticos
|
|
907
|
+
*/
|
|
908
|
+
async function shellAsyncWithRetry(command, options = {}, maxRetries = 3, baseDelay = 2000) {
|
|
909
|
+
let lastError;
|
|
910
|
+
|
|
911
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
912
|
+
try {
|
|
913
|
+
logInfo(`[Attempt ${attempt}/${maxRetries}] Executing: ${command.substring(0, 80)}...`);
|
|
914
|
+
const result = await shellAsync(command, options);
|
|
915
|
+
if (attempt > 1) {
|
|
916
|
+
logSuccess(`✓ Command succeeded after ${attempt} attempts`);
|
|
917
|
+
}
|
|
918
|
+
return result;
|
|
919
|
+
} catch (error) {
|
|
920
|
+
lastError = error;
|
|
921
|
+
|
|
922
|
+
// Si no es recuperable, fallar inmediatamente
|
|
923
|
+
if (!isRecoverableError(error)) {
|
|
924
|
+
throw error;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// Si es el último intento, fallar
|
|
928
|
+
if (attempt === maxRetries) {
|
|
929
|
+
logError(`✗ Command failed after ${maxRetries} attempts`);
|
|
930
|
+
throw error;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Calcular delay con backoff exponencial
|
|
934
|
+
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
935
|
+
log(`⚠️ Recoverable error (${error.message}), retrying in ${delay}ms...`, 'yellow');
|
|
936
|
+
|
|
937
|
+
// Esperar antes de reintentar
|
|
938
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
throw lastError;
|
|
943
|
+
}
|
|
944
|
+
|
|
890
945
|
/**
|
|
891
946
|
* Ejecuta un comando shell de forma asíncrona con output en tiempo real
|
|
892
947
|
*/
|
|
@@ -1225,10 +1280,10 @@ async function executeDeploy(command) {
|
|
|
1225
1280
|
? '/var/www/tenminuteia-prod/'
|
|
1226
1281
|
: '/var/www/tenminuteia-staging/');
|
|
1227
1282
|
|
|
1228
|
-
// Construir base del comando SSH
|
|
1283
|
+
// Construir base del comando SSH con timeout largo para conexiones lentas
|
|
1229
1284
|
const sshBase = sshKeyPath
|
|
1230
|
-
? `ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no ${ssh_user}@${server_host}`
|
|
1231
|
-
: `ssh ${ssh_user}@${server_host}`;
|
|
1285
|
+
? `ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no -o ConnectTimeout=60 -o ServerAliveInterval=10 ${ssh_user}@${server_host}`
|
|
1286
|
+
: `ssh -o ConnectTimeout=60 -o ServerAliveInterval=10 ${ssh_user}@${server_host}`;
|
|
1232
1287
|
|
|
1233
1288
|
// Step 1: Git clone/pull on server
|
|
1234
1289
|
currentStep = 'git_setup_server';
|
|
@@ -1240,7 +1295,7 @@ async function executeDeploy(command) {
|
|
|
1240
1295
|
try {
|
|
1241
1296
|
logInfo(`Attempting to update existing repository...`);
|
|
1242
1297
|
const gitPullCmd = `${sshBase} "cd ${projectFolder} && git fetch origin && git checkout ${branch} && git pull origin ${branch}"`;
|
|
1243
|
-
await
|
|
1298
|
+
await shellAsyncWithRetry(gitPullCmd, {}, 5, 3000); // 5 reintentos, 3s base delay
|
|
1244
1299
|
logSuccess(`Code updated on server`);
|
|
1245
1300
|
} catch (pullError) {
|
|
1246
1301
|
// Si falla el pull, probablemente el repo no existe
|
|
@@ -1260,7 +1315,7 @@ async function executeDeploy(command) {
|
|
|
1260
1315
|
// Crear directorio padre y clonar
|
|
1261
1316
|
const parentDir = projectFolder.substring(0, projectFolder.lastIndexOf('/'));
|
|
1262
1317
|
const cloneCmd = `${sshBase} "mkdir -p ${parentDir} && git clone ${repoUrl} ${projectFolder} && cd ${projectFolder} && git checkout ${branch}"`;
|
|
1263
|
-
await
|
|
1318
|
+
await shellAsyncWithRetry(cloneCmd, {}, 5, 3000); // 5 reintentos, 3s base delay
|
|
1264
1319
|
|
|
1265
1320
|
logSuccess(`Repository cloned successfully`);
|
|
1266
1321
|
}
|
|
@@ -1273,7 +1328,7 @@ async function executeDeploy(command) {
|
|
|
1273
1328
|
steps.push({ step: currentStep, status: 'running' });
|
|
1274
1329
|
|
|
1275
1330
|
const npmInstallCmd = `${sshBase} "cd ${projectFolder} && npm ci"`;
|
|
1276
|
-
await
|
|
1331
|
+
await shellAsyncWithRetry(npmInstallCmd, {}, 3, 2000); // 3 reintentos, 2s base delay
|
|
1277
1332
|
|
|
1278
1333
|
steps[steps.length - 1].status = 'success';
|
|
1279
1334
|
logSuccess(`Dependencies installed on server`);
|
|
@@ -1284,7 +1339,7 @@ async function executeDeploy(command) {
|
|
|
1284
1339
|
steps.push({ step: currentStep, status: 'running' });
|
|
1285
1340
|
|
|
1286
1341
|
const npmBuildCmd = `${sshBase} "cd ${projectFolder} && npm run build"`;
|
|
1287
|
-
await
|
|
1342
|
+
await shellAsyncWithRetry(npmBuildCmd, {}, 3, 2000); // 3 reintentos, 2s base delay
|
|
1288
1343
|
|
|
1289
1344
|
steps[steps.length - 1].status = 'success';
|
|
1290
1345
|
logSuccess(`Build completed on server`);
|
|
@@ -1295,7 +1350,7 @@ async function executeDeploy(command) {
|
|
|
1295
1350
|
steps.push({ step: currentStep, status: 'running' });
|
|
1296
1351
|
|
|
1297
1352
|
const reloadNginxCmd = `${sshBase} "sudo nginx -t && sudo systemctl reload nginx"`;
|
|
1298
|
-
await
|
|
1353
|
+
await shellAsyncWithRetry(reloadNginxCmd, {}, 3, 2000); // 3 reintentos, 2s base delay
|
|
1299
1354
|
|
|
1300
1355
|
steps[steps.length - 1].status = 'success';
|
|
1301
1356
|
logSuccess(`Nginx reloaded`);
|