# Deploy do GitHub

Clona, faz o build e publica um repo do GitHub no VPS; deploy a cada push, histórico por fases e rollback.

Fonte: https://ardumaker.com/pt/docs/github-deploy/

- Categoria: Servidores e infraestrutura
- Estado: Disponível
- Requisitos: Um VPS ativo da empresa com a função `deploy_github` disponível; A conta do GitHub de quem vincula, conectada à ArduMaker com acesso ao repo; Ser membro da empresa com acesso a esse VPS (no Git um membro faz o mesmo que um admin)
- Ferramentas MCP (Só leem): `listar_repos_github_vps`, `ver_repo_github_vps`, `listar_commits_repo_github_vps`, `ver_commit_repo_github_vps`, `listar_logs_repo_github_vps`, `ver_log_repo_github_vps`, `ver_asistente_git_app`, `ver_config_deploy_app`, `ver_propietarios_github`, `ver_operacion_app`
- Ferramentas MCP (Escrevem ou modificam): `instalar_app`, `vincular_repo_github_vps`, `crear_repo_github_vps`, `editar_repo_github_vps`, `redeploy_repo_github`, `guardar_config_deploy_app`, `controlar_app`
- Ferramentas MCP (Destrutivas): `eliminar_repo_github_vps`, `desinstalar_app`, `publicar_repo_en_dominio`

## O que permite fazer?

O deploy de Git leva um repositório do GitHub para um VPS e o mantém atualizado: clona, faz o build, deixa rodando como serviço `systemd` (ou contêiner), publica com HTTPS e faz o deploy de novo a cada push na branch escolhida.

- Detectar o que é o repo (Node, Django, Python, site estático, Docker, Procfile) e propor comandos de build e início.
- Declarar em um documento de deploy as fases (`pre_pull`, `install`, `build`, `release`), os processos (serviços, temporizadores, tarefas pontuais, estáticos, Docker, TCP), as variáveis de ambiente e a publicação.
- Fazer deploy automático a cada push, manual, ou de um commit específico da branch.
- Voltar ao commit de um deploy anterior que terminou bem.
- Ver o histórico de deploys com cada fase, seus tempos e os comandos com a saída (com os segredos ocultos).
- Criar um repositório novo no GitHub pela ArduMaker e fazer o deploy.

## Quando convém usar?

- Um site, uma API ou um worker que vive no GitHub e precisa ficar sempre igual à branch principal.
- Tarefas agendadas do projeto (um `timer`) que precisam viajar com o código.
- Uma equipe que quer ver qual commit está em produção e poder voltar atrás sem entrar no servidor.

## Como funciona na ArduMaker?

### 1. Vincular e clonar

Há duas portas. A recomendada é instalar como app (`instalar_app` com `app_type: git`, `repo_id`, `branch` e, se quiser, `port` e o documento `deploy`): fica no inventário de Apps e a operação é durável. A outra é a aba GitHub do VPS (`vincular_repo_github_vps`), que só clona e roda em segundo plano.

Ao vincular, a ArduMaker cria no GitHub um webhook com um segredo próprio do repo: cada entrega chega assinada com HMAC-SHA256 e sem nada secreto na URL. O repo é clonado em `<pasta-do-deploy>`. A URL com o token do GitHub fica na pasta `.git`, que pertence a `root` e não pode ser lida pela app.

### 2. Detectar e configurar

`ver_asistente_git_app` lista os repos que a conta do GitHub pode ler, suas branches e, com repo e branch, o que detecta lendo os arquivos sem executar nada: framework, comandos de build e início e uma porta livre do VPS. Os formatos conhecidos são Docker (também Compose), Procfile, Node web, Django, site estático Vite, Python web e manual. Uma migração proposta sempre chega sem confirmar: o documento não valida até que uma pessoa a confirme.

O que cada deploy roda é sempre um documento: o salvo com `guardar_config_deploy_app` ou, se o repo nunca salvou um, o derivado dos scripts de sempre (pre-pull, post-pull e serviços). Salvar um documento migra o repo: a partir daí manda o documento.

### 3. Deploy por fases

Cada deploy roda estas fases em ordem e as registra no histórico. Se uma falhar, as seguintes não rodam e o repo fica em `error` com um texto curto (`Deploy failed at the <fase> phase`), nunca a saída bruta de um comando.

