Pular para o conteúdo

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.

  • Token com projects:write, deploy e env:write.
  • Integração GitHub instalada no time (o installation_id identifica a instalação). A rota para listar instalações não faz parte do contrato v13: pegue o installation_id no painel.

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. Sem watch_paths, todo push na branch o afeta.
  • Cada serviço escolhe seu build.builder (auto detecta pela raiz do serviço).
Janela do terminal
# INSTALL: installation_id da integração GitHub do time
NAME="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 Go
curl -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.js
curl -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.

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.

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.

Janela do terminal
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ço
curl -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):

Janela do terminal
SUF=$(date +%s)-$RANDOM
H="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 cliente
G=$(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 2
done
[ "$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.