Skip to content

About

CLI em JavaScript puro para deploy incremental e seguro em VPS Linux. Funciona diretamente no PowerShell: a máquina local precisa apenas de Node.js e de uma chave SSH; não usa rsync, WSL, MSYS2, scp nem o executável ssh local.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

AutoDeploy V3

CLI em JavaScript puro para deploy incremental e seguro em VPS Linux. Funciona diretamente no PowerShell: a máquina local precisa apenas de Node.js e de uma chave SSH; não usa rsync, WSL, MSYS2, scp nem o executável ssh local.

O transporte é feito com ssh2 (SSH + SFTP), e cada release tem um manifest SHA-256. Depois do primeiro deploy, só arquivos adicionados ou modificados são enviados; a nova release começa como cópia da release ativa e recebe as alterações.

Início rápido

npm install
npm link
autodeploy init      # detecta docker/pm2, cria deploy.config.json e AUTODEPLOY.md
autodeploy doctor    # confere SSH, docker/pm2, permissões na VPS
autodeploy deploy --dry-run
autodeploy deploy

init pergunta só o essencial (host, usuário, health check opcional), adiciona deploy.config.json ao .gitignore e gera o AUTODEPLOY.md: um arquivo de contexto na raiz do projeto, com formato de skill, que explica a uma IA (Claude Code, Cursor...) como publicar, quais comandos usar, o que nunca fazer e como resolver erros comuns. Ele não é enviado à VPS.

Na VPS continuam necessários curl para health checks e PM2 ou Docker Compose conforme o runtime.

Comandos

Comando O que faz
init Cria config + AUTODEPLOY.md (--force recria; --host --user --port --health --yes para não-interativo)
doctor Verifica a VPS antes do deploy
deploy [--dry-run] [--yes] [--include-env] Deploy incremental com rollback automático
status / logs [-f] [--tail N] Estado e logs da aplicação
restart Reinicia a release ativa
releases / rollback [id] Lista releases / volta para a anterior
unlock Libera o lock de um deploy interrompido
server add <nome> / server list Perfis de servidor reutilizáveis

--yes nunca pergunta nada e nunca envia .env por conta própria (use --include-env ou envPolicy).

Configuração mínima

Só host, user e runtime são obrigatórios. Padrões: name vem do package.json/pasta, remotePath = /opt/apps/<name>, port 22, releases.keep 5.

{ "host": "1.2.3.4", "user": "deploy", "runtime": "docker",
  "persistentPaths": "auto",
  "docker": { "envFile": true, "requiredEnv": "auto" } }
  • server: em vez de host/user/port/privateKey, use "server": "minha-vps", cadastrado uma vez com autodeploy server add minha-vps (fica em ~/.autodeploy/servers.json). Valores do projeto vencem os do perfil.
  • docker.envFile: true: equivale a <remotePath>/.env.
  • docker.requiredEnv: "auto": usa as ${VAR} sem valor padrão do Compose.
  • persistentPaths: "auto": usa os bind mounts relativos do Compose (./data:/app/data), exceto pastas que existem localmente e não estão no .gitignore (provável código-fonte). O deploy imprime o que deduziu.
  • envPolicy: "ask" (padrão), "never" ou "overwrite": decide o que fazer com o .env sem perguntar toda vez.
  • respectGitignore: true: soma os padrões do .gitignore ao ignore (sem suporte a !negação).
  • Chaves desconhecidas geram aviso com sugestão (persistantPaths → persistentPaths).

Deploys e rollbacks ainda usam um lock remoto (.deploy.lock); se um deploy for interrompido, autodeploy unlock. Se o deploy falha antes de ativar, a release incompleta é removida. No PM2, o processo é recriado a partir de current a cada deploy (um pm2 restart reaproveitaria o diretório de uma release antiga).

autodeploy restart não envia arquivos nem cria release: ele reinicia a release apontada por current e executa o health check configurado.

Configuração V3

Crie deploy.config.json na raiz do projeto:

{
  "name": "project-botchat",
  "host": "122.122.122.122",
  "user": "user",
  "port": 22,
  "privateKey": "~/.ssh/id",
  "localPath": ".",
  "remotePath": "/var/www/project-botchat",
  "runtime": "docker",
  "ignore": ["node_modules", ".git", ".next", "dist", ".env", ".env.*"],
  "commands": { "build": [] },
  "docker": { "projectName": "project-botchat", "composeFile": "docker-compose.yml", "build": true, "force": false },
  "health": { "url": "http://127.0.0.1:3002/status", "timeout": 30000, "interval": 2000 },
  "releases": { "keep": 5 }
}

persistentPaths: o que não pertence à release

ignore diz o que não é enviado. persistentPaths diz o que não é copiado da release anterior para a nova — são coisas diferentes, e é a segunda que interessa para dados de execução.

"persistentPaths": ["data"]

Cada nome é um diretório na raiz da release, entra automaticamente no ignore e é criado em shared/ pelo deploy, com o dono da conta que deploya — antes que o container o crie como root e ninguém mais consiga olhar dentro. Serve para o que a aplicação escreve em tempo de execução: banco em arquivo, sessões, uploads, perfis de navegador. Sem isso a cópia entre releases arrasta esse conteúdo a cada deploy — e falha com Permission denied quando um container gravou ali como root, porque o usuário do deploy não consegue entrar no diretório. A CLI reconhece essa falha e diz qual nome acrescentar aqui.

Somente nomes de raiz são aceitos. data/perfil é recusado na validação: exclua data inteiro e aponte a aplicação para um caminho fora das releases, como <remotePath>/shared/data.

