Scripts de automação de deploy simples
1. Introdução à automação de deploy com Bash
1.1 O que é deploy automatizado e por que usar Bash?
Deploy automatizado é o processo de implantação de uma aplicação em ambiente de produção ou homologação sem intervenção manual repetitiva. O Bash é a escolha natural para essa tarefa por estar presente em praticamente todos os servidores Linux, ser maduro, confiável e permitir controle granular sobre cada etapa do processo.
Enquanto ferramentas como Ansible, Docker e Kubernetes oferecem abstrações poderosas, scripts Bash continuam sendo a base para tarefas rápidas, deploys simples e como camada de fallback em pipelines mais complexos.
1.2 Cenários comuns
Os scripts de deploy em Bash são amplamente utilizados para:
- Aplicações web estáticas (HTML, CSS, JavaScript)
- APIs REST em Node.js, PHP ou Python
- Microsserviços leves com poucas dependências
- Atualização de arquivos de configuração em servidores
1.3 Pré-requisitos
Antes de começar, certifique-se de ter:
- Acesso SSH ao servidor de destino
- rsync instalado (para transferência eficiente)
- git configurado com chaves SSH
- Variáveis de ambiente definidas (ou arquivo .env)
2. Estrutura básica de um script de deploy
2.1 Shebang e cabeçalho
#!/usr/bin/env bash
# deploy.sh - Script de deploy automatizado
# Autor: Seu Nome
# Data: 2025-03-01
# Versão: 1.0.0
set -euo pipefail
2.2 Variáveis de configuração
# Configurações do projeto
PROJECT_NAME="minha-app"
REMOTE_USER="deploy"
REMOTE_HOST="192.168.1.100"
REMOTE_PATH="/var/www/$PROJECT_NAME"
LOCAL_PATH="/home/user/projetos/$PROJECT_NAME"
BRANCH_DEFAULT="main"
RELEASES_DIR="$REMOTE_PATH/releases"
CURRENT_LINK="$REMOTE_PATH/current"
PREVIOUS_LINK="$REMOTE_PATH/previous"
KEEP_RELEASES=5
2.3 Funções principais
deploy() {
echo "[INFO] Iniciando deploy de $PROJECT_NAME..."
# Lógica principal será detalhada nas próximas seções
}
rollback() {
echo "[INFO] Executando rollback..."
# Reverte para versão anterior
}
check_status() {
echo "[INFO] Verificando status do deploy..."
# Valida se o deploy foi bem-sucedido
}
# Tratamento de erros
trap 'echo "[ERRO] Falha no deploy. Executando rollback..."; rollback; exit 1' ERR
3. Deploy via Git (clone e pull)
3.1 Clonagem em diretório temporário
deploy_via_git() {
local release_dir="$RELEASES_DIR/$(date +%Y%m%d_%H%M%S)"
local branch="${1:-$BRANCH_DEFAULT}"
echo "[INFO] Clonando branch $branch..."
git clone --depth 1 --branch "$branch" \
"git@github.com:usuario/$PROJECT_NAME.git" "$release_dir"
echo "[INFO] Release criada em: $release_dir"
}
3.2 Atualização incremental
deploy_via_pull() {
local current_release=$(readlink -f "$CURRENT_LINK" 2>/dev/null || echo "")
if [ -n "$current_release" ] && [ -d "$current_release/.git" ]; then
echo "[INFO] Atualizando release existente..."
cd "$current_release"
git fetch origin
git checkout "$BRANCH_DEFAULT"
git pull origin "$BRANCH_DEFAULT"
else
deploy_via_git "$BRANCH_DEFAULT"
fi
}
3.3 Validação de integridade
validate_release() {
local release_dir="$1"
# Verificar se arquivos essenciais existem
if [ ! -f "$release_dir/package.json" ] && [ ! -f "$release_dir/index.php" ]; then
echo "[ERRO] Arquivos essenciais não encontrados!"
return 1
fi
# Verificar hash do commit
local expected_hash=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
echo "[INFO] Hash do deploy: $expected_hash"
}
4. Deploy com rsync e sincronização remota
4.1 Sincronização básica
sync_with_rsync() {
local source_dir="$1"
local target_dir="$2"
rsync -avz --progress \
-e "ssh -p 22" \
--exclude=".git" \
--exclude="node_modules" \
--exclude=".env" \
--exclude="cache" \
--exclude="*.log" \
"$source_dir/" \
"$REMOTE_USER@$REMOTE_HOST:$target_dir/"
}
4.2 Exclusão de diretórios desnecessários
EXCLUDE_LIST=(
".git"
"node_modules"
"vendor"
".cache"
".npm"
"tests"
"*.md"
"docker-compose.yml"
".gitignore"
)
build_exclude_args() {
local args=""
for pattern in "${EXCLUDE_LIST[@]}"; do
args+=" --exclude=$pattern"
done
echo "$args"
}
sync_precise() {
local exclude_args=$(build_exclude_args)
rsync -avz --delete --checksum \
$exclude_args \
-e "ssh -i ~/.ssh/deploy_key" \
"$LOCAL_PATH/" \
"$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/current/"
}
4.3 Uso de --delete e --checksum
sync_with_checksum() {
echo "[INFO] Sincronizando com verificação de checksum..."
rsync -avz --delete --checksum \
--progress \
--partial \
--timeout=30 \
"$LOCAL_PATH/" \
"$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"
echo "[INFO] Sincronização concluída."
}
5. Gerenciamento de versões e rollback
5.1 Links simbólicos para versões
create_symlinks() {
local release_dir="$1"
# Atualizar link 'previous' com o atual
if [ -L "$CURRENT_LINK" ]; then
ln -sfn "$(readlink -f "$CURRENT_LINK")" "$PREVIOUS_LINK"
fi
# Criar novo link 'current'
ln -sfn "$release_dir" "$CURRENT_LINK"
echo "[INFO] Link 'current' aponta para: $release_dir"
}
5.2 Rollback automático
rollback() {
echo "[WARN] Executando rollback..."
if [ -L "$PREVIOUS_LINK" ]; then
local previous_release=$(readlink -f "$PREVIOUS_LINK")
ln -sfn "$previous_release" "$CURRENT_LINK"
echo "[INFO] Rollback realizado para: $previous_release"
else
echo "[ERRO] Nenhuma versão anterior disponível para rollback!"
exit 1
fi
# Reiniciar serviço após rollback
restart_service
}
5.3 Manutenção de histórico
cleanup_old_releases() {
echo "[INFO] Limpando releases antigos..."
local releases=($(ls -1t "$RELEASES_DIR" 2>/dev/null))
local count=${#releases[@]}
if [ "$count" -gt "$KEEP_RELEASES" ]; then
local to_remove=$((count - KEEP_RELEASES))
for ((i=KEEP_RELEASES; i<count; i++)); do
rm -rf "${RELEASES_DIR}/${releases[$i]}"
echo "[INFO] Removido release antigo: ${releases[$i]}"
done
fi
}
6. Hooks e pós-deploy
6.1 Comandos pós-sincronização
post_deploy_tasks() {
local release_dir="$1"
echo "[INFO] Executando tarefas pós-deploy..."
ssh "$REMOTE_USER@$REMOTE_HOST" << EOF
cd "$release_dir"
# Instalar dependências
if [ -f "package.json" ]; then
npm install --production
fi
if [ -f "composer.json" ]; then
composer install --no-dev
fi
# Executar migrações
if [ -f "artisan" ]; then
php artisan migrate --force
fi
# Build da aplicação
if [ -f "package.json" ]; then
npm run build
fi
EOF
}
6.2 Notificações
send_notification() {
local status="$1"
local message="$2"
# Notificação via Slack
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"[$status] Deploy $PROJECT_NAME: $message\"}" \
"https://hooks.slack.com/services/SEU/WEBHOOK/AQUI"
# Notificação por email (opcional)
if command -v mail &> /dev/null; then
echo "Deploy $PROJECT_NAME: $status - $message" | \
mail -s "[Deploy] $PROJECT_NAME - $status" admin@exemplo.com
fi
}
6.3 Limpeza de diretórios temporários
cleanup() {
echo "[INFO] Limpando diretórios temporários..."
# Remover diretórios temporários locais
rm -rf /tmp/deploy_*
# Limpar logs antigos no servidor
ssh "$REMOTE_USER@$REMOTE_HOST" "find $REMOTE_PATH/logs -name '*.log' -mtime +7 -delete"
cleanup_old_releases
}
7. Exemplo prático completo
7.1 Script completo para deploy de aplicação Node.js
#!/usr/bin/env bash
set -euo pipefail
# Configurações
PROJECT_NAME="api-node"
REMOTE_USER="deploy"
REMOTE_HOST="192.168.1.100"
REMOTE_PATH="/var/www/$PROJECT_NAME"
BRANCH="main"
RELEASES_DIR="$REMOTE_PATH/releases"
CURRENT_LINK="$REMOTE_PATH/current"
PREVIOUS_LINK="$REMOTE_PATH/previous"
# Funções
deploy() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local release_dir="$RELEASES_DIR/$timestamp"
echo "=== Iniciando deploy de $PROJECT_NAME ==="
# Etapa 1: Clonar repositório
echo "[1/5] Clonando repositório..."
git clone --depth 1 --branch "$BRANCH" \
"git@github.com:usuario/$PROJECT_NAME.git" "$release_dir"
# Etapa 2: Instalar dependências
echo "[2/5] Instalando dependências..."
cd "$release_dir"
npm install --production
# Etapa 3: Build
echo "[3/5] Executando build..."
npm run build
# Etapa 4: Sincronizar com servidor
echo "[4/5] Sincronizando com servidor remoto..."
rsync -avz --delete \
--exclude=".git" --exclude="node_modules" --exclude=".env" \
"$release_dir/" \
"$REMOTE_USER@$REMOTE_HOST:$release_dir/"
# Etapa 5: Atualizar links e reiniciar serviço
echo "[5/5] Atualizando links e reiniciando..."
ssh "$REMOTE_USER@$REMOTE_HOST" "
ln -sfn $release_dir $CURRENT_LINK
pm2 restart $PROJECT_NAME
"
echo "=== Deploy concluído com sucesso! ==="
}
rollback() {
echo "=== Executando rollback ==="
ssh "$REMOTE_USER@$REMOTE_HOST" "
if [ -L $PREVIOUS_LINK ]; then
ln -sfn \$(readlink -f $PREVIOUS_LINK) $CURRENT_LINK
pm2 restart $PROJECT_NAME
echo 'Rollback realizado com sucesso'
else
echo 'Nenhuma versão anterior disponível'
exit 1
fi
"
}
# Execução
case "${1:-deploy}" in
deploy)
deploy
;;
rollback)
rollback
;;
*)
echo "Uso: $0 {deploy|rollback}"
exit 1
;;
esac
7.2 Execução com argumentos
# Executar deploy
./deploy.sh
# Executar rollback
./deploy.sh rollback
# Exemplo com variáveis de ambiente
ENV=production BRANCH=main ./deploy.sh
7.3 Saída esperada
=== Iniciando deploy de api-node ===
[1/5] Clonando repositório...
Cloning into '/var/www/api-node/releases/20250301_143022'...
[2/5] Instalando dependências...
added 1250 packages in 15s
[3/5] Executando build...
Build completed in 8s
[4/5] Sincronizando com servidor remoto...
sent 2456789 bytes received 1234 bytes 12345.67 bytes/sec
[5/5] Atualizando links e reiniciando...
pm2 restarted api-node
=== Deploy concluído com sucesso! ===
8. Boas práticas e considerações finais
8.1 Versionamento do script de deploy
Mantenha o script de deploy no mesmo repositório da aplicação ou em um repositório separado de infraestrutura. Isso garante rastreabilidade e facilita a colaboração da equipe.
# Exemplo de versionamento
git tag v1.0.0-deploy-script
git push origin v1.0.0-deploy-script
8.2 Segurança
Nunca hardcode senhas ou chaves no script. Use:
- Variáveis de ambiente
- Arquivos .env protegidos
- Gerenciadores de secrets como HashiCorp Vault
- Chaves SSH com passphrase e ssh-agent
# Exemplo seguro
export DEPLOY_KEY=$(cat ~/.ssh/deploy_key)
ssh -i <(echo "$DEPLOY_KEY") user@host
8.3 Integração com CI/CD
Para ambientes mais complexos, integre seu script Bash com:
- GitHub Actions: Execute o script como step em workflows
- GitLab CI: Utilize o script em jobs do pipeline
- Jenkins: Chame o script via shell step
# Exemplo de integração com GitHub Actions
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Execute deploy script
run: |
chmod +x deploy.sh
./deploy.sh
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
O Bash continua sendo uma ferramenta indispensável para automação de deploy, especialmente em cenários simples e controlados. Com as técnicas apresentadas neste artigo, você pode construir scripts robustos, seguros e de fácil manutenção para suas aplicações.
Referências
- Documentação Oficial do Bash (GNU) — Guia completo sobre sintaxe, built-ins e boas práticas do Bash.
- Advanced Bash-Scripting Guide — Tutorial avançado com exemplos práticos de scripts para automação.
- rsync Man Page — Documentação detalhada do rsync, essencial para sincronização remota.
- Git SCM - Documentação Oficial — Referência completa sobre comandos git para automação de deploy.
- GitHub Actions - Workflow Syntax — Guia para integrar scripts Bash em pipelines de CI/CD.
- DigitalOcean - How to Use rsync — Tutorial prático sobre rsync para deploy de aplicações.
- ShellCheck - Shell Script Analysis Tool — Ferramenta online para análise estática de scripts Bash, evitando erros comuns.