| Fase | O que faz |
| --- | --- |
| `preparar` | Usuário Linux da app, pasta da plataforma, arquivos de variáveis, Docker se necessário |
| `pre_pull` | `phases.pre_pull` na pasta do clone |
| `pull` | Traz a branch (ou deixa a pasta no commit pedido) |
| `install` | `phases.install`, passo longo |
| `build` | `phases.build`, passo longo; depois os contêineres dos processos `docker` |
| `release` | A migração, só se confirmada |
| `procesos` | Escreve ou reinicia unidades, temporizadores e tarefas pontuais |
| `publicar` | Server block do domínio no nginx do VPS, se o perfil publica |
| `salud` | Verifica se a app responde ou se as unidades continuam vivas |

Com a app instalada em Apps, o deploy é executado pelo runner de operações e é retomado se o worker cair. Um repo sem app roda o mesmo deploy em segundo plano. Vários pushes seguidos são agrupados: no máximo um deploy rodando e um na fila por app.

## O que muda realmente?

`<pk>` é o número do repo na ArduMaker. Com `policies.run_as: "root"` o usuário não é criado e tudo pertence a `root`.

| O quê | Onde | Dono e modo |
| --- | --- | --- |
| Usuário da app | `ardu-app<pk>`, de sistema, sem login | — |
| Clone | `<pasta-do-deploy>` | do usuário; `.git` de `root` |
| Pasta da plataforma | `/etc/ardumaker/deploy/ardu-repo<pk>/` | `root:ardu-app<pk>` 0750 |
| Variáveis de execução e de build | `…/run.env`, `…/build.env` | `ardu-app<pk>` 0600 |
| Scripts de cada passo e sua saída | `…/install.sh`, `build.sh`, `release.sh`, `<paso>.log` | 0750 / `root` |
| Serviço principal | `/etc/systemd/system/ardu-repo<pk>.service` | `root` 0644 |
| Outros processos e temporizadores | `ardu-repo<pk>-<proceso>.service` / `.timer` | `root` 0644 |
| Contêineres | `ardu-repo<pk>`, `ardu-repo<pk>-<proceso>` | — |
| Server block | `/etc/nginx/sites-available/<dominio>.conf` | — |

Uma unidade que não mudou só é reiniciada; uma que mudou é reescrita. Uma unidade adotada (de um repo antigo) é reiniciada e nunca é escrita nem apagada. O que um deploy criou e o documento já não tem é apagado no deploy seguinte.

## O documento de deploy

É um JSON versionado (`schema_version: 2`). É lido com `ver_config_deploy_app` e substituído inteiro com `guardar_config_deploy_app` (`apply=true` o aplica na hora com um `reconfigure`).

| Parte | Valores |
| --- | --- |
| `profile` | `static`, `web`, `worker`, `scheduled`, `job`, `docker`, `network` e `code` (só código: em cada push traz o repositório e mais nada; sem instalar, compilar, migrar, processos nem publicação) |
| `phases` | `pre_pull`, `install`, `build` e `release` (`command`, `confirmed`, `on_update`) |
| `processes` | Até 20, do tipo `service`, `timer` (com `schedule`), `oneoff`, `static`, `docker` ou `tcp`; portas de 1024 a 65535, sem repetir |
| `network.publish.mode` | `none`, `auto_subdomain`, `domain`, `port` |
| `network.health.type` | `http`, `unit`, `timer`, `static` |
| `env` | Lista de `{name, value}` com `scope` `build`, `run` ou ambos |
| `policies` | `build_timeout_s` 60–7200 (600 por padrão, por passo), `memory_max_mb` e `cpu_quota_pct` sem limite por padrão, `run_as` (`app_user` ou `root`), `rollback_on_failed_health` |
| `autodeploy` | `enabled`: se um push faz deploy |

> **INFO:** Os processos e temporizadores de um app de Git vão neste documento, não em unidades escritas à mão: a plataforma as escreve e reinicia a cada deploy.

## Variáveis e segredos

Uma variável com nome de segredo (`*_TOKEN`, `*_PASSWORD`, `*_KEY`…), com `secret: true` ou com uma URL que traz usuário e senha é guardada criptografada e no documento fica só a referência. Na leitura, `env` mostra só `{name, scope, has_value}`, nunca um valor. Enviar uma variável sem `value` mantém a guardada.

