Files
RDO/scripts/auto-sync.js
2026-02-20 07:25:32 -03:00

358 lines
10 KiB
JavaScript

#!/usr/bin/env node
/**
* Auto-Sync Script para RDO Project
* Monitora mudanças em tempo real e sincroniza com GitHub a cada 15 segundos
* Autor: Sistema RDO
* Versão: 3.0
*/
import chokidar from 'chokidar';
import { exec } from 'child_process';
import fs from 'fs';
import path from 'path';
import util from 'util';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const execAsync = util.promisify(exec);
// Configurações
const CONFIG = {
REMOTE_REPO: 'https://github.com/Reifonas/TS_RDO.git',
BRANCH: 'main',
SYNC_INTERVAL: 15000, // 15 segundos
PROJECT_PATH: process.cwd(),
LOG_PATH: path.join(process.cwd(), 'logs'),
WATCH_PATTERNS: [
'**/*',
'!node_modules/**',
'!.git/**',
'!dist/**',
'!build/**',
'!logs/**',
'!*.log',
'!.env.local'
]
};
// Estado do aplicativo
let hasChanges = false;
let isProcessing = false;
let lastSyncTime = 0;
let changeQueue = new Set();
// Criar diretório de logs se não existir
if (!fs.existsSync(CONFIG.LOG_PATH)) {
fs.mkdirSync(CONFIG.LOG_PATH, { recursive: true });
}
/**
* Função de logging com timestamp
*/
function log(message, level = 'INFO') {
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
const logMessage = `[${timestamp}] [${level}] ${message}`;
// Console com cores
const colors = {
ERROR: '\x1b[31m', // Vermelho
WARN: '\x1b[33m', // Amarelo
SUCCESS: '\x1b[32m', // Verde
INFO: '\x1b[36m', // Ciano
RESET: '\x1b[0m' // Reset
};
console.log(`${colors[level] || colors.INFO}${logMessage}${colors.RESET}`);
// Salvar em arquivo
const logFile = path.join(CONFIG.LOG_PATH, `auto-sync-${new Date().toISOString().split('T')[0]}.log`);
fs.appendFileSync(logFile, logMessage + '\n');
}
/**
* Verificar se é um repositório Git
*/
async function isGitRepository() {
try {
await execAsync('git rev-parse --git-dir');
return true;
} catch {
return false;
}
}
/**
* Inicializar repositório Git
*/
async function initializeGitRepository() {
try {
log('Inicializando repositório Git...', 'INFO');
await execAsync('git init');
await execAsync(`git remote add origin ${CONFIG.REMOTE_REPO}`);
await execAsync(`git branch -M ${CONFIG.BRANCH}`);
log('Repositório Git inicializado com sucesso', 'SUCCESS');
return true;
} catch (error) {
log(`Erro ao inicializar repositório: ${error.message}`, 'ERROR');
return false;
}
}
/**
* Verificar se há mudanças no repositório
*/
async function checkForChanges() {
try {
const { stdout } = await execAsync('git status --porcelain');
return stdout.trim().length > 0;
} catch (error) {
log(`Erro ao verificar mudanças: ${error.message}`, 'ERROR');
return false;
}
}
/**
* Gerar mensagem de commit automática
*/
async function generateCommitMessage() {
try {
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
// Obter estatísticas das mudanças
const { stdout: modifiedFiles } = await execAsync('git diff --name-only HEAD').catch(() => ({ stdout: '' }));
const { stdout: newFiles } = await execAsync('git ls-files --others --exclude-standard').catch(() => ({ stdout: '' }));
const { stdout: deletedFiles } = await execAsync('git diff --name-only --diff-filter=D HEAD').catch(() => ({ stdout: '' }));
const changes = [];
if (modifiedFiles.trim()) changes.push(`Modified: ${modifiedFiles.trim().split('\n').length} files`);
if (newFiles.trim()) changes.push(`Added: ${newFiles.trim().split('\n').length} files`);
if (deletedFiles.trim()) changes.push(`Deleted: ${deletedFiles.trim().split('\n').length} files`);
let message = `Auto-sync: ${timestamp}`;
if (changes.length > 0) {
message += ` - ${changes.join(', ')}`;
}
return message;
} catch (error) {
return `Auto-sync: ${new Date().toISOString().replace('T', ' ').substring(0, 19)}`;
}
}
/**
* Sincronizar com GitHub
*/
async function syncWithGitHub() {
if (isProcessing) {
log('Sincronização já em andamento, aguardando...', 'WARN');
return false;
}
isProcessing = true;
try {
log('Iniciando sincronização com GitHub...', 'INFO');
// Verificar se é repositório Git
if (!(await isGitRepository())) {
log('Não é um repositório Git. Inicializando...', 'WARN');
if (!(await initializeGitRepository())) {
return false;
}
}
// Verificar mudanças
if (!(await checkForChanges())) {
log('Nenhuma mudança detectada', 'INFO');
return true;
}
// Pull das mudanças remotas (se houver)
try {
await execAsync(`git pull origin ${CONFIG.BRANCH} --rebase`);
log('Pull realizado com sucesso', 'INFO');
} catch (error) {
log('Primeira sincronização ou sem mudanças remotas', 'INFO');
}
// Adicionar arquivos
await execAsync('git add .');
log('Arquivos adicionados ao stage', 'INFO');
// Commit
const commitMessage = await generateCommitMessage();
await execAsync(`git commit -m "${commitMessage}"`);
log(`Commit realizado: ${commitMessage}`, 'INFO');
// Push
await execAsync(`git push origin ${CONFIG.BRANCH}`);
log('Push realizado com sucesso!', 'SUCCESS');
// Resetar estado
hasChanges = false;
changeQueue.clear();
lastSyncTime = Date.now();
return true;
} catch (error) {
log(`Erro durante sincronização: ${error.message}`, 'ERROR');
return false;
} finally {
isProcessing = false;
}
}
/**
* Inicializar monitoramento de arquivos
*/
function initializeWatcher() {
log('Inicializando monitoramento de arquivos...', 'INFO');
log(`Padrões monitorados: ${CONFIG.WATCH_PATTERNS.join(', ')}`, 'INFO');
const watcher = chokidar.watch(CONFIG.WATCH_PATTERNS, {
ignored: /(^|[\/\\])\../, // Ignorar arquivos ocultos
persistent: true,
ignoreInitial: true,
awaitWriteFinish: {
stabilityThreshold: 1000,
pollInterval: 100
}
});
// Eventos de mudança
watcher
.on('add', path => {
log(`Arquivo adicionado: ${path}`, 'INFO');
hasChanges = true;
changeQueue.add(path);
})
.on('change', path => {
log(`Arquivo modificado: ${path}`, 'INFO');
hasChanges = true;
changeQueue.add(path);
})
.on('unlink', path => {
log(`Arquivo removido: ${path}`, 'INFO');
hasChanges = true;
changeQueue.add(path);
})
.on('error', error => {
log(`Erro no monitoramento: ${error}`, 'ERROR');
})
.on('ready', () => {
log('Monitoramento de arquivos ativo!', 'SUCCESS');
});
return watcher;
}
/**
* Loop principal de sincronização
*/
function startSyncLoop() {
setInterval(async () => {
if (hasChanges && !isProcessing) {
const timeSinceLastSync = Date.now() - lastSyncTime;
if (timeSinceLastSync >= CONFIG.SYNC_INTERVAL) {
log(`Mudanças detectadas (${changeQueue.size} arquivos). Iniciando sincronização...`, 'INFO');
await syncWithGitHub();
} else {
const remainingTime = Math.ceil((CONFIG.SYNC_INTERVAL - timeSinceLastSync) / 1000);
log(`Aguardando ${remainingTime}s para próxima sincronização...`, 'INFO');
}
}
}, 5000); // Verificar a cada 5 segundos
}
/**
* Limpeza de logs antigos
*/
function cleanOldLogs() {
try {
const files = fs.readdirSync(CONFIG.LOG_PATH);
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 7); // Manter logs por 7 dias
files.forEach(file => {
if (file.endsWith('.log')) {
const filePath = path.join(CONFIG.LOG_PATH, file);
const stats = fs.statSync(filePath);
if (stats.mtime < cutoffDate) {
fs.unlinkSync(filePath);
log(`Log antigo removido: ${file}`, 'INFO');
}
}
});
} catch (error) {
log(`Erro ao limpar logs antigos: ${error.message}`, 'ERROR');
}
}
/**
* Tratamento de sinais do sistema
*/
function setupSignalHandlers() {
process.on('SIGINT', () => {
log('Recebido SIGINT. Finalizando...', 'WARN');
process.exit(0);
});
process.on('SIGTERM', () => {
log('Recebido SIGTERM. Finalizando...', 'WARN');
process.exit(0);
});
process.on('uncaughtException', (error) => {
log(`Erro não capturado: ${error.message}`, 'ERROR');
process.exit(1);
});
}
/**
* Função principal
*/
async function main() {
log('=== Auto-Sync RDO v3.0 ===', 'SUCCESS');
log(`Repositório: ${CONFIG.REMOTE_REPO}`, 'INFO');
log(`Branch: ${CONFIG.BRANCH}`, 'INFO');
log(`Intervalo de sincronização: ${CONFIG.SYNC_INTERVAL / 1000}s`, 'INFO');
log(`Diretório: ${CONFIG.PROJECT_PATH}`, 'INFO');
// Configurar tratamento de sinais
setupSignalHandlers();
// Limpar logs antigos
cleanOldLogs();
// Mudar para o diretório do projeto
process.chdir(CONFIG.PROJECT_PATH);
// Inicializar monitoramento
const watcher = initializeWatcher();
// Iniciar loop de sincronização
startSyncLoop();
log('Sistema de auto-sync ativo! Pressione Ctrl+C para parar.', 'SUCCESS');
// Manter o processo ativo
process.stdin.resume();
}
// Executar automaticamente
main().catch(error => {
console.error(`Erro fatal: ${error.message}`);
process.exit(1);
});
export { main, syncWithGitHub, log };