Versionamento e release¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-10-01
Aplica-se a: todo perfil e toda stack.
Como os repos da X-Adm são versionados, como uma release acontece e o contrato do /health. A norma
está na constituição; esta página é o detalhe.
SemVer — quando bumpar o quê¶
Todo repo segue MAJOR.MINOR.PATCH:
| Bump | Quando | Exemplo |
|---|---|---|
| MAJOR | quebra contrato: API incompatível, migração manual de dados ou config, comportamento que o usuário percebe como outro sistema | trocar o formato da planilha aceita |
| MINOR | funcionalidade nova, compatível | novo relatório, novo endpoint |
| PATCH | correção ou ajuste interno, sem mudança de contrato | bugfix, bump de dependência |
Onde mora a versão:
- Java —
gradle.propertiesou o build script (version). - Flutter —
version: X.Y.Z+Nnopubspec.yaml:X.Y.Zé o SemVer,+No build number (só cresce). - Node —
"version"dopackage.json. - Sem manifesto próprio (site estático, repo de config) — arquivo
VERSIONna raiz, sóX.Y.Z. - Lib (
xadm-commons) — cada módulo tem a sua versão, e a tag é<modulo>-vX.Y.Z.
O que é uma release¶
Uma release é um commit + uma tag + uma entrada de CHANGELOG, sempre juntos, feitos pela skill
/xadm-release — commit e tag nunca à mão:
- bump da versão no manifesto da stack, ou a versão que já subiu à mão;
- entrada no
CHANGELOG.md(Keep a Changelog, pt-BR, seções Adicionado / Alterado / Corrigido / Removido, e a### Migraçãoquando há o que migrar); - tag anotada
vX.Y.Z, igual à versão do passo 1; - push de commit e tag.
Versão subida antes da release: a versão do manifesto pode subir à mão antes da release — para
publicar no mavenLocal e testar o consumidor, por exemplo —, com o CHANGELOG ainda em [Unreleased].
Versão acima da última tag e sem tag própria é a pendente: a /xadm-release mantém o número, avisa se ele
é menor do que os commits pedem e aborta se ele já está no registro.
A skill canônica é o template
xadm-release-skill.md, que a
/xadm-docs instala. Projeto sem CHANGELOG.md ou sem tag: a primeira /xadm-release cria o arquivo a
partir do git log e trata a "primeira release".
A tag vX.Y.Z é o release: o pipeline.yml builda os alvos do trailer Deploy: da tag (fallback
build.targets do docs/app.json) e chama o control-plane. Push em master sem tag só publica o dev/
da doc.
Distribuição (CLI/worker)¶
CLI ou worker distribuído como artefato não tem deploy: a release publica o artefato versionado
(<app>-<versão>.jar) e quem consome instala. A mecânica de versão é a mesma; não há /health — a versão
aparece no --version e no log de startup (Java). O marco é a Release do Forgejo, não só a
tag: o job release-forgejo do pipeline.yml (opt-in) builda o artefato no CI e publica a Release com
as notas da seção do CHANGELOG e o asset anexado, e o operador baixa o artefato de lá. O job chama a API
do Forgejo com secrets.RELEASE_TOKEN (escopo write:repository, secret do repo no GitHub).
Health check obrigatório¶
Todo app deployável define um health check (HEALTHCHECK na imagem) — sem ele o Coolify troca o
container sem saber se o novo responde (Operação no Coolify).
- É liveness, nunca readiness: o
/healthdo HEALTHCHECK responde "o processo está vivo?" e nunca checa dependência — reiniciar o container porque o banco piscou não conserta o banco e dispara restart em cascata. Checagem de dependência é monitoramento, em mecanismo separado. - Boot longo se cobre com
retries, não comstart-period: o Coolify espera a janelastart-periodinteira antes de promover, então ela fica pequena (~5s), comintervalcurto (~15s) eretriesalto (~15, cerca de 225s de tolerância). O Micronaut roda o Flyway antes de aceitar conexão, então um/health/readyseparado não ajudaria sem boot assíncrono. Os números vivem noHEALTHCHECKdos Dockerfiles do kit.
Versão visível em runtime¶
A versão em runtime vem sempre da fonte do release (o manifesto da stack), nunca de literal no código —
literal mostra a versão de ontem e se afasta mais a cada release, porque a /xadm-release só toca o manifesto e o
CHANGELOG.
Shape do /health¶
Esta seção é a dona do contrato do /health de tudo que é servido por HTTP:
{ "status": "UP", "versao": "1.4.2", "flavor": "native", "commit": "3f2a…" }
- Obrigatório:
status(truthy quando vivo; o valor exato não é normativo) eversao(SemVer, da mesma fonte do release). Responde 200 quando vivo, anônimo, flat (campos no raiz, sem agregar indicadores de dependência). commit: o sha do build, obrigatório em app que roda o smoke de produção — é como ele confere a identidade do que está no ar.flavor("native"ou"jvm"): o que roda de fato, para distinguir dois deploys da mesma versão.- Opcionais:
started_ate o que mais ajudar o diagnóstico. Acrescentar campo é livre; mudar ou remover os obrigatórios é mudança de contrato e muda aqui primeiro.
Receita por stack¶
- Java/Micronaut — o
HealthControllere oInfoControllerdaxadm-comum-webservem o/healthflat (comversao,flavorecommit) e o/info({versao}); o app desliga os endpoints de management (endpoints.health.enabled: false,endpoints.info.enabled: false), senão as rotas colidem e toda request responde 400. A versão chega por umversion.propertiesgerado doproject.version, e a primeira linha domaina loga. A detecção doflavorlêorg.graalvm.nativeimage.imagecodedentro do método, nunca numstatic final— sob native-image o inicializador estático pode rodar no build, onde a property vale"buildtime". A versão do@OpenAPIDefinitioné a do contrato HTTP, outra coisa. - Java CLI (picocli) — o
--versionlê o mesmoversion.propertiespor umIVersionProvider. - Node — a
versiondopackage.json. - Flutter web — é servido por HTTP e tem
/healthcom{status, versao, commit}, gerado no build (o Dockerfile o escreve e o nginx o serve):versaovem doversion.jsonque oflutter build webgera, ecommitdoARG XADM_COMMITque o CI passa. Oversion.jsonnão ganha campo e não é servido como/health: é o artefato do auto-update do app (smoke de produção). - Flutter mobile — sem servidor e sem
/health: a versão aparece no app, viapackage_info_plus. - App sem HTTP (cron, worker) — loga a versão na primeira linha do startup.
- Site estático (o central) —
/versao.txt(rotuladosite X.Y.Z) e/commit.txt.
Três versões: norma, kit e site¶
O repo central tem três números, desacoplados:
| Norma (constituição) | Kit | Site | |
|---|---|---|---|
| O que versiona | as regras | templates, scripts e skills | o portal docs.xadm.biz |
| Fonte | frontmatter versao: da constituição |
arquivo KIT na raiz do central |
arquivo VERSION na raiz do central |
| Publicado em | /toolchain/constituicao-versao.txt |
/toolchain/kit-versao.txt |
/versao.txt, tag vX.Y.Z, CHANGELOG.md |
| No repo consumidor | docs/app.json constituicao |
docs/app.json kit |
— |
| Sobe quando | a norma muda | um artefato do kit muda | sai uma release do central |
flowchart TB
subgraph central["repo central"]
N["versao: da constituição<br/>(norma)"]
K["arquivo KIT<br/>(templates, scripts, skills)"]
V["arquivo VERSION<br/>(site)"]
end
N --> P1["/toolchain/constituicao-versao.txt"]
K --> P2["/toolchain/kit-versao.txt"]
K --> M["manifesto.json<br/>mudou_em por artefato"]
V --> P3["/versao.txt · tag vX.Y.Z · CHANGELOG"]
P1 --> H{"checa-constituicao.sh<br/>compara com o app.json"}
P2 --> H
M --> H
H -- "kit defasado" --> R["/xadm-docs re-deriva<br/>sem perguntar"]
H -- "norma defasada" --> A["/xadm-docs re-audita<br/>com aprovação"]
R --> J["docs/app.json do repo<br/>grava constituicao e kit"]
A --> J
Bump da norma: PATCH = redação ou esclarecimento, sem obrigação nova; MINOR = obrigação nova sobre um repo; MAJOR = mudança de estrutura ou de contrato que exige migração de todo repo (âncoras, campos, perfis).
Bump do kit: PATCH = ajuste de artefato; MINOR = artefato novo ou removido; MAJOR = redefinição do propósito de um artefato (um re-pull cego quebraria o repo — leva nota de migração no CHANGELOG).
- Versão-base: o
docs/app.jsonde cada repo registraconstituicaoekit; é o único lugar do número.VERSION,/versao.txte a tag do central são a versão do site, nunca a da norma — num clone local do central, a norma é oversao:do frontmatter. - Manifesto:
manifesto.jsonregistra, por artefato do kit, o hash, a versão do kit em que mudou (mudou_em),perfis,stacksedestino, a listaopcionais, a listaremovere a seçãoscripts(semdestino: o CI e as skills baixam frescos, e a cópia deles no repo é sobra); oscripts/gera-manifesto.py --updatecarimba, e o--checkdo CI do central falha se o manifesto não refletir o estado (norma, kit ou hash). - Carimbo no CHANGELOG do central: cada heading de release traz o sufixo
(constituição C.C.C · kit K.K.K), e a mensagem do commit de release traz as três versões. - Defasagem: o hook
checa-constituicao.shdiz, ao abrir a sessão, se defasou a norma, o kit ou os dois; a/xadm-docsre-deriva o kit sem perguntar e, com a norma nova, re-audita doc e código contra as páginas de engenharia lidas ao vivo (/toolchain/raw/engenharia/<pagina>.md). - Validador fresco: os scripts publicados imprimem na primeira linha
constituição C.C.C · kit K.K.K; quem valida baixa na hora e confere o kit contra okit-versao.txtantes de confiar no resultado.
O que o CI valida¶
O valida-release.py (publicado em /toolchain/) confere que a versão do manifesto é SemVer e tem
entrada ## [X.Y.Z] no CHANGELOG.md, sem link ](docs/…) nessa seção, e, na tag, que a tag bate com o
manifesto. O link reprova porque o CHANGELOG.md entra no site embutido em docs/, onde vira
docs/docs/…: o mkdocs build --strict só o acusaria no job docs, depois do gate. Roda como último step
do job gate do pipeline.yml, só na tag e mesmo com teste vermelho; o gate vermelho por ele barra o docs
e o deploy da tag.
- name: Validar contrato de release (SemVer, CHANGELOG e tag)
if: ${{ !cancelled() && startsWith(github.ref, 'refs/tags/v') }}
run: |
curl --connect-timeout 10 --max-time 60 -fsSL -o /tmp/valida-release.py https://docs.xadm.biz/toolchain/valida-release.py
python3 /tmp/valida-release.py . --tag "${GITHUB_REF#refs/tags/}"
- Lib: o gate do
xadm-commonsrodacheck javadoc(erro de javadoc não pode aparecer só no publish, depois da tag), e o release de um módulo valida comvalida-release.py <pasta-do-módulo> --tag <modulo>-vX.Y.Z. - Repo adotando o padrão: entre criar o
CHANGELOG.mde a primeira/xadm-release, o validador acusa "sem a seção da versão" — é o esperado.