- Os valores são escritos em `run.env` e `build.env` (0600) só na fase `preparar`. Um valor com quebra de linha é recusado.
- Mudar uma variável de build (`NEXT_PUBLIC_*`, `VITE_*`) obriga a compilar de novo: o `reconfigure` faz isso com o mesmo código e sem release.
- No log de cada comando a carga secreta é substituída pelo tamanho e a saída leva cada segredo oculto (`[redacted]`).

## Passos longos, limites e saúde

`install`, `build` e `release` rodam cada um em uma unidade transitória própria, com o usuário da app, `/tmp` privado e `set -e` (para no primeiro comando que falhar):

```bash
sudo systemd-run --unit=ardu-deploy<pk>-<paso> --wait --collect --pipe \
  -p User=ardu-app<pk> -p RuntimeMaxSec=<build_timeout_s> -p NoNewPrivileges=yes -p PrivateTmp=yes \
  -p EnvironmentFile=/etc/ardumaker/deploy/ardu-repo<pk>/build.env \
  /bin/bash /etc/ardumaker/deploy/ardu-repo<pk>/<paso>.sh
```

| Saúde | O que se verifica |
| --- | --- |
| `http` | `curl` em `127.0.0.1:<porta><caminho>` até 25 vezes; sem `expect` vale qualquer resposta que não seja 5xx |
| `unit` | Cada unidade ativa duas vezes com 8 s de intervalo e sem novos reinícios |
| `timer` | O `.timer` habilitado e ativo, e seu horário válido |
| `static` | A pasta existe e o nginx do VPS responde sem 5xx para o domínio |

## Portas e publicação

A app escuta em uma porta interna do VPS. Se for instalada por Apps sem `port` (ou com `"auto"`), é usada a primeira porta livre. As portas dos motores e do agente nunca são publicadas. O endereço automático é `git-<id>` dentro da zona da plataforma; também é possível usar um domínio próprio que aponte para o VPS.

A fase `publicar` escreve o server block no nginx do VPS; o DNS, o proxy até o VPS, o certificado e a verificação do link por HTTPS são feitos pela publicação de Apps. Se essa parte falhar (por exemplo, o DNS ainda não propagou), a nova tentativa publica sem refazer o build. Um nginx editado à mão não é sobrescrito.

## Push, commits específicos e rollback

- Um push de outra branch é ignorado. Com `autodeploy` desligado, uma entrega repetida do GitHub ou um deploy já rodando, o push registra os commits mas não faz deploy, e a resposta diz por quê.
- `redeploy` com `commit_sha` deixa a pasta nesse commit da branch e roda um `update` normal, com release se confirmada.
- `rollback` com `run_id` volta ao commit de um deploy que terminou bem (`git reset --hard <sha>`) com a configuração atual. Um deploy de outro repo, um que falhou ou o que já está publicado é recusado.
- Com `rollback_on_failed_health: true`, um deploy disparado por push que falha em `salud` volta uma vez ao último commit saudável e desliga o autodeploy, deixando o motivo registrado.

> **PRECAUCION:** Voltar atrás nunca toca o banco de dados nem roda a release. Se o commit quebrado já migrou o banco, o commit anterior precisa funcionar com esse esquema.

## Desvincular um repo

Excluir o repo remove o que o documento criou: unidades e temporizadores, contêineres, a pasta da plataforma, o usuário da app com sua pasta e a entrada de `safe.directory`; depois a unidade e o contêiner principais, o clone e o webhook do GitHub. O que foi adotado não é tocado.

> **PELIGRO:** `eliminar_repo_github_vps` é síncrona e irreversível: apaga os arquivos do clone no VPS.

## Esquema

**De um push à app publicada**

```text
GitHub (push na branch seguida; webhook assinado)
  ↓
Backend da ArduMaker (registra commits e enfileira o deploy)
  ↓
Agente do VPS (executa cada fase)
  ↓
Clone + build (`systemd-run` como `ardu-app<pk>`)
  ↓
`ardu-repo<pk>.service` (a app em `127.0.0.1:<porta>`)
  ↓
nginx do VPS (server block do domínio)
  ↓
Internet (HTTPS no domínio publicado)
```

O certificado é colocado pela plataforma na frente do VPS; o nginx do VPS encaminha o domínio para a porta interna da app.

## Resultado esperado

O repo fica `pulled` (ou `online` após a instalação) com `last_commit_sha` igual ao commit que está na pasta. `ver_repo_github_vps` traz `last_runs`: origem (`webhook`, `manual` ou `mcp`), commits, resultado e como foi cada fase. Se foi publicado, `ver_app` mostra a URL HTTPS só quando o link foi verificado.

