Code
Search code
Latest commit
deployai
Um gerenciador pequeno de deploys Docker Compose, com TUI, webhook assinado do GitHub, login opcional no GHCR e integração não-invasiva com um Caddy já existente.
Como funciona
Cada subdiretório de projects_dir é um projeto e contém exatamente os arquivos
operacionais da aplicação:
projects/
└── minha-app/
├── .env
├── compose.yml
└── config.toml
Em um deploy, o deployai:
- opcionalmente executa
git pull --ff-onlyno diretório do projeto; - autentica no registry, se configurado;
- valida o Compose e a rede externa;
- conecta o container Caddy à rede do projeto, caso necessário;
- executa
docker compose pulleup -d --waitpara todos os serviços habilitados no arquivo, respeitandodepends_one healthchecks; - grava atomicamente um snippet do Caddy e executa o reload.
O snippet só é publicado depois que os serviços ficam saudáveis. Em caso de falha, containers e logs são preservados para diagnóstico e a rota anterior do Caddy não é alterada.
Instalação
Requer Docker com o plugin Compose e um Caddy já em execução. Em Linux amd64
ou arm64, instale a release mais recente como o usuário que executará o
deployai:
curl -fsSL https://git.maot.dev/maot/deployai/raw/branch/main/install.sh | sh
O instalador detecta a arquitetura, baixa o binário e checksums.txt da release
mais recente, valida o SHA-256 e instala em ~/.local/bin/deployai. Para escolher
uma versão ou diretório:
curl -fsSL https://git.maot.dev/maot/deployai/raw/branch/main/install.sh \
| sh -s -- v0.1.0
curl -fsSL https://git.maot.dev/maot/deployai/raw/branch/main/install.sh \
| INSTALL_DIR=/opt/deployai/bin sh
Não execute com sudo se quiser usar o serviço systemd do usuário e o
auto-update. O diretório do binário deve permanecer gravável por esse usuário.
Para compilar do código-fonte, requer Go 1.23+:
make build
install -m 0755 deployai ~/.local/bin/deployai
Na primeira execução não é necessário criar a configuração manualmente. O
deployai abre um setup wizard, pergunta pelos diretórios, Caddy, webhook e GHCR,
valida cada resposta, apresenta uma revisão final, cria os diretórios e grava
~/.config/deployai/config.toml com permissão 0600.
O TUI é a interface padrão:
deployai
Ele lista o estado de cada Compose, permite navegar com ↑/↓, iniciar um
deploy com d/enter, configurar as credenciais do GitHub/GHCR com g,
consultar o webhook com s, rotacionar seu segredo com S e reler projetos e
estados com r. Pressione ? a qualquer momento para
abrir o cheatsheet de atalhos em um modal centralizado.
A interface ocupa todo o viewport do terminal. A lista de projetos, os detalhes do projeto selecionado e a atividade recente ficam em painéis visualmente separados. Uma action bar fixa na parte inferior mostra apenas os atalhos válidos para o contexto atual; assistentes e confirmações aparecem como modais, sem deslocar o workspace.
Pressione n para abrir o assistente modal de novos projetos. Ele coleta domínio,
serviço, porta, rede e dados do webhook, cria o config.toml, gera um segredo
seguro e oferece duas formas de iniciar os arquivos:
- modelo mínimo, com um
compose.ymlcontendoservicese o nome do serviço e um.envvazio; - colagem direta do
compose.ymle.envcompletos em editores multilinha.
O .env e o .webhook-secret são gravados com permissão 0600. Se alguma
etapa da criação falhar, o diretório parcial é removido e o projeto não aparece
na lista.
Para reabrir o wizard e alterar a configuração posteriormente:
deployai setup
Também é possível usar examples/config.toml como
referência e o projeto em
examples/projects/minha-app como base. Renomeie
.env.example para .env.
Também há dois comandos operacionais, úteis para serviço e automação:
deployai deploy minha-app
deployai serve
Use -config /caminho/config.toml antes do comando para escolher outro arquivo.
Serviço systemd
O webhook pode ser instalado como um serviço systemd do usuário, sem sudo:
export GITHUB_ACTOR="seu-usuario"
export GHCR_TOKEN="seu-token"
deployai service install
O instalador:
- grava as credenciais do registry em
~/.config/deployai/service.envcom permissão0600, se o login estiver habilitado; - cria
~/.config/systemd/user/deployai-webhook.service; - executa
systemctl --user daemon-reloadeenable --now.
Os segredos de webhook pertencem a cada projeto e não ficam no arquivo de ambiente do serviço. Consulte o serviço com:
systemctl --user status deployai-webhook
journalctl --user -u deployai-webhook -f
Para que serviços do usuário iniciem no boot mesmo sem uma sessão aberta, o administrador do host deve habilitar lingering uma vez:
sudo loginctl enable-linger "$USER"
O serviço usa exatamente o binário e o arquivo de configuração empregados no
momento da instalação. Execute deployai service install novamente depois de
mover o binário ou mudar o caminho do config.
Releases e atualização automática
O workflow .forgejo/workflows/release.yml
roda os testes e publica binários Linux estáticos para amd64 e arm64, além
de checksums.txt. Ele usa o token temporário do próprio Forgejo Actions; não é
necessário cadastrar um token pessoal. É necessário haver um runner online com
o label docker e Actions habilitado no repositório.
Crie e envie uma tag semântica para publicar:
git tag -a v0.1.0 -m "v0.1.0"
git push origin v0.1.0
Binários de release consultam a release mais recente ao iniciar. Quando há uma versão mais nova para sua arquitetura, o deployai baixa o asset, valida o SHA-256, substitui atomicamente seu próprio executável e reinicia o mesmo comando. Falha de rede ou de atualização gera apenas um aviso; a execução normal continua.
Consulte a versão ou desative a consulta automática quando necessário:
deployai version
DEPLOYAI_NO_UPDATE=1 deployai
Configuração global
projects_dir = "/srv/deployai/projects"
[caddy]
container = "caddy"
snippets_dir = "/srv/caddy/sites"
reload = ["caddy", "reload", "--config", "/etc/caddy/Caddyfile"]
[webhook]
listen = "127.0.0.1:9080"
[registry]
login = true
host = "ghcr.io"
username_env = "GITHUB_ACTOR"
token_env = "GHCR_TOKEN"
O diretório snippets_dir deve estar montado no container Caddy e importado
pelo Caddyfile principal. Por exemplo:
import /etc/caddy/sites/*.caddy
Se o Caddy usa uma configuração em JSON ou não importa esse diretório, deixe
snippets_dir vazio; o deploy continuará funcionando sem gerenciar rotas.
Configuração de projeto
hostnames = ["app.example.com"]
service = "minha-app"
port = 8080
proxy_network = "caddy"
compose_file = "compose.yml"
health_timeout = "90s"
repository = "minha-org/minha-app"
branch = "main"
git_pull = false
Todos os serviços habilitados no Compose são atualizados e iniciados durante o
deploy. service seleciona apenas o serviço HTTP que receberá o tráfego do
Caddy; ele não limita o deploy a esse serviço. É o nome DNS desse serviço na
rede Docker compartilhada, não uma porta publicada no host. A rede informada
deve existir e também ser declarada como
external no Compose. Use nomes de serviço únicos quando vários projetos
compartilharem a mesma rede, evitando DNS ambíguo. O serviço deve ter um
healthcheck; sem ele, o Compose só consegue confirmar que o container está em
execução.
GHCR
Com registry.login = true, defina:
export GITHUB_ACTOR="seu-usuario"
export GHCR_TOKEN="seu-token"
No TUI, pressione g para consultar o estado dessas credenciais e salvá-las
sem expor o token. Os valores ficam em ~/.config/deployai/service.env com
permissão 0600, são carregados automaticamente pelos comandos do deployai e
também são usados pelo serviço webhook na próxima inicialização.
Para imagens privadas, o token precisa ao menos de read:packages. O segredo é
enviado ao docker login por stdin e nunca aparece nos argumentos do processo.
Também é possível deixar login = false e usar as credenciais já armazenadas no
Docker credential store.
Webhook do GitHub
Inicie deployai serve sob seu supervisor de processos e exponha
POST /webhook/github/<projeto> por HTTPS. Cada projeto recebe automaticamente
um segredo aleatório de 256 bits em .webhook-secret, com permissão 0600.
No TUI, selecione o projeto e pressione s para consultar:
Endpoint: /webhook/github/minha-app
Secret: 7f1c...e92a
Cadastre no GitHub a URL completa desse endpoint e o segredo exibido. O endpoint
valida obrigatoriamente X-Hub-Signature-256 com o segredo daquele projeto e
também confirma que repositório e branch do payload correspondem ao
config.toml. Ele aceita estes eventos:
push, filtrado porrepositoryebranch;workflow_run, somente quando concluído com sucesso e na branch configurada;registry_package, filtrado pelo repositório.
GET /healthz pode ser usado pelo supervisor. Para pipelines de imagem, o evento
workflow_run do workflow que publica no GHCR costuma ser o gatilho mais
previsível.
Pressione S no TUI para rotacionar o segredo. A operação pede confirmação e o
webhook do GitHub precisará ser atualizado com o novo valor. Se o diretório do
projeto for um repositório Git, inclua .webhook-secret no .gitignore; esse
arquivo nunca deve ser versionado.
Sobre zero downtime
O MVP faz deploy com health gate e só troca/publica a rota depois da saúde ser confirmada. Isso evita encaminhar tráfego para uma aplicação ainda iniciando, mas o Docker Compose isoladamente não garante sobreposição entre a instância antiga e a nova. Zero downtime estrito requer blue-green (dois nomes/projetos de Compose e troca de upstream) ou um orquestrador com rolling update. Essa pode ser a próxima camada sem alterar o formato básico dos projetos.
Segurança e operação
- Não coloque tokens no TOML nem no
.envdo projeto. - Restrinja o socket Docker e execute o deployai apenas em host confiável; acesso ao Docker equivale, na prática, a privilégio administrativo no host.
- Exponha o webhook somente atrás de TLS e limite acesso ao TUI ao terminal do servidor.
- O app nunca cria, remove ou recria o container Caddy; no máximo o conecta à rede declarada, grava o snippet e executa o comando de reload configurado.