npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

simple-ssh-autodeploy

v0.3.1

Published

CLI multiplataforma para deploy incremental via SSH/SFTP com releases e rollback

Readme

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.

Instalação

npm install
npm link
autodeploy deploy --dry-run
autodeploy deploy
autodeploy restart

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

autodeploy restart não envia arquivos nem cria release: ele reinicia a release apontada por current e executa o health check configurado. Em Docker, usa docker compose restart; em PM2, aplica o mesmo comando de reinício usado no deploy.

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.