Usar contrato QA existente e documentar operacao e reversao
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Disponibilização automática da homologação
|
||||
|
||||
Destino: `https://teste-investidor.tempo.co.mz`. Branch escolhida pela Tempo: `main`.
|
||||
|
||||
## Trabalho dos programadores
|
||||
|
||||
1. Trabalhar numa branch, fazer commits e enviar para o Git existente.
|
||||
2. Rever e integrar em `main` segundo as designações já aprovadas pela Tempo.
|
||||
3. O push em `main` activa o webhook existente. A fila serializa as execuções; a revisão mais recente é verificada e compilada numa pasta nova.
|
||||
4. Conferir o commit efectivamente servido em `/build/revision.json`. Um commit no Git só está instalado quando consta desse endereço. Em caso de falha, a revisão anterior continua servida; o operador consulta os relatórios e logs protegidos.
|
||||
|
||||
Não editar a pasta servida. Uma alteração manual detectada interrompe a substituição até ser reconciliada, preservando os ficheiros existentes.
|
||||
|
||||
## O que segue o Git
|
||||
|
||||
Fontes, menus, estilos e formulários são instalados com o build da mesma revisão. Não são transportados por Git a base de dados, ficheiros carregados, contas, permissões, segredos nem estados de publicação.
|
||||
|
||||
As migrations são comparadas com o contrato nominal protegido do destino. Uma alteração nesse contrato exige análise e autorização específicas; este executor não executa SQL de migração no destino. A criação do schema durante a verificação usa exclusivamente PostgreSQL/PostGIS descartável, em `127.0.0.1:55441`, com nome terminado em `_test` e identidade conferida pela guarda QA.
|
||||
|
||||
## Operação e recuperação
|
||||
|
||||
O receptor `/deploy` conserva a protecção própria do Nginx e a assinatura HMAC do Gitea. As páginas públicas não exigem a credencial ambiental; as operações privadas conservam login, permissões e MFA aplicável.
|
||||
|
||||
O executor instalado e a configuração em `/etc/investmoz-deploy` são controlados pelo operador. Alterar as suas fontes no repositório não substitui automaticamente esse controlo. A imagem de CI é fixada por digest; não modifica o PostgreSQL ou PostGIS da aplicação.
|
||||
|
||||
O operador consulta:
|
||||
|
||||
- `systemctl status teste-investidor-deploy.service teste-investidor-deploy.path`
|
||||
- `/var/lib/investmoz-deploy/reports/`: commit solicitado, resultado e revisão instalada.
|
||||
- `/var/lib/investmoz-deploy/logs/`: detalhe protegido da verificação e compilação.
|
||||
|
||||
Para reverter, suspender primeiro novas execuções com `systemctl stop teste-investidor-deploy.path` e aguardar a execução em curso. Executar, como o utilizador de disponibilização existente:
|
||||
|
||||
```sh
|
||||
python3 /var/www/deploy/investmoz-pipeline.py --config /etc/investmoz-deploy/delivery.json --rollback
|
||||
```
|
||||
|
||||
A reversão verifica os hashes, repõe a release anterior e reinicia graciosamente os workers. Conserva a base, uploads, configurações partilhadas e dados posteriores. Não é restauro integral de dados. Repor uma release incompatível com dados/schema posteriores exige análise específica. Retomar a path unit apenas depois da decisão de corrigir ou voltar a disponibilizar a revisão integrada.
|
||||
|
||||
O checkout físico anterior é preservado; não é feito `reset --hard` nele. Assets com nomes hash permanecem num conjunto partilhado para conservar páginas já abertas. Não há eliminação automática de releases ou dados.
|
||||
|
||||
## Limites
|
||||
|
||||
Uma verificação automática não publica conteúdos nem comprova todos os percursos de negócio. Mantêm-se as provas aceites e as pendências funcionais da matriz. Falhas do fluxo devem indicar o comando e o log concreto, sem exposição de segredos ao visitante.
|
||||
@@ -52,7 +52,8 @@ def archive_source(data: bytes, root: Path) -> None:
|
||||
path = (root/item.name).resolve()
|
||||
if not path.is_relative_to(root.resolve()) or not (item.isfile() or item.isdir()):
|
||||
raise RuntimeError('Unsafe source archive entry')
|
||||
if item.name == '.env' or item.name == 'public/hot' or item.name.startswith('storage/app/private/'):
|
||||
if item.name == '.env' or item.name == 'public/hot' or (item.isfile()
|
||||
and item.name.startswith('storage/app/private/') and item.name != 'storage/app/private/.gitignore'):
|
||||
raise RuntimeError('Private/runtime files must not be distributed by Git')
|
||||
# Debian's installed Python predates tarfile extraction filters. Validate
|
||||
# every entry first, then create only ordinary directories/files ourselves.
|
||||
@@ -171,7 +172,7 @@ class Delivery:
|
||||
# Only the newly created, fully identified CI container and its anonymous volumes.
|
||||
self.run(['docker','rm','--force','--volumes',cid], timeout=60)
|
||||
|
||||
def build(self, target: str, release: Path) -> None:
|
||||
def build(self, target: str, release: Path, attach_runtime: bool = True) -> None:
|
||||
data = self.run(['git','--git-dir',self.mirror,'archive','--format=tar',target], timeout=120,log_output=False)
|
||||
# Binary archives must never be appended to text build logs.
|
||||
archive_source(data, release)
|
||||
@@ -193,13 +194,15 @@ class Delivery:
|
||||
# The guard is verified against the actual PostgreSQL identity, not only environment strings.
|
||||
guard = "require 'vendor/autoload.php'; $app=require 'bootstrap/app.php'; $app->make(Illuminate\\Contracts\\Console\\Kernel::class)->bootstrap(); if (!App\\Support\\IsolatedQaDatabase::allowsSchemaChanges()) exit(91);"
|
||||
self.run([self.c['php'],'-r',guard],cwd=release,env=env)
|
||||
self.run([self.c['php'],'artisan','migrate','--force','--no-interaction'],cwd=release,env=env)
|
||||
# The project intentionally gates later schemas behind nominal authenticated
|
||||
# installation. Reuse its accepted isolated baseline lifecycle test instead
|
||||
# of bypassing those guards with a generic migrate of every module.
|
||||
self.run([self.c['php'],'vendor/bin/phpunit','--configuration=phpunit.investmoz.xml','--do-not-cache-result'],cwd=release,env=env)
|
||||
self.run(['npm','ci','--no-audit','--no-fund'],cwd=release,env=env)
|
||||
self.run(['npm','run','build'],cwd=release,env=env)
|
||||
self.run(['npm','run','types:check'],cwd=release,env=env)
|
||||
self.run([self.c['php'],'tools/portal-diagnostics-deployment-guard.php'],cwd=release,env=env)
|
||||
self.run([self.c['php'],'vendor/bin/phpunit','--configuration=phpunit.xml','--testsuite','Unit','--do-not-cache-result'],cwd=release,env=env)
|
||||
self.run([self.c['php'],'vendor/bin/phpunit','--configuration=phpunit.investmoz.xml','--do-not-cache-result'],cwd=release,env=env)
|
||||
tests=sorted((release/'tests/frontend').glob('*.test.mjs'))
|
||||
self.run(['node','--test',*tests],cwd=release,env=env)
|
||||
# Final production vendor from the same lock, before linking any real configuration.
|
||||
@@ -210,6 +213,8 @@ class Delivery:
|
||||
for entry in manifest.values():
|
||||
for path in [entry.get('file'),*entry.get('css',[]),*entry.get('assets',[])]:
|
||||
if path and not (release/'public/build'/path).is_file(): raise RuntimeError('Missing compiled asset')
|
||||
if not attach_runtime:
|
||||
return
|
||||
# Discard only this release's synthetic runtime, retaining it privately outside the served tree.
|
||||
(release/'storage').rename(self.state/'qa-runtime'/release.name)
|
||||
(release/'.env').unlink()
|
||||
|
||||
Reference in New Issue
Block a user