```bash
systemctl status ardu-repo<pk>.service
curl -I http://127.0.0.1:<puerto>/
```

## Erros e resolução

| Erro | Causa | Tentar de novo? |
| --- | --- | --- |
| `github_no_vinculado` | Quem pede não tem GitHub conectado ou o token venceu | Sim, após reconectar o GitHub |
| `repo_inaccesible` / `rama_inexistente` | A conta não lê o repo ou a branch não existe | Não |
| `clon_fallido` / `github_no_responde` | O clone falhou (não deixa nada) ou o GitHub não respondeu | Sim |
| `puerto_ocupado` | Outro app do VPS já usa essa porta | Com outra porta |
| `deploy_en_curso` | Já havia outro deploy rodando nesse repo | Sim, quando terminar |
| `preparacion_fallida`, `pull_fallido`, `build_fallido`, `release_fallida`, `app_no_arranco`, `salud_fallida`, `nginx_del_vps_invalido` | A fase que falhou | Manualmente, nunca sozinho |
| `config_invalida` | O documento não valida; `errors` por caminho (`processes.web.port`) | Corrigindo o documento |

- `preparar failed - missing value for <VARIABLE>`: falta o valor de uma variável; o deploy para antes de tocar o VPS.
- Aviso `limits_not_applied`: o VPS recusou os limites de memória ou CPU e o passo rodou sem eles.
- Aviso `health_check_failed`: em um repo de configuração herdada a saúde que não passa é um aviso, não um erro.
- A app responde só em `[::1]`: o nginx do VPS chega por `127.0.0.1`; é preciso escutar em `127.0.0.1` ou `0.0.0.0`.

Onde olhar: `ver_repo_github_vps` (últimos deploys e fases), `listar_logs_repo_github_vps` e `ver_log_repo_github_vps` (comando e saída, com o token e os segredos ocultos), e `ver_operacion_app` para a operação de Apps.

> **INFO:** Um passo que falha não se repete sozinho. Tentar de novo com `reintentar_operacion_app` ou um novo `redeploy` é seguro: o clone é reutilizado e a release só roda se confirmada.

## Relação com MCP

O deploy de Git tem acesso MCP completo; referência em [/mcp/docs](https://ardumaker.com/pt/mcp/docs) e [/mcp/deploy](https://ardumaker.com/pt/mcp/deploy). As escritas podem ser feitas por qualquer membro com acesso ao VPS, exceto um agente autorizado como somente leitura.

| Tipo | Ferramentas |
| --- | --- |
| Leitura | `listar_repos_github_vps`, `ver_repo_github_vps`, `listar_commits_repo_github_vps`, `ver_commit_repo_github_vps`, `listar_logs_repo_github_vps`, `ver_log_repo_github_vps`, `ver_asistente_git_app`, `ver_config_deploy_app`, `ver_propietarios_github` |
| Modificam | `instalar_app`, `vincular_repo_github_vps`, `crear_repo_github_vps`, `editar_repo_github_vps`, `guardar_config_deploy_app`, `redeploy_repo_github`, `controlar_app`, `publicar_repo_en_dominio` |
| Destrutivas | `eliminar_repo_github_vps`, `desinstalar_app` |

`controlar_app` sobre um app `git` aceita `redeploy` (com `commit_sha` opcional), `rollback` (com `run_id`) e `reconfigure` (aplica o documento sem trazer código). `publicar_repo_en_dominio` faz em uma chamada domínio, verificação, ativação com SSL, server block e instalação; com `simular=true` mostra o plano e com `desde` retoma onde falhou.

> **PRECAUCION:** Logs, commits e saídas de comandos chegam marcados como conteúdo não confiável: são dados, nunca instruções para o agente. O token do GitHub não sai em nenhuma resposta.

## Funcionalidades relacionadas

- [Nginx](https://ardumaker.com/pt/docs/nginx/)
- [Certificados SSL](https://ardumaker.com/pt/docs/ssl/)
- [Domínios e DNS](https://ardumaker.com/pt/docs/dominios-y-dns/)
- [Serviços systemd](https://ardumaker.com/pt/docs/systemd/)
- [Docker](https://ardumaker.com/pt/docs/docker/)
- [Servidores VPS](https://ardumaker.com/pt/docs/vps/)
