Deploy (pipeline GitHub → Coolify)¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-17
O que é¶
Como o bi-transporte-xls chega em produção (excel.vantroba.xadm.biz). Todo o
CI/CD roda no GitHub Actions, num workflow único —
.github/workflows/pipeline.yml (norma da casa:
ADR central 0027). O
Forgejo (fonte.xadm.biz) é só o git de origem e o registry de imagens — não
roda CI.
A imagem é buildada fora do host, no runner do GitHub: o binário native
(GraalVM, do Dockerfile.native —
ADR central 0033;
adoção na decisão 0034). O
Dockerfile JVM fica no repo como fallback, fora do build.targets. O deploy é
disparado pelo control-plane do central
(ADR central 0026):
o pipeline faz POST https://central-backend.xadm.biz/api/ci/deploy e o central
resolve qual recurso do Coolify atualizar. O Coolify só puxa a imagem pronta
e troca o container com rolling update guiado pelo HEALTHCHECK — não builda nem
reage a push (auto-deploy desligado).
| Job | O que faz |
|---|---|
decide |
Liga as flags tests / docs / native conforme o evento (ver Quando usar), lê o bloco smoke e a versão do Java (toolchain.java) do docs/app.json. |
gate |
Guardas da casa (nav-drift, toolchain × Dockerfile, placeholder aninhado, POI em native, locale pt-BR, paridade dos Dockerfiles, libs no piso…) + ./gradlew check + contrato REST + aviso de modelagem. |
release-check |
Só em tag: confere SemVer × CHANGELOG × tag. |
docs |
Javadoc + OpenAPI, mkdocs build --strict (também em PR) e publicação no Garage (tag → snapshot da versão; master → dev/). |
build_native |
Em paralelo ao gate. Publica fonte.xadm.biz/xadm/vantroba-xls:native-amd64 (tag móvel) e :<sha>-native (tag imutável). É o job mais longo (~15 min). |
deploy |
Só com gate verde e, em tag, release-check verde. Anota o commit que está no ar (alvo de rollback) e faz o POST do alvo native. |
smoke |
Smoke de produção pós-deploy; reprovou → rollback automático (ver Reversão). |
O recurso vantroba-xls-native-pull (imagem vantroba-xls:native-amd64) é o dono
do domínio principal excel.vantroba.xadm.biz e responde também no próprio
excel-native.vantroba.xadm.biz. O recurso jar do Coolify fica parado. O
control-plane acha o recurso pelo slug da imagem, não por uuid.
Topologia¶
flowchart TB
gh["GitHub Actions<br/>pipeline.yml"]
reg[("Registry fonte.xadm.biz<br/>xadm/vantroba-xls")]
cp["control-plane<br/>central-backend.xadm.biz"]
coolify["Coolify"]
traefik["Traefik<br/>excel.vantroba.xadm.biz"]
native["vantroba-xls-native-pull<br/>excel-native.vantroba.xadm.biz"]
pg[("PostgreSQL<br/>vantroba_transporte")]
garage[("Garage<br/>arquivo-api.xadm.biz")]
ps["PowerSync<br/>ps.vantroba"]
gh -->|"push :native-amd64"| reg
gh -->|"POST no control-plane"| cp
cp -->|"atualiza o recurso"| coolify
coolify -->|"puxa a imagem"| reg
coolify --> native
traefik --> native
native --> pg
native --> garage
pg -->|"replicação lógica (bi_*)"| ps
- Traefik termina o TLS na borda e encaminha HTTP para a porta interna 8080 — o app vê HTTP, não HTTPS.
- O host do JDBC não é
localhost: é o nome do serviço Docker do Postgres na rede interna do Coolify (ex.:postgresql18), não o host da máquina. - O Garage (
arquivo-api.xadm.biz) guarda o binário dos.xlsx; o PowerSync (ps.vantroba) replica as tabelasbi_*do Postgres para os clientes móveis. - Runtime: binário native sobre
ubuntu:24.04(glibc 2.39; o builder-ol8compila na 2.28). PostgreSQL 18+. - Health check
/healthcom intervalo de 15 s (timeout 5 s, 15 tentativas) — o rolling update espera o container novo ficar healthy.
Quando usar¶
- Release nova — sempre pela skill
/xadm-release(nunca bump nem tag à mão): ela bumpa ogradle.properties, escreve o CHANGELOG, cria a tag anotadavX.Y.Ze faz o push. A tagv*roda o release: testes + snapshot da doc sempre, e build + deploy dos alvos do trailerDeploy:da tag anotada — sem trailer, cai nodocs/app.json→build.targets(aqui,native). - Redeploy sem bump (republicar o mesmo código, ex. depois de falha de
infra) —
workflow_dispatch(passo a passo abaixo). - Provisionar um ambiente novo — ver Provisionar ambiente novo no Coolify.
Push em master (ou PR) não deploya: roda só gate e/ou docs, conforme
os caminhos alterados (código → gate; docs/ ou mkdocs.yml → docs). O commit
chore(release) em master é pulado — quem roda o release é a tag.
Pré-requisitos¶
- Claude Code com a skill
/xadm-releasee permissão de push emfonte.xadm.biz/xadm/vantroba-bi-xls(o repo do GitHub é o espelho que o CI lê). - Toda lib da casa declarada no
build.gradle.ktspublicada no registro: a versão que só existe nomavenLocalnão chega ao runner, e a/xadm-releaserecusa. - Acesso ao GitHub Actions do repo, para acompanhar o run e disparar o
workflow_dispatch. -
Secrets do repo no GitHub — confira se um job falhar por falta deles:
Secret Usado por FORGEJO_USER/FORGEJO_TOKENbuild_native— push da imagem no registryfonte.xadm.bizCENTRAL_DEPLOY_TOKENdeployesmoke(rollback) —POSTno control-planeGLITCHTIP_API_TOKENsmoke, camada 3 — token read-only do GlitchTip. Ausente = smoke reprovaSMOKE_M2M_TOKENsmoke, rotam2m— o mesmo valor doVANTROBA_XLS_API_TOKENdo recursoSMOKE_TOKENsmoke, rotasession— o mesmo valor da envSMOKE_TOKENdo recursoDOCS_S3_ENDPOINT/DOCS_S3_ACCESS_KEY/DOCS_S3_SECRET_KEYdocs— publicação no Garage
Variáveis de ambiente (runtime, no Coolify)¶
Ao contrário de um app Flutter, aqui a configuração é de runtime: vive nas
variáveis de ambiente do recurso no Coolify. O único valor de build é o
XADM_COMMIT, que o pipeline carimba na imagem (vira o commit do /health);
não há Build Argument a configurar no Coolify, porque ele não builda.
Core (obrigatórias em produção)¶
| Variável | Padrão | Descrição |
|---|---|---|
VANTROBA_XLS_API_TOKEN |
— | Bearer do /api/** (POST /api/xls/processar e o reset REST). Nome pelo destino, padrão da casa (seguranca); renomeada de API_BEARER_TOKEN — ver a nota abaixo |
DATASOURCES_DEFAULT_URL |
jdbc:postgresql://localhost:5432/vantroba_transporte |
URL JDBC |
DATASOURCES_DEFAULT_USERNAME |
vantroba_transporte |
usuário do banco |
DATASOURCES_DEFAULT_PASSWORD |
— | senha do banco |
PORT |
8080 |
porta HTTP |
APP_BASE_URL |
https://excel.vantroba.xadm.biz |
base dos links em notificações |
API_BEARER_TOKEN→VANTROBA_XLS_API_TOKEN(renomeada na próxima release). Cadastre o nome novo no recurso do Coolify antes do deploy dessa release, com o mesmo valor. OBearerTokenEnvresolve em código a primeira env não-vazia:VANTROBA_XLS_API_TOKEN, depoisAPI_BEARER_TOKEN. Sem ambas, declaraapp.api-tokenvazia para axadm-segurancarecusar o boot em produção. Quem chama a API não é afetado. Apague a antiga somente quando todos os deploys usarem o nome novo e o smoke estiver verde; mantenha-a se ainda precisar reverter para a versão antiga.
Telegram (opcional — sem token, notificação off)¶
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID.
Object storage Garage (binário do XLS)¶
| Variável | Padrão | Descrição |
|---|---|---|
S3_FILE_STORAGE |
PSQL_GARAGE |
PSQL | PSQL_GARAGE | GARAGE (MAIÚSCULAS) |
GARAGE_ENDPOINT |
http://localhost:3900 |
em prod: Garage do arquivo-api.xadm.biz |
GARAGE_REGION |
garage |
bate com garage.toml |
GARAGE_ACCESS_KEY_ID / GARAGE_SECRET_ACCESS_KEY |
— | credencial do bucket, com os nomes que o broker da Central injeta (obrigatórias em prod) |
GARAGE_ACCESS_KEY / GARAGE_SECRET_KEY |
(dev key) | par antigo: lido só sem os nomes do broker, com WARN no boot |
GARAGE_BUCKET |
vantroba |
bucket dedicado |
GARAGE_CRIAR_BUCKET |
false |
true cria o bucket no startup (dev/teste); prod = false |
Envs
S3_*antigas seguem valendo. Oapplication.ymllê asS3_*como default das propriedadesgarage.*, e asGARAGE_*sobrescrevem por prioridade de fonte (env vence o YAML) — precedênciaGARAGE_*>S3_*> default, sem placeholder aninhado. A credencial segue o dual-read daxadm-comum-storage0.3.0: os nomes do broker primeiro, o par antigo como fallback (decisão 0034). Com o app no ar lendo os nomes do broker (sem oWARN), apague do recurso asS3_*e o parGARAGE_ACCESS_KEY/GARAGE_SECRET_KEY.S3_FILE_STORAGE(o modo,app.storage.modo) não muda.
Desenho e modos: etapa 03.
Error tracking (GlitchTip)¶
| Variável | Padrão | Descrição |
|---|---|---|
SENTRY_DSN |
— | DSN do projeto bi-transporte-xls no bug.xadm.biz — o mesmo do docs/app.json → features.glitchtip.dsn. Ausente = desligado. Confira pela chave: DSN de projeto inexistente perde todo erro em silêncio |
SENTRY_ENVIRONMENT |
production |
ambiente reportado |
SENTRY_TRACES_SAMPLE_RATE |
0.0 |
amostragem de tracing |
O SentryInitializer (da xadm-comum-web) roda antes do contexto Micronaut;
falha de init é engolida (nunca impede o boot). O nome antigo GLITCHTIP_DSN não é
lido: recurso que só tem ele sobe com o error tracking desligado, e o boot loga
WARN pedindo o rename.
Não setar SENTRY_RELEASE no Coolify. O release sai sozinho do XADM_COMMIT
carimbado na imagem (o sha da entrega), e o smoke pós-deploy usa
firstRelease == <sha> para achar exceção nova. SENTRY_RELEASE é override do
operador e ganha da lib: um vX.Y.Z ali descasa o release do sha e cega a camada 3 do
smoke (decisão 0029).
Reset da replicação PowerSync (POWERSYNC_* / COOLIFY_*)¶
Tabela completa (o runbook resetar-powersync aponta pra cá):
| Variável | Padrão | Descrição |
|---|---|---|
POWERSYNC_MONGODB_URI |
— | Connection string do Mongo do PowerSync. Vazia → o reset só simula o drop. O nome do DB é lido do path da URI |
POWERSYNC_MONGODB_DATABASE |
(do path da URI) | Override do nome do DB Mongo (raro) |
POWERSYNC_NUKE_CONFIRMATION |
— | Habilita o endpoint REST (a tela /admin não depende). Contrato: nome permanece "nuke" |
POWERSYNC_SLOT |
(auto-discover) | Vazio → descobre o slot do Mongo (sync_rules com state='ACTIVE'). Setar explícito só pra forçar/recovery em PG compartilhado |
POWERSYNC_PLUGIN |
pgoutput |
Plugin de replicação lógica do slot |
POWERSYNC_BOOTSTRAP_WAIT |
60 |
Segundos de espera extra pós-restart, antes da amostra final, pro bootstrap do PowerSync assentar |
COOLIFY_API_TOKEN |
— | Token Coolify (escrita/deploy). Setá-lo liga o restart automático (estratégia coolify); sem ele o restart é manual |
POWERSYNC_COOLIFY_NAME |
— | Nome exato do recurso PowerSync no Coolify (ex.: ps.vantroba). NAME ou UUID |
POWERSYNC_COOLIFY_UUID |
— | Alternativa ao _NAME: UUID do recurso |
COOLIFY_API_URL |
https://coolify.xadm.biz |
Em prod no mesmo daemon Docker do Coolify: http://coolify:8080 (HTTP interno — evita PKIX path building failed do hairpin NAT) |
POWERSYNC_HEALTH_URL |
— | URL de health-check do PowerSync conferida pós-restart |
POWERSYNC_RESTART_TIMEOUT |
120 |
Timeout (s) do polling do deployment Coolify |
Postgres: a role do app precisa do atributo REPLICATION (o reset dropa/cria o slot). Uma vez, como superuser: ALTER ROLE <role_do_app> WITH REPLICATION;.
Login das views (AUTH_*)¶
Runbook próprio com a tabela: google-auth.
Smoke de produção (SMOKE_TOKEN)¶
| Variável | Padrão | Descrição |
|---|---|---|
SMOKE_TOKEN |
— | Liga o seam POST /smoke/session da xadm-seguranca, que troca o token (cabeçalho X-Smoke-Token) por sessão de um principal smoke só-leitura. Vazia = seam desligado, e a rota session do smoke reprova. Mesmo valor do secret SMOKE_TOKEN do GitHub |
Passos¶
Release¶
- Rode
/xadm-releaseno Claude Code do repo. - Acompanhe o run do workflow
pipelineno GitHub Actions, disparado pela tagvX.Y.Z:gate,release-checkebuild_nativeem paralelo →deploy→smoke. OPOSTdo deploy é assíncrono (responde antes de o container novo servir) — quem confirma a entrega no ar é osmoke. - Smoke verde = produção confirmada. Vermelho: veja Reversão.
Redeploy sem bump¶
GitHub Actions → workflow pipeline → Run workflow, escolhendo como ref a
tag vigente (não master, que pode ter código ainda sem release) e marcando
native. Deixe tests marcado (default): sem ele o build roda, mas o deploy sai
pulado — deploy só com gate verde.
Provisionar ambiente novo no Coolify¶
- Banco (no Postgres compartilhado, como superuser) — criar antes do app:
Se for usar reset da replicação, garantir o atributo
CREATE DATABASE vantroba_transporte; CREATE USER vantroba_transporte WITH PASSWORD '<senha-forte>'; GRANT ALL PRIVILEGES ON DATABASE vantroba_transporte TO vantroba_transporte; \c vantroba_transporte GRANT ALL ON SCHEMA public TO vantroba_transporte;REPLICATIONna role e a existência dopowersync_role(ver decisão 0013). - Recurso Docker Image (pull-only, auto-deploy desligado), com o nome
canônico
<slug>-<target>-pull:vantroba-xls-native-pullna imagemfonte.xadm.biz/xadm/vantroba-xls:native-amd64. Não há webhook nem secret por recurso: o control-plane acha o recurso pelo slug da imagem (reconcile do central — ver deploy na infraestrutura). - Variáveis — todas as acima.
- Volume: mapear
/app/data/execucoes(WORKDIR /app+app.logs.dir: data/execucoes): é o histórico por processamento que a UI exibe, dado do app — nunca em/app/logs. - Health check: caminho
/health, porta8080(obrigatório — o rolling update espera ficar healthy). - Domínio:
excel.vantroba.xadm.biz(principal) eexcel-native.vantroba.xadm.bizno mesmo recurso. Procedimento e armadilhas na infraestrutura da casa.
Flyway: o histórico fica em
bi_transporte_flyway_schema_history. As migrations rodam no startup. Ao migrar de um schema antigo sem prefixo, rodarALTER TABLE bi_flyway_schema_history RENAME TO bi_transporte_flyway_schema_history;antes do deploy, senão o Flyway recria a tabela vazia e re-aplica tudo.Conformidade e retenção: os dados sensíveis do Excel ficam no PostgreSQL e no object storage; usar HTTPS sempre. Os logs de execução em disco (
app.logs.dir) não substituem auditoria fiscal — conservar as políticas de backup do banco e do volume de logs.
Verificação¶
- Job
smokeverde = produção confirmada. Ele roda depois dodeploye confere sozinho a identidade (ocommitdo/healthé o sha entregue), as rotas críticas do blocosmokedodocs/app.json(/health, a listagem do reset com o Bearer e/processamentoscom a sessão do seam) e nenhuma exceção nova no GlitchTip. Manifesto e porquê na decisão 0029; a norma, em smoke de produção. - Conferência manual (opcional):
curl https://excel.vantroba.xadm.biz/health→{"status":"UP","versao":"X.Y.Z","flavor":"native","commit":"<sha>"}. GET /processamentosabre (login em prod).- Logs do container no Coolify sem erro de Flyway/datasource e sem o
WARNde credencial antiga do Garage.
Reversão¶
Automática (smoke pós-deploy). Se o smoke reprova, o pipeline reverte sozinho
para a tag imutável fonte.xadm.biz/xadm/vantroba-xls:<sha anterior>-native (o
sha anterior é o commit que o /health mostrava antes do deploy), repola o
/health até confirmar a volta e deixa o run vermelho. Se o relatório disser
"rollback NÃO confirmado", produção pode estar quebrada: siga para a reversão
manual. Quando o smoke não reverte (outro deploy assumiu, falta de alvo, defeito
de configuração como GLITCHTIP_API_TOKEN ausente): ver a
norma do smoke.
Primeiro deploy após adotar o smoke: o
/healthno ar ainda não tinhacommit(lib < 0.9.0), então esse deploy não tem alvo de rollback — se reprovar, a reversão é manual. Dali em diante o rollback automático vale.Não pode a tag
<sha>-nativeque está no ar — é o alvo do rollback; apagá-la do registry transforma a reversão em "não confirmada" no pior momento.
Manual, quando preciso:
workflow_dispatchdopipelinetendo como ref a tag anterior boa, comnativemarcado — rebuilda e redeploya aquele código pelo caminho normal, com gate e smoke.- Break-glass (GitHub ou control-plane fora do ar): redeploy pelo painel do
Coolify, no recurso native, a partir de uma imagem
<sha>-nativeboa. É fora do fluxo — volte ao pipeline no deploy seguinte.
Fallback JVM (defeito que só o binário native tem): registre o motivo numa
decisão do app, ponha jar no build.targets, restaure do template do
pipeline.yml o build_jar e o step "Deploy jar", e religue o recurso
vantroba-xls-jar-pull (imagem :jar-amd64) com as mesmas variáveis.
A V17 (histórico em data/execucoes) aguenta a volta: a release anterior lê os caminhos novos pelo
volume, mas grava os logs que produzir em /app/logs, fora dele.
Migrations Flyway não revertem (nem no rollback do smoke) — se a versão nova introduziu migration incompatível, planejar a correção no banco antes de voltar.