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

68 lines
7.9 KiB
Python

from pathlib import Path
from datetime import datetime,timezone
import json,hashlib,zipfile,csv,collections,shutil
R=Path(__file__).resolve().parent.parent;P=R/'storage/app/private/investmoz-v2/portal-audit';O=R/'outputs/investmoz-internal-designation-20261011'
load=lambda p:json.loads(p.read_text(encoding='utf-8-sig'));sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
m=load(O/'manifesto.json');plan=load(P/'internal-designation-plan.json');S=Path(plan['source']);backup=load(P/'internal-designation-target-backup.json');rehearsal=load(O/'ensaio-disponibilizacao-reversao.json')
assert backup['result']==rehearsal['result']=='passed' and backup['candidate_revision']==m['delta_sha256']==rehearsal['delta']
assert backup['candidate_zip_sha256']==m['package_sha256']==sha(O/m['package'])
assert backup['candidate_manifest_sha256']==sha(O/'manifesto.json') and datetime.fromisoformat(backup['valid_until'])>datetime.now(timezone.utc)
assert len(backup['tables'])==295 and len(backup['sequences'])==31 and backup['active_originals_backed_up']==4 and backup['MFA_envelopes_readable']==2
assert backup['missing_files']==[] and rehearsal['base_restored']
for x in m['files']:
assert sha(S/x['path'])==x['after_sha256'] and (sha(R/x['path']) if (R/x['path']).is_file() else None)==x['before_sha256']
with zipfile.ZipFile(O/m['package']) as z:
expected={x['path']:x['after_sha256'] for x in m['files']}|{x['path']:x['sha256'] for x in m['assets']}
assert set(z.namelist())==set(expected) and len(z.namelist())==len(expected)
for name,value in expected.items():assert hashlib.sha256(z.read(name)).hexdigest()==value and not any(x in name.split('/') for x in ['.env','storage','vendor','node_modules'])
csv.field_size_limit(10_000_000);baseline=load(P/'internal-designation-matrix-baseline.json')
with (R/'docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv').open(encoding='utf-8-sig',newline='') as f:rows=list(csv.reader(f))
headers=rows[0];changes=[]
assert len(rows)==len(baseline['rows'])
for i,(a,b) in enumerate(zip(baseline['rows'],rows)):
assert len(a)==len(b)
for c,(before,after) in enumerate(zip(a,b)):
if before!=after:
assert a[0] in baseline['updates'] and headers[c]=='Evidência funcional' and after==baseline['updates'][a[0]][headers[c]]
changes.append(a[0])
assert len(changes)==3
proof={'result':'passed','revision':m['delta_sha256'][:12],'code_revision':m['delta_sha256'],'package_sha256':m['package_sha256'],
'manifest_sha256':sha(O/'manifesto.json'),'files':len(m['files']),'assets':len(m['assets']),
'matrix_requirements':len(rows)-1,'source_occurrences':sum(int(r[headers.index('Quantidade de linhas de origem')]) for r in rows[1:]),
'states':dict(collections.Counter(r[headers.index('Estado')] for r in rows[1:])),'matrix_changes':changes,'other_cells_and_closed_AB_C37_preserved':True,
'backup':{'verified_at':backup['verified_at'],'valid_until':backup['valid_until'],'tables':295,'sequences':31,'files':backup['file_count'],'originals':4,'MFA_readable':2,'missing_files':0,'restored_target':backup['restored_target'],'dump_sha256':backup['dump_sha256'],'files_sha256':backup['files_sha256']},
'QA_code_deployment_and_reversal_passed':True,'active_application_changes':0,'active_database_writes':0,
'real_contact_and_MFA': 'pending_own_registration_contact_and_MFA','internal_designation_requires_external_promoter_documents':False,
'workflow_active':False,'COUNTRY02_active':True,'new_revision_release_authorized':False}
(O/'verificacao-final.json').write_text(json.dumps(proof,indent=2,ensure_ascii=False),encoding='utf-8')
shutil.copyfile(P/'internal-designation-matrix-proof.json',O/'matriz-atualizacao.json')
for src,dest in [('internal-designation-active-readonly.json','destino-leitura.json'),('internal-designation-real-accounts-readonly.json','contas-reais-leitura.json')]:shutil.copyfile(P/src,O/dest)
(O/'backup-restauro.json').write_text(json.dumps(proof['backup'],indent=2,ensure_ascii=False),encoding='utf-8')
doc=O/'procedimento-e-evidencias.md';text=doc.read_text(encoding='utf-8')
text+=f"""
## Backup e reversão correspondentes à revisão fixa
Backup READ ONLY do destino investemoz:5432 concluído em {backup['verified_at']}, válido até {backup['valid_until']} ou alteração pertinente que exija renovação antes da intervenção. Correspondência conferida com revisão {m['delta_sha256']}, manifesto {sha(O/'manifesto.json')} e ZIP {m['package_sha256']}.
Restaurado exclusivamente no clone {backup['restored_target']['database']}:55441: 295 tabelas, 31 sequências, {backup['file_count']} ficheiros, quatro originais, dois conjuntos MFA legíveis e zero ficheiros em falta. A diferença de 288 para 295 tabelas decorre das sete tabelas instaladas legitimamente por 030000/040000; não houve nova migração nesta preparação. O âmbito de ficheiros inclui resources e o bundle, além do código, configuração privada e documentos necessários à reversão.
Ensaio da sequência na árvore QA: base → ZIP com administração OFF → repetição sem ações de dados → administração ON/operações OFF → contenção OFF → código e manifesto base repostos com hashes conferidos. O destino não foi restaurado. COUNTRY02, sessão HTTPS, chaves/histórico e serviços ativos preservados. Não aplicar migrations ou repetir bootstrap para disponibilizar esta administração.
A leitura do destino em 19:45 UTC mantém os três emails designados ainda ausentes, zero organizações/memberships/grants e registo independente do workflow. Não criar contas ou enviar mensagens nesta preparação. A confirmação de contacto/MFA e a atribuição dos papéis reais permanecem ações próprias dos titulares e da administração autenticada.
"""
doc.write_text(text,encoding='utf-8')
marker='<!-- internal-tempo-final-20261011 -->';execdoc=R/'docs/investmoz/v2/auditoria-portal/relatorio-executivo.md';old=execdoc.read_text(encoding='utf-8');assert marker not in old
execdoc.write_text(f"""{marker}
## Funções internas da Tempo: correção demonstrada em QA
A Tempo Lda define as funções da equipa. Corrigido o bloqueio que aplicava o pedido de Promotor externo a Aurora, designada para a operação do portal, sem poderes de gestão empresarial. Delta {m['delta_sha256'][:12]} preparado em QA, preservando 41a5ce778932 e a referência intermédia 1ab007740c44. Grants internos autenticados/auditados/idempotentes para os três papéis; promotor real e operador separados; autoria, permissões e MFA conservados. Só as seis categorias aprovadas aparecem na atribuição interna.
Chromium demonstrou atribuir → atualizar → nova sessão/MFA → recuperar e Promotor dashboard → criar com promotor real distinto → guardar → recuperar. 12 provas HTTP/persistência complementam a interface. O 422 de contrato desligado foi identificado na configuração do ensaio e resolvido abrindo conjuntamente os três gates necessários apenas em QA; todas as flags operacionais QA foram repostas OFF.
Backup/restauro do destino renovado para esta revisão: 295 tabelas, 31 sequências, {backup['file_count']} ficheiros, quatro originais e dois conjuntos MFA legíveis; zero perdas, restauro apenas no clone 55441. Troca/repetição/contenção/reversão do pacote ensaiadas em árvore QA. Ativo sem alterações, COUNTRY02 ON e workflow OFF. Contas/contacto/MFA e grants reais, disponibilização desta revisão e abertura do workflow continuam separados. Exigências de documentos/representação nos blocos históricos referem-se aos pedidos externos e não às designações internas confirmadas.
Procedimento, âmbito, hashes, provas e limites: outputs/investmoz-internal-designation-20261011/procedimento-e-evidencias.md e manifesto.json. Matriz existente: apenas três células de evidência AUD-PROFILE-01/03/31 atualizadas; {len(rows)-1} requisitos e 7739 ocorrências de origem preservados, sem reabrir A/B/C37.
"""+old,encoding='utf-8')
print(json.dumps(proof,ensure_ascii=False))