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

119 lines
10 KiB
Python

from pathlib import Path
from datetime import datetime, timezone
from collections import Counter
import hashlib, json, csv, zipfile
R=Path(__file__).resolve().parent.parent;P=R/'storage/app/private/investmoz-v2/portal-audit'
O=R/'outputs/investmoz-workflow-execution-preparation-20261011'
C=R/'outputs/investmoz-workflow-consolidated-20261011'
load=lambda p:json.loads(p.read_text(encoding='utf-8-sig'))
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
candidate=load(P/'consolidated-candidate-latest.json');manifest=load(C/'manifesto.json')
old_final=load(C/'verificacao-final.json')
for name,expected in old_final['delivery_files'].items():assert sha(C/name)==expected,name
assert sha(C/'manifesto.json')==candidate['manifest_sha256']
assert sha(C/candidate['zip'])==candidate['zip_sha256']
with zipfile.ZipFile(C/candidate['zip']) as z:
assert len(z.namelist())==181
for name,expected in manifest['payload'].items():assert hashlib.sha256(z.read(name)).hexdigest()==expected,name
for name,expected in manifest['base_hashes'].items():
path=R/name;assert (sha(path) if path.is_file() else None)==expected,name
for name,expected in manifest['unchanged_dependencies'].items():assert sha(R/name)==expected,name
prior=load(P/'consolidated-preservation-before.json')
# The earlier preservation baseline retains fixed98/delta117/service plans and monitored active runtime files.
preservation={}
for category,folder in [('fixed_candidate','investmoz-workflow-upgrade-20261011'),('delta_reference','investmoz-service-followup-20261011'),('service_plan','investmoz-workflow-service-plan-20261011')]:
entries=prior[category]
for name,expected in entries.items():assert sha(R/'outputs'/folder/name)==expected,name
preservation[category]={'files':len(entries),'changed':0}
active=load(P/'service-followup-preservation-before.json')['active_hashes']
for name,expected in active.items():assert sha(R/name)==expected,name
preservation['active_monitored_files']={'files':len(active),'changed':0}
backup=load(P/'workflow-execution-target-backup.json');compat=load(P/'workflow-execution-preflight-qa.json')
assert backup['result']==compat['result']=='passed'
assert backup['runtime']['organization_country_enabled']
for flag in ['opportunity_dashboard_access_enabled','profile_access_enabled','opportunity_specialized_drafts_enabled','opportunity_modular_profiles_qa_enabled','legacy_content_qa_enabled','investor_path_enabled']:
assert not backup['runtime'][flag],flag
group=Counter(name.split('/')[0] for name in backup['active_files_manifest'])
for filename in ['relatorio-preparacao.md','procedimento-unico.md']:
path=O/filename;text=path.read_text(encoding='utf-8')
replacements={
',867':', 867', '**:faltam':'**: faltam', 'PHP,XML':'PHP, XML',
'upload20M':'upload 20M', 'POST24M':'POST 24M', 'memória256M':'memória 256M',
'execução180s':'execução 180 s', 'permanece128M':'permanece 128M', 'lease210s':'lease 210 s',
'Preservar050000':'Preservar 050000', 'confirmou030000':'confirmou 030000',
'PHP/scanner,4':'PHP/scanner, 4', 'assinaturas,2':'assinaturas, 2',
'COUNTRY02 ON,050000':'COUNTRY02 ON, 050000', 'do plano:`':'do plano: `',
'Prévia409':'Prévia 409', 'de050000':'de 050000',
'ClamD),RunSimulation':'ClamD), RunSimulation', 'local),CompareMneBusiness':'local), CompareMneBusiness',
'OFF;030000':'OFF; 030000', 'duplicações,7':'duplicações, 7',
'PORTAL_PROFILE_ACCESS_ENABLED,PORTAL':'PORTAL_PROFILE_ACCESS_ENABLED, PORTAL',
'Scanner 3310 atualizado, tarefas/ACLs':'A atualização do scanner 3310, tarefas/ACLs',
'Composição: '+str(dict(group))+'.':
f'Composição: {group["private"]} originais privados + {group["runtime"]} ficheiros de runtime/configuração/bundle ativo + {group["active-code"]} de código ativo + {group["tls"]} TLS + {group["apache"]} Apache + {group["services"]} de serviços.'
}
for before,after in replacements.items():text=text.replace(before,after)
path.write_text(text,encoding='utf-8')
tag='<!-- workflow-execution-preparation-20261011 -->'
block=f'''{tag}
## Preparação técnica e habilitação operacional separadas
[Relatório da preparação](../../../../outputs/investmoz-workflow-execution-preparation-20261011/relatorio-preparacao.md), [proposta única de execução](../../../../outputs/investmoz-workflow-execution-preparation-20261011/procedimento-unico.md), [backup do destino](../../../../outputs/investmoz-workflow-execution-preparation-20261011/backup-destino-verificado.json) e [designações operacionais](../../../../outputs/investmoz-workflow-execution-preparation-20261011/designacoes-operacionais.md).
Candidata **41caea0f900f**, código **117d2d889701**, ZIP/manifesto imutáveis; 98a8e0b70892 e o plano histórico B1/B2 preservados. O responsável confirmou que ID 2/nala@tempo.co.mz e ID 13/onala@me.com são **duas pessoas diferentes**. Identificação e independência dos titulares resolvidas; nenhum bootstrap/grant ativo executado.
Backup/restauro do **destino real investemoz:5432** renovado **{backup['verified_at']}**, restaurado somente no novo clone55441: **288 tabelas/31 sequências/867 ficheiros**,4 originais/0 ausentes/2 MFA legíveis, linhas/seq/bytes iguais. Mais68 ficheiros de serviços face aos799, com scanner/config/assinaturas/PHP/XML e metadados de986 ACLs. DACLs/tarefas recuperadas como ficheiros/metadados, não aplicadas no Windows ativo. Compatibilidade e reversão vazia030000→040000 passaram no novo clone:288→291→295 tabelas,5 colunas; todas as linhas/colunas/seq e050000 preservadas. Não é instalação autenticada no destino.
**Uma autorização técnica proposta:**código/bundle+B1/B2+030000→040000 autenticadas+bootstrap cruzado dos dois titulares. Manter COUNTRY02 ON e os fluxos operacionais OFF. Readiness preparado apenas em armazenamento privado de recuperação, ainda não disponibilizado aos instaladores do destino. Fila ativa lida sem consumo/notificações externas; nova leitura imediatamente antes do worker, parar perante NotifyInterestReceived ou classe desconhecida. Intervenção no destino continua por autorizar.
**Habilitação operacional separada:**faltam designações de Promotores,Validadores e Publicadores, com conta existente,organização/representação legítima,tipos e âmbito. Isso condiciona a abertura dos fluxos, não a preparação/instalação técnica. As provas de LocalService,bootstrap,coedição,categorias,A/B e C37 válidas foram reaproveitadas. Nenhuma nova prova de dispositivos reais/acessibilidade integral foi declarada. Restauração/partilha individual,190000,legado,ofertas/modulares/SLA permanecem fora; restauro completo do destino exige decisão própria.
Mesma matriz:3401 requisitos/7739 ocorrências; três células de evidência atualizadas, nenhum ID/fonte/estado geral alterado. Os blocos seguintes preservam o histórico; as pendências de backup e de independência neles referidas ficam reconciliadas por este bloco atual.
<!-- /workflow-execution-preparation-20261011 -->
'''
for a,b in {'clone55441':'clone 55441', '**,4':'**, 4','Mais68':'Mais 68','aos799':'aos 799',
'de986':'de 986','vazia030000':'vazia 030000', ':288':': 288', ',5':', 5','e050000':'e 050000',
'**:código':'**: código','**:faltam':'**: faltam', 'Promotores,Validadores':'Promotores, Validadores',
'existente,organização':'existente, organização', 'legítima,tipos':'legítima, tipos',
'LocalService,bootstrap,coedição,categorias,A/B':'LocalService, bootstrap, coedição, categorias, A/B',
'individual,190000,legado,ofertas':'individual, 190000, legado, ofertas', 'matriz:3401':'matriz: 3401',
'ficheiros**,4':'ficheiros**, 4'}.items():block=block.replace(a,b)
for name in ['backlog.md','relatorio-executivo.md','plano-ativacao.md','workflow-criterios-saida-20261011.md']:
path=R/'docs/investmoz/v2/auditoria-portal'/name;text=path.read_text(encoding='utf-8')
assert tag not in text
path.write_text(block+text,encoding='utf-8')
csv.field_size_limit(16777216)
old=load(P/'workflow-execution-matrix-baseline.json')['rows']
new=list(csv.reader((R/'docs/investmoz/v2/auditoria-portal/matriz-requisitos.csv').open(encoding='utf-8-sig',newline='')))
assert len(new)==len(old)==3402 and new[0]==old[0]
diff=[]
for a,b in zip(old,new):
assert len(a)==len(b)
for c,(x,y) in enumerate(zip(a,b)):
if x!=y:
assert new[0][c]=='Evidência funcional' and b[0] in ['AUD-OPS-01','AUD-PROFILE-15','AUD-PROFILE-31']
diff.append({'id':b[0],'column':new[0][c]})
assert len(diff)==3
attestation=load(O/'manifesto-preparacao.json')
attestation['files']={p.name:sha(p) for p in sorted(O.iterdir()) if p.is_file() and p.name not in ['manifesto-preparacao.json','verificacao-final.json']}
(O/'manifesto-preparacao.json').write_text(json.dumps(attestation,indent=2,ensure_ascii=False),encoding='utf-8')
latest=load(P/'workflow-execution-preparation-latest.json');latest['preparation_manifest_sha256']=sha(O/'manifesto-preparacao.json')
(P/'workflow-execution-preparation-latest.json').write_text(json.dumps(latest,indent=2),encoding='utf-8')
result={'result':'passed','verified_at':datetime.now(timezone.utc).isoformat(),'candidate_revision':candidate['revision'],
'candidate_code_and_bundle_unchanged':True,'fixed_candidate_deliverables_preserved':len(old_final['delivery_files']),
'earlier_references_preserved':preservation,'destination_backup_physically_restored_only_in_QA':True,
'destination_tables':288,'destination_sequences':31,'restored_files':867,'originals':4,'missing':0,'MFA_readable':2,
'new_snapshot_compatibility_and_empty_transactional_reversal_passed':True,
'independence_confirmed_by_owner_for_existing_accounts':[2,13],
'matrix':{'requirements':3401,'source_occurrences':sum(int(dict(zip(new[0],r))['Quantidade de linhas de origem']) for r in new[1:]),
'changes':diff,'new_ids':0,'states_sources_versions_preserved':True},
'single_technical_authorization_proposed_not_granted':True,'operational_designations_separate':True,
'COUNTRY02_enabled':True,'other_flows_disabled':True,'active_database_services_acl_or_flag_writes':0,
'preparation_manifest_sha256':sha(O/'manifesto-preparacao.json')}
(O/'verificacao-final.json').write_text(json.dumps(result,indent=2,ensure_ascii=False),encoding='utf-8')
print(json.dumps({k:result[k] for k in ['result','candidate_revision','destination_tables','destination_sequences','restored_files','independence_confirmed_by_owner_for_existing_accounts','active_database_services_acl_or_flag_writes']}))