CI/CD para scripts Bash: validação automatizada
1. Fundamentos da Integração Contínua para Shell Scripts
1.1. Por que aplicar CI/CD a scripts Bash? Riscos comuns em ambientes de produção
Scripts Bash são frequentemente tratados como "código descartável", mas em ambientes de produção, um erro de sintaxe ou uma variável não sanitizada pode causar falhas catastróficas. Aplicar CI/CD a scripts Bash reduz riscos como:
- Quebra de pipelines de deploy devido a comandos mal formatados
- Vazamento de dados sensíveis por expansão incorreta de variáveis
- Inconsistências entre ambientes (desenvolvimento vs produção)
- Dificuldade de rastreamento de alterações e rollback
1.2. Ferramentas de pipeline: GitHub Actions, GitLab CI, Jenkins e alternativas leves
Para scripts Bash, ferramentas leves são preferíveis. Exemplos:
# GitHub Actions - .github/workflows/validate.yml
name: Validate Bash Scripts
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
run: shellcheck scripts/*.sh
# GitLab CI - .gitlab-ci.yml
stages:
- validate
shellcheck:
stage: validate
script:
- shellcheck scripts/*.sh
1.3. Estrutura de repositório ideal: organização de diretórios e versionamento semântico
projeto-bash/
├── scripts/ # Scripts principais
│ ├── deploy.sh
│ └── backup.sh
├── tests/ # Testes BATS
│ ├── test_deploy.bats
│ └── helpers.bash
├── lib/ # Bibliotecas compartilhadas
│ └── utils.sh
├── .github/ # CI/CD configs
│ └── workflows/
├── CHANGELOG.md
└── VERSION # Exemplo: 1.2.3
Versionamento semântico (MAJOR.MINOR.PATCH) aplicado a scripts permite rastrear mudanças críticas.
2. Análise Estática e Linting Automatizado
2.1. ShellCheck: configuração de regras, integração em pipelines e tratamento de falsos positivos
ShellCheck é a ferramenta padrão para análise estática de Bash. Integração básica:
# Instalação
sudo apt install shellcheck
# Uso em pipeline
shellcheck --severity=style scripts/*.sh
Para ignorar falsos positivos:
# shellcheck disable=SC2086
echo $var # SC2086: Double quote to prevent globbing
2.2. Verificação de sintaxe com bash -n e detecção de erros comuns
# Verificação rápida de sintaxe
bash -n scripts/deploy.sh || echo "Erro de sintaxe detectado"
# Exemplo de erro comum: variável não definida
#!/bin/bash
set -u # Erro se variável não definida
echo "$nome"
2.3. Linters complementares: shfmt para formatação consistente e bashate para boas práticas
# shfmt - formatação automática
shfmt -w scripts/*.sh
# bashate - verificação de estilo
pip install bashate
bashate -i E006 scripts/*.sh # Ignora regra E006 (linhas muito longas)
3. Testes Unitários e de Integração com BATS
3.1. BATS (Bash Automated Testing System): instalação, estrutura de testes e asserções
# Instalação
git clone https://github.com/bats-core/bats-core.git
cd bats-core && ./install.sh /usr/local
# Exemplo de teste - test_utils.bats
#!/usr/bin/env bats
setup() {
source ../lib/utils.sh
}
@test "funcao_soma retorna soma correta" {
run funcao_soma 2 3
[ "$output" -eq 5 ]
}
@test "funcao_valida_email rejeita formato invalido" {
run funcao_valida_email "invalido"
[ "$status" -eq 1 ]
}
3.2. Mocking de comandos externos e isolamento de funções com stub/bats-mock
# bats-mock - simular comandos externos
load 'bats-mock/stub'
@test "deploy chama ssh corretamente" {
stub ssh "echo 'comando ssh executado'"
run scripts/deploy.sh
[ "$output" = "comando ssh executado" ]
unstub ssh
}
3.3. Execução paralela de testes e geração de relatórios
# Execução paralela
bats --jobs 4 tests/*.bats
# Relatório JUnit XML (usando bats-report)
bats --formatter junit tests/*.bats > report.xml
4. Validação de Segurança e Boas Práticas
4.1. Análise dinâmica com ShellCheck --severity=error e regras customizadas
# Foco apenas em erros críticos
shellcheck --severity=error scripts/*.sh
# Regras customizadas via .shellcheckrc
cat > .shellcheckrc << EOF
disable=SC2154 # Variável referenciada mas não atribuída
enable=all
EOF
4.2. Verificação de injeção de comandos, variáveis não sanitizadas e uso seguro de eval
# Perigoso: injeção de comando
eval "echo $user_input" # Evitar
# Seguro: usar arrays
args=("$user_input")
echo "${args[@]}"
# Validação de entrada
sanitize_input() {
local input="$1"
if [[ "$input" =~ [^a-zA-Z0-9_] ]]; then
echo "Entrada inválida" >&2
return 1
fi
}
4.3. Scripts de hardening: validação de permissões, shebang correto e tratamento de signals
#!/bin/bash
set -euo pipefail # Modo estrito
trap 'cleanup' EXIT SIGINT SIGTERM
cleanup() {
rm -f /tmp/temp_file
echo "Cleanup executado"
}
# Validação de permissões
if [ "$(stat -c %a scripts/deploy.sh)" -ne 755 ]; then
echo "Permissão incorreta" >&2
exit 1
fi
5. Automação de Build e Empacotamento
5.1. Compilação de scripts em binários com shc ou bash2exe
# shc - compilar script para binário
shc -f scripts/deploy.sh -o deploy.bin
# bash2exe (alternativa)
pip install bash2exe
bash2exe scripts/deploy.sh -o deploy.exe
5.2. Geração de documentação automática: extração de help text e man pages
# Extrair help text
grep -A999 '^# HELP' scripts/deploy.sh | sed 's/^# //' > docs/deploy.md
# Gerar man page
cat > docs/deploy.1 << EOF
.TH DEPLOY 1 "2024-01-01" "1.0" "Bash Scripts"
.SH NAME
deploy \- Automated deployment script
.SH SYNOPSIS
deploy [options]
EOF
5.3. Criação de pacotes distribuíveis no pipeline
# Pipeline GitLab CI para criar pacote deb
stages:
- build
build-deb:
stage: build
script:
- mkdir -p package/usr/local/bin
- cp scripts/deploy.sh package/usr/local/bin/
- dpkg-deb --build package deploy-1.0.0.deb
artifacts:
paths:
- deploy-1.0.0.deb
6. Deploy Contínuo e Monitoramento
6.1. Estratégias de deploy: ambientes staging vs produção, rollback automático
#!/bin/bash
# scripts/deploy.sh
ENV="${1:-staging}"
VERSION=$(cat VERSION)
deploy_staging() {
scp "deploy-${VERSION}.deb" user@staging-server:/tmp/
ssh user@staging-server "dpkg -i /tmp/deploy-${VERSION}.deb"
}
deploy_production() {
deploy_staging # Primeiro testa em staging
scp "deploy-${VERSION}.deb" user@prod-server:/tmp/
ssh user@prod-server "dpkg -i /tmp/deploy-${VERSION}.deb"
}
rollback() {
local prev_version="$1"
scp "deploy-${prev_version}.deb" user@prod-server:/tmp/
ssh user@prod-server "dpkg -i /tmp/deploy-${prev_version}.deb"
}
6.2. Validação pós-deploy: testes de fumaça e health checks com scripts Bash
#!/bin/bash
# tests/smoke_test.sh
check_service() {
if curl -f http://localhost:8080/health; then
echo "Health check OK"
return 0
else
echo "Health check FAILED" >&2
return 1
fi
}
check_version() {
local expected="$1"
local actual=$(curl -s http://localhost:8080/version)
[ "$actual" = "$expected" ]
}
6.3. Notificações e logging: integração com Slack, e-mail e sistemas de alerta
#!/bin/bash
notify_slack() {
local message="$1"
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
https://hooks.slack.com/services/TOKEN
}
log_event() {
local level="$1"
local msg="$2"
echo "[$(date +%Y-%m-%dT%H:%M:%S)] [$level] $msg" >> /var/log/deploy.log
}
# Uso no pipeline
if ! deploy_production; then
notify_slack "Deploy falhou! Iniciando rollback..."
log_event "ERROR" "Deploy production falhou"
rollback "$PREV_VERSION"
fi
7. Manutenção e Evolução do Pipeline
7.1. Versionamento do pipeline: arquivos .gitlab-ci.yml, action.yml e Jenkinsfile
# .gitlab-ci.yml versionado
include:
- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
variables:
CI_DEBUG_TRACE: "false"
stages:
- lint
- test
- build
- deploy
lint:
stage: lint
script:
- shellcheck scripts/*.sh
7.2. Métricas de qualidade: cobertura de testes, tempo de execução e taxa de falhas
#!/bin/bash
# scripts/metrics.sh
calculate_coverage() {
local total=$(grep -c '^@test' tests/*.bats)
local passed=$(grep -c '^ok' test_report.tap)
echo "Cobertura: $((passed * 100 / total))%"
}
track_execution_time() {
local start=$(date +%s%N)
bats tests/*.bats
local end=$(date +%s%N)
echo "Tempo de execução: $(( (end - start) / 1000000 )) ms"
}
7.3. Atualização contínua: adaptação a novas versões do Bash e ferramentas auxiliares
# Verificar versão do Bash no pipeline
bash --version | grep -oP 'version \K[0-9]+\.[0-9]+'
# Atualizar ferramentas automaticamente
apt-get update && apt-get install -y shellcheck bats
Referências
- ShellCheck - Official Documentation — Ferramenta de análise estática para scripts Bash, com regras configuráveis e integração com pipelines CI/CD.
- BATS-Core: Bash Automated Testing System — Framework oficial para testes unitários e de integração em Bash, com suporte a asserções e mocking.
- GitHub Actions: Continuous Integration for Shell Scripts — Guia oficial da GitHub para configurar pipelines CI/CD que validam scripts Bash.
- GitLab CI/CD: Shell Script Validation — Documentação do GitLab para criação de pipelines com validação de scripts, incluindo exemplos práticos.
- OWASP: Command Injection Prevention for Shell Scripts — Guia de segurança para evitar injeção de comandos em scripts Bash, essencial para pipelines de validação.
- shfmt: Shell Formatter — Ferramenta de formatação automática para scripts Bash, útil para manter consistência de estilo no pipeline.
- bashate: Bash Style Guide Checker — Linter complementar para boas práticas de codificação em Bash, integrado a pipelines CI/CD.