mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:53:05 +00:00
320 lines
27 KiB
Markdown
320 lines
27 KiB
Markdown
# Runbook: deploy do site 1313 no homeserver (OPS53 + OPS71 — dispatch manual)
|
||
|
||
O deploy de produção é uma **action manual** (`workflow_dispatch` no GitHub
|
||
Actions, `.github/workflows/deploy.yml`): mergir em `main` **não** publica
|
||
nada. O operador dispara o deploy quando decide publicar; antes de tocar o
|
||
homeserver, o job `verify` (hosted) roda a suíte **full** (incl. e2e full).
|
||
|
||
## Gatilho e fluxo
|
||
|
||
1. GitHub → Actions → **Deploy (manual)** → Run workflow (ref: `main`).
|
||
2. Job `verify` (hosted `ubuntu-latest`, ~50 min): suíte full sem skips —
|
||
check-test-locations → lint → format → typecheck → knip → cycles → unit →
|
||
int (migrate+seed nos services) → build → e2e full.
|
||
3. Se `verify` verde, o job `deploy` (`needs: [verify]`,
|
||
`runs-on: [self-hosted, homeserver]`) roda no **runner do GitHub instalado
|
||
no homeserver** e executa `scripts/deploy-homeserver.sh <sha>` **localmente**
|
||
(sem SSH; o runner conecta outbound ao GitHub — funciona atrás do
|
||
Cloudflare tunnel; o hosted nunca toca o homeserver; o self-hosted não
|
||
conta minutos hosted).
|
||
4. O script, no **homeserver**: guarda de HEAD (`git ls-remote` de
|
||
`TEQO_REPO_URL` — default `https://github.com/fsolla/teqo.git`, público —
|
||
== SHA do job, senão "stale run" skip) → `flock` (serializa) → guard
|
||
"already deployed" (revision do container rodando) → **build do migrator**
|
||
(o estágio migrator não roda `next build` — builda mesmo contra o schema
|
||
antigo; BuildKit, secrets do `~/stack/teqo-1313.env`) → push do migrator em
|
||
`localhost:5000` (tag **qualificada de registry** — INF13: a única ref que o
|
||
compose referencia) → swap dos tags de imagem no
|
||
`~/stack/docker-compose.yml` (backup antes; sed idempotente aceita as formas
|
||
antiga e qualificada e escreve sempre a qualificada) → **migrations**
|
||
(`docker compose --profile maintenance run --rm teqo-1313-migrate </dev/null`,
|
||
já com a imagem do SHA novo) → **build do runner** (contra o banco JÁ
|
||
migrado — o `next build` da geração estática lê o schema novo, OPS66;
|
||
`--network host` com proxy socat `teqo-1313-build-proxy` na `stack_default`
|
||
para alcançar o `postgres`; o proxy é criado idempotentemente pelo script)
|
||
→ push do runner (tag qualificada de registry) → `docker compose up -d teqo-1313` → healthcheck →
|
||
smoke (`/`, `/campanha/login`, `/admin`, barreira 307, WebAuthn
|
||
login-options, `api/revalidate` com o secret real).
|
||
5. Falha = job vermelho; nada é publicado pela metade (rollback automático
|
||
após o swap).
|
||
|
||
**Primeiro deploy (verificação ao vivo):** depois do cutover OPS71, dispare o
|
||
deploy manual e confira no log do job `deploy` o `set-url` do workspace
|
||
(origem Forgejo → GitHub), o `deployed_sha` lido do container e o guard
|
||
"already deployed" num segundo dispatch do mesmo SHA.
|
||
|
||
## Onde roda cada coisa
|
||
|
||
| Máquina | Papel |
|
||
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| **GitHub hosted** (ubuntu) | CI de PR (`ci-pr.yml`, job `checks`) e job `verify` do `deploy.yml` (suíte full). Nunca toca o homeserver. |
|
||
| **homeserver** (8c/16GB) | todo o stack (`~/stack/docker-compose.yml`): postgres `teqo_1313`, forgejo, registry `localhost:5000`, cloudflared, `teqo-1313` na 127.0.0.1:1313; **runner self-hosted do GitHub** (labels `self-hosted`, `homeserver`; instalado 2026-08-19 — Issue #113 OPS71-INFRA; `~/actions-runner` v2.336.0, systemd user `actions.runner.fsolla-teqo.teqo-1313-runner.service` — `systemctl --user status/restart`); workspace `~/teqo-deploy` (clone de `github.com/fsolla/teqo`, público) |
|
||
| **workstation** | dev/agentes apenas — o runner do Forgejo foi **desligado** no cutover (o schedule do `ci.yml` antigo não tem mais razão; religar é reversível) |
|
||
|
||
Segredos **não** ficam no GitHub: o script sourceia `~/stack/teqo-1313.env` e
|
||
`~/stack/.env` (chmod 600) no próprio homeserver. O repo GitHub é público — o
|
||
clone do workspace não precisa de credencial. No GitHub ficam apenas os
|
||
secrets de integração: `FORGEJO_API_TOKEN` (flips pós-merge) e
|
||
`CURSOR_API_KEY` (archive helper, dormente).
|
||
|
||
## Cutover OPS71 (passos manuais, ordem)
|
||
|
||
1. Remotes locais (config compartilhada pelos worktrees):
|
||
`git remote set-url origin git@github.com:fsolla/teqo.git` e
|
||
`git remote add forgejo ssh://git@192.168.15.142:2222/fsolla/teqo.git`
|
||
(ou `https://git.solla.dev/fsolla/teqo.git`) — o repo Forgejo congela em
|
||
main; o tracker (Issues) segue vivo por API.
|
||
2. GitHub repo: secrets `FORGEJO_API_TOKEN` + `CURSOR_API_KEY`
|
||
(Settings → Secrets and variables → Actions) e **`Allow auto-merge`**
|
||
(Settings → General → Pull Requests; ou `PATCH /repos/...` com
|
||
`allow_auto_merge: true`). Repo é público: em Settings → Actions →
|
||
General, defina fork PR workflows como não executar (ou exigir aprovação).
|
||
3. PAT local: criar token GitHub com escopo `repo` e exportar `GITHUB_TOKEN`
|
||
(para `node scripts/github-pr.mjs` e `pnpm configure:branch-protection`).
|
||
4. `GITHUB_TOKEN=… pnpm configure:branch-protection` — required check
|
||
`CI (PR) / checks` (literal de match: `checks` — o GitHub casa pelo nome do
|
||
check-run; a UI exibe workflow/job; pin PR #742), 0 reviews,
|
||
`enforce_admins: true`. **Antes do primeiro push** (senão o PR mergearia
|
||
sem required check).
|
||
5. **Desligar o runner do Forgejo** na workstation (ex.:
|
||
`systemctl --user stop forgejo-runner`) — após o primeiro PR GitHub
|
||
validado; para o schedule do `ci.yml` antigo.
|
||
6. Instalar o runner self-hosted do GitHub no homeserver (labels
|
||
`self-hosted` + `homeserver`; usuário com docker + acesso a `~/stack`;
|
||
registrar em infra-solla: `STATE.md`).
|
||
7. Push do PR do OPS71 → CI verde → auto-merge → flip da Issue no Forgejo →
|
||
deploy manual de validação.
|
||
|
||
Rollback do cutover (se o GitHub falhar cedo): religar o runner do Forgejo e
|
||
abrir PRs no Forgejo a partir do main congelado — os workflows de
|
||
`.forgejo/workflows/` foram removidos na OPS71 Fase 2, mas restauram do git
|
||
history (`git revert` do PR da remoção, ou clone do main congelado do Forgejo)
|
||
— remotes: `git remote set-url origin ssh://git@192.168.15.142:2222/fsolla/teqo.git`.
|
||
|
||
## Rollback (do deploy)
|
||
|
||
A imagem anterior **não** fica mais local após a limpeza pós-deploy (INF3/F2:
|
||
`docker builder prune` + remoção das tags locais antigas, exceto o SHA em uso)
|
||
— o registry `localhost:5000` **nunca deleta** e é a fonte do rollback. O
|
||
compose anterior fica em `~/stack/docker-compose.yml.pre-<sha-do-deploy-que-falhou>`.
|
||
|
||
```bash
|
||
ssh homeserver
|
||
cd ~/stack
|
||
# 1. apontar o compose de volta para o SHA anterior (o que estava rodando):
|
||
# o backup do deploy que falhou é o compose com os tags antigos
|
||
ls docker-compose.yml.pre-* # escolha o anterior ao deploy ruim
|
||
cp docker-compose.yml.pre-<sha-anterior> docker-compose.yml
|
||
# 2. puxar do registry (a imagem antiga já não fica local após a limpeza;
|
||
# o compose referencia a tag qualificada — INF13 — e o pull basta):
|
||
docker pull localhost:5000/teqo-1313:<sha-anterior>
|
||
# Cuidado (janela de transição pós-INF13): backups gerados ANTES do fix
|
||
# usam tags bare (`image: teqo-1313:<sha>`); se o backup escolhido tiver
|
||
# tag sem prefixo, recrie o alias antes de subir:
|
||
# docker tag localhost:5000/teqo-1313:<sha-anterior> teqo-1313:<sha-anterior>
|
||
# 3. subir:
|
||
docker compose up -d teqo-1313
|
||
```
|
||
|
||
**Caveat:** uma migration de schema aplicada no passo de migrate **não** é
|
||
desfeita pelo rollback (Payload é append-only). Código velho sobre schema novo
|
||
pode se comportar mal — o caminho primário minimiza essa janela (migrate só
|
||
roda após migrator build+push+swap ok; rollout só após runner build ok). Desde
|
||
o OPS66 o migrate roda **antes** do build do runner (o build precisa do schema
|
||
novo): um build do runner que falhe depois do migrate deixa o banco à frente
|
||
do código — migrations são append-only e revisadas antes do deploy (checklist);
|
||
a correção se re-mergeia e o próximo deploy completo publica.
|
||
|
||
## Falhas conhecidas
|
||
|
||
| Sintoma | Causa | Tratamento |
|
||
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Job verde sem deploy ("stale run") | Push mais novo venceu; o dispatch apontava um SHA que não é mais o HEAD do main do GitHub | Nada a fazer — dispatch novo no main atual |
|
||
| Job verde sem deploy ("already deployed") | O container rodando já tem a revision do SHA do job (ex.: `workflow_dispatch` duplicado da mesma HEAD) | Nada a fazer — o site já roda esse SHA. Forçar rebuild da MESMA HEAD não é suportado via dispatch: edite o tag da imagem no compose (`image: localhost:5000/teqo-1313:<sha>-rebuild`) e `docker compose up -d`, ou espere um commit novo |
|
||
| Job `deploy` nunca roda / runner offline | Runner self-hosted do homeserver não instalado ou parado (passo manual do cutover OPS71) | Instalar/religar o runner (labels `self-hosted`, `homeserver`); os PRs e o `verify` não dependem dele — só o deploy |
|
||
| Deploy dispara mas o job `verify` falha | Regressão real na suíte full | Corrigir e re-dispatchar — o homeserver nunca é tocado |
|
||
| `No such image` ao recriar o container fora de deploy (ex.: `compose up -d` pós-reboot/migração) | O compose referenciava uma tag local bare (`teqo-1313:<sha>`) que desapareceu sem recriação (achado INF8/INF13, 24/08: só restava a ref do registry) | Corrigido no INF13: o compose passa a referenciar a tag **qualificada** (`localhost:5000/teqo-1313:<sha>`) a partir do primeiro deploy pós-fix — até lá, produção ainda roda o compose bare (recrie o alias via `docker tag` se necessário). Recovery geral: `docker pull localhost:5000/teqo-1313:<sha>` (+ `docker tag` se o compose em uso tiver tag bare) e `docker compose up -d` |
|
||
| Deploy para logo após o migrate ("Done." e nada mais, EXIT=0) | `docker compose run` anexa stdin por padrão — o container consome o resto do script; o bash chega a EOF e termina sem rodar o rollout | O script já usa `< /dev/null` no `run --rm` do migrate (não remover); sintoma visto 2026-08-17 no primeiro deploy |
|
||
| Build falha: `network mode "stack_default" not supported by buildkit` | BuildKit (drivers docker e docker-container) recusa rede bridge custom no `--network` | O script builda com `--network host` + proxy socat `teqo-1313-build-proxy` (na `stack_default`, publicado em 127.0.0.1:5433, criado idempotentemente com `restart: unless-stopped`) + `DATABASE_URL` reescrita para o loopback |
|
||
| Build OOM no homeserver | Laptop 8c/16GB com o stack ativo (~12GB livres medidos) | Re-dispatch; se recorrente, item futuro: build na workstation com túnel |
|
||
| Build do runner falha: `relation "..." does not exist` no `next build` | Migration nova criou tabela lida em geração estática. Pré-OPS66 a ordem era build→migrate e o deploy morria aqui para sempre (incidente 2026-08-18, S2) | Pós-OPS66 não deve ocorrer: migrate roda antes do build do runner. Se reaparecer, confira no log se o passo migrate rodou; recovery manual: `docker build --target migrator` + `docker run --rm --network stack_default --env-file ~/stack/teqo-1313.env localhost:5000/teqo-1313-migrator:<sha>` e re-dispatch do workflow |
|
||
| Workspace clone sem o SHA novo | O workspace antigo apontava para o Forgejo local (pré-OPS71) e o fetch não achou o SHA | O script agora re-aponta `origin` para `TEQO_REPO_URL` (idempotente) antes do fetch — se ainda falhar, `git -C ~/teqo-deploy remote -v` e corrigir manualmente |
|
||
| Migrate falha | Drift/erro de schema | Job vermelho; site segue no container antigo; corrigir e re-mergear |
|
||
| Smoke falha pós-up | Regressão de runtime | Rollback automático (restore + `up -d`) + job vermelho; investigar |
|
||
|
||
## Backup passivo no Forgejo (OPS76-FOLLOWUP, 2026-08-21)
|
||
|
||
O GitHub é a única fonte (main, branches, PRs, CI, tracker). O Forgejo mantém
|
||
um **backup passivo** via **pull mirror nativo**: o repo `teqo-backup` no
|
||
Forgejo (`https://git.solla.dev/fsolla/teqo-backup`) espelha o GitHub
|
||
automaticamente — **não serve push, não recebe PRs, é somente leitura**.
|
||
|
||
- **Repo:** `fsolla/teqo-backup` no Forgejo (`mirror: true`), criado via
|
||
`POST /api/v1/repos/migrate` com `clone_addr=https://github.com/fsolla/teqo.git`,
|
||
`service=github`, `mirror=true`, intervalo **8h** (`mirror_interval`).
|
||
- **Automação:** o próprio Forgejo sincroniza no intervalo (sem workflow).
|
||
Force sync manual: `POST /api/v1/repos/fsolla/teqo-backup/mirror-sync`
|
||
(o "Synchronize now" da UI).
|
||
- **Cobertura:** espelha todas as branches (54/54 verificadas) e tags; o
|
||
histórico do mirror reflete o do GitHub (que é a convergência OPS75 +
|
||
OPS76). Não espelha issues/PRs/labels (GitHub é o tracker).
|
||
- **Validação:** `git ls-remote` do `teqo-backup` deve retornar o mesmo SHA
|
||
de `refs/heads/main` do GitHub; conferir em `mirror_updated` o último sync.
|
||
- **O repo `fsolla/teqo` original do Forgejo** fica **congelado** no ponto da
|
||
convergência (OPS75) — arquivo histórico, sem mais uso.
|
||
|
||
## Segurança (decisão deliberada)
|
||
|
||
- O job `deploy` usa `runs-on: [self-hosted, homeserver]` — executa como o
|
||
usuário do runner no homeserver. Está **apenas** no `deploy.yml`
|
||
(dispatch manual), nunca no `ci-pr.yml`; o hosted (`verify`) não tem acesso
|
||
ao homeserver e o self-hosted não recebe secrets de produção (as envs vivem
|
||
no próprio homeserver). `main` só anda por PR mergeado com CI verde; o
|
||
deploy só roda com `verify` full verde. Fork-PRs não rodam CI (same-repo
|
||
gate + setting do repo).
|
||
- Segredos nunca ecoados: sem `set -x`, senhas via `--password-stdin` /
|
||
build-secrets; envs só no homeserver.
|
||
|
||
## OPS79 — última migração da plataforma antiga → nova (vertical campanha)
|
||
|
||
Operação de dados executada em 2026-08-23. Ver assistência lógica completa:
|
||
`docs/plans/ops79-ultima-migracao-dados-campanha.md` (intenção) e
|
||
`…campanha-impl.md` (impl). **A fonte (Neon, plataforma antiga) está congelada
|
||
desde o OPS80** — zero escritas desde 2026-08-23 22:30 UTC; a URL antiga segue
|
||
viva na Vercel apenas tecnicamente (desligamento de infra é o OPS81).
|
||
|
||
**Descoberta-chave da reconciliação:** a vertical campanha JÁ estava 100% íntegra
|
||
no `teo_1313` após o OPS51 (dump Neon→homeserver) — nenhum dado de operação de
|
||
campanha ficou órfão; o time de campanha não escreveu na URL antiga depois do
|
||
OPS51. O único residual real entre as plataformas era a `signature` pública
|
||
1486 (e seu contato/subscription), cujo contato originário **não existia** no
|
||
target (o id 2221 do target é OUTRA pessoa, Jorge Solla).
|
||
|
||
### Reconciliador (read-only, repetível)
|
||
|
||
Compara contagens + junta de IDs das 13 coleções da vertical e o conteúdo
|
||
semântico das join-tables de relações contra a fonte Neon.
|
||
|
||
```bash
|
||
ssh homeserver
|
||
cd ~/teqo-deploy
|
||
git fetch origin main && git checkout main && git pull --ff-only origin main
|
||
pnpm install
|
||
set -a; source ~/stack/.env; set +a # fornece NEON_DATABASE_URL
|
||
set -a; source ~/stack/teqo-1313.env; set +a # fornece DATABASE_URL (teqo_1313)
|
||
# o host `postgres` só resolve dentro da rede stack_default; do host usa-se o
|
||
# proxy socat do build (127.0.0.1:5433):
|
||
export DATABASE_URL="${DATABASE_URL/@postgres:5432/@127.0.0.1:5433}"
|
||
NEON_DATABASE_URL="$NEON_DATABASE_URL" DATABASE_URL="$DATABASE_URL" pnpm ops79:reconcile
|
||
```
|
||
|
||
Esperado: `19/19 PASS` (a `municipality_rels` reporta `+3 absorvida pós-OPS51` —
|
||
edições de portfólio na plataforma nova, não-dado). Exit `1` em qualquer nova
|
||
divergência. Nunca imprime PII.
|
||
|
||
### Migração do residual (one-off, com guard de confirmação)
|
||
|
||
Copia a `signature` 1486 + seu contato + subscription (a transação que o
|
||
`submitPetitionSignature` faz junto), reusando petition/consent já presentes e
|
||
criando o contato com **id novo** (nunca o 2221, ocupado por outra pessoa).
|
||
|
||
```bash
|
||
# dry-run (valida referentes e imprime o plano sem escrever):
|
||
NEON_DATABASE_URL="$NEON_DATABASE_URL" DATABASE_URL="$DATABASE_URL" pnpm ops79:migrate --dry-run
|
||
# aplica (ESCREVE em prod — exige a flag de intenção explícita):
|
||
OPS79_MIGRATE_CONFIRM=1 NEON_DATABASE_URL="$NEON_DATABASE_URL" DATABASE_URL="$DATABASE_URL" pnpm ops79:migrate --apply
|
||
```
|
||
|
||
Resultado registrado em 2026-08-23: contact novo **2225** (Juares Lagimar de
|
||
Souza), signature **1486** preservada (petition `fim-escala-6x1`, consent 2),
|
||
subscription **1492**; sequences ajustadas (`setval` com `true` → próximo id =
|
||
último+1). Sequências conferidas após: contact 2225 / signature 1486 /
|
||
subscription 1492. Pós-verificação: `signature` 1485=1485 (Neon×target) e
|
||
reconcile 19/19; smoke `/`, `/campanha`, `/admin` 200.
|
||
|
||
### Rollback
|
||
|
||
A migração é idempotente (fail-closed: aborta se referentes/ids divergirem). Se
|
||
precisar desfazer, DELETar as 3 linhas + ajustar sequences de volta:
|
||
`DELETE FROM signature WHERE id=1486; DELETE FROM subscription WHERE id=1492;
|
||
DELETE FROM contact_phones WHERE _parent_id=2225; DELETE FROM contact WHERE id=2225;`
|
||
e `setval('contact_id_seq',2224)`, `setval('signature_id_seq',1485)`,
|
||
`setval('subscription_id_seq',1491)`.
|
||
|
||
## OPS84 — Verificação de valores (não só entidades) da migração
|
||
|
||
Extensão do reconciliador OPS79: compara TODAS as colunas de conteúdo (não só
|
||
ids/contagens) das 13 tabelas da vertical campanha entre a fonte congelada
|
||
(Neon) e a base nova (`teqo_1313`). Foco confirmado em `vote_pledge` (votos
|
||
declarados + 3 cenários de estimativa).
|
||
|
||
**Resultado registrado em 2026-08-24:** 13/13 tabelas PASS — **0 divergências
|
||
de conteúdo**. A migração de valores está 100% íntegra. Neon e target com
|
||
dados idênticos em todas as 13 tabelas da vertical.
|
||
|
||
### Verificador de valores (read-only, repetível)
|
||
|
||
```bash
|
||
ssh homeserver
|
||
cd ~/teqo-deploy && git fetch origin main && git checkout main && git pull --ff-only origin main
|
||
pnpm install
|
||
set -a; source ~/stack/.env; set +a # fornece NEON_DATABASE_URL
|
||
set -a; source ~/stack/teqo-1313.env; set +a # fornece DATABASE_URL (teqo_1313)
|
||
export DATABASE_URL="${DATABASE_URL/@postgres:5432/@127.0.0.1:5433}"
|
||
|
||
# Todas as 13 tabelas (foco em vote_pledge):
|
||
NEON_DATABASE_URL="$NEON_DATABASE_URL" DATABASE_URL="$DATABASE_URL" \
|
||
pnpm ops84:reconcile-values
|
||
|
||
# Tabela isolada (debug/iteração):
|
||
NEON_DATABASE_URL="$NEON_DATABASE_URL" DATABASE_URL="$DATABASE_URL" \
|
||
pnpm ops84:reconcile-values --table vote_pledge
|
||
|
||
# Com relatório markdown (anexável ao issue):
|
||
NEON_DATABASE_URL="$NEON_DATABASE_URL" DATABASE_URL="$DATABASE_URL" \
|
||
pnpm ops84:reconcile-values --report /tmp/ops84-report.md
|
||
```
|
||
|
||
**Esperado:** `13/13 PASS` com 0 content diffs. Exit `1` se houver divergência
|
||
de conteúdo. Colunas classified em 3 buckets:
|
||
|
||
- **content** — falha o run (aceite: "0 divergência" ou "N listadas")
|
||
- **derived** — hook-derivadas (`declared_at/by`, `estimated_at/by`,
|
||
`created_at`, `updated_at`); informativas, não falham
|
||
- **sensitive** — PII; valores nunca impressos, só contados
|
||
|
||
Colunas jsonb comparadas via `col::text` (preserva `1` vs `1.0` como
|
||
divergência; SQL NULL ≠ JSON null). Zero normalização (trim/round/coalesce).
|
||
|
||
### Fallback: Neon inacessível (OPS81 já executado)
|
||
|
||
Se o Neon estiver inacessível (OPS81 desligou a infra), usar o dump do OPS51
|
||
como base de comparação:
|
||
|
||
```bash
|
||
# Restaurar dump em scratch DB local:
|
||
createdb teqo_1313_compare_src
|
||
pg_restore -d teqo_1313_compare_src \
|
||
/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump
|
||
|
||
# Apontar NEON_DATABASE_URL para o scratch:
|
||
NEON_DATABASE_URL="postgresql://teqo:teqo@localhost:5432/teqo_1313_compare_src" \
|
||
DATABASE_URL="$DATABASE_URL" \
|
||
pnpm ops84:reconcile-values --report /tmp/ops84-report-dump.md
|
||
```
|
||
|
||
**Nota:** dump é pré-freeze (2026-08-17) — deltas legítimos entre 08-17 e
|
||
08-23 (quando o Neon foi congelado) aparecem como divergências derivadas. O
|
||
relatório declara qual base foi usada.
|
||
|
||
### Rollback
|
||
|
||
Não há escrita; "rollback" = rodar de novo (idempotente, read-only).
|
||
|
||
## Referências
|
||
|
||
- `scripts/deploy-homeserver.sh` — o script (fonte da verdade do fluxo)
|
||
- `.github/workflows/deploy.yml` — verify → deploy
|
||
- `docs/plans/ops53-ci-deploy-homeserver*.md` — intenção e decisões (era Forgejo)
|
||
- `docs/plans/ops71-ci-github-actions-tracker-forgejo*.md` — o cutover
|
||
- infra-solla: `STATE.md`, `plano-infra-final.md` §"Arquitetura de deploy"
|