2026-10-13 08:09:07 -05:00
// Arma una carpeta lista para copiar al servidor: release/devolucion-proveedores/
//
// npm run publicar compila todo y arma la carpeta
// npm run publicar -- --sin-build reutiliza los builds que ya existen (libs/shared/dist, apps/*/dist)
//
// Contenido: la API compilada, la web compilada y solo las dependencias de producción de la API
// (sin Angular CLI ni herramientas de desarrollo). Las dependencias de la API son JavaScript puro,
// así que se puede publicar desde un Mac o Linux y ejecutar en Windows.
import { spawnSync } from 'node:child_process' ;
import { cpSync , existsSync , mkdirSync , readFileSync , readdirSync , rmSync , statSync , writeFileSync } from 'node:fs' ;
import { dirname , join , resolve } from 'node:path' ;
import { fileURLToPath } from 'node:url' ;
const raiz = resolve ( dirname ( fileURLToPath ( import . meta . url )), '..' );
const destino = join ( raiz , 'release' , 'devolucion-proveedores' );
const app = join ( destino , 'app' );
const sinBuild = process . argv . includes ( '--sin-build' );
const npm = process . platform === 'win32' ? 'npm.cmd' : 'npm' ;
function ejecutar ( comando , args , cwd ) {
console . log ( `\n$ ${ comando } ${ args . join ( ' ' ) } ( ${ cwd } )` );
// shell: true en Windows porque npm es un .cmd
const r = spawnSync ( comando , args , { cwd , stdio : 'inherit' , shell : process . platform === 'win32' });
if ( r . status !== 0 ) {
console . error ( `\nFalló: ${ comando } ${ args . join ( ' ' ) } ` );
process . exit ( r . status ?? 1 );
}
}
function copiar ( origen , destinoRelativo ) {
const desde = join ( raiz , origen );
if ( ! existsSync ( desde )) {
console . error ( `Falta ${ origen } . Ejecute "npm run publicar" sin --sin-build (o "npm run build" antes).` );
process . exit ( 1 );
}
cpSync ( desde , join ( app , destinoRelativo ?? origen ), { recursive : true });
}
2026-10-15 16:03:00 -05:00
/** Commit corto de git (con "-modificado" si hay cambios sin commit), o null si no hay git o commits. */
function commitActual () {
const git = (... args ) => spawnSync ( 'git' , args , { cwd : raiz , encoding : 'utf8' });
const head = git ( 'rev-parse' , '--short' , 'HEAD' );
if ( head . status !== 0 ) return null ;
const cambios = git ( 'status' , '--porcelain' ). stdout . trim (). length > 0 ;
return ` ${ head . stdout . trim () }${ cambios ? '-modificado' : '' } ` ;
}
2026-10-13 08:09:07 -05:00
function tamano ( carpeta ) {
let total = 0 ;
for ( const e of readdirSync ( carpeta , { withFileTypes : true , recursive : true })) {
if ( e . isFile ()) total += statSync ( join ( e . parentPath , e . name )). size ;
}
return ` ${ ( total / 1024 / 1024 ). toFixed ( 1 ) } MB` ;
}
console . log ( 'Publicando Devolución a Proveedores' );
if ( ! sinBuild ) ejecutar ( npm , [ 'run' , 'build' ], raiz );
rmSync ( destino , { recursive : true , force : true });
mkdirSync ( app , { recursive : true });
// Lo mínimo para que `npm ci` resuelva los mismos paquetes del package-lock: el package.json raíz
// y el de cada workspace (la web no se instala, pero debe estar declarada para que el lock coincida).
copiar ( 'package.json' );
copiar ( 'package-lock.json' );
copiar ( 'libs/shared/package.json' );
copiar ( 'libs/shared/dist' );
copiar ( 'apps/api/package.json' );
copiar ( 'apps/api/dist' );
copiar ( 'apps/api/.env.example' );
copiar ( 'apps/web/package.json' );
copiar ( 'apps/web/dist/web/browser' );
ejecutar ( npm , [ 'ci' , '--omit=dev' , '--workspace=@dp/shared' , '--workspace=api' , '--no-audit' , '--no-fund' ], app );
2026-10-15 16:03:00 -05:00
// npm deja enlaces simbólicos (el de cada workspace y los atajos de .bin) que no sobreviven al copiar la
// carpeta de un Mac/Linux a Windows. La API importa @dp/shared al ejecutarse, así que ese enlace se
// reemplaza por una copia real; los demás no se usan y se quitan.
const nodeModules = join ( app , 'node_modules' );
rmSync ( join ( nodeModules , 'api' ), { recursive : true , force : true });
const compartido = join ( nodeModules , '@dp' , 'shared' );
rmSync ( compartido , { recursive : true , force : true }); // quita solo el enlace, no su destino
mkdirSync ( compartido , { recursive : true });
cpSync ( join ( app , 'libs' , 'shared' , 'package.json' ), join ( compartido , 'package.json' ));
cpSync ( join ( app , 'libs' , 'shared' , 'dist' ), join ( compartido , 'dist' ), { recursive : true });
rmSync ( join ( app , 'libs' ), { recursive : true , force : true });
for ( const e of readdirSync ( nodeModules , { withFileTypes : true , recursive : true })) {
if ( e . name === '.bin' ) rmSync ( join ( e . parentPath , e . name ), { recursive : true , force : true });
}
const enlaces = readdirSync ( app , { withFileTypes : true , recursive : true })
. filter (( e ) => e . isSymbolicLink ())
. map (( e ) => join ( e . parentPath , e . name ));
if ( enlaces . length > 0 ) {
console . error ( `\nQuedaron enlaces simbólicos en el paquete (no sobreviven en Windows):\n ${ enlaces . join ( '\n ' ) } ` );
process . exit ( 1 );
}
2026-10-13 08:09:07 -05:00
// Archivos de despliegue para Windows junto a la carpeta app/
for ( const archivo of readdirSync ( join ( raiz , 'deploy' , 'windows' ))) {
cpSync ( join ( raiz , 'deploy' , 'windows' , archivo ), join ( destino , archivo ));
}
for ( const carpeta of [ 'logs' , 'certificados' ]) mkdirSync ( join ( destino , carpeta ), { recursive : true });
writeFileSync ( join ( destino , 'certificados' , 'LEEME.txt' ), 'Ponga aquí el certificado HTTPS (.pfx, o .key y .crt) y apunte el .env a estos archivos.\n' );
2026-10-15 16:03:00 -05:00
// Versión: el número sale del package.json raíz (npm run version:parche|menor|mayor). La API lee
// app/version.json al arrancar y la web la muestra en el login y en la barra superior.
const { name , version } = JSON . parse ( readFileSync ( join ( raiz , 'package.json' ), 'utf8' ));
const publicado = new Date (). toISOString ();
const commit = commitActual ();
writeFileSync ( join ( app , 'version.json' ), ` ${ JSON . stringify ({ version , publicado , commit } , null, 2)}\n` );
2026-10-13 08:09:07 -05:00
writeFileSync (
join ( destino , 'VERSION.txt' ),
2026-10-15 16:03:00 -05:00
` ${ name } ${ version } \nPublicado: ${ publicado } \nCommit: ${ commit ?? '(sin git)' } \nNode de la publicación: ${ process . version } \nSistema: ${ process . platform } \n` ,
2026-10-13 08:09:07 -05:00
);
console . log ( `\nListo: ${ destino } ( ${ tamano ( destino ) } )` );
2026-10-15 16:03:00 -05:00
console . log ( `Versión ${ version } · commit ${ commit ?? '(sin git)' } ` );
if ( commit ? . endsWith ( '-modificado' )) console . log ( 'Atención: había cambios sin commit; el release no corresponde exactamente a ese commit.' );
2026-10-13 08:09:07 -05:00
console . log ( 'Siguiente paso: copie esa carpeta al servidor y siga DEPLOY.md.' );