Execução de testes de Shell em pipelines de integração contínua
Integre ShellCheck e Bats ao GitHub Actions para exigir verificações bem-sucedidas em cada alteração do Shell.
Execução de testes de Shell em pipelines de integração contínua é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.
Por que a CI é importante para scripts de Shell
Scripts de Shell são código — e, como todo código, merecem barreiras automatizadas de qualidade. Sem CI, um erro de digitação em um script de implantação pode chegar silenciosamente à produção e causar uma interrupção às 3h da manhã.
Uma pipeline de CI sólida para projetos Bash impõe duas coisas em cada solicitação de pull:
- Análise estática por meio do
ShellCheck— detecta erros de sintaxe, padrões inseguros e problemas de portabilidade POSIX antes mesmo de o script ser executado. - Testes unitários/de integração por meio do
Bats(Bash Automated Testing System) — executa suas funções e verifica se o comportamento está correto.
Juntos, eles formam uma rede de segurança que torna a refatoração mais tranquila e acelera a integração de novos colaboradores. Nesta lição, as duas ferramentas são integradas ao GitHub Actions, a plataforma gratuita de CI mais comum para projetos de código aberto e de equipes pequenas.
Introdução ao GitHub Actions para projetos de Shell
GitHub Actions é uma CI/CD orientada a eventos integrada ao GitHub. Um fluxo de trabalho é um arquivo YAML armazenado em .github/workflows/. Ele é acionado por eventos (push, pull_request etc.) e executa tarefas em executores hospedados.
Conceitos principais que precisa conhecer:
on:— o acionador (por exemplo,push,pull_request)jobs:— unidades paralelas de trabalho, cada uma em uma VM novasteps:— comandos de Shell sequenciais ou ações reutilizáveis dentro de uma tarefaruns-on:— a imagem do executor (usamosubuntu-latest)
Os arquivos de fluxo de trabalho precisam ser confirmados no repositório. O GitHub os detecta automaticamente — nenhuma configuração externa é necessária.
# Minimal skeleton — .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
shell-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Steps go here"Instalando o ShellCheck em um fluxo de trabalho
O ShellCheck vem pré-instalado nos executores ubuntu-latest, portanto, na maioria dos casos, não é necessário executar nenhuma etapa de instalação. No entanto, a versão pré-instalada pode estar atrasada em relação à versão mais recente. Para obter compilações reproduzíveis, fixe uma versão específica.
Duas estratégias de instalação:
- Usar o binário pré-instalado — é a opção mais simples e suficiente para a maioria dos projetos.
- Instalar uma versão fixada por meio do arquivo tarball oficial da versão do GitHub — garante a mesma versão do analisador localmente e na CI.
A etapa abaixo mostra a abordagem com versão fixada, usando uma cadeia de versão fixa armazenada como variável de ambiente, o que transforma as atualizações em uma alteração de uma linha.
# .github/workflows/ci.yml — ShellCheck install step
- name: Install ShellCheck
env:
SC_VERSION: v0.10.0
run: |
curl -sSfL \
"https://github.com/koalaman/shellcheck/releases/download/${SC_VERSION}/shellcheck-${SC_VERSION}.linux.x86_64.tar.xz" \
| tar -xJf - --strip-components=1 -C /usr/local/bin shellcheck-${SC_VERSION}/shellcheck
shellcheck --versionExecutando o ShellCheck em todos os scripts
Após a instalação, é necessário ter uma etapa que descubra e analise todos os scripts de Shell no repositório. Use find para localizar os arquivos e, em seguida, encaminhe-os para shellcheck.
Opções importantes:
-e SC2034— exclui uma regra específica (use com moderação e acompanhada de um comentário).--severity=warning— falha somente em avisos ou problemas mais graves (ignorando sugestões de estilo).-x— segue diretivassourcepara analisar também os arquivos incluídos.
Se shellcheck encontrar qualquer problema, ele termina com código diferente de zero, o que faz a etapa da CI falhar automaticamente — não é necessária lógica adicional.
# .github/workflows/ci.yml — ShellCheck lint step
- name: Lint shell scripts
run: |
# Find all .sh files and files with a bash/sh shebang
mapfile -t scripts < <(
find . -type f -name '*.sh' -not -path './.git/*'
)
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No shell scripts found — skipping.'
exit 0
fi
echo "Linting ${#scripts[@]} file(s)..."
shellcheck --severity=warning -x "${scripts[@]}"O que é o Bats e como ele funciona
O Bats (Bash Automated Testing System) é uma estrutura de testes para Bash compatível com TAP. Cada arquivo de teste é um arquivo .bats que contém blocos @test.
Um teste é aprovado quando seu corpo termina com código 0 e reprovado quando termina com código diferente de zero. O Bats fornece variáveis e funções auxiliares:
$status— código de saída do último comandorun.$output— saída combinada de stdout+stderr do último comandorun.$lines— matriz de linhas da saída.run <cmd>— executa um comando sem reprovar o teste quando a saída é diferente de zero.
O auxiliar run é essencial — sem ele, um comando com falha interromperia o teste antes que pudesse inspecionar $status.
#!/usr/bin/env bats
# tests/greet.bats
setup() {
# Runs before every @test block
source "${BATS_TEST_DIRNAME}/../lib/greet.sh"
}
@test "greet outputs hello with the given name" {
run greet "Alice"
[ "$status" -eq 0 ]
[ "$output" = "Hello, Alice!" ]
}
@test "greet fails when no argument is provided" {
run greet
[ "$status" -eq 1 ]
[[ "$output" == *"Usage"* ]]
}Instalando o Bats-Core por meio de um submódulo do Git
A maneira padrão de adicionar o Bats a um projeto é como um submódulo do Git. Isso fixa um commit específico, mantém a versão do executor idêntica à do desenvolvimento local e evita a dependência de gerenciadores de pacotes.
Execute estes comandos uma vez localmente e depois confirme o resultado:
git submodule add https://github.com/bats-core/bats-core test/batsgit submodule add https://github.com/bats-core/bats-support test/test_helper/bats-supportgit submodule add https://github.com/bats-core/bats-assert test/test_helper/bats-assert
Na CI, restaure os submódulos com actions/checkout@v4 e a opção submodules: recursive. A etapa abaixo mostra a configuração completa do checkout.
# .github/workflows/ci.yml — checkout with submodules
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive # restores bats-core + helpersExecutando testes do Bats na CI
Quando o Bats estiver disponível (por meio de um submódulo ou da instalação de um pacote), executar os testes exige um único comando. Aponte-o para um diretório, e o Bats descobrirá recursivamente todos os arquivos .bats com a opção --recursive.
A opção --formatter tap gera a saída no formato TAP (Test Anything Protocol), que muitos sistemas de CI analisam para gerar relatórios de testes. O formatador pretty padrão é melhor para leitura humana nos registros brutos.
Use --timing para identificar precocemente testes lentos — um teste que leva mais de 5 segundos geralmente indica uma chamada de rede indesejada ou um mock ausente.
# .github/workflows/ci.yml — Bats test step
- name: Run Bats tests
run: |
# If installed as a submodule:
./test/bats/bin/bats \
--recursive \
--timing \
tests/
# If installed via apt or brew (alternative):
# bats --recursive --timing tests/Um fluxo de trabalho completo: ShellCheck + Bats
Agora, combine tudo em um único arquivo de fluxo de trabalho pronto para produção. Estas são as boas práticas aplicadas:
- Duas tarefas separadas (
lintetest) são executadas em paralelo, proporcionando um retorno mais rápido. - A tarefa
testdeclaraneeds: lint, portanto os testes só são executados depois que a análise é aprovada — evitando desperdiçar minutos do executor com código obviamente quebrado. - Versões fixadas das ações (
@v4) evitam falhas inesperadas causadas por atualizações upstream. - Um bloco
permissions:restringe o token do fluxo de trabalho ao mínimo necessário.
# .github/workflows/ci.yml
name: Shell CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
lint:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
run: |
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
[[ ${#scripts[@]} -gt 0 ]] && shellcheck --severity=warning -x "${scripts[@]}"
test:
name: Bats Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Run tests
run: ./test/bats/bin/bats --recursive --timing tests/Armazenando dependências em cache para execuções mais rápidas
Quando os auxiliares do Bats ou outras ferramentas são instalados por um gerenciador de pacotes dentro do fluxo de trabalho, o armazenamento em cache acelera significativamente as execuções seguintes. O GitHub Actions fornece a ação actions/cache para isso.
Pontos principais para um armazenamento em cache eficaz:
- Use uma chave de cache que inclua o sistema operacional, o nome da ferramenta e um hash do arquivo de bloqueio — assim, o cache é invalidado automaticamente quando as dependências mudam.
- Um fallback
restore-keyspermite que o fluxo de trabalho use um cache desatualizado em vez de começar do zero quando não encontra uma correspondência. - Para submódulos do Git, o armazenamento em cache raramente é necessário, pois o checkout de submódulos é rápido. O cache é mais valioso para instalações de
npm,pipou de ferramentas compiladas.
# .github/workflows/ci.yml — cache step example
- name: Cache Bats npm helpers
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-bats-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-bats-
- name: Install helpers
run: npm ci # uses cache when availableProteção de ramificações: impondo verificações aprovadas
Uma fluxo de trabalho de CI que não bloqueia mesclagens é, no melhor dos casos, apenas consultivo. As regras de proteção de ramificações do GitHub transformam suas verificações em barreiras obrigatórias.
Para configurá-las: acesse Settings → Branches → Add rule para main e habilite:
- Require status checks to pass before merging — selecione ShellCheck e Bats Tests pelo nome.
- Require branches to be up to date before merging — impede que uma PR aprovada em uma base desatualizada integre código quebrado.
- Do not allow bypassing the above settings — aplica as regras inclusive aos administradores do repositório.
Com essas regras em vigor, o único caminho para a mesclagem é uma PR com todas as tarefas da CI aprovadas — exatamente a rede de segurança desejada.
Depurando etapas da CI com falha localmente
Quando uma execução da CI falha, o ciclo de correção mais rápido é reproduzir a falha localmente antes de enviar outra confirmação. Duas técnicas:
- Execute os comandos exatos da etapa com falha no seu terminal — a CI executa um Shell simples, portanto os comandos podem ser reproduzidos por copiar e colar.
- Use
act— uma ferramenta que executa fluxos de trabalho do GitHub Actions localmente dentro do Docker, oferecendo a correspondência mais próxima possível com o ambiente do executor hospedado.
Uma fonte comum de falhas que ocorrem apenas na CI é a incompatibilidade entre versões de ferramentas no seu Mac (por exemplo, find BSD no macOS em comparação com find GNU no Ubuntu). Sempre teste com opções --posix ou use act para executar localmente a imagem do Ubuntu.
#!/usr/bin/env bash
# run_ci_locally.sh — mimic the CI lint step on your machine
set -euo pipefail
echo '=== ShellCheck ==='
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No .sh files found.'
else
shellcheck --severity=warning -x "${scripts[@]}"
echo "Linted ${#scripts[@]} file(s) — OK"
fi
echo '=== Bats ==='
./test/bats/bin/bats --recursive --timing tests/Verificação de conhecimento: conceitos de pipeline de CI
Teste sua compreensão da integração do ShellCheck e do Bats ao GitHub Actions.
Recapitulação: CI de Shell com ShellCheck e Bats
Nesta lição, você criou uma pipeline de CI completa para projetos Bash usando o GitHub Actions. Veja o que foi abordado:
- Fundamentos do GitHub Actions — o YAML do fluxo de trabalho fica em
.github/workflows/, é acionado por push e pull_request e executa tarefas em executoresubuntu-latest. - ShellCheck — pré-instalado nos executores Ubuntu; use
findpara descobrir scripts e--severity=warning -xcomo uma barreira prática de análise. - Bats via submódulo — fixe o bats-core e os auxiliares como submódulos do Git; restaure-os na CI com
submodules: recursivena ação de checkout. - Ordenação das tarefas — use
needs:para que os testes sejam executados somente depois da aprovação da análise, mantendo um retorno rápido e evitando desperdício de processamento. - Proteção de ramificações — imponha verificações de status nas configurações do GitHub para que nenhuma PR seja integrada sem uma CI aprovada.
- Reprodução local — copie os comandos da CI diretamente para o terminal ou use
actpara depurar falhas sem confirmações adicionais.
Com essa pipeline em vigor, cada alteração no Shell é validada automaticamente antes de chegar à sua ramificação principal.
Perguntas Frequentes
A aula “Execução de testes de Shell em pipelines de integração contínua” é grátis?
Sim — o texto completo de “Execução de testes de Shell em pipelines de integração contínua” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que vou aprender em “Execução de testes de Shell em pipelines de integração contínua”?
Integre ShellCheck e Bats ao GitHub Actions para exigir verificações bem-sucedidas em cada alteração do Shell. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DevOps Bootcamp?
Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Execução de testes de Shell em pipelines de integração contínua”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DevOps Bootcamp?
Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Teste unitário de funções com Bats-core
- Simulação de comandos e criação de substitutos para ferramentas externas
- Dispositivos de teste, ambientes temporários e cobertura
- Execução de testes de Shell em pipelines de integração contínua