Files
Investidor-V2/tools/portal-stability-build.py
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

49 lines
3.4 KiB
Python

"""Build production sources in QA, retaining the approved package and active bundle."""
from pathlib import Path
from datetime import datetime, timezone
import hashlib, json, os, subprocess
ROOT=Path(__file__).resolve().parent.parent
PRIVATE=ROOT/'storage/app/private/investmoz-v2/portal-audit'
read=lambda p:json.loads(Path(p).read_text(encoding='utf-8-sig'))
sha=lambda p:hashlib.sha256(Path(p).read_bytes()).hexdigest()
plan=read(PRIVATE/'portal-stability-latest.json');source=Path(plan['source'])
active={p.relative_to(ROOT/'public/build').as_posix():sha(p) for p in (ROOT/'public/build').rglob('*') if p.is_file()}
# Route generation bootstraps the same guarded QA configuration and local class map.
(source/'artisan').write_text('''#!/usr/bin/env php
<?php
if (($argv[1] ?? '') !== 'wayfinder:generate') throw new RuntimeException('Only isolated route generation is allowed');
define('LARAVEL_START', microtime(true));
$app = require __DIR__.'/tools/portal-stability-runtime.php';
$status = $app->handleCommand(new Symfony\\Component\\Console\\Input\\ArgvInput);
exit($status);
''',encoding='utf-8')
# The shared bootstrap must return the app when invoked through the guarded artisan.
runtime=source/'tools/portal-stability-runtime.php'
text=runtime.read_text(encoding='utf-8')
text=text.replace("if (PHP_SAPI !== 'cli') { return $app; }", "if (PHP_SAPI !== 'cli' || basename($_SERVER['SCRIPT_FILENAME'] ?? '') === 'artisan') { return $app; }")
runtime.write_text(text,encoding='utf-8')
inputs={p.relative_to(source).as_posix():sha(p) for folder in ['app','config','routes','resources','database/migrations','bootstrap']
for p in (source/folder).rglob('*') if p.is_file() and 'cache' not in p.relative_to(source).parts}
for name in ['composer.json','composer.lock','package.json','package-lock.json','tsconfig.json','vite.config.ts']:
inputs[name]=sha(source/name)
env=dict(os.environ);env.pop('INVESTMOZ_QA_BUILD',None)
env['PATH']='C:/laragon/bin/php/php-8.5.11-Win32-vs17-x64'+os.pathsep+env['PATH']
env['VITE_APP_NAME']='InvestMoz'
log=PRIVATE/'portal-stability-production-build.log'
with log.open('w',encoding='utf-8') as stream:
result=subprocess.run(['npm.cmd','run','build'],cwd=source,env=env,stdout=stream,stderr=subprocess.STDOUT,shell=True)
assert result.returncode==0,'Build failed; inspect the private build log'
assets={p.relative_to(source/'public/build').as_posix():sha(p) for p in (source/'public/build').rglob('*') if p.is_file()}
assert 'manifest.json' in assets
manifest=read(source/'public/build/manifest.json')
for item in manifest.values():
for name in ['file','css','assets']:
values=item.get(name,[]);values=[values] if isinstance(values,str) else values
assert all((source/'public/build'/p).is_file() for p in values)
assert active=={p.relative_to(ROOT/'public/build').as_posix():sha(p) for p in (ROOT/'public/build').rglob('*') if p.is_file()}
proof={'result':'passed','at':datetime.now(timezone.utc).isoformat(),'mode':'production','inputs':inputs,
'assets':assets,'build_manifest_sha256':assets['manifest.json'],'active_assets_preserved':True,
'active_writes':0,'source':str(source),'input_hash':hashlib.sha256(json.dumps(inputs,sort_keys=True,separators=(',',':')).encode()).hexdigest()}
(PRIVATE/'portal-stability-production.json').write_text(json.dumps(proof,indent=2),encoding='utf-8')
print(json.dumps({k:proof[k] for k in ['result','mode','build_manifest_sha256','input_hash','active_assets_preserved','active_writes']}))