Files
SGF/scripts/purchasing-single-installation.mjs
T
2026-11-04 09:37:20 +02:00

23 lines
5.1 KiB
JavaScript

import fs from 'node:fs';import path from 'node:path';import crypto from 'node:crypto';import net from 'node:net';import {spawn,spawnSync} from 'node:child_process';
const command=process.argv[2]??'status';if(!['status','update','start'].includes(command))throw Error('Use status, start or update; no provisioning action exists');
const run=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-current.json')),tag='cc50a276c10c',root=path.join(run.root,'private/receipt-composition-'+tag),s=JSON.parse(fs.readFileSync(path.join(root,'state.json'))),server=JSON.parse(fs.readFileSync(path.join(root,'demo-server.json')));
if(run.port!==55439||s.tag!==tag||server.port!==8085||s.env.DB_DATABASE!=='budget_core_sgf_'+tag||s.nativeConnection.database!=='compras_receipts_test_'+tag||s.env.DB_PORT!=='55439'||s.env.SGF_ISOLATED_REGRESSION_RUN!==run.id||!s.integrated)throw Error('Only the existing selected installation is allowed');
const hash=p=>crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex');
if(command==='status'){console.log(JSON.stringify({installation:tag,port:8085,single:s.singleInstallation===true,new_database:false,new_server:false,entry:'http://127.0.0.1:8085/compras'}));process.exit(0);}
if(command==='start'){
if(!s.singleInstallation)throw Error('Selected installation not consolidated');
try{const response=await fetch('http://127.0.0.1:8085/compras',{signal:AbortSignal.timeout(5000)});if(response.status===200&&response.headers.get('x-sgf-installation')===tag){console.log('Compras disponível: http://127.0.0.1:8085/compras (instalação existente)');process.exit(0);}throw Error('Port8085 is occupied by an unexpected or unavailable application');}catch(error){if(error.message.startsWith('Port8085'))throw error;}
await new Promise((resolve,reject)=>{const probe=net.createServer();probe.once('error',reject);probe.listen(8085,'127.0.0.1',()=>probe.close(resolve));});
const stdout=fs.openSync(path.join(root,'demo-stdout.log'),'a'),stderr=fs.openSync(path.join(root,'demo-stderr.log'),'a'),child=spawn('C:/laragon/tools/sgf-tempo/php-8.4.27/php.exe',['-S','127.0.0.1:8085','-t',path.join(s.app,'public'),path.resolve('scripts/receipt-composition-http.php')],{cwd:s.app,env:s.env,detached:true,windowsHide:true,stdio:['ignore',stdout,stderr]});
fs.writeFileSync(path.join(root,'demo-server.json'),JSON.stringify({...server,pid:child.pid,restarted_existing_installation:new Date().toISOString()},null,2));child.unref();console.log('Compras iniciada na instalação existente: http://127.0.0.1:8085/compras');process.exit(0);
}
const php='C:/laragon/tools/sgf-tempo/php-8.4.27/php.exe',files=['app/Modules/Purchasing/PurchasingEntry.php','resources/views/purchasing-entry.blade.php','resources/views/purchasing-workspace.blade.php','resources/views/partials/purchasing-navigation.blade.php','resources/views/supplier-workspace.blade.php','resources/js/PurchasingHome.jsx',...['purchasing-exit','purchasing-board','purchasing-presenter','purchasing-focus','request-workspace','sourcing-workspace','order-workspace','receipt-workspace','supplier-workspace','supplier-invoice-workspace','supplier-obligation-workspace','obligation-evidence'].map(n=>'public/'+n+'.mjs')];
for(const file of files.filter(f=>f.endsWith('.php')&&!f.includes('/views/'))){const p=spawnSync(php,['-l',file],{encoding:'utf8',windowsHide:true});if(p.status!==0)throw Error('PHP syntax failed: '+file);}
const stamp=new Date().toISOString().replace(/[^0-9]/g,'')+'-'+crypto.randomBytes(3).toString('hex'),backup=path.join(root,'updates',stamp);fs.mkdirSync(backup,{recursive:true});const journal=[];
for(const file of files){const target=path.join(s.app,file);if(fs.existsSync(target)){const saved=path.join(backup,file);fs.mkdirSync(path.dirname(saved),{recursive:true});fs.copyFileSync(target,saved);}journal.push({file,before:fs.existsSync(target)?hash(target):null,after:hash(file)});fs.mkdirSync(path.dirname(target),{recursive:true});fs.copyFileSync(file,target);}
for(const file of fs.readdirSync('public/build/assets')){const relative='public/build/assets/'+file;const target=path.join(s.app,relative);if(!fs.existsSync(target))fs.copyFileSync(relative,target);else if(hash(target)!==hash(relative))throw Error('Immutable asset content differs');}
const manifest=path.join(s.app,'public/build/manifest.json');fs.copyFileSync(manifest,path.join(backup,'build-manifest.json'));fs.copyFileSync('public/build/manifest.json',manifest);
// Commit the composition flag last. Its file is private and never printed or copied into the repo.
fs.copyFileSync(path.join(root,'state.json'),path.join(backup,'state.json'));s.singleInstallation=true;fs.writeFileSync(path.join(root,'state.next.json'),JSON.stringify(s,null,2));fs.renameSync(path.join(root,'state.next.json'),path.join(root,'state.json'));
fs.writeFileSync(path.join(backup,'code-update.json'),JSON.stringify({installation:tag,port:8085,files:journal,new_database:false,new_role:false,new_server:false,cache_cleared:false,build_manifest_sha256:hash(manifest)},null,2));console.log(JSON.stringify({updated:true,installation:tag,port:8085,backup:stamp,files:files.length,new_database:false,new_server:false}));