gitignore

This commit is contained in:
2026-02-18 14:58:21 +01:00
parent b069d047ec
commit d4ad5d9c61
6 changed files with 1168 additions and 0 deletions

183
src/index.ts Normal file
View File

@@ -0,0 +1,183 @@
#!/usr/bin/node
import { existsSync } from 'fs';
import fs from 'fs/promises';
import path from 'path';
import { Pool } from 'pg';
import { env } from 'process';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
const db = new Pool({
host: env.POSTGRES_HOST,
user: env.POSTGRES_USER,
port: Number(env.POSTGRES_PORT),
password: env.POSTGRES_PASSWORD
})
type MigrationFile = {
version: string;
fileName: string;
fullPath: string;
}
/**
* Carga de variables de entorno manual (para evitar dependencias como dotenv)
* Busca el .env en el directorio actual (CWD)
*/
async function loadEnv(envpath?: string) {
const envPath = envpath ?? path.join(process.cwd(), '.env');
if (existsSync(envPath)) {
const content = await fs.readFile(envPath, 'utf-8');
content.split('\n').forEach(line => {
const [key, value] = line.split('=');
if (key && value) process.env[key.trim()] = value.trim();
});
console.log(' Archivo .env cargado desde el directorio actual.');
}
}
/**
* Parseo de argumentos manual: --target <version>
*/
function getArgs() {
const argv = yargs(hideBin(process.argv))
.option("target", {
alias: "t",
type: 'string',
description: "Versión objetivo de la migracion",
requiresArg: false
})
.parse()
console.log("args", argv)
/* Antiguo */
const args = process.argv.slice(2);
const targetIdx = args.indexOf('--target') !== -1 ? args.indexOf('--target') : args.indexOf('-t');
const envIdx = args.indexOf('--env') !== -1 ? args.indexOf('--env') : args.indexOf('-e');
const migrtionsIdx = args.indexOf("")
if (targetIdx === -1 || !args[targetIdx + 1]) {
console.error('❌ Error: Debes especificar una versión objetivo con --target o -t');
process.exit(1);
}
return {
target: args[targetIdx + 1],
env: args[envIdx + 1]
};
}
function versionToValue(version: string) {
return version.split("_")[0]!
.split(".")
.slice(0, 3)
.map(e => parseInt(e))
}
/**
* Para poder ordenar las verisones con 1 - 3 valores
*/
function compareVersions(va: string, vb: string) {
const { max } = Math
const partesa = versionToValue(va)
const partesb = versionToValue(vb)
const maxLen = max(partesa.length, partesb.length)
for (let i = 0; i < maxLen; i++) {
const partea = partesa[i] ?? 0
const parteb = partesb[i] ?? 0
if (partea == parteb) continue;
else if (partea > parteb) return 1;
else return -1
}
return 0
}
/**
* Lógica principal de ejecución de migraciones
* @param targetVersion Versión objetivo pasada por el usuario
* @param currentVersion Versión actual obtenida de la DB
*/
async function runMigrations(targetVersion: string, currentVersion: string, migrationDir: string) {
const dbClient = await db.connect()
try {
const files = await fs.readdir(migrationDir);
const pendingMigrations: MigrationFile[] = files
.map(f => (<MigrationFile>{
version: path.parse(f).name,
fileName: f,
fullPath: path.join(migrationDir, f)
}))
// 1. Filtrar las migraciones > que la actual
.filter(v => compareVersions(v.version, currentVersion) == -1)
// 2. Filtra las migraciones <= que la objetivo
.filter(v => compareVersions(v.version, targetVersion) >= 0)
.sort((a, b) => compareVersions(a.version, b.version));
if (pendingMigrations.length === 0) {
console.log("✅ La base de datos ya está actualizada.");
return;
}
console.log("Migraciones pendietes", pendingMigrations)
console.log(`🚀 Aplicando ${pendingMigrations.length} migraciones...`);
// Iniciamos Transacción (Ejemplo conceptual con un cliente genérico)
// await db.query('BEGIN');
await dbClient.query("BEGIN")
for (const migration of pendingMigrations) {
const sql = await fs.readFile(migration.fullPath, 'utf8');
console.log(` -> Aplicando: ${migration.fileName}`);
// Ejecutar SQL
await dbClient.query(sql);
// Actualizar tabla de versión
// En principio esto se hace en el archivo pero estoy pensando en meterlo en el script para
// evitar olvidos
/* await dbClient.query(`
INSERT INTO db_versions (
version,
notes
)
VALUES (
$1,
'Fechas modificadas para que todas sean en base a UTC'
);
`)
*/
}
// await db.query('COMMIT');
await db.query("COMMIT")
console.log("✨ Migraciones completadas con éxito.");
} catch (error) {
// Si algo falla, el rollback es vital en DevOps
// await db.query('ROLLBACK');
console.error("❌ Error durante la migración. Se ha realizado un rollback automático.");
console.error(error);
await db.query("ROLLBACK")
process.exit(1);
} finally {
dbClient.release()
}
}
/**
* *******************************************************************
* *******************************************************************
*/
async function main() {
const { target: targetVersion, env: envPath } = getArgs();
await loadEnv(envPath)
}
main().then(e => console.log(e)).catch(console.error)