Monorepo
Um repositório com apps/web e apps/api vira dois serviços do mesmo projeto, cada um com seu root_dir. O projeto é o grupo: os serviços se enxergam pela rede privada.
Pré-requisitos
Seção intitulada “Pré-requisitos”- Token com
projects:write,deployeenv:write. - Integração GitHub instalada no time (o
installation_ididentifica a instalação). A rota para listar instalações não faz parte do contrato v13: pegue oinstallation_idno painel.
Fonte git por serviço
Seção intitulada “Fonte git por serviço”A fonte do serviço é {"type": "git", "installation_id": "...", "repo": "org/repo", "branch": "main", "root_dir": "/apps/api", "watch_paths": ["apps/api/**"]}.
root_dir: subdiretório do repositório que é a raiz do serviço (detecção da stack, Dockerfile e contexto de build partem dele). Não pode sair do repositório.watch_paths: padrões que decidem se um push afeta o serviço. Semwatch_paths, todo push na branch o afeta.- Cada serviço escolhe seu
build.builder(autodetecta pela raiz do serviço).
# INSTALL: installation_id da integração GitHub do timeNAME="mono-$(date +%s)-$RANDOM"P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \ -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \ -H "Idempotency-Key: $NAME-projeto" -d "{\"name\": \"$NAME\"}" | jq -r .id)
# API privada (só o front alcança), em Gocurl -sS -X POST "$NEBULAR_API/v1/projects/$P/services" \ -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \ -d "{\"name\": \"api\", \"kind\": \"app\", \"visibility\": \"private\", \"port\": 8080, \"auto_deploy\": true, \"build\": {\"builder\": \"go\"}, \"source\": {\"type\": \"git\", \"installation_id\": \"$INSTALL\", \"repo\": \"org/repo\", \"branch\": \"main\", \"root_dir\": \"/apps/api\", \"watch_paths\": [\"apps/api/**\"]}}"
# front público, em Next.jscurl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" \ -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \ -d "{\"visibility\": \"public\", \"slug\": \"$NAME\", \"port\": 3000, \"auto_deploy\": true, \"build\": {\"builder\": \"node\"}, \"source\": {\"type\": \"git\", \"installation_id\": \"$INSTALL\", \"repo\": \"org/repo\", \"branch\": \"main\", \"root_dir\": \"/apps/web\", \"watch_paths\": [\"apps/web/**\", \"packages/ui/**\"]}}"| Resposta | O que fazer |
|---|---|
400 invalid_request |
root_dir inválido ou source.type desconhecido. |
404 |
Projeto inexistente ou de outro time (indistinguíveis). |
409 name_taken / slug_taken |
Nome ou slug já em uso. |
Auto-deploy por git
Seção intitulada “Auto-deploy por git”Com auto_deploy: true, o push na branch aciona o deploy. O webhook do GitHub encontra todos os serviços ligados ao repositório e à branch; com watch_paths, só os afetados pelos arquivos do push. Se mais de um serviço do grupo for afetado, o resultado é um deploy de grupo.
Deploy de grupo
Seção intitulada “Deploy de grupo”POST /v1/projects/{p}/deploy publica o grupo em ordem topológica das referências: o serviço referenciado sobe antes de quem o referencia (por exemplo api antes de web, se web usa ${{api.PRIVATE_URL}}). Se o build de um serviço falhar, os dependentes ficam blocked; os que já estavam ativos continuam no ar.
G=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/deploy" \ -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \ -H "Idempotency-Key: grupo-mono-1" -d '{}' | jq -r .id)
# polling do GRUPO: um GET mostra o status geral e o de cada serviçocurl -sS "$NEBULAR_API/v1/projects/$P/group-deployments/$G" -H "Authorization: Bearer $NEBULAR_TOKEN" \ | jq -r '.status, (.items[] | "\(.service_id) \(.status)")'O corpo é {} com Content-Type: application/json; o POST também é aceito sem corpo (nesse caso o Content-Type é dispensável). O polling é feito no grupo (GET /v1/projects/{p}/group-deployments/{g}), não serviço a serviço. Repita a consulta com backoff (2 s, dobrando até 30 s, até ~10 min) até o status do grupo deixar de ser running: ready é sucesso; failed, canceled ou superseded são finais, e o item failed traz o deployment_id cujo log de build você lê (GET .../deployments/{d}/logs). Os itens têm status próprio (queued, building, deploying, ready, failed, blocked, skipped).
Exemplo executável (projeto de imagem; primeiro com {}, depois sem corpo):
SUF=$(date +%s)-$RANDOMH="Authorization: Bearer $NEBULAR_TOKEN"P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \ -d "{\"name\": \"mono3-$SUF\"}" | jq -r .id)curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \ -d '{"source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' >/dev/null
# 1) com Content-Type e {}: a forma que funciona em qualquer clienteG=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/deploy" -H "$H" -H "Content-Type: application/json" \ -H "Idempotency-Key: mono3-a-$SUF" -d '{}' | jq -r .id)for i in $(seq 1 40); do st=$(curl -sS "$NEBULAR_API/v1/projects/$P/group-deployments/$G" -H "$H" | jq -r .status) [ "$st" = running ] || break sleep 2done[ "$st" = ready ]
# 2) sem corpo e sem Content-Type: também aceito (vale {})curl -sS -X POST "$NEBULAR_API/v1/projects/$P/deploy" -H "$H" -H "Idempotency-Key: mono3-b-$SUF"| Estado ou erro | O que fazer |
|---|---|
blocked |
Um serviço do qual este depende falhou: conserte-o primeiro e redeploye o grupo. |
422 unresolved_reference ao disparar o deploy |
Uma ${{serviço.VAR}} aponta para variável ou serviço inexistente (a lista vem no erro). |
422 reference_cycle ao disparar o deploy |
Os serviços se referenciam em ciclo: quebre o ciclo. |
429 |
Espere Retry-After. |
Rollback é por serviço: POST .../services/{s}/deployments/{d}/rollback (Rollback). Para variáveis e referências entre serviços, veja Variáveis e referências.