la version ya no es PK
This commit is contained in:
139
src/index.ts
139
src/index.ts
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/node
|
||||
|
||||
import { constants } from 'buffer';
|
||||
import { existsSync } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
@@ -60,19 +59,25 @@ function getArgs() {
|
||||
type: "string",
|
||||
description: "Path del directorio de migrations"
|
||||
})
|
||||
.option("versionTable", {
|
||||
alias: "v",
|
||||
type: "string",
|
||||
description: "Nombre de la tabla donde se almacenan las versiones de la BDD, por defecto 'db_versions'",
|
||||
default: "db_versions"
|
||||
})
|
||||
.parse() as {
|
||||
target: string,
|
||||
env: string,
|
||||
migrations: string
|
||||
migrations: string,
|
||||
versionTable: "db_versions" | string
|
||||
}
|
||||
|
||||
console.log("args", argv)
|
||||
/* Antiguo */
|
||||
|
||||
//console.log("args", argv)
|
||||
return {
|
||||
target: argv.target,
|
||||
env: argv.env,
|
||||
migrations: argv.migrations
|
||||
migrations: argv.migrations,
|
||||
versionTable: argv.versionTable
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,17 +111,89 @@ function compareVersions(va: string, vb: string) {
|
||||
return 0
|
||||
}
|
||||
|
||||
async function getCurrentVersion(db: Pool) {
|
||||
async function getCurrentVersion(db: Pool, versionTable: string) {
|
||||
try {
|
||||
const lastVersion = await db.query<DBVersion>(`
|
||||
SELECT * FROM db_versions
|
||||
SELECT * FROM ${versionTable}
|
||||
ORDER BY creation_date DESC
|
||||
LIMIT 1
|
||||
`)
|
||||
return lastVersion.rows[0]
|
||||
} catch (e) {
|
||||
console.error("Error leyendo la tabla de versiones ", versionTable, e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Si se estuviese lanzando el script sin que exista una tabla con el regstro
|
||||
* de versiones se crea una nueva con 0.0.0 como version inicial
|
||||
*/
|
||||
async function initVersionTable(db: Pool, versionTable: string) {
|
||||
const client = await db.connect()
|
||||
|
||||
const ddlCreateVersionTable = `
|
||||
CREATE TABLE IF NOT EXISTS ${versionTable} (
|
||||
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
version TEXT PRIMARY KEY, -- version semantica x.x.x,
|
||||
notes TEXT,
|
||||
creation_date TIMESTAMP NOT NULL DEFAULT (now() at time zone 'utc'),
|
||||
stable BOOLEAN DEFAULT FALSE -- Si la version ha sido testada y se puede desplegar
|
||||
);
|
||||
`
|
||||
|
||||
const insertFistValue = `
|
||||
INSERT INTO ${versionTable} (
|
||||
version,
|
||||
notes
|
||||
)
|
||||
VALUES (
|
||||
'0.0.0',
|
||||
'Versión base'
|
||||
);
|
||||
`
|
||||
|
||||
try {
|
||||
await client.query("BEGIN")
|
||||
|
||||
await client.query(ddlCreateVersionTable)
|
||||
await client.query(insertFistValue)
|
||||
|
||||
await client.query("COMMIT")
|
||||
return 0
|
||||
} catch (e) {
|
||||
client.query("ROLLBACK")
|
||||
console.error("[x] No se ha podido crear la tabla de versiones de la BDD")
|
||||
console.error(e)
|
||||
} finally {
|
||||
client.release()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function writeAppliedVersion(db: Pool, versionTable: string, version: MigrationFile) {
|
||||
const client = await db.connect()
|
||||
const insertVersion = `
|
||||
INSERT INTO ${versionTable} (
|
||||
version,
|
||||
notes
|
||||
)
|
||||
VALUES (
|
||||
$1,
|
||||
$2
|
||||
);
|
||||
`
|
||||
const parts = version.version.split("_")
|
||||
try {
|
||||
await client.query("BEGIN")
|
||||
await client.query(insertVersion, parts)
|
||||
await client.query("COMMIT")
|
||||
return 0
|
||||
} catch (e) {
|
||||
client.query("ROLLBACK")
|
||||
console.error("[x] No se ha podido insertar la nueva version de la BDD")
|
||||
console.error(e)
|
||||
} finally {
|
||||
client.release()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +205,8 @@ async function getCurrentVersion(db: Pool) {
|
||||
async function runMigrations(args: {
|
||||
targetVersion: string,
|
||||
currentVersion?: string,
|
||||
migrationDir: string
|
||||
migrationDir: string,
|
||||
versionTable: string
|
||||
}) {
|
||||
let db;
|
||||
try {
|
||||
@@ -147,16 +225,19 @@ async function runMigrations(args: {
|
||||
|
||||
const dbClient = await db.connect()
|
||||
try {
|
||||
const versionBdd = (await getCurrentVersion(db, args.versionTable))?.version
|
||||
|
||||
if (versionBdd == undefined) {
|
||||
await initVersionTable(db, args.versionTable)
|
||||
}
|
||||
|
||||
// 1º La version explicita 2º La versión almacenada en BDD 3º 0.0.0 como version base
|
||||
const versionBdd = (await getCurrentVersion(db))?.version
|
||||
if (versionBdd == undefined) {
|
||||
console.log("[x] Error buscando la ultima version de la base de datos")
|
||||
}
|
||||
const currentVersion = args.currentVersion ?? versionBdd ?? "0.0.0"
|
||||
console.log("[i] Migrando desde la version " + currentVersion + " a la version " + args.targetVersion)
|
||||
const files = await fs.readdir(args.migrationDir);
|
||||
console.log("Directorio de migraciones", args.migrationDir)
|
||||
console.log("Archivos de migraciones", files)
|
||||
const pendingMigrations: MigrationFile[] = files
|
||||
.map(f => (<MigrationFile>{
|
||||
version: path.parse(f).name,
|
||||
@@ -173,11 +254,10 @@ async function runMigrations(args: {
|
||||
console.log("[o] La base de datos ya está actualizada.");
|
||||
return;
|
||||
}
|
||||
console.log("[i] Migraciones pendietes", pendingMigrations)
|
||||
console.log("[i] Migraciones pendietes", pendingMigrations.map(e => e.version))
|
||||
console.log(`[i] 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');
|
||||
@@ -185,29 +265,19 @@ async function runMigrations(args: {
|
||||
|
||||
// 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'
|
||||
);
|
||||
`)
|
||||
*/
|
||||
console.log(` -> Aplicado correctamente: ${migration.fileName}`);
|
||||
}
|
||||
|
||||
await db.query("COMMIT")
|
||||
await dbClient.query("COMMIT")
|
||||
|
||||
const ultimaVersion = pendingMigrations[pendingMigrations.length - 1]
|
||||
await writeAppliedVersion(db, args.versionTable, ultimaVersion)
|
||||
console.log("[o] Migraciones completadas con éxito.");
|
||||
console.log("[o] Última version aplicada: ", ultimaVersion.version);
|
||||
} catch (error) {
|
||||
console.error("[x] Error durante la migración. Se ha realizado un rollback automático.");
|
||||
console.error(error);
|
||||
await db.query("ROLLBACK")
|
||||
await dbClient.query("ROLLBACK")
|
||||
process.exit(1);
|
||||
} finally {
|
||||
dbClient.release()
|
||||
@@ -216,13 +286,18 @@ async function runMigrations(args: {
|
||||
|
||||
/**
|
||||
* *******************************************************************
|
||||
* MAIN
|
||||
* *******************************************************************
|
||||
*/
|
||||
|
||||
async function main() {
|
||||
const args = getArgs();
|
||||
await loadEnv(args.env);
|
||||
runMigrations({ targetVersion: args.target, migrationDir: args.migrations })
|
||||
await runMigrations({
|
||||
targetVersion: args.target,
|
||||
migrationDir: args.migrations,
|
||||
versionTable: args.versionTable
|
||||
})
|
||||
}
|
||||
|
||||
main().then(e => console.log(e)).catch(console.error)
|
||||
|
||||
Reference in New Issue
Block a user