26 lines
4.6 KiB
JavaScript
26 lines
4.6 KiB
JavaScript
import fs from 'node:fs';import path from 'node:path';import crypto from 'node:crypto';import {spawnSync} from 'node:child_process';
|
|
const php='C:/laragon/tools/sgf-tempo/php-8.4.27/php.exe',psql='C:/Program Files/PostgreSQL/17/bin/psql.exe',repo=process.cwd();
|
|
const state=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-current.json','utf8'));
|
|
if(!/^[a-f0-9]{12}$/.test(state.id)||state.port!==55439||path.resolve(state.root)!==path.resolve('C:/laragon/data/sgf-tempo/run/modular-regression-'+state.id))throw Error('QA cluster refused');
|
|
const tag=crypto.randomBytes(6).toString('hex'),root=path.join(state.root,'private','requisition-composition-'+tag),app=path.join(root,'app'),bundle=path.join(root,'bundle'),database='budget_core_sgf_'+tag,role='ir_composition_'+tag,password=crypto.randomBytes(32).toString('hex');
|
|
fs.mkdirSync(root,{recursive:false});fs.mkdirSync(app);fs.mkdirSync(bundle);
|
|
const source=path.resolve('../compras'),sourceList=fs.readFileSync('app/Modules/Purchasing/InternalRequisitionQa.php','utf8').split('public const FILES = [')[1].split('];')[0],files=[...sourceList.matchAll(/'([^']+)'/g)].map(m=>m[1]),hash=f=>crypto.createHash('sha256').update(fs.readFileSync(f)).digest('hex'),pins={};
|
|
if(files.length!==10)throw Error('Unexpected composition file set');
|
|
for(const file of files){const from=path.join(source,file),to=path.join(bundle,file);if(fs.lstatSync(from).isSymbolicLink())throw Error('Source link refused');fs.mkdirSync(path.dirname(to),{recursive:true});fs.copyFileSync(from,to);pins[file]=hash(to);if(pins[file]!==hash(from))throw Error('Source changed while copying');}
|
|
fs.writeFileSync(path.join(bundle,'manifest.json'),JSON.stringify({format:'sgf.requisitions.qa-bundle.v1',files:pins},null,2));
|
|
for(const dir of ['app','bootstrap','config','routes','resources','database'])fs.cpSync(dir,path.join(app,dir),{recursive:true,filter:p=>!p.replaceAll('\\','/').includes('bootstrap/cache')});
|
|
for(const pkg of ['budget-control','payables','general-ledger','module-updates','purchasing'])fs.cpSync(path.join('packages',pkg),path.join(app,'packages',pkg),{recursive:true});
|
|
fs.symlinkSync(path.join(repo,'vendor'),path.join(app,'vendor'),'junction');
|
|
fs.copyFileSync('composer.json',path.join(app,'composer.json'));
|
|
for(const dir of ['bootstrap/cache','storage/logs','storage/framework/cache/data','storage/framework/sessions','storage/framework/views'])fs.mkdirSync(path.join(app,dir),{recursive:true});
|
|
const env={...process.env,APP_ENV:'budget-testing',APP_KEY:'base64:'+crypto.randomBytes(32).toString('base64'),APP_DEBUG:'false',APP_BASE_PATH:app,DB_CONNECTION:'pgsql',DB_HOST:'127.0.0.1',DB_PORT:'55439',DB_DATABASE:database,DB_USERNAME:role,DB_PASSWORD:password,DB_URL:'',SESSION_DRIVER:'file',CACHE_STORE:'array',QUEUE_CONNECTION:'sync',BCRYPT_ROUNDS:'4',SGF_ISOLATED_REGRESSION_RUN:state.id,SGF_REQUISITIONS_QA_ENABLED:'true',SGF_REQUISITIONS_QA_BUNDLE:bundle.replaceAll('\\','/'),SGF_REQUISITIONS_QA_MANIFEST:hash(path.join(bundle,'manifest.json')),IR_COMPOSITION_STATE:path.join(root,'state.json')};
|
|
// Private settings only; neither the proof summary nor console contains passwords.
|
|
fs.writeFileSync(path.join(root,'state.json'),JSON.stringify({tag,root,app,bundle,database,role,env},null,2));
|
|
const admin=fs.readFileSync(path.join(state.root,'private/admin-password.txt'),'utf8').trim(),pgEnv={...process.env,PGPASSWORD:admin};
|
|
const pg=input=>spawnSync(psql,['-h','127.0.0.1','-p','55439','-U','sgf_regression_owner','-d','postgres','-At','-v','ON_ERROR_STOP=1'],{input,encoding:'utf8',windowsHide:true,env:pgEnv});
|
|
const identity=pg("SELECT current_setting('cluster_name');");if(identity.status!==0||identity.stdout.trim()!=='sgf_modular_regression_'+state.id)throw Error('Physical cluster mismatch');
|
|
const created=pg(`CREATE ROLE ${role} LOGIN PASSWORD '${password}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;\nCREATE DATABASE ${database} OWNER ${role};\nREVOKE ALL ON DATABASE ${database} FROM PUBLIC;`);if(created.status!==0)throw Error('Provisioning incomplete; preserve and reconcile this private run without resetting');
|
|
const result=spawnSync(php,[path.join(repo,'scripts/requisition-composition-proof.php')],{cwd:app,env,encoding:'utf8',windowsHide:true,timeout:120000,maxBuffer:2*1024*1024});
|
|
const complete=fs.existsSync(path.join(root,'result.json'))&&JSON.parse(fs.readFileSync(path.join(root,'result.json'))).passed===true;
|
|
fs.writeFileSync(path.join(root,'process-output.txt'),result.stdout+'\n'+result.stderr);console.log(JSON.stringify({tag,exit_code:result.status,proof_present:complete,qa_only:true}));process.exitCode=result.status===0&&complete?0:1;
|