Se privateKey for omitido, a CLI procura ~/.ssh/id_ed25519 e depois ~/.ssh/id_rsa. O conteúdo da chave nunca é registrado em logs. Há exemplos completos para PM2 e Docker nos arquivos deploy.config.*.example.json.

Configs V2 permanecem aceitas: afterUpload, healthCheck e keepReleases continuam funcionando. As novas equivalências são commands.build, health e releases.keep.

Fluxo

  1. Lê a release apontada por current e seu .autodeploy-manifest.json.
  2. Escaneia os arquivos locais com SHA-256 por stream e mostra A/M/D e o total estimado de upload.
  3. Cria releases/YYYYMMDD-HHmmss; se houver release anterior, faz cp -a dela dentro da VPS, pulando os persistentPaths.
  4. Cria os diretórios necessários e envia apenas A/M via SFTP, com até quatro uploads simultâneos.
  5. Remove D apenas da nova release, grava o manifest e liga shared/.env como .env da release quando existir.
  6. Executa o build, atualiza current, reinicia o runtime e aguarda o health check na VPS.
  7. Se reinício ou health check falhar, restaura current para a release anterior e reinicia novamente. Releases antigas são limpas somente após sucesso, e uma limpeza que falhe sai como aviso: a release nova já está ativa e saudável, então apresentá-la como erro levaria a desfazer um deploy que deu certo.

No primeiro deploy, todos os arquivos são tratados como adicionados. Sem mudanças, nenhuma release é criada. Em --dry-run, nada é criado, enviado, removido, construído, reiniciado ou ativado.

Segurança

  • remotePath deve ser um caminho absoluto específico; caminhos como /, /var, /var/www, /root e /tmp são bloqueados.
  • Todos os caminhos de arquivos remotos passam por validação de pertencimento à release, bloqueando traversal (..).
  • .env e .env.* são protegidos por padrão. Antes do deploy interativo, a CLI pergunta se devem ser enviados; responda s para incluí-los na release ou use --include-env. Quando enviados, eles não sobrescrevem os arquivos em shared/.
  • A release ativa nunca é alterada durante upload ou build.

Runtime

Para PM2, informe pm2.script (ou pm2.restartCommand). Para Docker, informe docker.projectName e opcionalmente docker.composeFile. Defina docker.force: true para reconstruir todas as imagens sem usar cache: a CLI executará docker compose build --no-cache antes de docker compose up -d. O comando Docker e o PM2 executam a partir de current, portanto o rollback usa a release anterior corretamente.

Troca de Compose e .env

Quando um deploy Docker termina com sucesso, a CLI salva a identidade do stack (nome do projeto e arquivo Compose) em .autodeploy-runtime.json, fora das releases. Se um deploy futuro mudar docker.projectName ou docker.composeFile, ela:

  1. remove o arquivo Compose antigo da cópia da nova release;
  2. executa docker compose down --remove-orphans no stack anterior;
  3. sobe somente o stack definido pela configuração nova.

Assim, containers órfãos e o Compose antigo não ficam ativos na VPS. A primeira execução após atualizar esta CLI grava esse estado sem alterar o comportamento normal.

Projetos já existentes que precisam trocar o Compose imediatamente, antes de haver esse estado remoto, podem informar a origem uma única vez:

{
  "docker": {
    "projectName": "novo-nome",
    "composeFile": "compose.prod.yml",
    "previousProjectName": "nome-antigo",
    "previousComposeFile": "docker-compose.yml"
  }
}

Depois de um deploy bem-sucedido, remova previousProjectName e previousComposeFile: eles deixam de ser necessários.

Arquivos .env continuam protegidos por padrão. Use autodeploy deploy --include-env para enviar os .env locais: a CLI remove antes o symlink copiado da release anterior e grava o arquivo da nova release, sem sobrescrever shared/.env.

99-scrapper: .env e dados persistentes

Use deploy.config.99-scrapper.example.json como configuração do projeto. O exemplo mantém data/ fora do manifest, do upload e da cópia entre releases. Portanto SQLite legado, sessão do WhatsApp e perfis do Chromium só podem existir em /opt/apps/99-scrapper/shared/data.

O arquivo de segredos existe somente na VPS, em /opt/apps/99-scrapper/.env, e a própria CLI o cria no primeiro deploy, a partir do .env local, com modo 600. Não há passo manual no servidor. Nos deploys seguintes ele é mantido como está — a VPS é a fonte da verdade dele — e responder que sim à pergunta sobre .env é o que autoriza sobrescrevê-lo com o local. A release recebe apenas um symlink para ele. Ele precisa conter valores reais para:

POSTGRES_DB=scrapper
POSTGRES_USER=scrapper
POSTGRES_PASSWORD=<defina-uma-senha-forte-na-vps>
POSTGRES_HOST_PORT=5433
APP_DATA_DIR=/opt/apps/99-scrapper/shared/data

Com docker.envFile configurado, antes de apontar current para a nova release a CLI exporta esse arquivo, confirma que todas essas variáveis não estão vazias e executa docker compose --env-file /opt/apps/99-scrapper/.env -f <nova-release>/docker-compose.yml config --quiet. Só depois executa o up -d --build. A release recebe apenas um symlink de .env para esse arquivo persistente, para manter compatibilidade com env_file: .env no Compose.

About

CLI em JavaScript puro para deploy incremental e seguro em VPS Linux. Funciona diretamente no PowerShell: a máquina local precisa apenas de Node.js e de uma chave SSH; não usa rsync, WSL, MSYS2, scp nem o executável ssh local.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages